Files
Umbra-Bookshelf/umbra-bookshelf/docs/KNOWN-ISSUES.md
bdm 0bd78e587a Resolve the known issues; switch licence to GPL
Closes the three problems that blocked the consolidation and records the rest
as tasks.

Licence: GPLv2 or later (WordPress.org compatible)

  LICENSE is now the full GPLv2 text and NOTICE is gone (it was Apache
  specific). All 101 SPDX headers read GPL-2.0-or-later and every shipped
  header says "GPLv2 or later".

  This also fixes an inconsistency that predated the consolidation: the theme's
  style.css declared GPLv2 while its PHP files carried Apache headers.

  Old/ and archive/pre-umbra-releases/ keep their original Apache headers on
  purpose -- they are frozen snapshots, not shipped code.

Schema: the catalogue schema is canonical for bd_books and bd_series

  Adds Umbra_Schema_Migration. The catalogue and library modules previously
  both created those two tables with different columns, so whichever plugin
  activated first won and the other read and wrote against the wrong shape.

  The migration detects a library-shaped table, RENAMES it aside (never drops
  it), creates the canonical table and copies rows across:

    - book IDs preserved, keeping bd_book_meta, bd_reviews, bd_ratings and
      bd_review_shortlinks valid
    - title/slug/description resolved from the linked post
    - the library's single isbn/asin land on the ebook columns; amazon_asin is
      also set so existing product links keep working
    - series migrated first so books.series_id still resolves
    - post linkage rewritten as _bd_bookshelf_book_id (preserved meta key)
    - publish_date kept as _bd_bookshelf_publish_date (no catalogue column)
    - bd_ratings goodreads/amazon folded into rating_goodreads/rating_amazon
    - rows whose post is gone are skipped with a log line, not turned into
      unusable books
    - duplicate slugs de-duplicated, since catalogue slug is UNIQUE

  It runs before dbDelta, and on admin_init as well as activation, because
  plugin updates do not re-run activation hooks. Ordering matters: dbDelta adds
  columns but never drops them, so pointed at a library-shaped table it would
  graft on catalogue columns and leave a table satisfying neither schema.

  Umbra_Bookshelf_Helper_Install no longer creates bd_books or bd_series.

Scraping: Goodreads only, one implementation

  There were three Goodreads implementations and two Amazon ones; both library
  "scrapers" were empty stubs attached to cron events that fired on schedule to
  do nothing, and both real scraper classes were never called at all.

  Umbra_Amazon_Scraper is deleted and the duplicate HTML parsing is gone from
  Umbra_Rating_Sync, which now delegates to Umbra_Goodreads_Scraper. The two
  library stubs and their cron registrations are removed. One schedule
  survives: bd_bookshelf_sync_ratings, every 12 hours.

  The rating_amazon column is retained (it holds data and is populated during
  migration) and Amazon retail links are unaffected.

  A new rating fetch failure keeps the last known value instead of writing zero.
  The admin ratings notice no longer warns about Amazon scraping.

Umbra_SEO is now instantiated

  It registers its hooks from a constructor and the bootstrap never constructed
  it, so the class was dead code and no SEO output was produced. Its meta, Open
  Graph, Twitter card and sitemap hooks now attach. Completing SEO for generated
  book detail pages is docs/TASKS.md #2.

Assets that 404'd

  - assets/images/default-cover.png now holds a real 512x815 cover
    (WorkingTitleCover_512x815.png). The helper returned this path but the
    repository contained no images at all.
  - email/assets/js/admin.js is committed as an empty placeholder. It was
    enqueued on every email admin page but had never been committed.

Tests

  - tests/schema_migration_test.php: 42 checks against a real database, driven
    through the sqlite3 CLI on a throwaway file. Covers shape detection, row
    mapping, ID preservation, orphan handling, rating folding, idempotency and
    slug collisions.
  - tests/run_all.sh runs every suite.
  - The declared version is now checked against the plugin header rather than a
    literal, so a version bump cannot silently break the suite.

Docs

  - KNOWN-ISSUES.md rewritten: three resolved, three remaining.
  - TASKS.md added: email admin behaviour, book detail page SEO, batching the
    rating sync, reclaiming legacy tables, reconciling the two book models,
    retiring the Old/ archive.
  - SPEC and ARCHITECTURE updated for schema ownership, the migration ordering
    constraint, and the single scraper.

Version 1.1.0.
2026-10-22 09:47:00 -07:00

174 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Known issues
Defects and carried-over problems. For intended work, see `TASKS.md`.
Status at 1.1.0: the three issues that blocked the consolidation are resolved.
What remains is listed first.
---
## Open
### 1. Rating sync blocks a worker with `sleep()`
`includes/books/class-umbra-rating-sync.php`
`sync_all_ratings()` iterates up to 999 books and calls `sleep(2)` between each,
inside a cron run — roughly 33 minutes of a blocked PHP worker per tick.
Pre-existing. Deliberately left alone when Amazon scraping was removed, because
that change was scoped to dropping a source. Fix suggestion is in `TASKS.md` §3.
### 2. The two modules still model books differently
Making the catalogue schema canonical resolved the *table* conflict, but the
catalogue (row in `bd_books`) and the library (`book` custom post type with
per-post meta) are still independent models with no synchronisation between
them. Editing a book in one place does not update the other.
See `TASKS.md` §5 for the decision to make.
### 3. Set-aside legacy tables are never cleaned up
On a reconciled installation, `bd_books_legacy_library` and
`bd_series_legacy_library` remain indefinitely. Inert, but untidy. See
`TASKS.md` §4.
---
## Resolved in 1.1.0
### Catalogue schema is now canonical for `bd_books` and `bd_series`
**Was:** the catalogue and library modules both created `bd_books` and
`bd_series`, with different columns. Whichever activated first won; the other
either no-opped or errored, then read and wrote against the wrong shape. The
divergence was:
```
catalogue wp_bd_books id, title, slug, description, cover_image, isbn_*,
asin_*, amazon_asin, goodreads_id, rating_*, series_id
library wp_bd_books id, post_id, pen_name_id, series_id, volume_number,
asin, isbn, publish_date, sort_order
```
**Now:** `includes/class-umbra-schema-migration.php` runs before `dbDelta` on
activation and on update. If it finds a library-shaped table it renames it
aside (**never drops it**), creates the canonical table, and copies rows across:
* book **IDs are preserved**, which keeps `bd_book_meta`, `bd_reviews`,
`bd_ratings` and `bd_review_shortlinks` valid
* `title`, `slug` and `description` are resolved from the linked post
* the library's single `isbn`/`asin` land on the ebook columns; `amazon_asin` is
also populated so existing product links keep working
* `series_id` is preserved; the series row is migrated first so it still resolves
* post linkage is written back as `_bd_bookshelf_book_id` (preserved meta key)
* `publish_date` has no catalogue column, so it is kept as
`_bd_bookshelf_publish_date` post meta
* goodreads/amazon values in `bd_ratings` are folded into the catalogue's
`rating_goodreads` / `rating_amazon` columns
Rows whose post no longer exists are skipped with a log entry rather than
creating an unusable book. Duplicate slugs are de-duplicated, because the
catalogue's `slug` column is `UNIQUE`.
Verified by `tests/schema_migration_test.php` — 42 checks against a real
database, covering shape detection, row mapping, ID preservation, orphan
handling, rating folding, idempotency and slug collisions.
### Only Goodreads scraping remains
**Was:** three Goodreads implementations and two Amazon ones:
| Implementation | State before |
|---|---|
| `Umbra_Goodreads_Scraper` | complete, 325 lines, **never called** |
| `Umbra_Rating_Sync::fetch_goodreads_rating()` | working duplicate |
| library `refresh_goodreads_data()` | empty stub, but scheduled daily |
| `Umbra_Amazon_Scraper` | complete, 296 lines, **never called** |
| `Umbra_Rating_Sync::fetch_amazon_rating()` | working duplicate |
Both library "scrapers" were empty stubs attached to cron events, so
`bd_bookshelf_goodreads_refresh` (daily) and `bd_bookshelf_amazon_scrape`
(six-hourly) fired on schedule to do nothing.
**Now:** Goodreads only, and one implementation of it.
`Umbra_Amazon_Scraper` is deleted; the duplicate HTML parsing is gone from
`Umbra_Rating_Sync`, which now delegates to `Umbra_Goodreads_Scraper`. The two
library stubs and their cron registrations are removed. The surviving schedule
is the single `bd_bookshelf_sync_ratings` event every 12 hours.
The `rating_amazon` **column** is retained — it holds existing data and is
populated from `bd_ratings` during migration. Amazon retail *links* are
unaffected; only rating scraping was removed. The two former hooks are still
cleared on deactivation and uninstall so no stale schedules survive an upgrade.
### Licence is GPLv2 or later
**Was:** plugin headers said `GPLv2 or later` while `LICENSE`, `NOTICE` and 101
SPDX headers said Apache-2.0. The theme was worse: `style.css` declared GPLv2
while its PHP files carried Apache headers.
**Now:** GPL-2.0-or-later throughout, which is what WordPress.org requires and
what the theme already claimed. `LICENSE` is the full GPLv2 text; `NOTICE` is
removed (it was Apache-specific); every SPDX header reads
`GPL-2.0-or-later`.
`Old/` and `archive/pre-umbra-releases/` keep their original Apache headers on
purpose — they are frozen pre-consolidation snapshots, not shipped code.
### Default cover was missing
`Umbra_Bookshelf_Helper` returned `assets/images/default-cover.png` as the
fallback cover, but the repository contained no images at all, so every
cover-less book rendered a broken image. A real cover
(`WorkingTitleCover_512x815.png`, 512×815) is now committed at that path.
Replace with final artwork before release if this was only a working title.
### Email admin script did not exist
`Umbra_Simple_Email_Admin::enqueue_assets()` enqueued `email/assets/js/admin.js`
on every email admin page, but the file was never committed — a guaranteed 404.
An empty placeholder is now committed with a comment explaining its history.
Defining its behaviour is `TASKS.md` §1.
### `Umbra_SEO` was loaded but never instantiated
It registers its hooks from a constructor, and the bootstrap did not construct
it, so the class was dead code. It is now instantiated and its meta/Open
Graph/Twitter/sitemap hooks attach. Completing SEO for generated book detail
pages is `TASKS.md` §2.
---
## Fixed in 1.0.0 (the consolidation)
* **`Cannot declare class` fatal.** `class-bdm-theme-compatibility.php` declared
`BD_BookShelfHelper`, the same class name as the library plugin's own helper.
Latent while the two were separate plugins — it only fired when both were
active — but an unconditional fatal once merged. The shim is now
`Umbra_Theme_Compatibility`, with `class_alias()` preserving the legacy name
so the unmodified theme keeps working.
* **Duplicate `bd_bookshelf_import_csv` AJAX handler.** `Umbra_Admin` and
`Umbra_Migration` both registered it; the second silently replaced the first.
The import page now lives solely in `Umbra_Admin` (the variant wired to
`admin/js/umbra-bookshelf-admin.js`); `Umbra_Migration` keeps only the
preview/run endpoints it uniquely provides.
* **Duplicate `bd-bookshelf-settings` submenu.** Catalogue and library both
claimed the slug, leaving one Settings screen unreachable. Now
`umbra-bookshelf-settings` and `umbra-bookshelf-library-settings`.
* **Duplicate admin menu.** All three modules called `add_menu_page`. Now one
top-level menu, registered by the bootstrap at `admin_menu` priority 9.
* **Colliding asset handles.** Catalogue and library both enqueued
`bd-bookshelf-admin`; the second enqueue was silently discarded. Handles are
now unique per module.
* **Modules could miss their own `init` callbacks.** Constructing modules from
inside an `init` callback means their `init` registrations race the hook being
dispatched, so the `book` post type and `[bd-book]` shortcode could fail to
register. Modules now construct on `plugins_loaded`, behind a re-entrancy
guard.
* **Over-broad asset hook check.** The library module's
`strpos($hook, 'bd-bookshelf')` matched every catalogue and email screen once
all slugs shared the `umbra-bookshelf-` prefix. Now matched against its own
slug list.