Skip to content

feat: scripture protection plugin, package install script, and library bump to v1.1.6 - #13

Merged
bcordis merged 7 commits into
mainfrom
feat/system-plugin-disarm
Jul 30, 2026
Merged

feat: scripture protection plugin, package install script, and library bump to v1.1.6#13
bcordis merged 7 commits into
mainfrom
feat/system-plugin-disarm

Conversation

@bcordis

@bcordis bcordis commented Jul 29, 2026

Copy link
Copy Markdown
Member

Problem

lib_cwmscripture ≤ 1.1.4 declared <uninstall><sql> pointing at DROP TABLE statements. Joomla's LibraryAdapter uninstalls the installed library before writing the new one, so that SQL ran on every update and destroyed every locally downloaded translation.

1.1.6 removed the block — but that only protects the upgrade after this one. The uninstall SQL Joomla executes during an update is the one already in JPATH_LIBRARIES, belonging to the installed version. A site on 1.1.4 is destroyed by the very update that fixes it.

Nothing in the incoming package can prevent it

InstallerAdapter::install() runs in this order (verified against Joomla 5.4.7):

  1. setupInstallPaths()
  2. checkExtensionInFilesystem() ← triggers the old library's uninstall, executing its DROP TABLEs
  3. setupScriptfile()
  4. triggerManifestScript('preflight')

The incoming script file is not loaded until step 4. A library-side preflight disarm is impossible. The only place to intervene is code already resident on the site, running before the installer.

What this PR contains

plg_system_cwmscripture — new system plugin that blanks the legacy SQL on onAfterRoute while the admin is on com_installer, before they click Update. plg_system_proclaim does this for Proclaim sites; this covers stacks that ship the library without Proclaim (Living Word, third-party consumers), which is the case a library-only update leaves completely unprotected.

Detection distinguishes executable statements from mentions inside -- comments, so the already-disarmed 1.1.5/1.1.6 files — which describe the problem in prose and therefore contain the words "DROP TABLE" — are left alone:

Version Detected as armed
v1.1.4 yes → disarmed
v1.1.5 no (comment only)
v1.1.6 no (comment only)

build/script.install.php — the package install script, wired via <scriptfile> and package.installer. Blanks the installed library's legacy SQL in preflight() before the library child installs, and dropScriptureTablesIfLastConsumer() asks ConsumerRegistry::installedExcluding() whether anything outside this package still uses the shared tables before removing them.

enablePlugin('cwmscripture', 'system') in postflight — Joomla installs plugins disabled, and a disabled protection plugin never fires. Shipping it off by default would leave the protection absent on exactly the sites least recently touched — the ones most likely to still carry a destructive 1.1.4 library. Only flips a plugin that is currently disabled, so an admin who deliberately turned it off is not overridden.

blockChildUninstall in build/pkg_cwmscripture.xml, matching pkg_proclaim.xml, so the library cannot be pulled out from under the plugins shipped alongside it.

Library submodule → v1.1.6. Until this moved, the package shipped a pre-fix library and re-armed the destructive SQL on every site it touched — observed directly in Joomla-Bible-Study/lib_cwmscripture#17 run 3. Supersedes #11, which targeted v1.1.5; that version still declares <uninstall><sql> and has neither ConsumerRegistry nor the uninstall guard that this install script now calls.

Verification

  • php -l clean on all PHP; xmllint clean on the package manifest; cwm-build.config.json parses
  • Disarm detection checked against all three released SQL files (table above)

Not runtime-tested. The plugin has not been installed and exercised on a site, and the enablePlugin path has not been run. The test that matters is a 1.1.4 → 1.1.6 library-only update on a site with seeded translations, confirming the plugin disarms before the update and the translations survive. Proclaim's composer test:upgrade covers the package path but not this one.

Related: Joomla-Bible-Study/lib_cwmscripture#21, Joomla-Bible-Study/CWMLivingWord#85, #12.

🤖 Generated with Claude Code

bcordis and others added 2 commits July 29, 2026 13:54
lib_cwmscripture up to 1.1.4 declared <uninstall><sql> pointing at DROP
TABLE statements, and Joomla's LibraryAdapter uninstalls the installed
library before writing the new one — so that SQL ran on every UPDATE and
destroyed every locally downloaded translation.

1.1.6 removed the block, but that only protects the upgrade *after* this
one: the uninstall SQL Joomla executes during an update is the one
already in JPATH_LIBRARIES, belonging to the installed version. A site on
1.1.4 is still destroyed by the very update that fixes it.

Nothing inside the incoming package can prevent that.
InstallerAdapter::install() calls checkExtensionInFilesystem() — which
triggers the old library's uninstall — before triggerManifestScript(
'preflight') loads the new script file. By the time any incoming code
runs the tables are gone. Verified against Joomla 5.4.7.

The only remaining place to intervene is code already resident on the
site, running before the installer. plg_system_proclaim does this for
Proclaim sites; the library carries its own update server and is listed
separately in the Update Manager, so a library-only update runs no
package code at all. This plugin covers the stacks that ship the library
without Proclaim — Living Word, and any third-party consumer.

It blanks the file on onAfterRoute while the admin is on com_installer,
before they click Update. Detection distinguishes executable statements
from mentions inside -- comments, so the already-disarmed 1.1.5 and 1.1.6
files (which describe the problem in prose) are left alone; checked
against all three released versions.

The file is rewritten rather than deleted: an older manifest still
references it by path and parseSQLFiles() resolves it against the
installed extension root.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… bump the library

Reconciles the package work that was split across two checkouts and adds
the missing piece that made plg_system_cwmscripture inert.

Brings in from the working branch:

- build/script.install.php — blanks the installed library's legacy
  uninstall SQL in preflight, before the library child installs, plus
  dropScriptureTablesIfLastConsumer() which asks the library's
  ConsumerRegistry whether anything outside this package still uses the
  shared tables before removing them
- <scriptfile> and blockChildUninstall in build/pkg_cwmscripture.xml
- package.installer and script.install.php in cwm-build.config.json
- the CLAUDE.md note explaining why the preflight disarm exists

Adds enablePlugin('cwmscripture', 'system') to postflight. Joomla installs
plugins disabled, and a disabled plg_system_cwmscripture never fires — its
whole job is to disarm the legacy SQL before an admin runs a library-only
update, so shipping it off by default would leave the protection absent on
exactly the sites least recently touched, which are the ones most likely
to still carry a destructive 1.1.4 library. It only flips a plugin that is
currently disabled, so an admin who deliberately turned it off is not
overridden on the next update.

Bumps the nested libraries/lib_cwmscripture pointer from 3babb2a
(v1.1.4-14) to v1.1.6, which carries the manifest with no <uninstall><sql>
at all, ConsumerRegistry, and the uninstall dependency guard. Until this
moved, the package shipped a pre-fix library and re-armed the destructive
SQL on every site it touched. Supersedes PR #11, which targeted v1.1.5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bcordis bcordis changed the title feat(plugin): add plg_system_cwmscripture to protect downloaded Bibles feat: scripture protection plugin, package install script, and library bump to v1.1.6 Jul 29, 2026
bcordis and others added 2 commits July 29, 2026 14:14
Extracts the armed/not-armed decision into a pure, public
Cwmscripture::sqlIsArmed() so it can be tested without a real
JPATH_LIBRARIES path, and covers it.

The subtle case is the one the naive check gets wrong: the already-
disarmed 1.1.5 and 1.1.6 files describe the old behaviour in prose, so
they contain the words "DROP TABLE" inside comments. A substring search
flags them as dangerous and rewrites files that are perfectly fine.
Tests pin both directions plus lowercase, indentation, CRLF endings, an
unrelated DELETE, and a live statement appended below the reassuring
comment block.

Registers the plugin namespace for autoloading and adds its src to the
phpunit source set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Joomla tracks no dependencies between extensions, so lib_cwmscripture cannot
discover who relies on it. Both plugins were protected only by the library's
hardcoded FIRST_PARTY list, which exists as a fallback for versions predating
the registry API rather than as the intended mechanism.

Registering means the library refuses to be uninstalled while either plugin is
installed, and the shared #__bsms_bible_* tables are not dropped while they
still read them.

Uses the library's own entry point (new in 1.1.7), so each plugin needs four
lines rather than the ~50 that hand-rolled autoloading, guards and error
handling would take. The group is passed explicitly: it is part of the
registry's unique key, and a plugin registered without it never matches on
lookup.

Closes #12

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ti98Cc63bmWcXva2G4GdNq
@bcordis

bcordis commented Jul 30, 2026

Copy link
Copy Markdown
Member Author

Added d25a3db: both plugins now register themselves with the library's consumer registry, closing #12.

Uses the entry point added in lib_cwmscripture 1.1.7 (Joomla-Bible-Study/lib_cwmscripture#26), so each plugin needs four lines rather than the ~50 that hand-rolled autoloading, guards and error handling would take:

if (!is_file($helper = JPATH_LIBRARIES . '/cwmscripture/src/consumer.php')) {
    return;
}

$registry = require $helper;
$registry->register('scripturelinks', 'plugin', 'content', 'Scripture Links (plg_content_scripturelinks)');

The group is passed explicitly — it is part of the registry's unique key, and a plugin registered without it never matches on lookup. register and unregister are branched rather than called dynamically, because unregister() takes no display name.

Verified in Proclaim's upgrade harness on a real 10.4.0 → 10.4.1 upgrade: OK scripturelinks (content) registered itself as a consumer. The task plugin is not shipped by pkg_proclaim, so that harness reports it as "not installed here, nothing to register" — its registration path is the same code.

Note: this branch's nested libraries/lib_cwmscripture pointer is still at v1.1.6, which has no entry point. The is_file() guard means the calls no-op harmlessly until the pointer moves to 1.1.7, so nothing breaks — but registration does not actually happen until then, and the plugins stay covered only by the library's FIRST_PARTY fallback.

bcordis and others added 2 commits July 29, 2026 21:55
v1.1.7 adds src/consumer.php, the entry point both plugins' install scripts
call to register themselves as consumers (#12). Until this pointer moved, the
package shipped a library without it: the is_file() guard meant those calls
no-opped silently, so neither plugin actually registered and both stayed
covered only by the library's hardcoded FIRST_PARTY fallback.

Also brings ConsumerScanner, so an extension that never registers is still
detected by its references to the library namespace.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ti98Cc63bmWcXva2G4GdNq
Picks up the library's verifyMediaSources config, so building this package also
runs the stale-artifact check on the library rather than trusting whatever sits
in its media/ directory. lib_cwmscripture#25 is the case that check exists for,
and this package is one of the two that bundles the library.

No change to what ships. The previous pointer was already v1.1.7, so the bundled
library carried src/consumer.php and ConsumerScanner before this; the library's
cwm-build.config.json and composer.json are build-time only and are not in the
manifest's <files>. Verified: the rebuilt pkg_cwmscripture assembles with all
four children and the bundled lib_cwmscripture.zip is 89 files either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ti98Cc63bmWcXva2G4GdNq
@bcordis

bcordis commented Jul 30, 2026

Copy link
Copy Markdown
Member Author

Added a5c504f: nested libraries/lib_cwmscripture bumped from 1980675 (v1.1.7) to addc517 — lib main after Joomla-Bible-Study/lib_cwmscripture#27.

That PR added the library's verifyMediaSources config, so cwm-package now runs the stale-artifact check on the library while building this package instead of trusting whatever sits in its media/. lib_cwmscripture#25 is the case it exists for, and this package is one of the two that bundles the library.

Nothing shipped changes. The previous pointer was already v1.1.7, so the bundled library carried src/consumer.php and ConsumerScanner before this — the plugins' registration calls were already live. The library's cwm-build.config.json and composer.json are build-time only and are not in the manifest's <files>.

Verified by rebuilding: pkg_cwmscripture assembles with all four children, and the bundled lib_cwmscripture.zip is 89 files before and after.

Proclaim's equivalent bump is Joomla-Bible-Study/Proclaim#1373.

php-cs-fixer (single_blank_line_at_eof) failed the PR with exit 8, which skipped
the PHPUnit, build and verify steps behind it. Nothing else in the file changes.

Slightly pointed: the file it landed in is the one asserting a disarmed SQL file
is still recognised, and its own final line was missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ti98Cc63bmWcXva2G4GdNq
@bcordis
bcordis merged commit ee719e9 into main Jul 30, 2026
1 check passed
@bcordis
bcordis deleted the feat/system-plugin-disarm branch July 30, 2026 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant