Repository navigation
Releases: jwincek/shelterkit-donations
Releases · jwincek/shelterkit-donations
Release list
3.0.0
2.0.1
[2.0.1] - 2026-07-05
Fixed
- WordPress 6.7+ "translation loading for the
shelter-donationsdomain was triggered too
early" notice on every admin request.Meta_Boxes::init()(running atplugins_loaded)
eagerly built the meta-box config, whose projection translates manifest labels before the
initaction. Hook registration now uses a new keys-only
Field_Manifest::get_meta_box_post_types()(never translates), and the translated config
is built lazily on first access — the same patternList_Columnsalready used.
2.0.0
[2.0.0] - 2026-07-05
Changed
- Renamed the plugin's public identity to
shelter-donations("Shelter Donations") in
preparation for the first WordPress.org release, joining theshelter-*plugin family
(shelter-pet-sync, shelter-events-wrapper). This is a breaking release for existing
installs — followmigration-scripts/MIGRATION-2.0.0.mdwhen upgrading a live site.- Text domain:
starter-shelter→shelter-donations(plugin header, all gettext calls,
translation template renamed tolanguages/shelter-donations.pot). - Main plugin file:
starter-shelter.php→shelter-donations.php(existing installs must
reactivate; the plugin directory must be renamed toshelter-donations/to match the slug). - Block namespace:
starter-shelter/*→shelter-donations/*(12 blocks), including the
Interactivity API store namespaces and block-binding sources. Stored content that uses
these blocks must be migrated withwp search-replace— see the migration guide. - Editor bootstrap global:
window.starterShelterBlocks→window.shelterDonationsBlocks. - WP-CLI command:
wp starter-shelter validate→wp shelter-donations validate. - Display name, log prefixes, and metadata (composer, PHPCS ruleset, Plugin/Author URI)
updated to the new identity; Author is now "VCPA Humane Society", crediting the
organization the plugin was originally built for.
- Text domain:
- Unchanged on purpose (no data migration needed): the
Starter_Shelter\PHP namespace and
STARTER_SHELTER_*constants (internal-only), allstarter_shelter_*hook/filter/cron names,
allsd_*option/meta keys and post types, and WooCommerce product/order data. - Dist packaging:
.distignorenow also excludes dev artifacts (phpcs.xml.dist,
.editorconfig,.wp-env.json, PHPUnit caches,AUDIT-*.md,migration-scripts/).
1.2.0
[1.2.0] - 2026-06-23
Added
- My Account → Annual Statement now produces a real, printable tax receipt. The "Print / Save as PDF" button opens a standalone, print-optimized contribution statement (no theme/account chrome) that auto-opens the browser print dialog — so members get a PDF via their browser with no server-side PDF dependency. It itemizes the year's donations, memorials, and memberships with a grand total, includes the organization details (from the WooCommerce store settings) and the donor's name/address, and carries a deductibility note that's filterable (
starter_shelter_receipt_tax_note) so a site can add its EIN / 501(c)(3) language. The donor is resolved from the session, so a member only ever prints their own statement. (Previously the Print button just reloaded the full account page.) - My Account → Donor Dashboard now has an editable Contact Details form (name, phone, mailing address), pre-filled from the donor record. Saving posts through the nonce-verified PRG handler and the owner-scoped
shelter-donors/update-profile+shelter-donors/update-addressabilities — completing the self-service set (previously the account area was entirely read-only). - My Account → My Memberships now lets members manage their memberships directly: an auto-renew toggle and a Cancel membership action (with confirm) on each active membership, plus the existing Renew link (now pointing at the configured membership page instead of the site home). Both post through the same nonce-verified PRG handler, which checks the membership belongs to the current user's donor before acting; cancellation runs through the
shelter-memberships/cancelability. Cancelling clears the term so the member also drops off the public Members Wall. - My Account → My Memberships now lets a member control their public recognition: a "List me on the public Members Wall" preference (opt-out model) that members can toggle themselves. This is the self-service counterpart to the Members Wall — previously a member had no way to opt out. Posts back via a nonce-verified PRG handler that resolves the current user's own donor (so a user can only change their own record), sets
is_anonymousacross their memberships, and the recognition-wall ability honors it immediately. Logic lives in WooCommerce-freeMy_Account::set_recognition_preference()/is_listed_publicly()(integration-tested). - New Members Wall block (
starter-shelter/members-wall) — a static (SSR, no-build) tiered recognition wall of active members who haven't opted out, grouped into membership-level sections ordered highest-first. Business members show their approved logo (toggleable) and link to their website; individuals show their display name. Inspector controls: group-by-tier, show-logos, columns, max-items. Backed by a new public read abilityshelter-memberships/recognition-wall(active + not anonymous; resolves business name or donor display name; sorts by tier amount then name). Opt-out model: members appear unlessis_anonymousis set — so placing this block publishes the names/logos of all current active members. Renders nothing on the front end when no members qualify. - New Business Member Slideshow block (
starter-shelter/business-slideshow) — a no-build, Interactivity-API (WP 6.9/7.0) carousel of active business members with an approved logo. All eligible members are server-rendered into a CSS-transform track and rotated client-side (no fetch/loading state); off-screen slides areinertfor correct tab order and screen-reader behavior. Autoplay pauses on hover/focus, respectsprefers-reduced-motion, and offers prev/next + play-pause + dot controls; an explicit pause isn't overridden by hover. Each logo links to the member's website (target="_blank" rel="noopener noreferrer") when set; the business-name caption is an optional block attribute (plus autoplay toggle, interval, and max-items in the inspector). Backed by a new public read abilityshelter-memberships/business-directory(active +membership_type=business+logo_status=approved+ resolvable logo, sorted by business name) and a newlogo_urlcomputed field onsd_membership(mirrors the memorialphoto_url). Renders nothing on the front end when no members qualify. - Memorial wall now has a Dedication filter dropdown (All Dedications / In Memory Of / In Honor Of) alongside the existing Type (person/pet) filter — the two axes are independent and compose. Mirrors the type filter end-to-end:
shelter-memorials/listgained adedicationinput (all/memory/honor),list_memorials()adds adedication_typequery constraint, and the block render + Interactivity store thread it through thememorial-dedicationURL param (server re-render on change, bookmarkable, carried through pagination + load-more, and reset by Clear). No new block attribute — the control defaults to "all". - PHPUnit test suites (PHPUnit 9.6 — the version the WordPress core test framework supports) with CI jobs across the PHP 8.1–8.3 matrix. The manifest validator checks structural contracts statically; these tests cover runtime behavior it can't see.
- Unit suite (
composer test,unit-testsjob): pure functions, no WordPress bootstrap — the memorial/dedication helpers (normalize_memorial_type,normalize_dedication_type,get_memorial_type_label,get_dedication_type_label) andDonor_Lookup::sanitize_display_name. The few WP functions these call (__,wp_strip_all_tags) are stubbed intests/bootstrap.php; runs in ~15ms with no database. - Integration suite (
composer test:integration,integration-testsjob): boots a real WordPress 7.0 + MySQL (no WooCommerce — the code under test is WC-free and the plugin self-guards) and exercises the create/list data flows across all three entities: memorials (donor display-name preference, dedication persistence + filter, anonymous path, hydrator computed labels, CSV Dedication column), donations (allocation default + override, anonymous, donor lifetime-giving accumulation, pre-resolved donor_id, campaign attach, list-by-allocation), and memberships (tier/type/amount, +1-year term, tier-amount fallback, business fields, lifetime giving, and renewal extending an active term + recording the order). Also covers the SQL aggregation reports —shelter-donations/get-stats(totals, count, distinct-donor count, average, by-allocation breakdown, campaign scoping),shelter-reports/dashboard-stats(cross-entity roll-up of donations / active+new memberships / memorials / donor totals), and the type-awareshelter-reports/campaign-progress(donation-drive raised vs membership-drive member-count, goal/progress/remaining math, tier filtering, the 100%-cap, and is_active) — the hand-written SUM/COUNT queries the Query builder can't express and where a silent miscount would hide. Backed bybin/install-wp-tests.sh, a curl-based (no-svn) installer for the WP test library; seetests/README.md. - Tooling:
phpunit/phpunit ^9.6inrequire-dev;config.platform.phppinned to 8.1.0 andcomposer.lockcommitted for reproducible CI; the CIintegration-testsjob runs against a MySQL service container.
- Unit suite (
dedication_type(In Memory Of / In Honor Of) is now surfaced on the display layers, not just stored. Three places: the memorial wall block shows it as an eyebrow label above each honoree name (both the SSR fallback and the Interactivity client template); the admin Memorials list table shows it as a sub-line under the Type (Person/Pet) badge; and the memorial CSV export gains a "Dedication" column. Driven by a newdedication_type_labelcomputed field on thesd_memorialmanifest (mirroringmemorial_type_label), backed by two new helpersHelpers\normalize_dedication_type()andHelpers\get_dedication_type_label(). Legacy rows with an empty_sd_dedication_typeread as "In Memory Of" (the field default).- Memorial admin edit screen now exposes the memorial-form's two orthogonal axes — Dedication (In Memory Of / In Honor Of) and Honoree Type (Person / Pet) — as separate fields in the (classic) meta box, instead of the single conflated
human/pet/honorselect. Rendered declaratively from the manifest. (An alternative block-editor sidebar UX exists inassets/js/memorial-panel.jsbut is parked/un-enqueued — see that file's header for why and how to revive it.) - New first-class
dedication_typememorial field (memory/honor, defaultmemory,show_in_rest). Wired end-to-end: manifest field +shelter-memorials/createinput +createability persists_sd_dedication_type+ product input-mapping reads_sd_dedication_typefrom cart/order item meta. This closes a data-loss gap: the memorial-form already collected the customer's In Memory / In Honor choice and the cart handler saved it to order-item meta, but it was never mapped into the memorial post — so the wall couldn't distinguish a memorial from an honorarium. New memorials (customer-submitted and admin-entered) now persist it. - New
shelter-reports/campaigns-progress(plural) batch ability returns campaign-progress entries for many campaigns in 2-3 SQL queries: one GROUP BY for donation totals, one GROUP BY for membership counts, plus aupdate_termmeta_cacheprime. Per-campaign tier_filter falls back to a focused count (rare path). Reports → Campaigns tab now uses this single call instead of N invocations of the singularshelter-reports/campaign-progress; the singular version stays as-is for one-off lookups (e.g., campaign-card). shelter-reports/campaign-reportability is now type-aware. Donation drives return{ progress: { total_raised, ... }, donations: [...] }(unchanged shape); membership drives return{ progress: { member_count, percent_of_goal, remaining }, memberships: [...] }. The per-campaign CSV export on the Reports → Campaigns tab now produces a membership-shaped CSV (M...