No local Bible texts are installed yet.
+ +| Translation | Canonical books | Status |
|---|---|---|
| + | + | Complete' : esc_html('Missing: ' . implode(', ', $row['missing'])); ?> | +
This checks whether each installed text has at least one row for every canonical Bible book after source-code interpretation.
+ +This plugin is designed for Bible texts that are public domain, openly licensed, or otherwise legally permitted for local storage and display. It does not provide import tools for restricted commercial translations unless a future integration explicitly supports proper licensing.
@@ -748,6 +788,29 @@ if (!class_exists('Bible_Reference_Tooltips')) { return $wpdb->get_results("SELECT translation, COUNT(*) AS verses, MAX(source) AS source FROM " . self::table() . " GROUP BY translation ORDER BY translation ASC"); } + private function text_integrity_summary() { + global $wpdb; + $expected = array_map(function($book) { return $book['abbr']; }, $this->book_registry()); + $translations = $wpdb->get_col("SELECT DISTINCT translation FROM " . self::table() . " ORDER BY translation ASC"); + $summary = []; + foreach ((array)$translations as $translation) { + $found = $wpdb->get_col($wpdb->prepare( + "SELECT DISTINCT book FROM " . self::table() . " WHERE translation = %s ORDER BY book ASC", + $translation + )); + $found = array_values(array_unique(array_map('strtoupper', array_map('strval', $found ?: [])))); + $missing = array_values(array_diff($expected, $found)); + $summary[] = [ + 'translation' => strtoupper((string)$translation), + 'expected' => count($expected), + 'found' => count(array_intersect($expected, $found)), + 'missing' => $missing, + 'ok' => !$missing, + ]; + } + return $summary; + } + private function available_translations() { return [ 'WEB' => [ @@ -1028,22 +1091,7 @@ if (!class_exists('Bible_Reference_Tooltips')) { private function repair_book_aliases() { if (get_option('brt_alias_repair_version') === self::VERSION) return; global $wpdb; - $repairs = [ - 'JAM' => ['JAS', 'James'], - 'JMS' => ['JAS', 'James'], - 'JM' => ['JAS', 'James'], - 'JA' => ['JAS', 'James'], - 'JAC' => ['JAS', 'James'], - 'JACOB' => ['JAS', 'James'], - 'EPISTLEOFJAMES' => ['JAS', 'James'], - 'THEEPISTLEOFJAMES' => ['JAS', 'James'], - 'JOH' => ['JHN', 'John'], - 'JOE' => ['JOL', 'Joel'], - 'NAH' => ['NAM', 'Nahum'], - 'PHI' => ['PHP', 'Philippians'], - 'PHL' => ['PHM', 'Philemon'], - ]; - foreach ($repairs as $old => $new) { + foreach ($this->canonical_book_repairs() as $old => $new) { $wpdb->query($wpdb->prepare( "UPDATE IGNORE " . self::table() . " SET book = %s, book_name = %s WHERE book = %s", $new[0], @@ -1056,23 +1104,7 @@ if (!class_exists('Bible_Reference_Tooltips')) { $new[0] )); } - $name_repairs = [ - 'jam' => ['JAS', 'James'], - 'jms' => ['JAS', 'James'], - 'jm' => ['JAS', 'James'], - 'ja' => ['JAS', 'James'], - 'jas' => ['JAS', 'James'], - 'jac' => ['JAS', 'James'], - 'jacob' => ['JAS', 'James'], - 'epistleofjames' => ['JAS', 'James'], - 'theepistleofjames' => ['JAS', 'James'], - 'joh' => ['JHN', 'John'], - 'joe' => ['JOL', 'Joel'], - 'nah' => ['NAM', 'Nahum'], - 'phi' => ['PHP', 'Philippians'], - 'phl' => ['PHM', 'Philemon'], - ]; - foreach ($name_repairs as $old_name => $new) { + foreach ($this->canonical_book_name_repairs() as $old_name => $new) { $wpdb->query($wpdb->prepare( "UPDATE IGNORE " . self::table() . " SET book = %s, book_name = %s WHERE LOWER(REPLACE(REPLACE(REPLACE(book_name, ' ', ''), '.', ''), '-', '')) = %s", $new[0], @@ -1083,6 +1115,28 @@ if (!class_exists('Bible_Reference_Tooltips')) { update_option('brt_alias_repair_version', self::VERSION, false); } + private function canonical_book_repairs() { + $repairs = []; + foreach ($this->book_registry() as $book) { + foreach (array_merge($book['aliases'], $book['source_aliases']['ebible_vpl']) as $alias) { + $key = strtoupper(preg_replace('/[^A-Z0-9]/i', '', (string)$alias)); + if ($key && $key !== $book['abbr']) $repairs[$key] = [$book['abbr'], $book['name']]; + } + } + return $repairs; + } + + private function canonical_book_name_repairs() { + $repairs = []; + foreach ($this->book_registry() as $book) { + foreach (array_merge([$book['name']], $book['aliases'], $book['source_aliases']['ebible_vpl']) as $alias) { + $key = $this->alias_key($alias); + if ($key) $repairs[$key] = [$book['abbr'], $book['name']]; + } + } + return $repairs; + } + private function repair_missing_james_verses() { if (get_option('brt_james_backfill_version') === self::VERSION) return; global $wpdb; @@ -1126,20 +1180,7 @@ if (!class_exists('Bible_Reference_Tooltips')) { return <<<'JS' (function(){ if (!window.BibleReferenceTooltips || !BibleReferenceTooltips.rest) return; - const bookAliases = [ - 'Genesis','Gen','Ge','Gn','Exodus','Exod','Exo','Ex','Leviticus','Lev','Le','Lv','Numbers','Num','Nu','Nm','Nb','Deuteronomy','Deut','Deu','Dt', - 'Joshua','Josh','Jos','Judges','Judg','Jdg','Jg','Ruth','Rut','Ru','1\\s*Samuel','1\\s*Sam','1\\s*Sa','2\\s*Samuel','2\\s*Sam','2\\s*Sa', - '1\\s*Kings','1\\s*Kgs','1\\s*Ki','2\\s*Kings','2\\s*Kgs','2\\s*Ki','1\\s*Chronicles','1\\s*Chron','1\\s*Chr','2\\s*Chronicles','2\\s*Chron','2\\s*Chr', - 'Ezra','Ezr','Nehemiah','Neh','Ne','Esther','Est','Job','Psalms','Psalm','Psa','Ps','Proverbs','Prov','Pro','Pr','Ecclesiastes','Eccl','Ecc', - 'Song\\s*of\\s*Solomon','Song\\s*of\\s*Songs','Song','Sos','Sng','Isaiah','Isa','Jeremiah','Jer','Lamentations','Lam','Ezekiel','Ezek','Ezk','Eze', - 'Daniel','Dan','Dn','Hosea','Hos','Joel','Jol','Joe','Jl','Amos','Amo','Obadiah','Obad','Oba','Jonah','Jon','Micah','Mic','Nahum','Nah','Nam','Habakkuk','Hab','Zephaniah','Zeph','Zep', - 'Haggai','Hag','Zechariah','Zech','Zec','Malachi','Mal','Matthew','Matt','Mat','Mt','Mark','Mrk','Mk','Mar','Luke','Luk','Lk','John','Jhn','Jn', - 'Acts','Act','Romans','Rom','Ro','1\\s*Corinthians','1\\s*Cor','1\\s*Co','2\\s*Corinthians','2\\s*Cor','2\\s*Co','Galatians','Gal','Ephesians','Eph', - 'Philippians','Phil','Php','Colossians','Col','1\\s*Thessalonians','1\\s*Thess','1\\s*Th','2\\s*Thessalonians','2\\s*Thess','2\\s*Th', - '1\\s*Timothy','1\\s*Tim','1\\s*Ti','2\\s*Timothy','2\\s*Tim','2\\s*Ti','Titus','Tit','Philemon','Phlm','Phm','Hebrews','Heb', - 'James\\.?','Jas\\.?','Ja\\.?','Jam\\.?','Jms\\.?','Jm\\.?','Jac\\.?','Jacob','Epistle\\s*of\\s*James','1\\s*Peter','1\\s*Pet','1\\s*Pe','2\\s*Peter','2\\s*Pet','2\\s*Pe','1\\s*John','1\\s*Jn','1\\s*Jhn','1\\s*Jo', - '2\\s*John','2\\s*Jn','2\\s*Jhn','2\\s*Jo','3\\s*John','3\\s*Jn','3\\s*Jhn','3\\s*Jo','Jude','Jud','Revelation','Rev','Re' - ]; + const bookAliases = Array.isArray(BibleReferenceTooltips.bookAliases) ? BibleReferenceTooltips.bookAliases : []; const book = '(?:' + bookAliases.sort((a,b) => b.length - a.length).join('|') + ')'; const chapterPattern = BibleReferenceTooltips.tagChapters ? '(?:\\s*[:.]\\s*\\d{1,3}(?:\\s*[-\\u2013\\u2014]\\s*\\d{1,3})?)?' : '\\s*[:.]\\s*\\d{1,3}(?:\\s*[-\\u2013\\u2014]\\s*\\d{1,3})?'; const refPattern = '\\b(' + book + ')[\\s.]*\\d{1,3}' + chapterPattern + '\\b'; diff --git a/plugins/bible-reference-tooltips/readme.txt b/plugins/bible-reference-tooltips/readme.txt index 069f4c5..3600798 100644 --- a/plugins/bible-reference-tooltips/readme.txt +++ b/plugins/bible-reference-tooltips/readme.txt @@ -4,7 +4,7 @@ Tags: bible, scripture, references, tooltips, verses Requires at least: 6.0 Tested up to: 7.0.1 Requires PHP: 7.4 -Stable tag: 1.1.5 +Stable tag: 1.1.6 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -49,6 +49,12 @@ Yes. The plugin is standalone and appears under Settings in the WordPress admin. == Changelog == += 1.1.6 = + +* Centralized Bible book aliases and eBible source-code interpretation into one registry. +* Added an admin text-integrity panel for installed Bible book coverage. +* Expanded smoke-test coverage for source-code normalization, human reference aliases, and compact references. + = 1.1.5 = * Added the public workspace conventions and tracked Docker test lab as the bounded release workflow. diff --git a/tools/testlab/README.md b/tools/testlab/README.md index 524a7d4..6b87bd7 100644 --- a/tools/testlab/README.md +++ b/tools/testlab/README.md @@ -6,14 +6,13 @@ The lab mounts plugin source from this workspace into disposable WordPress conta ## Environments -Initial matrix: +Default matrix: | Environment | WordPress Image | PHP | Database | | --- | --- | --- | --- | | `wp7-php84` | `wordpress:php8.4-apache` | 8.4 | MariaDB 11 | -| `wp6-php74` | `wordpress:php7.4-apache` with WP `6.8.2` download | 7.4 | MariaDB 10.6 | -The `wp7-php84` image installs the latest WordPress available in the image line. The `wp6-php74` environment uses the available PHP 7.4 WordPress image, then forces a WordPress 6.8.2 core download before install. The test script records the actual WordPress version after install. +The `wp7-php84` image installs the latest WordPress available in the image line. Legacy WordPress/PHP environments should be added only for an explicit compatibility pass, not as part of the default bounded release gate. ## Commands diff --git a/tools/testlab/config/environments.json b/tools/testlab/config/environments.json index 4822279..0bab0c7 100644 --- a/tools/testlab/config/environments.json +++ b/tools/testlab/config/environments.json @@ -4,12 +4,5 @@ "wordpressImage": "wordpress:php8.4-apache", "dbImage": "mariadb:11", "hostPort": 8070 - }, - { - "name": "wp6-php74", - "wordpressImage": "wordpress:php7.4-apache", - "dbImage": "mariadb:10.6", - "hostPort": 8068, - "wordpressVersion": "6.8.2" } ] diff --git a/tools/testlab/results/wp6-php74.json b/tools/testlab/results/wp6-php74.json deleted file mode 100644 index e88d697..0000000 --- a/tools/testlab/results/wp6-php74.json +++ /dev/null @@ -1,149 +0,0 @@ -{ - "environment": "wp6-php74", - "wordpressImage": "wordpress:php7.4-apache", - "dbImage": "mariadb:10.6", - "hostPort": 8068, - "startedAt": "2026-07-11T19:49:33.8231872-05:00", - "checks": [ - { - "name": "wp-cli available", - "passed": true, - "detail": "" - }, - { - "name": "wordpress core version prepared", - "passed": true, - "detail": "target=6.8.2" - }, - { - "name": "wordpress installed", - "passed": true, - "detail": "installed" - }, - { - "name": "runtime versions", - "passed": true, - "detail": "WP=6.8.2 PHP=7.4.33" - }, - { - "name": "plugin activated", - "passed": true, - "detail": "" - }, - { - "name": "plugin version detected", - "passed": true, - "detail": "version=1.1.5" - }, - { - "name": "verse table created", - "passed": true, - "detail": "wp_brt_verses" - }, - { - "name": "WEB import completed", - "passed": true, - "detail": "WEB import complete: 36,601 verses installed." - }, - { - "name": "KJV import completed", - "passed": true, - "detail": "KJV import complete: 35,948 verses installed." - }, - { - "name": "ASV import completed", - "passed": true, - "detail": "ASV import complete: 31,086 verses installed." - }, - { - "name": "YLT import completed", - "passed": true, - "detail": "YLT import complete: 31,102 verses installed." - }, - { - "name": "translation rows stored", - "passed": true, - "detail": "[{\"translation\":\"ASV\",\"row_count\":\"31086\"},{\"translation\":\"KJV\",\"row_count\":\"35948\"},{\"translation\":\"WEB\",\"row_count\":\"36601\"},{\"translation\":\"YLT\",\"row_count\":\"31102\"}]" - }, - { - "name": "canonical book coverage", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup John 3:16", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup John 3.16", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Jn3:16", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Romans 8:28", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup James 1:5", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Mark 10:45", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Mark10:45", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Mk 10:45", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Ezekiel 36:26", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup Eze 36:26", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup 2 John 1:6", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup 2Jn1:6", - "passed": true, - "detail": "ok" - }, - { - "name": "REST lookup 2 Jo 1:6", - "passed": true, - "detail": "ok" - }, - { - "name": "test post created", - "passed": true, - "detail": "" - } - ], - "phpVersion": "7.4.33", - "wordpressVersion": "6.8.2", - "pluginVersion": "1.1.5", - "completedAt": "2026-07-11T19:52:42.5557675-05:00", - "passed": true -} diff --git a/tools/testlab/results/wp7-php84.json b/tools/testlab/results/wp7-php84.json index 703a476..33a0c4a 100644 --- a/tools/testlab/results/wp7-php84.json +++ b/tools/testlab/results/wp7-php84.json @@ -3,7 +3,7 @@ "wordpressImage": "wordpress:php8.4-apache", "dbImage": "mariadb:11", "hostPort": 8070, - "startedAt": "2026-07-11T19:46:20.2262126-05:00", + "startedAt": "2026-07-11T20:12:33.4678261-05:00", "checks": [ { "name": "wp-cli available", @@ -28,13 +28,18 @@ { "name": "plugin version detected", "passed": true, - "detail": "version=1.1.5" + "detail": "version=1.1.6" }, { "name": "verse table created", "passed": true, "detail": "wp_brt_verses" }, + { + "name": "interpretation registry", + "passed": true, + "detail": "ok" + }, { "name": "WEB import completed", "passed": true, @@ -90,6 +95,16 @@ "passed": true, "detail": "ok" }, + { + "name": "REST lookup Jas 1:5", + "passed": true, + "detail": "ok" + }, + { + "name": "REST lookup Jm 1:5", + "passed": true, + "detail": "ok" + }, { "name": "REST lookup Mark 10:45", "passed": true, @@ -105,6 +120,11 @@ "passed": true, "detail": "ok" }, + { + "name": "REST lookup Mar 10:45", + "passed": true, + "detail": "ok" + }, { "name": "REST lookup Ezekiel 36:26", "passed": true, @@ -138,7 +158,7 @@ ], "phpVersion": "8.4.23", "wordpressVersion": "7.0.1", - "pluginVersion": "1.1.5", - "completedAt": "2026-07-11T19:49:33.7883010-05:00", + "pluginVersion": "1.1.6", + "completedAt": "2026-07-11T20:16:05.3978156-05:00", "passed": true } diff --git a/tools/testlab/scripts/common.ps1 b/tools/testlab/scripts/common.ps1 index 53170eb..97f6db0 100644 --- a/tools/testlab/scripts/common.ps1 +++ b/tools/testlab/scripts/common.ps1 @@ -7,7 +7,7 @@ $GeneratedRoot = Join-Path $LabRoot 'generated' $ResultsRoot = Join-Path $LabRoot 'results' $PluginSource = Join-Path $WorkspaceRoot 'plugins\bible-reference-tooltips' $PluginSlug = 'bible-reference-tooltips' -$ExpectedPluginVersion = '1.1.5' +$ExpectedPluginVersion = '1.1.6' function Get-TestLabEnvironments { Get-Content -LiteralPath $ConfigPath -Raw | ConvertFrom-Json diff --git a/tools/testlab/scripts/run-env.ps1 b/tools/testlab/scripts/run-env.ps1 index 7121464..7573ab7 100644 --- a/tools/testlab/scripts/run-env.ps1 +++ b/tools/testlab/scripts/run-env.ps1 @@ -80,6 +80,48 @@ echo $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->prefix . 'brt_v $tableExists = (Invoke-WpCli $environment 'wp eval-file /tmp/brt-table.php --path=/var/www/html' | Out-String).Trim() Add-Check 'verse table created' ($tableExists -eq 'wp_brt_verses') $tableExists + $interpretationPhp = @' +getMethod($method); + $m->setAccessible(true); + return $m->invokeArgs($plugin, $args); +}; +$checks = []; +foreach ([ + ['parse_reference', 'James 1:5', 'JAS'], + ['parse_reference', 'Jas 1:5', 'JAS'], + ['parse_reference', 'Jm 1:5', 'JAS'], + ['parse_reference', 'Mark10:45', 'MRK'], + ['parse_reference', '2Jn1:6', '2JN'], + ['parse_reference', 'Eze 36:26', 'EZK'], +] as $case) { + $parsed = $call($case[0], $case[1]); + if (!$parsed || $parsed['book'] !== $case[2]) $checks[] = $case[1] . '=>' . json_encode($parsed); +} +foreach ([ + ['EZE 36:26 source', 'EZE', 'EZK'], + ['MAR 10:45 source', 'MAR', 'MRK'], + ['JOH 3:16 source', 'JOH', 'JHN'], + ['JAM 1:5 source', 'JAM', 'JAS'], + ['1JO 1:9 source', '1JO', '1JN'], +] as $case) { + $book = $call('normalize_source_book', $case[1], 'ebible_vpl'); + if (!$book || $book['abbr'] !== $case[2]) $checks[] = $case[0] . '=>' . json_encode($book); +} +$aliases = $call('frontend_book_alias_patterns'); +$alias_keys = array_map('strtolower', $aliases); +foreach (['James', 'Jm', 'Eze', 'Mar', '2\\s*Jn'] as $needle) { + if (!in_array(strtolower($needle), $alias_keys, true)) $checks[] = 'missing frontend alias ' . $needle; +} +echo $checks ? json_encode($checks) : 'ok'; +'@ + Write-ContainerPhpFile $environment '/tmp/brt-interpretation.php' $interpretationPhp + $interpretation = (Invoke-WpCli $environment 'wp eval-file /tmp/brt-interpretation.php --path=/var/www/html' | Out-String).Trim() + Add-Check 'interpretation registry' ($interpretation -eq 'ok') $interpretation + $translationsToImport = @('WEB', 'KJV', 'ASV', 'YLT') foreach ($translationCode in $translationsToImport) { $importPhp = @" @@ -126,7 +168,7 @@ echo $missing ? json_encode($missing) : 'ok'; $bookCoverage = (Invoke-WpCli $environment 'wp eval-file /tmp/brt-book-coverage.php --path=/var/www/html' | Out-String).Trim() Add-Check 'canonical book coverage' ($bookCoverage -eq 'ok') $bookCoverage - $refs = @('John 3:16', 'John 3.16', 'Jn3:16', 'Romans 8:28', 'James 1:5', 'Mark 10:45', 'Mark10:45', 'Mk 10:45', 'Ezekiel 36:26', 'Eze 36:26', '2 John 1:6', '2Jn1:6', '2 Jo 1:6') + $refs = @('John 3:16', 'John 3.16', 'Jn3:16', 'Romans 8:28', 'James 1:5', 'Jas 1:5', 'Jm 1:5', 'Mark 10:45', 'Mark10:45', 'Mk 10:45', 'Mar 10:45', 'Ezekiel 36:26', 'Eze 36:26', '2 John 1:6', '2Jn1:6', '2 Jo 1:6') foreach ($ref in $refs) { $lookupPhp = @"