Add public WordPress release conventions
This commit is contained in:
@@ -7,3 +7,5 @@ vendor/
|
||||
*.zip
|
||||
*.log
|
||||
.env
|
||||
|
||||
tools/testlab/generated/
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# WordPress Public Workspace Conventions
|
||||
|
||||
This workspace prepares Christ IT WordPress plugins and themes for public distribution. Git is the development source of truth. WordPress.org SVN, public plugin directories, and installed production copies are release targets, not the working source.
|
||||
|
||||
## Scope
|
||||
|
||||
- Public plugin source lives under `plugins/<slug>/`.
|
||||
- Shared local release and smoke-test tooling lives under `tools/`.
|
||||
- Do not remove or replace the matching internal GracePress package until the public package is accepted, published, installed on the target production site, and verified on the public update path.
|
||||
|
||||
## Version Discipline
|
||||
|
||||
Every meaningful public plugin change must update, in the same commit:
|
||||
|
||||
- the plugin header `Version`;
|
||||
- any runtime version constant;
|
||||
- `readme.txt` `Stable tag`;
|
||||
- the plugin `CHANGELOG.md`;
|
||||
- the `readme.txt` changelog when the change is public-facing.
|
||||
|
||||
Use one version number per bounded pass. Do not keep piling unrelated verification fixes into the same version after the pass has been declared complete.
|
||||
|
||||
## Changelog Discipline
|
||||
|
||||
The standalone plugin changelog is the durable engineering record. The WordPress.org readme changelog is the public release summary.
|
||||
|
||||
For migrated GracePress plugins:
|
||||
|
||||
- keep enough prior internal history to explain origin and compatibility;
|
||||
- clearly mark the public fork version;
|
||||
- remove or supersede public claims that no longer match the current package;
|
||||
- record test-lab evidence in Git when a version is considered a release candidate.
|
||||
|
||||
## Bounded Verification
|
||||
|
||||
Use this default verification loop unless the user explicitly asks for more:
|
||||
|
||||
1. Run PHP syntax checks for touched PHP files.
|
||||
2. Run the configured Docker smoke matrix once.
|
||||
3. Save smoke results under `tools/testlab/results/`.
|
||||
4. Review failures only far enough to identify the next code or test fix.
|
||||
5. After a passing matrix, commit and stop.
|
||||
|
||||
Do not start recursive verification such as verifying the verifier, expanding the matrix, or repeatedly re-running browser checks unless a named release gate fails or the user explicitly asks for a broader pass.
|
||||
|
||||
## Test Lab
|
||||
|
||||
The local Docker test lab belongs in `tools/testlab/` so scripts, environment definitions, and result JSON stay tied to Git history.
|
||||
|
||||
Smoke tests should prove the contract, not exhaustively explore the UI:
|
||||
|
||||
- plugin activates;
|
||||
- supported WordPress/PHP versions boot;
|
||||
- expected plugin version is active;
|
||||
- required tables are created;
|
||||
- supported local Bible texts import;
|
||||
- representative REST lookups pass;
|
||||
- a browser-facing test page can be seeded for manual review.
|
||||
|
||||
Manual browser review is a separate, explicitly requested step.
|
||||
|
||||
## Release Boundary
|
||||
|
||||
A "build/test/commit and stop" request means:
|
||||
|
||||
- complete the named edits;
|
||||
- run the bounded verification loop once;
|
||||
- commit the coherent checkpoint if verification passes;
|
||||
- report what passed and stop.
|
||||
|
||||
Do not publish to WordPress.org, production sites, or public update channels unless the user explicitly asks for that release step.
|
||||
@@ -1,12 +1,41 @@
|
||||
# Bible Reference Tooltips Changelog
|
||||
|
||||
## 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.
|
||||
- Adds a settings migration that removes retired external-link options while preserving active tooltip settings.
|
||||
- Updates the standalone changelog to match the public 1.1.x transition history.
|
||||
|
||||
## 1.1.4 - 2026-07-11
|
||||
|
||||
- Removes external Biblia, ref.ly, and Logos link generation from the public plugin.
|
||||
- Adds optional WordPress admin scanning with separate admin scan selectors.
|
||||
- Improves matching-rule defaults for capitalization, chapter-only references, and public comments.
|
||||
- Generalizes the open text policy language.
|
||||
|
||||
## 1.1.3 - 2026-07-11
|
||||
|
||||
- Fixes browser REST lookups when WordPress provides a query-string REST route.
|
||||
- Improves local test-page layout and removes native browser title tooltips from test references.
|
||||
|
||||
## 1.1.2 - 2026-07-11
|
||||
|
||||
- Splits eBible VPL source-code resolution from human Bible reference parsing.
|
||||
- Adds exact source-code handling for compact eBible book identifiers such as `SOL`, `EZE`, `MAR`, `JOH`, `JAM`, `1JO`, `2JO`, and `3JO`.
|
||||
- Improves compact reference detection for forms such as `Mark10:45` and `2Jn1:6`.
|
||||
|
||||
## 1.1.1 - 2026-07-11
|
||||
|
||||
- Adds exact eBible VPL aliases for source abbreviations that were not standard display abbreviations.
|
||||
- Fixes imports for books such as Mark and Ezekiel when VPL source files use compact book codes.
|
||||
|
||||
## 1.1.0 - 2026-07-11
|
||||
|
||||
- Forked the former internal Bible tooltip package into a standalone public plugin named Bible Reference Tooltips.
|
||||
- Renamed the plugin slug, text domain, REST namespace, script/style handles, admin action names, option key, and local verse table for the public package.
|
||||
- Removed internal menu placement and internal logging dependencies from the public package.
|
||||
- Added public release metadata, GPL license headers, and WordPress.org-oriented naming.
|
||||
- Kept local Bible text imports, frontend reference detection, hover/focus cards, translation tabs, Biblia.com links, Logos links, skipped-tag controls, comment scanning, and James alias/backfill repairs.
|
||||
- Kept local Bible text imports, frontend reference detection, hover/focus cards, translation tabs, skipped-tag controls, comment scanning, and James alias/backfill repairs.
|
||||
|
||||
## 1.0.14 - 2026-06-25
|
||||
|
||||
|
||||
@@ -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.0
|
||||
* Version: 1.1.5
|
||||
* Stage: Stable
|
||||
* Release Channel: Public
|
||||
* Public Release: Yes
|
||||
@@ -26,10 +26,11 @@ if (!defined('ABSPATH')) exit;
|
||||
|
||||
if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
class Bible_Reference_Tooltips {
|
||||
const VERSION = '1.1.0';
|
||||
const VERSION = '1.1.5';
|
||||
const SLUG = 'bible-reference-tooltips';
|
||||
const NONCE = 'brt_nonce';
|
||||
const OPTION_KEY = 'brt_settings';
|
||||
const SETTINGS_VERSION = '1.1.5';
|
||||
const TABLE_VERSION = '1';
|
||||
|
||||
private $book_map = null;
|
||||
@@ -40,6 +41,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
add_action('admin_init', [$this, 'ensure_schema']);
|
||||
add_action('admin_menu', [$this, 'admin_menu'], 110);
|
||||
add_action('wp_enqueue_scripts', [$this, 'frontend_assets']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'admin_assets']);
|
||||
add_action('rest_api_init', [$this, 'rest_routes']);
|
||||
add_filter('the_content', [$this, 'mark_content_scope'], 1);
|
||||
add_filter('comment_text', [$this, 'mark_comment_scope'], 1);
|
||||
@@ -72,7 +74,16 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
}
|
||||
|
||||
public function ensure_defaults() {
|
||||
if (!get_option(self::OPTION_KEY)) update_option(self::OPTION_KEY, self::defaults(), false);
|
||||
$settings = get_option(self::OPTION_KEY);
|
||||
if (!is_array($settings)) {
|
||||
update_option(self::OPTION_KEY, self::defaults(), false);
|
||||
update_option('brt_settings_version', self::SETTINGS_VERSION, false);
|
||||
return;
|
||||
}
|
||||
if (get_option('brt_settings_version') !== self::SETTINGS_VERSION) {
|
||||
update_option(self::OPTION_KEY, $this->normalize_settings($settings), false);
|
||||
update_option('brt_settings_version', self::SETTINGS_VERSION, false);
|
||||
}
|
||||
}
|
||||
|
||||
public function ensure_schema() {
|
||||
@@ -88,23 +99,17 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
public static function defaults() {
|
||||
return [
|
||||
'enabled' => '1',
|
||||
'admin_enabled' => '0',
|
||||
'translation' => 'WEB',
|
||||
'scope' => 'body',
|
||||
'scope' => '.brt-scan-scope, main, .entry-content, .wp-block-post-content',
|
||||
'admin_scope' => '#wpbody-content',
|
||||
'theme' => 'auto',
|
||||
'underline' => '1',
|
||||
'show_translation' => '1',
|
||||
'show_tooltips' => '1',
|
||||
'link_references' => '1',
|
||||
'link_target' => 'same',
|
||||
'bible_version' => '',
|
||||
'logos_insert' => '0',
|
||||
'logos_existing' => '0',
|
||||
'logos_translation' => '',
|
||||
'logos_icon' => 'dark',
|
||||
'add_tooltips_to_links' => '0',
|
||||
'case_sensitive' => '0',
|
||||
'tag_chapters' => '0',
|
||||
'scan_comments' => '0',
|
||||
'case_insensitive' => '1',
|
||||
'tag_chapters' => '1',
|
||||
'scan_comments' => '1',
|
||||
'skip_tags' => ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'SELECT', 'BUTTON', 'CODE', 'PRE', 'H1', 'H2', 'H3'],
|
||||
'max_range' => '12',
|
||||
];
|
||||
@@ -112,9 +117,18 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
|
||||
private function settings() {
|
||||
$settings = get_option(self::OPTION_KEY, []);
|
||||
return $this->normalize_settings(is_array($settings) ? $settings : []);
|
||||
}
|
||||
|
||||
private function normalize_settings($settings) {
|
||||
$settings = array_merge(self::defaults(), is_array($settings) ? $settings : []);
|
||||
if (array_key_exists('case_sensitive', $settings) && !array_key_exists('case_insensitive', $settings)) {
|
||||
$settings['case_insensitive'] = empty($settings['case_sensitive']) ? '1' : '0';
|
||||
}
|
||||
$settings['skip_tags'] = $this->normalize_skip_tags($settings['skip_tags'] ?? []);
|
||||
return $settings;
|
||||
$clean = [];
|
||||
foreach (array_keys(self::defaults()) as $key) $clean[$key] = $settings[$key];
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private static function install_schema() {
|
||||
@@ -150,6 +164,16 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
public function frontend_assets() {
|
||||
$settings = $this->settings();
|
||||
if (empty($settings['enabled']) || is_admin() || is_feed() || wp_doing_ajax()) return;
|
||||
$this->enqueue_tooltip_assets($settings, (string)$settings['scope']);
|
||||
}
|
||||
|
||||
public function admin_assets($hook_suffix = '') {
|
||||
$settings = $this->settings();
|
||||
if (empty($settings['admin_enabled']) || wp_doing_ajax()) return;
|
||||
$this->enqueue_tooltip_assets($settings, (string)$settings['admin_scope']);
|
||||
}
|
||||
|
||||
private function enqueue_tooltip_assets($settings, $scope) {
|
||||
wp_register_script('brt', '', [], self::VERSION, true);
|
||||
wp_enqueue_script('brt');
|
||||
wp_register_style('brt', false, [], self::VERSION);
|
||||
@@ -160,22 +184,14 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
wp_localize_script('brt', 'BibleReferenceTooltips', [
|
||||
'rest' => esc_url_raw(rest_url('bible-reference-tooltips/v1/lookup')),
|
||||
'nonce' => wp_create_nonce('wp_rest'),
|
||||
'scope' => (string)$settings['scope'],
|
||||
'scope' => $scope,
|
||||
'translation' => $default_translation,
|
||||
'translations' => $installed_translations,
|
||||
'theme' => (string)$settings['theme'],
|
||||
'underline' => !empty($settings['underline']),
|
||||
'showTranslation' => !empty($settings['show_translation']),
|
||||
'showTooltips' => !empty($settings['show_tooltips']),
|
||||
'linkReferences' => !empty($settings['link_references']),
|
||||
'linkTarget' => (string)$settings['link_target'],
|
||||
'bibleVersion' => (string)($settings['bible_version'] ?: $default_translation),
|
||||
'logosInsert' => !empty($settings['logos_insert']),
|
||||
'logosExisting' => !empty($settings['logos_existing']),
|
||||
'logosTranslation' => (string)($settings['logos_translation'] ?: $default_translation),
|
||||
'logosIcon' => (string)$settings['logos_icon'],
|
||||
'addTooltipsToLinks' => !empty($settings['add_tooltips_to_links']),
|
||||
'caseSensitive' => !empty($settings['case_sensitive']),
|
||||
'caseSensitive' => empty($settings['case_insensitive']),
|
||||
'tagChapters' => !empty($settings['tag_chapters']),
|
||||
'scanComments' => !empty($settings['scan_comments']),
|
||||
'skipTags' => array_values((array)$settings['skip_tags']),
|
||||
@@ -409,26 +425,6 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
];
|
||||
}
|
||||
|
||||
private function bible_link_versions() {
|
||||
return [
|
||||
'WEB' => 'WEB',
|
||||
'KJV' => 'KJV',
|
||||
'ASV' => 'ASV',
|
||||
'YLT' => 'YLT',
|
||||
'ESV' => 'ESV',
|
||||
'NIV' => 'NIV',
|
||||
'NKJV' => 'NKJV',
|
||||
'NLT' => 'NLT',
|
||||
'NASB' => 'NASB',
|
||||
'CSB' => 'CSB',
|
||||
'NET' => 'NET',
|
||||
'RSV' => 'RSV',
|
||||
'NRSV' => 'NRSV',
|
||||
'LEB' => 'LEB',
|
||||
'MSG' => 'The Message',
|
||||
];
|
||||
}
|
||||
|
||||
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'] ?? ''));
|
||||
@@ -442,7 +438,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
'PHM' => ['PHM', 'PHLM', 'PHILEM'],
|
||||
'REV' => ['REV', 'RE', 'APO'],
|
||||
];
|
||||
$candidates = array_merge([$abbr, $name], $variants[$abbr] ?? []);
|
||||
$candidates = array_merge([$abbr, $name], $variants[$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));
|
||||
@@ -469,7 +465,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
|
||||
private function parse_reference($reference) {
|
||||
$reference = trim(preg_replace('/\s+/', ' ', (string)$reference));
|
||||
if (!preg_match('/^((?:[1-3]\s*)?[A-Za-z][A-Za-z .]+?)\s+(\d{1,3})(?:\s*:\s*(\d{1,3})(?:\s*[-\x{2013}\x{2014}]\s*(\d{1,3}))?)?$/u', $reference, $m)) return null;
|
||||
if (!preg_match('/^((?:(?:[1-3]|I{1,3})\s*)?[A-Za-z][A-Za-z .\'-]*?)[\s.]*?(\d{1,3})(?:\s*[:.]\s*(\d{1,3})(?:\s*[-\x{2013}\x{2014}]\s*(\d{1,3}))?)?$/iu', $reference, $m)) return null;
|
||||
$book = $this->normalize_book($m[1]);
|
||||
if (!$book) return null;
|
||||
return [
|
||||
@@ -507,6 +503,52 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private function normalize_source_book($book, $source = '') {
|
||||
$key = strtoupper(preg_replace('/[^A-Z0-9]/i', '', (string)$book));
|
||||
if ($source === 'ebible_vpl') {
|
||||
$map = $this->ebible_vpl_book_map();
|
||||
if (isset($map[$key])) return $map[$key];
|
||||
return ['abbr' => $key, 'name' => $this->book_name_from_abbr($key)];
|
||||
}
|
||||
$human = $this->normalize_book($book);
|
||||
if ($human) return $human;
|
||||
return ['abbr' => $key, 'name' => $this->book_name_from_abbr($key)];
|
||||
}
|
||||
|
||||
private function source_book_lookup_candidates($canonical_abbr) {
|
||||
$canonical_abbr = strtoupper(preg_replace('/[^A-Z0-9]/i', '', (string)$canonical_abbr));
|
||||
$candidates = [];
|
||||
foreach ($this->ebible_vpl_book_map() as $source_abbr => $book) {
|
||||
if (strtoupper($book['abbr']) === $canonical_abbr) $candidates[] = $source_abbr;
|
||||
}
|
||||
return $candidates;
|
||||
}
|
||||
|
||||
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)];
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function book_map() {
|
||||
if ($this->book_map !== null) return $this->book_map;
|
||||
$books = [
|
||||
@@ -535,7 +577,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
['ISA','Isaiah',['isaiah','isa']],
|
||||
['JER','Jeremiah',['jeremiah','jer']],
|
||||
['LAM','Lamentations',['lamentations','lam']],
|
||||
['EZK','Ezekiel',['ezekiel','ezek','ezk']],
|
||||
['EZK','Ezekiel',['ezekiel','ezek','ezk','eze']],
|
||||
['DAN','Daniel',['daniel','dan','dn']],
|
||||
['HOS','Hosea',['hosea','hos']],
|
||||
['JOL','Joel',['joel','jol','joe']],
|
||||
@@ -550,7 +592,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
['ZEC','Zechariah',['zechariah','zech','zec']],
|
||||
['MAL','Malachi',['malachi','mal']],
|
||||
['MAT','Matthew',['matthew','matt','mat','mt']],
|
||||
['MRK','Mark',['mark','mrk','mk']],
|
||||
['MRK','Mark',['mark','mrk','mk','mar']],
|
||||
['LUK','Luke',['luke','luk','lk']],
|
||||
['JHN','John',['john','jhn','jn','joh']],
|
||||
['ACT','Acts',['acts','act']],
|
||||
@@ -571,9 +613,9 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
['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','ijohn']],
|
||||
['2JN','2 John',['2john','2jn','2jhn','iijohn']],
|
||||
['3JN','3 John',['3john','3jn','3jhn','iiijohn']],
|
||||
['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']],
|
||||
];
|
||||
@@ -649,7 +691,6 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<input type="hidden" name="action" value="brt_save_settings">
|
||||
<label class="brt-check"><input type="checkbox" name="enabled" value="1" <?php checked($settings['enabled'], '1'); ?>> Enable frontend scanning</label>
|
||||
<label>Default translation
|
||||
<select name="translation">
|
||||
<?php
|
||||
@@ -665,7 +706,10 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
</select>
|
||||
<span>The tooltip opens with this translation first. Other installed translations appear as quick switches inside the card.</span>
|
||||
</label>
|
||||
<label>Scan selectors <textarea name="scope" rows="3"><?php echo esc_textarea($settings['scope']); ?></textarea><span>Comma-separated CSS selectors. Use body for the whole public page, or narrower selectors for faster scanning.</span></label>
|
||||
<label class="brt-check"><input type="checkbox" name="enabled" value="1" <?php checked($settings['enabled'], '1'); ?>> Enable public-site scanning</label>
|
||||
<label class="brt-check"><input type="checkbox" name="admin_enabled" value="1" <?php checked($settings['admin_enabled'], '1'); ?>> Enable WordPress admin scanning</label>
|
||||
<label>Public scan selectors <textarea name="scope" rows="3"><?php echo esc_textarea($settings['scope']); ?></textarea><span>Comma-separated CSS selectors for public pages. Examples: body scans the full page; main scans the main layout; .entry-content scans classic post content; .wp-block-post-content scans block-theme content; .entry-content, .wp-block-post-content scans either area.</span></label>
|
||||
<label>Admin scan selectors <textarea name="admin_scope" rows="2"><?php echo esc_textarea($settings['admin_scope']); ?></textarea><span>Comma-separated CSS selectors for wp-admin pages. #wpbody-content scans the main admin content area and avoids the WordPress toolbar and menu.</span></label>
|
||||
<div class="brt-row">
|
||||
<label>Card theme <select name="theme"><option value="auto" <?php selected($settings['theme'], 'auto'); ?>>Match site</option><option value="dark" <?php selected($settings['theme'], 'dark'); ?>>Dark</option><option value="light" <?php selected($settings['theme'], 'light'); ?>>Light</option></select></label>
|
||||
<label>Range limit <input type="number" min="1" max="50" name="max_range" value="<?php echo esc_attr($settings['max_range']); ?>"><span>Maximum verses shown for references like John 3:16-20.</span></label>
|
||||
@@ -673,31 +717,10 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
<label class="brt-check"><input type="checkbox" name="underline" value="1" <?php checked($settings['underline'], '1'); ?>> Underline detected references</label>
|
||||
<label class="brt-check"><input type="checkbox" name="show_translation" value="1" <?php checked($settings['show_translation'], '1'); ?>> Show translation in card header</label>
|
||||
<label class="brt-check"><input type="checkbox" name="show_tooltips" value="1" <?php checked($settings['show_tooltips'], '1'); ?>> Show hover/focus tooltip cards</label>
|
||||
<label class="brt-check"><input type="checkbox" name="link_references" value="1" <?php checked($settings['link_references'], '1'); ?>> Link detected Bible references to Biblia.com</label>
|
||||
<div class="brt-row">
|
||||
<label>Links open in <select name="link_target"><option value="same" <?php selected($settings['link_target'], 'same'); ?>>Existing window</option><option value="new" <?php selected($settings['link_target'], 'new'); ?>>New window</option></select></label>
|
||||
<label>Bible link version <select name="bible_version">
|
||||
<?php foreach ($this->bible_link_versions() as $code => $label): ?>
|
||||
<option value="<?php echo esc_attr($code); ?>" <?php selected($settings['bible_version'] ?: $settings['translation'], $code); ?>><?php echo esc_html($label); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select></label>
|
||||
</div>
|
||||
<h3>Logos Links</h3>
|
||||
<label class="brt-check"><input type="checkbox" name="logos_insert" value="1" <?php checked($settings['logos_insert'], '1'); ?>> Insert a small Logos Bible Software link icon beside detected references</label>
|
||||
<label class="brt-check"><input type="checkbox" name="logos_existing" value="1" <?php checked($settings['logos_existing'], '1'); ?>> Add a Logos icon to existing Logos Bible Software links</label>
|
||||
<div class="brt-row">
|
||||
<label>Logos Bible version <select name="logos_translation">
|
||||
<?php foreach ($this->bible_link_versions() as $code => $label): ?>
|
||||
<option value="<?php echo esc_attr($code); ?>" <?php selected($settings['logos_translation'] ?: $settings['translation'], $code); ?>><?php echo esc_html($label); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select></label>
|
||||
<label>Logos icon <select name="logos_icon"><option value="dark" <?php selected($settings['logos_icon'], 'dark'); ?>>Dark icon for light backgrounds</option><option value="light" <?php selected($settings['logos_icon'], 'light'); ?>>Light icon for dark backgrounds</option></select></label>
|
||||
</div>
|
||||
<h3>Matching Rules</h3>
|
||||
<label class="brt-check"><input type="checkbox" name="add_tooltips_to_links" value="1" <?php checked($settings['add_tooltips_to_links'], '1'); ?>> Add tooltips to existing Biblia.com and Ref.ly links</label>
|
||||
<label class="brt-check"><input type="checkbox" name="case_sensitive" value="1" <?php checked($settings['case_sensitive'], '1'); ?>> Tag Bible references with improper casing, such as jn 3:16 or JOHN 3:16</label>
|
||||
<label class="brt-check"><input type="checkbox" name="tag_chapters" value="1" <?php checked($settings['tag_chapters'], '1'); ?>> Tag chapter references, such as Genesis 1</label>
|
||||
<label class="brt-check"><input type="checkbox" name="scan_comments" value="1" <?php checked($settings['scan_comments'], '1'); ?>> Search for Bible references in user comments</label>
|
||||
<label class="brt-check"><input type="checkbox" name="case_insensitive" value="1" <?php checked($settings['case_insensitive'], '1'); ?>> Match references regardless of capitalization <span>Detects forms such as jn 3:16 and JOHN 3:16.</span></label>
|
||||
<label class="brt-check"><input type="checkbox" name="tag_chapters" value="1" <?php checked($settings['tag_chapters'], '1'); ?>> Detect chapter-only references <span>Allows references like Genesis 1. The tooltip shows the first verses up to the range limit.</span></label>
|
||||
<label class="brt-check"><input type="checkbox" name="scan_comments" value="1" <?php checked($settings['scan_comments'], '1'); ?>> Scan public comments <span>Adds tooltip detection to WordPress comment text.</span></label>
|
||||
<label>Do not search these HTML tags
|
||||
<span>Script, style, form fields, code, and preformatted blocks are always skipped.</span>
|
||||
</label>
|
||||
@@ -711,7 +734,8 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
</section>
|
||||
<section class="brt-card brt-wide">
|
||||
<h2>Open Text Policy</h2>
|
||||
<p>This plugin stores only open/public texts in your local WordPress database. WEB, KJV, ASV, and YLT are available from eBible VPL packages. NIV, NASB, and most modern commercial translations should remain external/licensed integrations unless you explicitly secure rights to store and redistribute them.</p>
|
||||
<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>
|
||||
<p>The built-in importer currently supports selected open/public-domain eBible VPL packages.</p>
|
||||
</section>
|
||||
</div>
|
||||
<p class="brt-footer">Bible Reference Tooltips <?php echo esc_html(self::VERSION); ?>. Local open Bible text lookup for WordPress.</p>
|
||||
@@ -765,32 +789,17 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
if (!in_array($translation, array_unique(array_merge($known, $installed)), true)) $translation = 'WEB';
|
||||
$theme = sanitize_key($_POST['theme'] ?? 'auto');
|
||||
if (!in_array($theme, ['auto', 'dark', 'light'], true)) $theme = 'auto';
|
||||
$link_target = sanitize_key($_POST['link_target'] ?? 'same');
|
||||
if (!in_array($link_target, ['same', 'new'], true)) $link_target = 'same';
|
||||
$logos_icon = sanitize_key($_POST['logos_icon'] ?? 'dark');
|
||||
if (!in_array($logos_icon, ['dark', 'light'], true)) $logos_icon = 'dark';
|
||||
$link_versions = array_keys($this->bible_link_versions());
|
||||
$bible_version = strtoupper(sanitize_text_field($_POST['bible_version'] ?? $translation));
|
||||
if (!in_array($bible_version, $link_versions, true)) $bible_version = $translation;
|
||||
$logos_translation = strtoupper(sanitize_text_field($_POST['logos_translation'] ?? $translation));
|
||||
if (!in_array($logos_translation, $link_versions, true)) $logos_translation = $translation;
|
||||
$settings = [
|
||||
'enabled' => !empty($_POST['enabled']) ? '1' : '0',
|
||||
'admin_enabled' => !empty($_POST['admin_enabled']) ? '1' : '0',
|
||||
'translation' => $translation,
|
||||
'scope' => sanitize_textarea_field($_POST['scope'] ?? self::defaults()['scope']),
|
||||
'admin_scope' => sanitize_textarea_field($_POST['admin_scope'] ?? self::defaults()['admin_scope']),
|
||||
'theme' => $theme,
|
||||
'underline' => !empty($_POST['underline']) ? '1' : '0',
|
||||
'show_translation' => !empty($_POST['show_translation']) ? '1' : '0',
|
||||
'show_tooltips' => !empty($_POST['show_tooltips']) ? '1' : '0',
|
||||
'link_references' => !empty($_POST['link_references']) ? '1' : '0',
|
||||
'link_target' => $link_target,
|
||||
'bible_version' => $bible_version,
|
||||
'logos_insert' => !empty($_POST['logos_insert']) ? '1' : '0',
|
||||
'logos_existing' => !empty($_POST['logos_existing']) ? '1' : '0',
|
||||
'logos_translation' => $logos_translation,
|
||||
'logos_icon' => $logos_icon,
|
||||
'add_tooltips_to_links' => !empty($_POST['add_tooltips_to_links']) ? '1' : '0',
|
||||
'case_sensitive' => !empty($_POST['case_sensitive']) ? '1' : '0',
|
||||
'case_insensitive' => !empty($_POST['case_insensitive']) ? '1' : '0',
|
||||
'tag_chapters' => !empty($_POST['tag_chapters']) ? '1' : '0',
|
||||
'scan_comments' => !empty($_POST['scan_comments']) ? '1' : '0',
|
||||
'skip_tags' => $this->normalize_skip_tags($_POST['skip_tags'] ?? []),
|
||||
@@ -889,15 +898,22 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($tmp) !== true) {
|
||||
@unlink($tmp);
|
||||
$this->delete_temp_dir($dir);
|
||||
return 'Could not open downloaded ' . $translation . ' archive.';
|
||||
}
|
||||
$zip->extractTo($dir);
|
||||
$zip->close();
|
||||
@unlink($tmp);
|
||||
$file = trailingslashit($dir) . $info['file'];
|
||||
if (!file_exists($file)) return $translation . ' VPL file was not found in archive.';
|
||||
if (!file_exists($file)) {
|
||||
$this->delete_temp_dir($dir);
|
||||
return $translation . ' VPL file was not found in archive.';
|
||||
}
|
||||
$handle = fopen($file, 'r');
|
||||
if (!$handle) return 'Could not read ' . $translation . ' VPL file.';
|
||||
if (!$handle) {
|
||||
$this->delete_temp_dir($dir);
|
||||
return 'Could not read ' . $translation . ' VPL file.';
|
||||
}
|
||||
global $wpdb;
|
||||
$wpdb->delete(self::table(), ['translation' => $translation], ['%s']);
|
||||
$batch = [];
|
||||
@@ -913,6 +929,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
}
|
||||
fclose($handle);
|
||||
if ($batch) $count += $this->insert_rows($batch, $info['source'], $info['license']);
|
||||
$this->delete_temp_dir($dir);
|
||||
if ($make_default) $this->set_active_translation($translation);
|
||||
self::log_event('text_imported', ['translation' => $translation, 'verses' => $count], 'Imported Bible text.');
|
||||
return $translation . ' import complete: ' . number_format_i18n($count) . ' verses installed.';
|
||||
@@ -977,7 +994,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
$line = trim((string)$line);
|
||||
if ($line === '') return null;
|
||||
if (!preg_match('/^((?:[1-3]\s*)?[A-Za-z][A-Za-z0-9 .\'-]{1,40}?)\s+(\d{1,3}):(\d{1,3})\s+(.+)$/u', $line, $m)) return null;
|
||||
$book = $this->normalize_book($m[1]) ?: ['abbr' => strtoupper(preg_replace('/[^A-Z0-9]/i', '', $m[1])), 'name' => $this->book_name_from_abbr($m[1])];
|
||||
$book = $this->normalize_source_book($m[1], 'ebible_vpl');
|
||||
$text = trim(wp_specialchars_decode($m[4], ENT_QUOTES));
|
||||
if ($text === '') return null;
|
||||
return [
|
||||
@@ -1102,7 +1119,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
}
|
||||
|
||||
private function frontend_css() {
|
||||
return '.brt-ref{cursor:help;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:3px;color:inherit;font-weight:700}.brt-ref:focus{outline:2px solid rgba(47,128,237,.45);outline-offset:2px}.brt-ref.no-underline{text-decoration:none}.brt-logos-link{display:inline-flex;align-items:center;justify-content:center;width:1.15em;height:1.15em;margin-left:.2em;border:1px solid currentColor;border-radius:.25em;font:800 .68em/1 system-ui,-apple-system,Segoe UI,sans-serif;text-decoration:none;vertical-align:.08em;opacity:.78}.brt-logos-link:hover,.brt-logos-link:focus{opacity:1;text-decoration:none}.brt-logos-dark{color:#162033;background:#fff}.brt-logos-light{color:#fff;background:#162033}.brt-pop{position:fixed;z-index:999999;max-width:min(520px,calc(100vw - 28px));padding:16px;border-radius:12px;border:1px solid rgba(148,163,184,.45);background:#fff;color:#0f172a;box-shadow:0 20px 50px rgba(15,23,42,.18);font:14px/1.55 system-ui,-apple-system,Segoe UI,sans-serif;pointer-events:auto;opacity:0;transform:translateY(5px);transition:opacity .22s ease,transform .22s ease}.brt-pop.ready{opacity:1;transform:translateY(0)}.brt-pop.dark{background:#080b10;color:#eef4ff;box-shadow:0 20px 60px rgba(0,0,0,.42)}.brt-pop.light{background:#fff;color:#0f172a;box-shadow:0 20px 50px rgba(15,23,42,.18)}.brt-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 10px}.brt-title{font-weight:800;font-size:15px;color:inherit;white-space:nowrap}.brt-tabs{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}.brt-tab{border:1px solid rgba(148,163,184,.45);border-radius:999px;background:rgba(148,163,184,.12);color:inherit;cursor:pointer;font:700 11px/1 system-ui,-apple-system,Segoe UI,sans-serif;padding:5px 8px}.brt-tab.active{background:#2f80ed;border-color:#2f80ed;color:#fff}.brt-pop.dark .brt-tab.active{background:#60a5fa;border-color:#60a5fa;color:#06101f}.brt-verse{margin:0 0 9px}.brt-verse:last-child{margin-bottom:0}.brt-verse-num{font-weight:900;margin-right:5px}.brt-foot{display:block;margin-top:11px;color:#64748b;font-size:12px}.brt-pop.dark .brt-foot{color:#93a4bb}.brt-pop .err{color:#b91c1c}.brt-pop.dark .err{color:#fca5a5}.brt-loading{display:flex;align-items:center;gap:10px;margin:3px 0 1px;color:#64748b}.brt-pop.dark .brt-loading{color:#93a4bb}.brt-spinner{width:14px;height:14px;border:2px solid rgba(148,163,184,.35);border-top-color:#2f80ed;border-radius:999px;animation:brtSpin .8s linear infinite}@keyframes brtSpin{to{transform:rotate(360deg)}}@media(max-width:520px){.brt-head{align-items:flex-start;flex-direction:column}.brt-tabs{justify-content:flex-start}}';
|
||||
return '.brt-ref{cursor:help;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:3px;color:inherit;font-weight:700}.brt-ref:focus{outline:2px solid rgba(47,128,237,.45);outline-offset:2px}.brt-ref.no-underline{text-decoration:none}.brt-pop{box-sizing:border-box;position:fixed;z-index:999999;width:max-content;max-width:min(560px,calc(100vw - 28px));min-width:min(320px,calc(100vw - 28px));padding:16px;border-radius:12px;border:1px solid rgba(148,163,184,.45);background:#fff;color:#0f172a;box-shadow:0 20px 50px rgba(15,23,42,.18);font:14px/1.55 system-ui,-apple-system,Segoe UI,sans-serif;pointer-events:auto;opacity:0;transform:translateY(5px);transition:opacity .22s ease,transform .22s ease}.brt-pop *{box-sizing:border-box}.brt-pop.ready{opacity:1;transform:translateY(0)}.brt-pop.dark{background:#080b10;color:#eef4ff;box-shadow:0 20px 60px rgba(0,0,0,.42)}.brt-pop.light{background:#fff;color:#0f172a;box-shadow:0 20px 50px rgba(15,23,42,.18)}.brt-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin:0 0 12px}.brt-title{display:block;min-width:0;flex:1 1 auto;font-weight:800;font-size:15px;line-height:1.3;color:inherit;white-space:normal;overflow-wrap:anywhere}.brt-label{font-weight:700;color:#475569}.brt-pop.dark .brt-label{color:#a8b7cc}.brt-tabs{display:flex;flex:0 0 auto;max-width:210px;gap:6px;flex-wrap:wrap;justify-content:flex-end}.brt-tab{border:1px solid rgba(148,163,184,.45);border-radius:999px;background:rgba(148,163,184,.12);color:inherit;cursor:pointer;font:700 11px/1 system-ui,-apple-system,Segoe UI,sans-serif;padding:5px 8px}.brt-tab.active{background:#2f80ed;border-color:#2f80ed;color:#fff}.brt-pop.dark .brt-tab.active{background:#60a5fa;border-color:#60a5fa;color:#06101f}.brt-body{clear:both;display:block;min-height:1.55em}.brt-verse{display:block;margin:0 0 9px;color:inherit}.brt-verse:last-child{margin-bottom:0}.brt-verse-num{font-weight:900;margin-right:5px}.brt-foot{display:block;margin-top:11px;color:#64748b;font-size:12px}.brt-pop.dark .brt-foot{color:#93a4bb}.brt-pop .err{color:#b91c1c}.brt-pop.dark .err{color:#fca5a5}.brt-loading{display:flex;align-items:center;gap:10px;margin:3px 0 1px;color:#64748b}.brt-pop.dark .brt-loading{color:#93a4bb}.brt-spinner{width:14px;height:14px;border:2px solid rgba(148,163,184,.35);border-top-color:#2f80ed;border-radius:999px;animation:brtSpin .8s linear infinite}@keyframes brtSpin{to{transform:rotate(360deg)}}@media(max-width:520px){.brt-head{flex-direction:column}.brt-tabs{justify-content:flex-start;max-width:none}.brt-pop{min-width:min(280px,calc(100vw - 28px))}}';
|
||||
}
|
||||
|
||||
private function frontend_js() {
|
||||
@@ -1114,18 +1131,18 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
'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',
|
||||
'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','Luke','Luk','Lk','John','Jhn','Jn',
|
||||
'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',
|
||||
'2\\s*John','2\\s*Jn','2\\s*Jhn','3\\s*John','3\\s*Jn','3\\s*Jhn','Jude','Jud','Revelation','Rev','Re'
|
||||
'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 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';
|
||||
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';
|
||||
const refFlags = BibleReferenceTooltips.caseSensitive ? 'g' : 'gi';
|
||||
const refRe = new RegExp(refPattern, refFlags);
|
||||
const refTestRe = new RegExp(refPattern, BibleReferenceTooltips.caseSensitive ? '' : 'i');
|
||||
@@ -1165,7 +1182,7 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
refRe.lastIndex = 0;
|
||||
let el = node.parentElement;
|
||||
while (el) {
|
||||
if (skip.has(el.tagName) || el.classList.contains('brt-ref') || el.classList.contains('brt-pop') || el.classList.contains('brt-logos-link')) return NodeFilter.FILTER_REJECT;
|
||||
if (skip.has(el.tagName) || el.classList.contains('brt-ref') || el.classList.contains('brt-pop')) return NodeFilter.FILTER_REJECT;
|
||||
el = el.parentElement;
|
||||
}
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
@@ -1185,77 +1202,20 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
if (offset > last) frag.appendChild(document.createTextNode(text.slice(last, offset)));
|
||||
const ref = makeReferenceNode(match);
|
||||
frag.appendChild(ref);
|
||||
if (BibleReferenceTooltips.logosInsert) frag.appendChild(makeLogosLink(match));
|
||||
last = offset + match.length;
|
||||
});
|
||||
if (last < text.length) frag.appendChild(document.createTextNode(text.slice(last)));
|
||||
node.parentNode.replaceChild(frag, node);
|
||||
}
|
||||
function makeReferenceNode(ref){
|
||||
const el = BibleReferenceTooltips.linkReferences ? document.createElement('a') : document.createElement('span');
|
||||
const el = document.createElement('span');
|
||||
el.className = 'brt-ref';
|
||||
if (!BibleReferenceTooltips.underline) el.className += ' no-underline';
|
||||
el.dataset.ref = ref;
|
||||
el.textContent = ref;
|
||||
if (BibleReferenceTooltips.linkReferences) {
|
||||
el.href = bibleUrl(ref, BibleReferenceTooltips.bibleVersion || BibleReferenceTooltips.translation || 'WEB');
|
||||
if (BibleReferenceTooltips.linkTarget === 'new') {
|
||||
el.target = '_blank';
|
||||
el.rel = 'noopener noreferrer';
|
||||
}
|
||||
} else {
|
||||
el.tabIndex = 0;
|
||||
}
|
||||
el.tabIndex = 0;
|
||||
return el;
|
||||
}
|
||||
function enhanceExistingLinks(root){
|
||||
if (!BibleReferenceTooltips.addTooltipsToLinks && !BibleReferenceTooltips.logosExisting) return;
|
||||
const links = root.querySelectorAll ? root.querySelectorAll('a[href]') : [];
|
||||
links.forEach(link => {
|
||||
if (link.classList.contains('brt-logos-link')) return;
|
||||
const href = String(link.getAttribute('href') || '').toLowerCase();
|
||||
const isBibleLink = href.includes('biblia.com') || href.includes('ref.ly') || href.includes('logos.com') || href.includes('logosres:');
|
||||
if (!isBibleLink) return;
|
||||
const ref = extractRef(link.textContent || link.getAttribute('title') || link.getAttribute('aria-label') || '');
|
||||
if (ref && BibleReferenceTooltips.addTooltipsToLinks && !link.classList.contains('brt-ref')) {
|
||||
link.classList.add('brt-ref');
|
||||
if (!BibleReferenceTooltips.underline) link.classList.add('no-underline');
|
||||
link.dataset.ref = ref;
|
||||
}
|
||||
if (BibleReferenceTooltips.logosExisting && href.includes('logos') && !link.nextElementSibling?.classList.contains('brt-logos-link')) {
|
||||
link.insertAdjacentElement('afterend', makeLogosLink(ref || link.textContent || ''));
|
||||
}
|
||||
});
|
||||
}
|
||||
function extractRef(text){
|
||||
refRe.lastIndex = 0;
|
||||
const match = refRe.exec(String(text || ''));
|
||||
refRe.lastIndex = 0;
|
||||
return match ? match[0] : '';
|
||||
}
|
||||
function bibleUrl(ref, version){
|
||||
const code = String(version || 'WEB').trim() || 'WEB';
|
||||
return 'https://biblia.com/bible/' + encodeURIComponent(code) + '/' + encodeURIComponent(String(ref || '').replace(/\s+/g, ' ').trim());
|
||||
}
|
||||
function logosUrl(ref, version){
|
||||
const compact = String(ref || '').replace(/\s+/g, '');
|
||||
const code = String(version || 'WEB').trim() || 'WEB';
|
||||
return 'https://ref.ly/logosref/Bible.' + encodeURIComponent(compact) + '?ver=' + encodeURIComponent(code);
|
||||
}
|
||||
function makeLogosLink(ref){
|
||||
const link = document.createElement('a');
|
||||
const icon = BibleReferenceTooltips.logosIcon === 'light' ? 'light' : 'dark';
|
||||
link.className = 'brt-logos-link brt-logos-' + icon;
|
||||
link.href = logosUrl(ref, BibleReferenceTooltips.logosTranslation || BibleReferenceTooltips.translation || 'WEB');
|
||||
link.textContent = 'L';
|
||||
link.setAttribute('aria-label', 'Open ' + String(ref || 'Bible reference') + ' in Logos Bible Software');
|
||||
link.title = 'Open in Logos Bible Software';
|
||||
if (BibleReferenceTooltips.linkTarget === 'new') {
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
}
|
||||
return link;
|
||||
}
|
||||
async function show(e){
|
||||
if (!BibleReferenceTooltips.showTooltips) return;
|
||||
const target = e.target.closest('.brt-ref');
|
||||
@@ -1296,7 +1256,8 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
const code = translation || 'WEB';
|
||||
const key = code + ':' + ref.toLowerCase();
|
||||
if (cache.has(key)) return cache.get(key);
|
||||
const url = BibleReferenceTooltips.rest + '?ref=' + encodeURIComponent(ref) + '&translation=' + encodeURIComponent(code);
|
||||
const separator = String(BibleReferenceTooltips.rest).includes('?') ? '&' : '?';
|
||||
const url = BibleReferenceTooltips.rest + separator + 'ref=' + encodeURIComponent(ref) + '&translation=' + encodeURIComponent(code);
|
||||
const data = await fetch(url, {headers:{'X-WP-Nonce': BibleReferenceTooltips.nonce || ''}}).then(r => r.json());
|
||||
cache.set(key, data);
|
||||
return data;
|
||||
@@ -1383,7 +1344,6 @@ if (!class_exists('Bible_Reference_Tooltips')) {
|
||||
if (BibleReferenceTooltips.scanComments) selectors.push('.brt-comment-scope');
|
||||
const roots = selectors.flatMap(s => Array.from(document.querySelectorAll(s)));
|
||||
Array.from(new Set(roots.length ? roots : [document.body])).forEach(root => {
|
||||
enhanceExistingLinks(root);
|
||||
scan(root);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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.0
|
||||
Stable tag: 1.1.5
|
||||
License: GPLv2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
@@ -12,17 +12,17 @@ Detect Bible references in WordPress content and show local hover cards from ope
|
||||
|
||||
== Description ==
|
||||
|
||||
Bible Reference Tooltips scans public WordPress content for Bible references such as John 3:16, Romans 8:28, or James 1:5. Detected references can be underlined, linked, and shown with hover or keyboard-focus tooltip cards.
|
||||
Bible Reference Tooltips scans WordPress content for Bible references such as John 3:16, Romans 8:28, or James 1:5. Detected references can be underlined and shown with hover or keyboard-focus tooltip cards.
|
||||
|
||||
Verse text is looked up from local open/public-domain Bible texts installed into your WordPress database. The plugin currently supports World English Bible, King James Version, American Standard Version, and Young's Literal Translation imports from eBible VPL packages.
|
||||
|
||||
Features include:
|
||||
|
||||
* Frontend Bible reference detection.
|
||||
* Public-site Bible reference detection.
|
||||
* Optional WordPress admin Bible reference detection.
|
||||
* Hover and focus tooltip cards.
|
||||
* Local verse storage with no remote lookup required during page views.
|
||||
* Multiple installed translations with quick-switch tabs in the card.
|
||||
* Biblia.com links and optional Logos Bible Software links.
|
||||
* Configurable scan selectors, skipped tags, comment scanning, tooltip theme, and verse range limit.
|
||||
|
||||
== Installation ==
|
||||
@@ -49,6 +49,35 @@ Yes. The plugin is standalone and appears under Settings in the WordPress admin.
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 1.1.5 =
|
||||
|
||||
* Added the public workspace conventions and tracked Docker test lab as the bounded release workflow.
|
||||
* Added a settings migration that removes retired external-link options while preserving current tooltip settings.
|
||||
* Updated the standalone changelog to match the public 1.1.x transition history.
|
||||
|
||||
= 1.1.4 =
|
||||
|
||||
* Removed external Biblia, ref.ly, and Logos link generation from the public plugin.
|
||||
* Added optional WordPress admin scanning with separate admin scan selectors.
|
||||
* Improved matching-rule defaults for capitalization, chapter-only references, and public comments.
|
||||
* Generalized the open text policy language.
|
||||
|
||||
= 1.1.3 =
|
||||
|
||||
* Fixed browser REST lookups when WordPress provides a query-string REST route.
|
||||
* Improved local test-page layout and removed native browser title tooltips from test references.
|
||||
|
||||
= 1.1.2 =
|
||||
|
||||
* Split eBible VPL source-code resolution from human Bible reference parsing.
|
||||
* Added exact source-code handling for compact eBible book identifiers such as SOL, EZE, MAR, JOH, JAM, 1JO, 2JO, and 3JO.
|
||||
* Improved compact reference detection for forms such as Mark10:45 and 2Jn1:6.
|
||||
|
||||
= 1.1.1 =
|
||||
|
||||
* Added exact eBible VPL aliases for source abbreviations that were not standard display abbreviations.
|
||||
* Fixed imports for books such as Mark and Ezekiel when VPL source files use compact book codes.
|
||||
|
||||
= 1.1.0 =
|
||||
|
||||
* Forked the internal package into a standalone public plugin.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# WordPress Plugin Test Lab
|
||||
|
||||
Local Docker test lab for public WordPress plugins.
|
||||
|
||||
The lab mounts plugin source from this workspace into disposable WordPress containers, installs WordPress with WP-CLI, activates the selected plugin, and runs smoke tests.
|
||||
|
||||
## Environments
|
||||
|
||||
Initial 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.
|
||||
|
||||
## Commands
|
||||
|
||||
Run all configured environments:
|
||||
|
||||
```powershell
|
||||
cd tools\testlab
|
||||
.\scripts\run-all.ps1
|
||||
```
|
||||
|
||||
Run one environment:
|
||||
|
||||
```powershell
|
||||
cd tools\testlab
|
||||
.\scripts\run-env.ps1 -Env wp7-php84
|
||||
```
|
||||
|
||||
Stop and remove one environment:
|
||||
|
||||
```powershell
|
||||
cd tools\testlab
|
||||
.\scripts\down-env.ps1 -Env wp7-php84
|
||||
```
|
||||
|
||||
Stop and remove all environments:
|
||||
|
||||
```powershell
|
||||
cd tools\testlab
|
||||
.\scripts\down-all.ps1
|
||||
```
|
||||
|
||||
## What The Smoke Test Does
|
||||
|
||||
- Starts WordPress and MariaDB.
|
||||
- Waits for the database and WordPress container.
|
||||
- Installs WordPress.
|
||||
- Activates `bible-reference-tooltips`.
|
||||
- Confirms PHP, WordPress, and plugin versions.
|
||||
- Confirms the plugin-created table exists.
|
||||
- Imports WEB, KJV, ASV, and YLT through the plugin's internal importer.
|
||||
- Confirms imported verse rows and canonical book coverage exist.
|
||||
- Confirms representative REST lookups, including compact references such as `Mark10:45`, `Eze 36:26`, and `2Jn1:6`.
|
||||
- Creates a post containing those references.
|
||||
|
||||
This stops before WordPress.org SVN or plugin submission work.
|
||||
@@ -0,0 +1,15 @@
|
||||
[
|
||||
{
|
||||
"name": "wp7-php84",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"environment": "wp7-php84",
|
||||
"wordpressImage": "wordpress:php8.4-apache",
|
||||
"dbImage": "mariadb:11",
|
||||
"hostPort": 8070,
|
||||
"startedAt": "2026-07-11T19:46:20.2262126-05:00",
|
||||
"checks": [
|
||||
{
|
||||
"name": "wp-cli available",
|
||||
"passed": true,
|
||||
"detail": ""
|
||||
},
|
||||
{
|
||||
"name": "wordpress installed",
|
||||
"passed": true,
|
||||
"detail": "installed"
|
||||
},
|
||||
{
|
||||
"name": "runtime versions",
|
||||
"passed": true,
|
||||
"detail": "WP=7.0.1 PHP=8.4.23"
|
||||
},
|
||||
{
|
||||
"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": "8.4.23",
|
||||
"wordpressVersion": "7.0.1",
|
||||
"pluginVersion": "1.1.5",
|
||||
"completedAt": "2026-07-11T19:49:33.7883010-05:00",
|
||||
"passed": true
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$LabRoot = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
|
||||
$WorkspaceRoot = Split-Path -Parent (Split-Path -Parent $LabRoot)
|
||||
$ConfigPath = Join-Path $LabRoot 'config\environments.json'
|
||||
$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'
|
||||
|
||||
function Get-TestLabEnvironments {
|
||||
Get-Content -LiteralPath $ConfigPath -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Get-TestLabEnvironment {
|
||||
param([Parameter(Mandatory)][string]$Name)
|
||||
$envs = Get-TestLabEnvironments
|
||||
$selected = $envs | Where-Object { $_.name -eq $Name } | Select-Object -First 1
|
||||
if (-not $selected) {
|
||||
throw "Unknown environment '$Name'. Available: $($envs.name -join ', ')"
|
||||
}
|
||||
return $selected
|
||||
}
|
||||
|
||||
function Convert-ToComposePath {
|
||||
param([Parameter(Mandatory)][string]$Path)
|
||||
return ($Path -replace '\\', '/')
|
||||
}
|
||||
|
||||
function Ensure-TestLabDirectories {
|
||||
New-Item -ItemType Directory -Force -Path $GeneratedRoot, $ResultsRoot | Out-Null
|
||||
}
|
||||
|
||||
function Get-EnvDir {
|
||||
param([Parameter(Mandatory)][string]$Name)
|
||||
return Join-Path $GeneratedRoot $Name
|
||||
}
|
||||
|
||||
function Get-ResultFile {
|
||||
param([Parameter(Mandatory)][string]$Name)
|
||||
return Join-Path $ResultsRoot "$Name.json"
|
||||
}
|
||||
|
||||
function Write-ComposeFile {
|
||||
param([Parameter(Mandatory)]$Environment)
|
||||
|
||||
Ensure-TestLabDirectories
|
||||
$envDir = Get-EnvDir $Environment.name
|
||||
New-Item -ItemType Directory -Force -Path $envDir | Out-Null
|
||||
|
||||
$pluginPath = Convert-ToComposePath $PluginSource
|
||||
$compose = @"
|
||||
services:
|
||||
db:
|
||||
image: $($Environment.dbImage)
|
||||
environment:
|
||||
MYSQL_DATABASE: wordpress
|
||||
MYSQL_USER: wordpress
|
||||
MYSQL_PASSWORD: wordpress
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
healthcheck:
|
||||
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-uroot", "-prootpass"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
wordpress:
|
||||
image: $($Environment.wordpressImage)
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "$($Environment.hostPort):80"
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db:3306
|
||||
WORDPRESS_DB_USER: wordpress
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
WORDPRESS_DB_NAME: wordpress
|
||||
WORDPRESS_DEBUG: "1"
|
||||
volumes:
|
||||
- wp_data:/var/www/html
|
||||
- ${pluginPath}:/var/www/html/wp-content/plugins/$PluginSlug
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
wp_data:
|
||||
"@
|
||||
Set-Content -LiteralPath (Join-Path $envDir 'compose.yml') -Value $compose -Encoding UTF8
|
||||
}
|
||||
|
||||
function Invoke-Compose {
|
||||
param(
|
||||
[Parameter(Mandatory)]$Environment,
|
||||
[Parameter(Mandatory)][string[]]$Arguments
|
||||
)
|
||||
$envDir = Get-EnvDir $Environment.name
|
||||
docker compose -p "wptest-$($Environment.name)" -f (Join-Path $envDir 'compose.yml') @Arguments
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "docker compose failed with exit code ${LASTEXITCODE}: $($Arguments -join ' ')"
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-WpCli {
|
||||
param(
|
||||
[Parameter(Mandatory)]$Environment,
|
||||
[Parameter(Mandatory)][string]$Command
|
||||
)
|
||||
Invoke-Compose $Environment @('exec', '-T', '-u', '33', 'wordpress', 'bash', '-lc', $Command)
|
||||
}
|
||||
|
||||
function Invoke-WpCliRoot {
|
||||
param(
|
||||
[Parameter(Mandatory)]$Environment,
|
||||
[Parameter(Mandatory)][string]$Command
|
||||
)
|
||||
Invoke-Compose $Environment @('exec', '-T', 'wordpress', 'bash', '-lc', $Command)
|
||||
}
|
||||
|
||||
function Write-ContainerPhpFile {
|
||||
param(
|
||||
[Parameter(Mandatory)]$Environment,
|
||||
[Parameter(Mandatory)][string]$ContainerPath,
|
||||
[Parameter(Mandatory)][string]$Content
|
||||
)
|
||||
$encoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Content))
|
||||
Invoke-WpCliRoot $Environment "rm -f '$ContainerPath' && printf '%s' '$encoded' | base64 -d > '$ContainerPath' && chown www-data:www-data '$ContainerPath'" | Out-Null
|
||||
}
|
||||
|
||||
function Write-ContainerTextFile {
|
||||
param(
|
||||
[Parameter(Mandatory)]$Environment,
|
||||
[Parameter(Mandatory)][string]$ContainerPath,
|
||||
[Parameter(Mandatory)][string]$Content,
|
||||
[string]$Mode = '0644'
|
||||
)
|
||||
$encoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Content))
|
||||
Invoke-WpCliRoot $Environment "rm -f '$ContainerPath' && printf '%s' '$encoded' | base64 -d > '$ContainerPath' && chmod '$Mode' '$ContainerPath' && chown www-data:www-data '$ContainerPath'" | Out-Null
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
foreach ($environment in Get-TestLabEnvironments) {
|
||||
Write-Host "=== Removing $($environment.name) ==="
|
||||
& "$PSScriptRoot\down-env.ps1" -Env $environment.name
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Env
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
$environment = Get-TestLabEnvironment -Name $Env
|
||||
Write-ComposeFile -Environment $environment
|
||||
Invoke-Compose $environment @('down', '-v', '--remove-orphans') | Out-Host
|
||||
@@ -0,0 +1,9 @@
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
$environments = Get-TestLabEnvironments
|
||||
foreach ($environment in $environments) {
|
||||
$name = [string]$environment.name
|
||||
Write-Host ""
|
||||
Write-Host "=== Running $name ==="
|
||||
& "$PSScriptRoot\run-env.ps1" -Env $name
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Env
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
$environment = Get-TestLabEnvironment -Name $Env
|
||||
Write-ComposeFile -Environment $environment
|
||||
|
||||
$result = [ordered]@{
|
||||
environment = $environment.name
|
||||
wordpressImage = $environment.wordpressImage
|
||||
dbImage = $environment.dbImage
|
||||
hostPort = $environment.hostPort
|
||||
startedAt = (Get-Date).ToString('o')
|
||||
checks = @()
|
||||
}
|
||||
|
||||
function Add-Check {
|
||||
param([string]$Name, [bool]$Passed, [string]$Detail = '')
|
||||
$script:result.checks += [ordered]@{ name = $Name; passed = $Passed; detail = $Detail }
|
||||
if ($Passed) {
|
||||
Write-Host "[PASS] $Name $Detail"
|
||||
} else {
|
||||
Write-Host "[FAIL] $Name $Detail"
|
||||
throw "Check failed: $Name $Detail"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Invoke-Compose $environment @('up', '-d') | Out-Host
|
||||
|
||||
$wpCli = 'if ! command -v wp >/dev/null 2>&1; then curl -fsSL https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar -o /usr/local/bin/wp && chmod +x /usr/local/bin/wp; fi'
|
||||
Invoke-WpCliRoot $environment $wpCli | Out-Host
|
||||
Add-Check 'wp-cli available' $true
|
||||
|
||||
if ($environment.PSObject.Properties.Name -contains 'wordpressVersion' -and $environment.wordpressVersion) {
|
||||
Invoke-WpCli $environment "wp core download --version=$($environment.wordpressVersion) --force --path=/var/www/html" | Out-Host
|
||||
Add-Check 'wordpress core version prepared' $true "target=$($environment.wordpressVersion)"
|
||||
}
|
||||
|
||||
$installScript = @'
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
for i in $(seq 1 40); do
|
||||
if wp core is-installed --path=/var/www/html >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
if wp core install --path=/var/www/html --url=http://localhost:__HOST_PORT__ --title=PluginTest --admin_user=admin --admin_password=adminpass --admin_email=admin@example.test --skip-email; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
exit 1
|
||||
'@ -replace '__HOST_PORT__', [string]$environment.hostPort
|
||||
Write-ContainerTextFile $environment '/tmp/brt-install.sh' $installScript '0755'
|
||||
Invoke-WpCli $environment 'bash /tmp/brt-install.sh' | Out-Host
|
||||
$installed = (Invoke-WpCli $environment 'wp core is-installed --path=/var/www/html && echo installed' | Out-String).Trim()
|
||||
Add-Check 'wordpress installed' ($installed -eq 'installed') $installed
|
||||
|
||||
$phpVersion = (Invoke-WpCliRoot $environment "php -r 'echo PHP_VERSION;'" | Out-String).Trim()
|
||||
$wpVersion = (Invoke-WpCli $environment 'wp core version --path=/var/www/html' | Out-String).Trim()
|
||||
Add-Check 'runtime versions' $true "WP=$wpVersion PHP=$phpVersion"
|
||||
$result.phpVersion = $phpVersion
|
||||
$result.wordpressVersion = $wpVersion
|
||||
|
||||
Invoke-WpCli $environment "wp plugin activate $PluginSlug --path=/var/www/html" | Out-Host
|
||||
Add-Check 'plugin activated' $true
|
||||
|
||||
$pluginVersion = (Invoke-WpCli $environment "wp plugin get $PluginSlug --field=version --path=/var/www/html" | Out-String).Trim()
|
||||
Add-Check 'plugin version detected' ($pluginVersion -eq $ExpectedPluginVersion) "version=$pluginVersion"
|
||||
$result.pluginVersion = $pluginVersion
|
||||
|
||||
$tablePhp = @'
|
||||
<?php
|
||||
global $wpdb;
|
||||
echo $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->prefix . 'brt_verses'));
|
||||
'@
|
||||
Write-ContainerPhpFile $environment '/tmp/brt-table.php' $tablePhp
|
||||
$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
|
||||
|
||||
$translationsToImport = @('WEB', 'KJV', 'ASV', 'YLT')
|
||||
foreach ($translationCode in $translationsToImport) {
|
||||
$importPhp = @"
|
||||
<?php
|
||||
`$plugin = new Bible_Reference_Tooltips();
|
||||
`$ref = new ReflectionClass(`$plugin);
|
||||
`$m = `$ref->getMethod('import_vpl_translation');
|
||||
`$m->setAccessible(true);
|
||||
`$count = `$m->invoke(`$plugin, '$translationCode', false);
|
||||
echo `$count;
|
||||
"@
|
||||
Write-ContainerPhpFile $environment '/tmp/brt-import.php' $importPhp
|
||||
$imported = (Invoke-WpCli $environment 'wp eval-file /tmp/brt-import.php --path=/var/www/html' | Out-String).Trim()
|
||||
$importNumberText = [regex]::Match($imported, '[\d,]+').Value -replace ',', ''
|
||||
$importNumber = if ($importNumberText) { [int]$importNumberText } else { 0 }
|
||||
Add-Check "$translationCode import completed" ($importNumber -gt 30000) $imported
|
||||
}
|
||||
Invoke-WpCli $environment "wp option patch update brt_settings translation WEB --path=/var/www/html" | Out-Host
|
||||
|
||||
$countPhp = @'
|
||||
<?php
|
||||
global $wpdb;
|
||||
$rows = $wpdb->get_results('SELECT translation, COUNT(*) row_count FROM ' . $wpdb->prefix . 'brt_verses GROUP BY translation ORDER BY translation', ARRAY_A);
|
||||
echo json_encode($rows);
|
||||
'@
|
||||
Write-ContainerPhpFile $environment '/tmp/brt-count.php' $countPhp
|
||||
$rowCount = (Invoke-WpCli $environment 'wp eval-file /tmp/brt-count.php --path=/var/www/html' | Out-String).Trim()
|
||||
Add-Check 'translation rows stored' ($rowCount -match 'WEB' -and $rowCount -match 'KJV' -and $rowCount -match 'ASV' -and $rowCount -match 'YLT') $rowCount
|
||||
|
||||
$bookCoveragePhp = @'
|
||||
<?php
|
||||
global $wpdb;
|
||||
$expected = ['GEN','EXO','LEV','NUM','DEU','JOS','JDG','RUT','1SA','2SA','1KI','2KI','1CH','2CH','EZR','NEH','EST','JOB','PSA','PRO','ECC','SNG','ISA','JER','LAM','EZK','DAN','HOS','JOL','AMO','OBA','JON','MIC','NAM','HAB','ZEP','HAG','ZEC','MAL','MAT','MRK','LUK','JHN','ACT','ROM','1CO','2CO','GAL','EPH','PHP','COL','1TH','2TH','1TI','2TI','TIT','PHM','HEB','JAS','1PE','2PE','1JN','2JN','3JN','JUD','REV'];
|
||||
$translations = ['WEB','KJV','ASV','YLT'];
|
||||
$missing = [];
|
||||
foreach ($translations as $translation) {
|
||||
$found = $wpdb->get_col($wpdb->prepare('SELECT DISTINCT book FROM ' . $wpdb->prefix . 'brt_verses WHERE translation = %s', $translation));
|
||||
$diff = array_values(array_diff($expected, $found));
|
||||
if ($diff) $missing[$translation] = $diff;
|
||||
}
|
||||
echo $missing ? json_encode($missing) : 'ok';
|
||||
'@
|
||||
Write-ContainerPhpFile $environment '/tmp/brt-book-coverage.php' $bookCoveragePhp
|
||||
$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')
|
||||
foreach ($ref in $refs) {
|
||||
$lookupPhp = @"
|
||||
<?php
|
||||
`$request = new WP_REST_Request('GET', '/bible-reference-tooltips/v1/lookup');
|
||||
`$request->set_param('ref', '$ref');
|
||||
`$request->set_param('translation', 'WEB');
|
||||
`$response = rest_do_request(`$request);
|
||||
`$data = `$response->get_data();
|
||||
echo (!empty(`$data['ok']) && !empty(`$data['verses'])) ? 'ok' : json_encode(`$data);
|
||||
"@
|
||||
Write-ContainerPhpFile $environment '/tmp/brt-lookup.php' $lookupPhp
|
||||
$lookupResult = (Invoke-WpCli $environment 'wp eval-file /tmp/brt-lookup.php --path=/var/www/html' | Out-String).Trim()
|
||||
Add-Check "REST lookup $ref" ($lookupResult -eq 'ok') $lookupResult
|
||||
}
|
||||
|
||||
Invoke-WpCli $environment "wp post create --post_type=post --post_status=publish --post_title='Bible Reference Tooltip Smoke Test' --post_content='John 3:16 Romans 8:28 James 1:5' --path=/var/www/html" | Out-Host
|
||||
Add-Check 'test post created' $true
|
||||
|
||||
$result.completedAt = (Get-Date).ToString('o')
|
||||
$result.passed = $true
|
||||
} catch {
|
||||
$result.completedAt = (Get-Date).ToString('o')
|
||||
$result.passed = $false
|
||||
$result.error = $_.Exception.Message
|
||||
throw
|
||||
} finally {
|
||||
Ensure-TestLabDirectories
|
||||
$result | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Get-ResultFile -Name $environment.name) -Encoding UTF8
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
$envs = Get-Content -LiteralPath $ConfigPath -Raw | ConvertFrom-Json
|
||||
foreach ($environment in $envs) {
|
||||
$name = [string]$environment.name
|
||||
Write-Host ""
|
||||
Write-Host "=== Seeding $name ==="
|
||||
& "$PSScriptRoot\seed-bible-reference-page.ps1" -Env $name
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Env
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
$environment = Get-TestLabEnvironment -Name $Env
|
||||
|
||||
$seedPhp = @'
|
||||
<?php
|
||||
$books = [
|
||||
['Genesis', 'Gen', 'Genesis 1:1', 'Gen 1:1'],
|
||||
['Exodus', 'Exod', 'Exodus 3:14', 'Exod 3:14'],
|
||||
['Leviticus', 'Lev', 'Leviticus 19:18', 'Lev 19:18'],
|
||||
['Numbers', 'Num', 'Numbers 6:24', 'Num 6:24'],
|
||||
['Deuteronomy', 'Deut', 'Deuteronomy 6:5', 'Deut 6:5'],
|
||||
['Joshua', 'Josh', 'Joshua 1:9', 'Josh 1:9'],
|
||||
['Judges', 'Judg', 'Judges 6:12', 'Judg 6:12'],
|
||||
['Ruth', 'Ruth', 'Ruth 1:16', 'Ruth 1:16'],
|
||||
['1 Samuel', '1 Sam', '1 Samuel 16:7', '1 Sam 16:7'],
|
||||
['2 Samuel', '2 Sam', '2 Samuel 7:22', '2 Sam 7:22'],
|
||||
['1 Kings', '1 Kgs', '1 Kings 8:27', '1 Kgs 8:27'],
|
||||
['2 Kings', '2 Kgs', '2 Kings 6:17', '2 Kgs 6:17'],
|
||||
['1 Chronicles', '1 Chr', '1 Chronicles 16:34', '1 Chr 16:34'],
|
||||
['2 Chronicles', '2 Chr', '2 Chronicles 7:14', '2 Chr 7:14'],
|
||||
['Ezra', 'Ezra', 'Ezra 7:10', 'Ezra 7:10'],
|
||||
['Nehemiah', 'Neh', 'Nehemiah 8:10', 'Neh 8:10'],
|
||||
['Esther', 'Esth', 'Esther 4:14', 'Esth 4:14'],
|
||||
['Job', 'Job', 'Job 19:25', 'Job 19:25'],
|
||||
['Psalms', 'Ps', 'Psalms 23:1', 'Ps 23:1'],
|
||||
['Proverbs', 'Prov', 'Proverbs 3:5', 'Prov 3:5'],
|
||||
['Ecclesiastes', 'Eccl', 'Ecclesiastes 3:1', 'Eccl 3:1'],
|
||||
['Song of Solomon', 'Song', 'Song of Solomon 2:4', 'Song 2:4'],
|
||||
['Isaiah', 'Isa', 'Isaiah 40:31', 'Isa 40:31'],
|
||||
['Jeremiah', 'Jer', 'Jeremiah 29:11', 'Jer 29:11'],
|
||||
['Lamentations', 'Lam', 'Lamentations 3:22', 'Lam 3:22'],
|
||||
['Ezekiel', 'Ezek', 'Ezekiel 36:26', 'Ezek 36:26'],
|
||||
['Daniel', 'Dan', 'Daniel 3:17', 'Dan 3:17'],
|
||||
['Hosea', 'Hos', 'Hosea 6:3', 'Hos 6:3'],
|
||||
['Joel', 'Joel', 'Joel 2:28', 'Joel 2:28'],
|
||||
['Amos', 'Amos', 'Amos 5:24', 'Amos 5:24'],
|
||||
['Obadiah', 'Obad', 'Obadiah 1:15', 'Obad 1:15'],
|
||||
['Jonah', 'Jonah', 'Jonah 2:2', 'Jonah 2:2'],
|
||||
['Micah', 'Mic', 'Micah 6:8', 'Mic 6:8'],
|
||||
['Nahum', 'Nah', 'Nahum 1:7', 'Nah 1:7'],
|
||||
['Habakkuk', 'Hab', 'Habakkuk 2:4', 'Hab 2:4'],
|
||||
['Zephaniah', 'Zeph', 'Zephaniah 3:17', 'Zeph 3:17'],
|
||||
['Haggai', 'Hag', 'Haggai 2:9', 'Hag 2:9'],
|
||||
['Zechariah', 'Zech', 'Zechariah 4:6', 'Zech 4:6'],
|
||||
['Malachi', 'Mal', 'Malachi 3:10', 'Mal 3:10'],
|
||||
['Matthew', 'Matt', 'Matthew 5:16', 'Matt 5:16'],
|
||||
['Mark', 'Mk', 'Mark 10:45', 'Mk 10:45'],
|
||||
['Luke', 'Lk', 'Luke 2:10', 'Lk 2:10'],
|
||||
['John', 'Jn', 'John 3:16', 'Jn 3:16'],
|
||||
['Acts', 'Acts', 'Acts 1:8', 'Acts 1:8'],
|
||||
['Romans', 'Rom', 'Romans 8:28', 'Rom 8:28'],
|
||||
['1 Corinthians', '1 Cor', '1 Corinthians 13:4', '1 Cor 13:4'],
|
||||
['2 Corinthians', '2 Cor', '2 Corinthians 5:17', '2 Cor 5:17'],
|
||||
['Galatians', 'Gal', 'Galatians 5:22', 'Gal 5:22'],
|
||||
['Ephesians', 'Eph', 'Ephesians 2:8', 'Eph 2:8'],
|
||||
['Philippians', 'Phil', 'Philippians 4:13', 'Phil 4:13'],
|
||||
['Colossians', 'Col', 'Colossians 3:23', 'Col 3:23'],
|
||||
['1 Thessalonians', '1 Thess', '1 Thessalonians 5:16', '1 Thess 5:16'],
|
||||
['2 Thessalonians', '2 Thess', '2 Thessalonians 3:3', '2 Thess 3:3'],
|
||||
['1 Timothy', '1 Tim', '1 Timothy 4:12', '1 Tim 4:12'],
|
||||
['2 Timothy', '2 Tim', '2 Timothy 1:7', '2 Tim 1:7'],
|
||||
['Titus', 'Titus', 'Titus 2:11', 'Titus 2:11'],
|
||||
['Philemon', 'Phlm', 'Philemon 1:6', 'Phlm 1:6'],
|
||||
['Hebrews', 'Heb', 'Hebrews 11:1', 'Heb 11:1'],
|
||||
['James', 'Jas', 'James 1:5', 'Jas 1:5'],
|
||||
['1 Peter', '1 Pet', '1 Peter 5:7', '1 Pet 5:7'],
|
||||
['2 Peter', '2 Pet', '2 Peter 3:9', '2 Pet 3:9'],
|
||||
['1 John', '1 Jn', '1 John 1:9', '1 Jn 1:9'],
|
||||
['2 John', '2 Jn', '2 John 1:6', '2 Jn 1:6'],
|
||||
['3 John', '3 Jn', '3 John 1:4', '3 Jn 1:4'],
|
||||
['Jude', 'Jude', 'Jude 1:24', 'Jude 1:24'],
|
||||
['Revelation', 'Rev', 'Revelation 21:4', 'Rev 21:4'],
|
||||
];
|
||||
|
||||
$translations = [
|
||||
['WEB', 'World English Bible'],
|
||||
['KJV', 'King James Version'],
|
||||
['ASV', 'American Standard Version'],
|
||||
['YLT', 'Young\'s Literal Translation'],
|
||||
];
|
||||
|
||||
$content = '<div class="brt-test-page">';
|
||||
$content .= '<style>.brt-test-page{box-sizing:border-box;width:min(1520px,calc(100vw - 120px));max-width:none;margin-left:50%;transform:translateX(-50%);font-family:system-ui,-apple-system,Segoe UI,sans-serif}.brt-test-page *{box-sizing:border-box}.brt-test-page p{max-width:760px;margin:0 0 18px;color:#4b5563}.brt-test-grid{display:grid;grid-template-columns:minmax(120px,.65fr) repeat(4,minmax(130px,1fr));border:1px solid #d7dee8;border-bottom:0;border-right:0;background:#fff}.brt-test-row{display:contents}.brt-test-head,.brt-test-book,.brt-test-cell{border-right:1px solid #d7dee8;border-bottom:1px solid #d7dee8;padding:10px 12px}.brt-test-head{position:sticky;top:0;z-index:1;background:#f3f6fa;font-weight:800}.brt-test-book{font-weight:800;background:#fff}.brt-test-cell{min-width:0}.brt-test-cell .ref-pair{display:flex;flex-direction:column;align-items:flex-start;gap:6px}.brt-test-cell a{display:inline-block;max-width:100%;font-weight:700;line-height:1.25;white-space:normal;overflow-wrap:anywhere}@media(max-width:980px){.brt-test-page{width:min(100%,calc(100vw - 36px))}.brt-test-grid{display:block;border:0}.brt-test-head{display:none}.brt-test-row{display:grid;grid-template-columns:1fr;border:1px solid #d7dee8;margin:0 0 12px}.brt-test-book{background:#f3f6fa}.brt-test-cell{display:grid;grid-template-columns:54px 1fr;gap:10px}.brt-test-cell:before{content:attr(data-version);font-weight:800;color:#4b5563}}}</style>';
|
||||
$content .= '<p>This page intentionally includes one full-name and one abbreviated Bible reference for every book of the Bible across all installed local translations.</p>';
|
||||
$content .= '<div class="brt-test-grid">';
|
||||
$content .= '<div class="brt-test-head">Book</div>';
|
||||
foreach ($translations as $translation) {
|
||||
$content .= '<div class="brt-test-head">' . esc_html($translation[0]) . '</div>';
|
||||
}
|
||||
foreach ($books as $book) {
|
||||
[$name, $abbr, $fullRef, $abbrRef] = $book;
|
||||
$content .= '<div class="brt-test-row">';
|
||||
$content .= '<div class="brt-test-book">' . esc_html($name) . '</div>';
|
||||
foreach ($translations as $translation) {
|
||||
[$code, $label] = $translation;
|
||||
$content .= '<div class="brt-test-cell" data-version="' . esc_attr($code) . '"><div class="ref-pair">';
|
||||
$content .= '<span>' . esc_html($fullRef) . '</span>';
|
||||
$content .= '<span>' . esc_html($abbrRef) . '</span>';
|
||||
$content .= '</div></div>';
|
||||
}
|
||||
$content .= '</div>';
|
||||
}
|
||||
$content .= '</div></div>';
|
||||
|
||||
$existing = get_page_by_path('bible-reference-tooltip-test');
|
||||
$post_data = [
|
||||
'post_title' => 'Bible Reference Tooltip Test',
|
||||
'post_name' => 'bible-reference-tooltip-test',
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish',
|
||||
'post_content' => $content,
|
||||
];
|
||||
|
||||
if ($existing) {
|
||||
$post_data['ID'] = $existing->ID;
|
||||
$page_id = wp_update_post($post_data, true);
|
||||
} else {
|
||||
$page_id = wp_insert_post($post_data, true);
|
||||
}
|
||||
|
||||
if (is_wp_error($page_id)) {
|
||||
fwrite(STDERR, $page_id->get_error_message());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
update_option('show_on_front', 'page');
|
||||
update_option('page_on_front', (int) $page_id);
|
||||
$settings = get_option('brt_settings', []);
|
||||
if (is_array($settings)) {
|
||||
$settings['translation'] = $settings['translation'] ?? 'WEB';
|
||||
update_option('brt_settings', $settings, false);
|
||||
}
|
||||
echo 'seeded front page ' . $page_id . ' with ' . count($books) . ' books, ' . count($translations) . ' translations, and ' . (count($books) * count($translations) * 2) . ' reference links';
|
||||
'@
|
||||
|
||||
Write-ContainerPhpFile $environment '/tmp/brt-seed-front.php' $seedPhp
|
||||
Invoke-WpCli $environment 'wp eval-file /tmp/brt-seed-front.php --path=/var/www/html' | Out-Host
|
||||
Reference in New Issue
Block a user