Fortify Bible reference interpretation

This commit is contained in:
2026-07-11 20:16:27 -05:00
parent 5835e5a3b2
commit f2bc638509
9 changed files with 284 additions and 326 deletions
@@ -1,5 +1,11 @@
# Bible Reference Tooltips Changelog
## 1.1.6 - 2026-07-12
- Centralizes Bible book aliases, eBible source-code aliases, lookup candidates, repair maps, and frontend detection aliases into one registry.
- Adds an admin text-integrity panel showing canonical book coverage for installed translations.
- Expands the Docker smoke matrix with direct interpretation-registry checks and additional alias REST lookups.
## 1.1.5 - 2026-07-12
- Adds the public workspace conventions and tracked Docker test lab as the bounded release workflow for this public package.
@@ -3,7 +3,7 @@
* Plugin Name: Bible Reference Tooltips
* Plugin URI: https://christit.com/bible-reference-tooltips
* Description: Local, open Bible reference tooltips for WordPress content. Detects verse references on page load and serves hover cards from a local verse table.
* Version: 1.1.5
* Version: 1.1.6
* Stage: Stable
* Release Channel: Public
* Public Release: Yes
@@ -26,11 +26,11 @@ if (!defined('ABSPATH')) exit;
if (!class_exists('Bible_Reference_Tooltips')) {
class Bible_Reference_Tooltips {
const VERSION = '1.1.5';
const VERSION = '1.1.6';
const SLUG = 'bible-reference-tooltips';
const NONCE = 'brt_nonce';
const OPTION_KEY = 'brt_settings';
const SETTINGS_VERSION = '1.1.5';
const SETTINGS_VERSION = '1.1.6';
const TABLE_VERSION = '1';
private $book_map = null;
@@ -191,6 +191,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
'underline' => !empty($settings['underline']),
'showTranslation' => !empty($settings['show_translation']),
'showTooltips' => !empty($settings['show_tooltips']),
'bookAliases' => $this->frontend_book_alias_patterns(),
'caseSensitive' => empty($settings['case_insensitive']),
'tagChapters' => !empty($settings['tag_chapters']),
'scanComments' => !empty($settings['scan_comments']),
@@ -428,17 +429,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
private function book_lookup_candidates($parsed) {
$abbr = strtoupper(preg_replace('/[^A-Z0-9]/i', '', $parsed['book'] ?? ''));
$name = strtoupper(preg_replace('/[^A-Z0-9]/i', '', $parsed['book_name'] ?? ''));
$variants = [
'JHN' => ['JHN', 'JOH', 'JOHN'],
'PSA' => ['PSA', 'PS', 'PSM', 'PSALM', 'PSALMS'],
'SNG' => ['SNG', 'SON', 'SOS', 'SONG'],
'JAS' => ['JAS', 'JA', 'JAM', 'JMS', 'JM', 'JAC', 'JACOB', 'EPISTLEOFJAMES', 'THEEPISTLEOFJAMES'],
'JOL' => ['JOL', 'JOE'],
'NAM' => ['NAM', 'NAH'],
'PHM' => ['PHM', 'PHLM', 'PHILEM'],
'REV' => ['REV', 'RE', 'APO'],
];
$candidates = array_merge([$abbr, $name], $variants[$abbr] ?? [], $this->source_book_lookup_candidates($abbr));
$candidates = array_merge([$abbr, $name], $this->book_registry_aliases($abbr), $this->source_book_lookup_candidates($abbr));
foreach ($this->book_map() as $alias => $entry) {
if (strtoupper($entry['abbr']) !== $abbr) continue;
$candidates[] = strtoupper(preg_replace('/[^A-Z0-9]/i', '', $alias));
@@ -451,14 +442,13 @@ if (!class_exists('Bible_Reference_Tooltips')) {
private function book_name_lookup_candidates($parsed) {
$abbr = strtoupper(preg_replace('/[^A-Z0-9]/i', '', $parsed['book'] ?? ''));
$names = [$parsed['book_name'] ?? '', $abbr];
foreach ($this->book_map() as $alias => $entry) {
if (strtoupper($entry['abbr']) !== $abbr) continue;
$names[] = $entry['name'];
$names[] = $alias;
$names[] = strtoupper($alias);
}
if ($abbr === 'JAS') {
$names = array_merge($names, ['James', 'Jas', 'JAS', 'Ja', 'JA', 'Jam', 'JAM', 'Jms', 'JMS', 'Jm', 'JM', 'Jac', 'JAC', 'Jacob', 'Epistle of James', 'The Epistle of James']);
$book = $this->book_registry_entry($abbr);
if ($book) {
$names[] = $book['name'];
foreach ($book['aliases'] as $alias) {
$names[] = $alias;
$names[] = strtoupper($alias);
}
}
return array_values(array_unique(array_filter(array_map('strval', $names))));
}
@@ -525,115 +515,145 @@ if (!class_exists('Bible_Reference_Tooltips')) {
}
private function ebible_vpl_book_map() {
$source_to_canonical = [
'GEN' => 'GEN', 'EXO' => 'EXO', 'LEV' => 'LEV', 'NUM' => 'NUM', 'DEU' => 'DEU',
'JOS' => 'JOS', 'JDG' => 'JDG', 'RUT' => 'RUT', '1SA' => '1SA', '2SA' => '2SA',
'1KI' => '1KI', '2KI' => '2KI', '1CH' => '1CH', '2CH' => '2CH', 'EZR' => 'EZR',
'NEH' => 'NEH', 'EST' => 'EST', 'JOB' => 'JOB', 'PSA' => 'PSA', 'PRO' => 'PRO',
'ECC' => 'ECC', 'SNG' => 'SNG', 'SOL' => 'SNG', 'ISA' => 'ISA', 'JER' => 'JER', 'LAM' => 'LAM',
'EZE' => 'EZK', 'EZK' => 'EZK', 'DAN' => 'DAN', 'HOS' => 'HOS', 'JOL' => 'JOL', 'JOE' => 'JOL',
'AMO' => 'AMO', 'OBA' => 'OBA', 'JON' => 'JON', 'MIC' => 'MIC', 'NAM' => 'NAM', 'NAH' => 'NAM',
'HAB' => 'HAB', 'ZEP' => 'ZEP', 'HAG' => 'HAG', 'ZEC' => 'ZEC', 'MAL' => 'MAL',
'MAT' => 'MAT', 'MAR' => 'MRK', 'MRK' => 'MRK', 'LUK' => 'LUK', 'JHN' => 'JHN', 'JOH' => 'JHN',
'ACT' => 'ACT', 'ROM' => 'ROM', '1CO' => '1CO', '2CO' => '2CO', 'GAL' => 'GAL',
'EPH' => 'EPH', 'PHP' => 'PHP', 'PHI' => 'PHP', 'COL' => 'COL', '1TH' => '1TH', '2TH' => '2TH',
'1TI' => '1TI', '2TI' => '2TI', 'TIT' => 'TIT', 'PHM' => 'PHM', 'HEB' => 'HEB',
'JAS' => 'JAS', 'JAM' => 'JAS', '1PE' => '1PE', '2PE' => '2PE', '1JO' => '1JN', '1JN' => '1JN',
'2JO' => '2JN', '2JN' => '2JN', '3JO' => '3JN', '3JN' => '3JN', 'JUD' => 'JUD',
'REV' => 'REV',
];
$map = [];
foreach ($source_to_canonical as $source_abbr => $canonical_abbr) {
$map[$source_abbr] = ['abbr' => $canonical_abbr, 'name' => $this->book_name_from_abbr($canonical_abbr)];
foreach ($this->book_registry() as $book) {
foreach ($book['source_aliases']['ebible_vpl'] as $source_abbr) {
$map[$source_abbr] = ['abbr' => $book['abbr'], 'name' => $book['name']];
}
}
return $map;
}
private function book_map() {
if ($this->book_map !== null) return $this->book_map;
$books = [
['GEN','Genesis',['gen','ge','gn']],
['EXO','Exodus',['exo','ex','exod']],
['LEV','Leviticus',['lev','le','lv']],
['NUM','Numbers',['num','nu','nm','nb']],
['DEU','Deuteronomy',['deut','deu','dt']],
['JOS','Joshua',['josh','jos']],
['JDG','Judges',['judg','jdg','jg']],
['RUT','Ruth',['ruth','rut']],
['1SA','1 Samuel',['1samuel','1sam','isamuel','isam']],
['2SA','2 Samuel',['2samuel','2sam','iisamuel','iisam']],
['1KI','1 Kings',['1kings','1kgs','1ki','ikings']],
['2KI','2 Kings',['2kings','2kgs','2ki','iikings']],
['1CH','1 Chronicles',['1chronicles','1chron','1chr','ichronicles']],
['2CH','2 Chronicles',['2chronicles','2chron','2chr','iichronicles']],
['EZR','Ezra',['ezra','ezr']],
['NEH','Nehemiah',['neh','ne']],
['EST','Esther',['esther','est']],
['JOB','Job',['job']],
['PSA','Psalms',['psalms','psalm','psa','ps']],
['PRO','Proverbs',['proverbs','prov','pro','pr']],
['ECC','Ecclesiastes',['ecclesiastes','eccl','ecc','qoheleth']],
['SNG','Song of Solomon',['songofsolomon','songofsongs','song','sos','sng']],
['ISA','Isaiah',['isaiah','isa']],
['JER','Jeremiah',['jeremiah','jer']],
['LAM','Lamentations',['lamentations','lam']],
['EZK','Ezekiel',['ezekiel','ezek','ezk','eze']],
['DAN','Daniel',['daniel','dan','dn']],
['HOS','Hosea',['hosea','hos']],
['JOL','Joel',['joel','jol','joe']],
['AMO','Amos',['amos','amo']],
['OBA','Obadiah',['obadiah','oba','obad']],
['JON','Jonah',['jonah','jon']],
['MIC','Micah',['micah','mic']],
['NAM','Nahum',['nahum','nah','nam']],
['HAB','Habakkuk',['habakkuk','hab']],
['ZEP','Zephaniah',['zephaniah','zeph','zep']],
['HAG','Haggai',['haggai','hag']],
['ZEC','Zechariah',['zechariah','zech','zec']],
['MAL','Malachi',['malachi','mal']],
['MAT','Matthew',['matthew','matt','mat','mt']],
['MRK','Mark',['mark','mrk','mk','mar']],
['LUK','Luke',['luke','luk','lk']],
['JHN','John',['john','jhn','jn','joh']],
['ACT','Acts',['acts','act']],
['ROM','Romans',['romans','rom','ro']],
['1CO','1 Corinthians',['1corinthians','1cor','1co','icorinthians']],
['2CO','2 Corinthians',['2corinthians','2cor','2co','iicorinthians']],
['GAL','Galatians',['galatians','gal']],
['EPH','Ephesians',['ephesians','eph']],
['PHP','Philippians',['philippians','phil','php','phi']],
['COL','Colossians',['colossians','col']],
['1TH','1 Thessalonians',['1thessalonians','1thess','1th','ithessalonians']],
['2TH','2 Thessalonians',['2thessalonians','2thess','2th','iithessalonians']],
['1TI','1 Timothy',['1timothy','1tim','1ti','itimothy']],
['2TI','2 Timothy',['2timothy','2tim','2ti','iitimothy']],
['TIT','Titus',['titus','tit']],
['PHM','Philemon',['philemon','phm']],
['HEB','Hebrews',['hebrews','heb']],
['JAS','James',['james','jas','ja','jam','jms','jm','jac','jacob','epistleofjames','theepistleofjames']],
['1PE','1 Peter',['1peter','1pet','1pe','ipeter']],
['2PE','2 Peter',['2peter','2pet','2pe','iipeter']],
['1JN','1 John',['1john','1jn','1jhn','1jo','ijohn','ijn','ijhn','ijo']],
['2JN','2 John',['2john','2jn','2jhn','2jo','iijohn','iijn','iijhn','iijo']],
['3JN','3 John',['3john','3jn','3jhn','3jo','iiijohn','iiijn','iiijhn','iiijo']],
['JUD','Jude',['jude','jud']],
['REV','Revelation',['revelation','rev','re']],
];
$map = [];
foreach ($books as $book) {
$name_key = strtolower(preg_replace('/[^a-z0-9]/i', '', $book[1]));
$map[$name_key] = ['abbr' => $book[0], 'name' => $book[1]];
foreach ($book[2] as $alias) $map[$alias] = ['abbr' => $book[0], 'name' => $book[1]];
foreach ($this->book_registry() as $book) {
$map[$this->alias_key($book['name'])] = ['abbr' => $book['abbr'], 'name' => $book['name']];
foreach ($book['aliases'] as $alias) $map[$this->alias_key($alias)] = ['abbr' => $book['abbr'], 'name' => $book['name']];
}
return $this->book_map = $map;
}
private function book_registry_entry($abbr) {
$abbr = strtoupper(preg_replace('/[^A-Z0-9]/i', '', (string)$abbr));
foreach ($this->book_registry() as $book) {
if ($book['abbr'] === $abbr) return $book;
}
return null;
}
private function book_registry_aliases($abbr) {
$book = $this->book_registry_entry($abbr);
if (!$book) return [];
$aliases = [$book['abbr'], $book['name']];
$aliases = array_merge($aliases, $book['aliases'], $book['source_aliases']['ebible_vpl']);
return array_map(function($alias) {
return strtoupper(preg_replace('/[^A-Z0-9]/i', '', (string)$alias));
}, $aliases);
}
private function alias_key($alias) {
return strtolower(preg_replace('/[^a-z0-9]/i', '', (string)$alias));
}
private function frontend_book_alias_patterns() {
$patterns = [];
foreach ($this->book_registry() as $book) {
foreach (array_merge([$book['name']], $book['aliases']) as $alias) {
$patterns[] = $this->frontend_book_alias_pattern($alias);
}
}
usort($patterns, function($a, $b) { return strlen($b) - strlen($a); });
return array_values(array_unique(array_filter($patterns)));
}
private function frontend_book_alias_pattern($alias) {
$alias = trim((string)$alias);
if ($alias === '') return '';
$pattern = preg_quote($alias, '/');
$pattern = preg_replace('/\\\\\s+/', '\\\\s*', $pattern);
$pattern = preg_replace('/^([1-3])([A-Za-z])/', '$1\\\\s*$2', $pattern);
return str_replace('\\ ', '\\s*', $pattern);
}
private function book_registry() {
return [
['abbr'=>'GEN','name'=>'Genesis','aliases'=>['gen','ge','gn'],'source_aliases'=>['ebible_vpl'=>['GEN']]],
['abbr'=>'EXO','name'=>'Exodus','aliases'=>['exo','ex','exod'],'source_aliases'=>['ebible_vpl'=>['EXO']]],
['abbr'=>'LEV','name'=>'Leviticus','aliases'=>['lev','le','lv'],'source_aliases'=>['ebible_vpl'=>['LEV']]],
['abbr'=>'NUM','name'=>'Numbers','aliases'=>['num','nu','nm','nb'],'source_aliases'=>['ebible_vpl'=>['NUM']]],
['abbr'=>'DEU','name'=>'Deuteronomy','aliases'=>['deut','deu','dt'],'source_aliases'=>['ebible_vpl'=>['DEU']]],
['abbr'=>'JOS','name'=>'Joshua','aliases'=>['josh','jos'],'source_aliases'=>['ebible_vpl'=>['JOS']]],
['abbr'=>'JDG','name'=>'Judges','aliases'=>['judg','jdg','jg'],'source_aliases'=>['ebible_vpl'=>['JDG']]],
['abbr'=>'RUT','name'=>'Ruth','aliases'=>['ruth','rut'],'source_aliases'=>['ebible_vpl'=>['RUT']]],
['abbr'=>'1SA','name'=>'1 Samuel','aliases'=>['1samuel','1sam','i samuel','isamuel','isam'],'source_aliases'=>['ebible_vpl'=>['1SA']]],
['abbr'=>'2SA','name'=>'2 Samuel','aliases'=>['2samuel','2sam','ii samuel','iisamuel','iisam'],'source_aliases'=>['ebible_vpl'=>['2SA']]],
['abbr'=>'1KI','name'=>'1 Kings','aliases'=>['1kings','1kgs','1ki','i kings','ikings'],'source_aliases'=>['ebible_vpl'=>['1KI']]],
['abbr'=>'2KI','name'=>'2 Kings','aliases'=>['2kings','2kgs','2ki','ii kings','iikings'],'source_aliases'=>['ebible_vpl'=>['2KI']]],
['abbr'=>'1CH','name'=>'1 Chronicles','aliases'=>['1chronicles','1chron','1chr','i chronicles','ichronicles'],'source_aliases'=>['ebible_vpl'=>['1CH']]],
['abbr'=>'2CH','name'=>'2 Chronicles','aliases'=>['2chronicles','2chron','2chr','ii chronicles','iichronicles'],'source_aliases'=>['ebible_vpl'=>['2CH']]],
['abbr'=>'EZR','name'=>'Ezra','aliases'=>['ezra','ezr'],'source_aliases'=>['ebible_vpl'=>['EZR']]],
['abbr'=>'NEH','name'=>'Nehemiah','aliases'=>['neh','ne'],'source_aliases'=>['ebible_vpl'=>['NEH']]],
['abbr'=>'EST','name'=>'Esther','aliases'=>['esther','est'],'source_aliases'=>['ebible_vpl'=>['EST']]],
['abbr'=>'JOB','name'=>'Job','aliases'=>['job'],'source_aliases'=>['ebible_vpl'=>['JOB']]],
['abbr'=>'PSA','name'=>'Psalms','aliases'=>['psalms','psalm','psa','ps','psm'],'source_aliases'=>['ebible_vpl'=>['PSA']]],
['abbr'=>'PRO','name'=>'Proverbs','aliases'=>['proverbs','prov','pro','pr'],'source_aliases'=>['ebible_vpl'=>['PRO']]],
['abbr'=>'ECC','name'=>'Ecclesiastes','aliases'=>['ecclesiastes','eccl','ecc','qoheleth'],'source_aliases'=>['ebible_vpl'=>['ECC']]],
['abbr'=>'SNG','name'=>'Song of Solomon','aliases'=>['songofsolomon','song of songs','songofsongs','song','sos','sng','sol'],'source_aliases'=>['ebible_vpl'=>['SNG','SOL']]],
['abbr'=>'ISA','name'=>'Isaiah','aliases'=>['isaiah','isa'],'source_aliases'=>['ebible_vpl'=>['ISA']]],
['abbr'=>'JER','name'=>'Jeremiah','aliases'=>['jeremiah','jer'],'source_aliases'=>['ebible_vpl'=>['JER']]],
['abbr'=>'LAM','name'=>'Lamentations','aliases'=>['lamentations','lam'],'source_aliases'=>['ebible_vpl'=>['LAM']]],
['abbr'=>'EZK','name'=>'Ezekiel','aliases'=>['ezekiel','ezek','ezk','eze'],'source_aliases'=>['ebible_vpl'=>['EZK','EZE']]],
['abbr'=>'DAN','name'=>'Daniel','aliases'=>['daniel','dan','dn'],'source_aliases'=>['ebible_vpl'=>['DAN']]],
['abbr'=>'HOS','name'=>'Hosea','aliases'=>['hosea','hos'],'source_aliases'=>['ebible_vpl'=>['HOS']]],
['abbr'=>'JOL','name'=>'Joel','aliases'=>['joel','jol','joe'],'source_aliases'=>['ebible_vpl'=>['JOL','JOE']]],
['abbr'=>'AMO','name'=>'Amos','aliases'=>['amos','amo'],'source_aliases'=>['ebible_vpl'=>['AMO']]],
['abbr'=>'OBA','name'=>'Obadiah','aliases'=>['obadiah','oba','obad'],'source_aliases'=>['ebible_vpl'=>['OBA']]],
['abbr'=>'JON','name'=>'Jonah','aliases'=>['jonah','jon'],'source_aliases'=>['ebible_vpl'=>['JON']]],
['abbr'=>'MIC','name'=>'Micah','aliases'=>['micah','mic'],'source_aliases'=>['ebible_vpl'=>['MIC']]],
['abbr'=>'NAM','name'=>'Nahum','aliases'=>['nahum','nah','nam'],'source_aliases'=>['ebible_vpl'=>['NAM','NAH']]],
['abbr'=>'HAB','name'=>'Habakkuk','aliases'=>['habakkuk','hab'],'source_aliases'=>['ebible_vpl'=>['HAB']]],
['abbr'=>'ZEP','name'=>'Zephaniah','aliases'=>['zephaniah','zeph','zep'],'source_aliases'=>['ebible_vpl'=>['ZEP']]],
['abbr'=>'HAG','name'=>'Haggai','aliases'=>['haggai','hag'],'source_aliases'=>['ebible_vpl'=>['HAG']]],
['abbr'=>'ZEC','name'=>'Zechariah','aliases'=>['zechariah','zech','zec'],'source_aliases'=>['ebible_vpl'=>['ZEC']]],
['abbr'=>'MAL','name'=>'Malachi','aliases'=>['malachi','mal'],'source_aliases'=>['ebible_vpl'=>['MAL']]],
['abbr'=>'MAT','name'=>'Matthew','aliases'=>['matthew','matt','mat','mt'],'source_aliases'=>['ebible_vpl'=>['MAT']]],
['abbr'=>'MRK','name'=>'Mark','aliases'=>['mark','mrk','mk','mar'],'source_aliases'=>['ebible_vpl'=>['MRK','MAR']]],
['abbr'=>'LUK','name'=>'Luke','aliases'=>['luke','luk','lk'],'source_aliases'=>['ebible_vpl'=>['LUK']]],
['abbr'=>'JHN','name'=>'John','aliases'=>['john','jhn','jn','joh'],'source_aliases'=>['ebible_vpl'=>['JHN','JOH']]],
['abbr'=>'ACT','name'=>'Acts','aliases'=>['acts','act'],'source_aliases'=>['ebible_vpl'=>['ACT']]],
['abbr'=>'ROM','name'=>'Romans','aliases'=>['romans','rom','ro'],'source_aliases'=>['ebible_vpl'=>['ROM']]],
['abbr'=>'1CO','name'=>'1 Corinthians','aliases'=>['1corinthians','1cor','1co','i corinthians','icorinthians'],'source_aliases'=>['ebible_vpl'=>['1CO']]],
['abbr'=>'2CO','name'=>'2 Corinthians','aliases'=>['2corinthians','2cor','2co','ii corinthians','iicorinthians'],'source_aliases'=>['ebible_vpl'=>['2CO']]],
['abbr'=>'GAL','name'=>'Galatians','aliases'=>['galatians','gal'],'source_aliases'=>['ebible_vpl'=>['GAL']]],
['abbr'=>'EPH','name'=>'Ephesians','aliases'=>['ephesians','eph'],'source_aliases'=>['ebible_vpl'=>['EPH']]],
['abbr'=>'PHP','name'=>'Philippians','aliases'=>['philippians','phil','php','phi'],'source_aliases'=>['ebible_vpl'=>['PHP','PHI']]],
['abbr'=>'COL','name'=>'Colossians','aliases'=>['colossians','col'],'source_aliases'=>['ebible_vpl'=>['COL']]],
['abbr'=>'1TH','name'=>'1 Thessalonians','aliases'=>['1thessalonians','1thess','1th','i thessalonians','ithessalonians'],'source_aliases'=>['ebible_vpl'=>['1TH']]],
['abbr'=>'2TH','name'=>'2 Thessalonians','aliases'=>['2thessalonians','2thess','2th','ii thessalonians','iithessalonians'],'source_aliases'=>['ebible_vpl'=>['2TH']]],
['abbr'=>'1TI','name'=>'1 Timothy','aliases'=>['1timothy','1tim','1ti','i timothy','itimothy'],'source_aliases'=>['ebible_vpl'=>['1TI']]],
['abbr'=>'2TI','name'=>'2 Timothy','aliases'=>['2timothy','2tim','2ti','ii timothy','iitimothy'],'source_aliases'=>['ebible_vpl'=>['2TI']]],
['abbr'=>'TIT','name'=>'Titus','aliases'=>['titus','tit'],'source_aliases'=>['ebible_vpl'=>['TIT']]],
['abbr'=>'PHM','name'=>'Philemon','aliases'=>['philemon','phm','phlm','philemeon'],'source_aliases'=>['ebible_vpl'=>['PHM']]],
['abbr'=>'HEB','name'=>'Hebrews','aliases'=>['hebrews','heb'],'source_aliases'=>['ebible_vpl'=>['HEB']]],
['abbr'=>'JAS','name'=>'James','aliases'=>['james','jas','ja','jam','jms','jm','jac','jacob','epistleofjames','the epistle of james','theepistleofjames'],'source_aliases'=>['ebible_vpl'=>['JAS','JAM']]],
['abbr'=>'1PE','name'=>'1 Peter','aliases'=>['1peter','1pet','1pe','i peter','ipeter'],'source_aliases'=>['ebible_vpl'=>['1PE']]],
['abbr'=>'2PE','name'=>'2 Peter','aliases'=>['2peter','2pet','2pe','ii peter','iipeter'],'source_aliases'=>['ebible_vpl'=>['2PE']]],
['abbr'=>'1JN','name'=>'1 John','aliases'=>['1john','1jn','1jhn','1jo','i john','ijohn','ijn','ijhn','ijo'],'source_aliases'=>['ebible_vpl'=>['1JN','1JO']]],
['abbr'=>'2JN','name'=>'2 John','aliases'=>['2john','2jn','2jhn','2jo','ii john','iijohn','iijn','iijhn','iijo'],'source_aliases'=>['ebible_vpl'=>['2JN','2JO']]],
['abbr'=>'3JN','name'=>'3 John','aliases'=>['3john','3jn','3jhn','3jo','iii john','iiijohn','iiijn','iiijhn','iiijo'],'source_aliases'=>['ebible_vpl'=>['3JN','3JO']]],
['abbr'=>'JUD','name'=>'Jude','aliases'=>['jude','jud'],'source_aliases'=>['ebible_vpl'=>['JUD']]],
['abbr'=>'REV','name'=>'Revelation','aliases'=>['revelation','rev','re','apo'],'source_aliases'=>['ebible_vpl'=>['REV']]],
];
}
public function admin_page() {
if (!current_user_can('manage_options')) return;
$settings = $this->settings();
$stats = $this->translation_stats();
$translations = $this->available_translations();
$installed = $this->installed_translations();
$integrity = $this->text_integrity_summary();
$installed_by_code = [];
foreach ($installed as $row) $installed_by_code[$row['code']] = $row;
$msg = sanitize_text_field($_GET['msg'] ?? '');
@@ -732,6 +752,26 @@ if (!class_exists('Bible_Reference_Tooltips')) {
<button class="button button-primary">Save Settings</button>
</form>
</section>
<section class="brt-card brt-wide">
<h2>Text Integrity</h2>
<?php if (!$integrity): ?>
<p>No local Bible texts are installed yet.</p>
<?php else: ?>
<table class="widefat striped brt-table">
<thead><tr><th>Translation</th><th>Canonical books</th><th>Status</th></tr></thead>
<tbody>
<?php foreach ($integrity as $row): ?>
<tr>
<td><strong><?php echo esc_html($row['translation']); ?></strong></td>
<td><?php echo esc_html((int)$row['found'] . ' / ' . (int)$row['expected']); ?></td>
<td><?php echo $row['ok'] ? '<span class="brt-pill">Complete</span>' : esc_html('Missing: ' . implode(', ', $row['missing'])); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p class="description">This checks whether each installed text has at least one row for every canonical Bible book after source-code interpretation.</p>
<?php endif; ?>
</section>
<section class="brt-card brt-wide">
<h2>Open Text Policy</h2>
<p>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.</p>
@@ -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';
+7 -1
View File
@@ -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.