=== Membership Manager ===
Contributors:      jeffdalewb3isp
Tags:              membership, members, paypal, access control, roster
Requires at least: 6.0
Tested up to:      6.8
Stable tag:        1.22.0
Requires PHP:      7.4
License:           GPL-2.0+
License URI:       https://www.gnu.org/licenses/gpl-2.0.html

Full-featured membership management with PayPal payments, tiered levels, access control,
an active member roster, and automated Dec 31 renewal reminders.

== Description ==

Membership Manager provides everything a club or organisation needs to manage members
directly inside WordPress:

**Core Features**
* Membership levels with custom pricing — Standard (annual), Lifetime (no renewal), Silent Key, and Guest / Visitor (attendance-only, no roster listing by default)
* PayPal REST API integration (sandbox + live) for online registration and renewal payments
* Automatic Dec 31 anniversary expiry with optional roll-forward for late-year sign-ups
* Renewal reminder emails sent automatically at 60, 30, 15, 7, and 1 day(s) before expiration
* Per-page / per-post access control with a meta box — restrict any content to one or more levels
* Active member roster shortcode ([mm_member_roster]) — visible to active members only
* In memoriam shortcode ([mm_roster_in_memoriam]) — Silent Keys on any page (same access as the roster)
* Member privacy controls — each member chooses which contact fields appear on the roster
* Optional WordPress user account linking — members without a WP account still appear on the roster
* CSV bulk import and export with full column mapping and duplicate detection
* Full admin backend: searchable member list, edit all fields, transaction history, settings
* Treasurer view (v1.18.0+) — under Memberships → Treasurer: quick payment entry, core member edits, and recent-event attendance for one member at a time
* Configurable officer titles (v1.20.0+) — under Memberships → Settings: add, edit, remove, and reorder Title / Officer Role options; restore built-in defaults; included in settings JSON export

**Attendance & Events (v1.8.0+)**
* Events & Meetings calendar — create meetings and events with date, time, location, and virtual meeting URL
* Per-event attendance tracking — mark members as in-person or virtual (Zoom, Google Meet, Teams, Webex)
* Annual attendance report — per-member attendance counts with year filter and CSV download
* Year-end prize drawing pool — each event attended earns one entry; report visualises the full drawing pool
* DXCC Certified credential flag per member — required (with 2+ events attended) for Full Member qualification

**Member Approval Workflow (v1.9.0+)**
* New signups land in a dedicated "Pending Approvals" admin page (with menu count badge) for admin review before they appear on the roster
* Approve sends the welcome email and activates the membership; Deny marks the record Inactive
* Members linked to an existing WordPress user account skip approval automatically
* Per-admin opt-in for "New Member" and "New Payment" email notifications, configured under Settings → Admin Notifications
* Cash/cheque payments also fire the new-payment notification (in addition to the member's confirmation email)

**Officer Titles (v1.9.1+)**
* Per-member title field — President, Past President, Vice-President, Secretary, Treasurer, Director, Membership Coordinator, Prize Chairman
* Officers are surfaced at the top of the admin members list and the public roster, in title order
* Title badge displayed beside the member's name in both lists

**Shortcodes**
* [mm_registration_form] — public sign-up form with PayPal/Stripe checkout
* [mm_member_roster]      — active + lifetime members (active members only; expired within grace period)
* [mm_roster_in_memoriam] — Silent Keys in memoriam (active members only; empty when none listed)
* [mm_my_membership]     — logged-in member's status, expiry, and privacy preferences
* [mm_renew_form]        — public self-service renewal — email + callsign lookup; optional price summary before checkout when Public Renewal Page is assigned (no login required)
* [mm_events_calendar]   — public month-grid calendar of club events with prev/next navigation and inline event details

== Installation ==

1. Upload the `membership-manager` folder to `/wp-content/plugins/`.
2. Activate the plugin in **Plugins → Installed Plugins**.
3. Go to **Memberships → Settings** and enter your PayPal API credentials.
4. Create at least one membership level under **Memberships → Levels**.
5. Create pages for Registration, Roster, and My Membership and add the shortcodes.
6. Assign pages in **Memberships → Settings → Page Assignments**.

When you update the plugin, database schema upgrades run automatically on the next page
load — no manual steps required.

== Frequently Asked Questions ==

= Do members need a WordPress account? =
No. Members can be added manually by an admin and appear on the roster without ever
logging in to WordPress. A WP account is only required for members who want to manage
their own privacy settings or renew online.

= Can I import my existing membership list? =
Yes. Go to **Memberships → Import / Export**, download the CSV template to see the
required format, then upload your filled-in file. The importer matches duplicates by
email, then by callsign + name.

= When do renewal reminders go out? =
A WP-Cron job runs daily at 08:00 and sends reminders at 60, 30, 15, 7, and 1 day(s)
before December 31st. Reminders are only sent once per interval per member per year.

= What PayPal credentials do I need? =
Create an app in the PayPal Developer Dashboard (developer.paypal.com) to get a
Client ID and Secret Key. Use Sandbox mode for testing.

= How do I mark a member as a Silent Key? =
In the Members list click **Silent Key** next to the member's name. They appear in
the **In Memoriam** table wherever you place **[mm_roster_in_memoriam]** (members-only),
not on the main **[mm_member_roster]** page.

== Screenshots ==

1. Admin Dashboard — member stats at a glance
2. Members List — search, filter, roster toggle, and quick actions
3. Member Edit — all fields, WP user link, privacy settings
4. Membership Levels — Standard, Lifetime, Silent Key, and Guest / Visitor types
5. Import / Export — CSV upload with column reference
6. Public Registration Form
7. Member Roster (In Memoriam available via separate shortcode)
8. My Membership — status and privacy preferences

== Changelog ==

= 1.22.0 — 2026-05-07 =
* Added: DXCC Standings panel on the member-edit screen — queries the external dxcc_standings table for the most recent monthly snapshot matching the member's callsign; shows all modes with entity count, Honor Roll, and Lifetime flags
* Added: Mismatch detection between the dxcc_standings table and the dxcc_certified checkbox, with a one-click Sync button to align them
* Added: sync_dxcc admin action (nonce-protected) to set or clear the DXCC Certified flag from the standings panel
* No database schema changes

= 1.21.3 — 2026-05-07 =
* Changed: Check-in success message personalised with member first name (QRZ nickname preferred when available)
* No database schema changes

= 1.21.2 — 2026-05-07 =
* Added: Country field on member records (member-edit form, CSV import/export, visitor check-in auto-fill from QRZ)
* Added: PR (Puerto Rico), GU (Guam), VI (Virgin Islands) to the State dropdown
* Database: new mm_members.country column (VARCHAR 100, default empty)

= 1.21.1 — 2026-05-07 =
* Changed: Events list location column now truncates long addresses with ellipsis; column widths rebalanced
* Changed: Action icon styles consolidated into admin.css; icons slightly larger and more visible
* No database schema changes

= 1.21.0 — 2026-05-07 =
* Added: [mm_checkin] shortcode — mobile-friendly event self check-in kiosk with QR code; attendees enter their callsign to check into the current event
* Added: MM_Attendance::checkin() — duplicate-safe single-record attendance insert
* Added: Visitor auto-creation — unknown callsigns are looked up via QRZ/Callook and added as visitor members automatically
* Added: Event Check-In Page option in Settings → Page Assignments (mm_checkin_page); included in JSON export/import
* Added: 📱 Check-In icon per event row in the Events admin list (links to the check-in page for that event)
* Fixed: QRZ lookup no longer missing the email field (MM_QRZ::extract was omitting email)
* Fixed: Admin member-edit callsign lookup now fills the email field (JS lookupTargets map was missing email key)
* No database schema changes

= 1.20.2 — 2026-05-01 =
* Added: [mm_roster_in_memoriam] shortcode — Silent Keys table on any members-only page
* Changed: [mm_member_roster] no longer includes in memoriam; MM_DB::get_roster_members() excludes silent_key (use get_roster_silent_keys() for the new shortcode)
* No database schema changes

= 1.20.1 — 2026-04-28 =
* Changed: User manual updated for Guest / Visitor (levels, filters, Treasurer, attendance prize pool, dashboard, Settings reference, FAQ); developer checklist mentions marketing snippets
* No database schema changes

= 1.20.0 — 2026-04-28 =
* Added: Officer titles & roles are configurable under Memberships → Settings — add, edit, remove, reorder roles; optional restore to built-in defaults; included in settings JSON export/import
* Changed: User manual §13 and member edit screen describe the new settings location
* No database schema changes

= 1.19.1 — 2026-04-28 =
* Changed: Synced public marketing HTML (`main page.txt`, `plugin page.txt`) with the current release — version badges, version history, Guest / Visitor level on the plugin page, and corrected 1.18.0 release notes (Treasurer view)
* No database changes

= 1.19.0 — 2026-04-28 =
* Added: Guest / Visitor membership level — track attendance with no dues, no renewal, hidden from the main member list (optional filter to show) and public roster; not on the public signup form; prize report excludes guest entries from the drawing pool
* Added: CSV member_type visitor (and synonyms) plus import alignment when the level is visitor-type
* No new database columns (upgrade syncs existing members on visitor-type levels)

= 1.18.0 — 2026-04-29 =
* Added: Memberships → Treasurer — simplified workflow to record cash/cheque payments, edit essential member fields, and update attendance for the 12 most recent events
* No database changes

= 1.17.1 — 2026-04-28 =
* Fixed: Added a submenu icon for Pending Approvals in the Memberships admin menu
* No database changes

= 1.17.0 — 2026-04-27 =
* Added: Events list now includes a one-click PDF attendance sheet export per event/meeting
* Added: PDF sheet header includes event name, date, and location for clean printouts at check-in tables
* Added: Attendance-sheet roster includes only Active, Expired, and Inactive members (excludes Silent Key and Archived)
* No database changes

= 1.16.0 — 2026-04-27 =
* Added: Settings export/import now includes newer settings keys — Stripe keys, callsign lookup settings, admin notification recipients, and newer page assignment options (My Membership + Public Renewal Page)
* Changed: Sensitive-credentials export/import coverage expanded beyond PayPal to include Stripe secrets and QRZ password (still role-gated)
* Changed: Import/Export settings UI copy now reflects the full set of settings and sensitive credentials included
* No database changes

= 1.15.0 — 2026-04-27 =
* Added: Renewal price summary on the Public Renewal Page before PayPal/Stripe — shows level and amount after email/callsign lookup or from reminder email links when that page is assigned in Settings
* Changed: My Membership renew buttons and `{renew_url}` in reminder emails use the same summary step when the Public Renewal Page is set
* No database changes

= 1.14.0 — 2026-04-27 =
* Added: Self-service renewal for members without a WordPress account
* Added: Tokenized one-click renewal links in renewal-reminder emails
* Added: New [mm_renew_form] public shortcode — email + callsign lookup, redirects straight to checkout
* Added: "Reset Renewal Token" button on the member edit screen for invalidating leaked links
* Added: Public Renewal Page entry in Settings → Page Assignments
* Database: New `renewal_token` column on `wp_mm_members` — backfilled for existing members on first page load after upgrade

= 1.13.1 — 2026-04-27 =
* Changed: Callsign lookup output is now title-cased — names and addresses come through as "Jeff Dale" and "123 Main St" instead of the FCC's ALL CAPS format. State codes, ZIP, callsign, grid, and license class are unchanged.

= 1.13.0 — 2026-04-27 =
* Added: Stripe Checkout integration alongside PayPal — both can be enabled simultaneously
* Added: Payment Method radio on the registration form when both gateways are configured (PayPal vs Credit/Debit Card)
* Added: My Membership renew prompt offers separate PayPal / Card buttons when both are configured
* Added: New Stripe Integration settings panel (publishable + secret key, auto-detects test vs live mode)
* Added: Gateway column on the Transactions admin list with colour-coded badges
* Database: New `payment_gateway` column on wp_mm_transactions (defaults to 'paypal' for existing rows)

= 1.12.0 — 2026-04-27 =
* Added: Callsign lookup integration with two selectable providers — Callook.info (free, US-only) or QRZ.com XML Subscriber API (paid subscription)
* Added: Lookup button on the public registration form and admin Add Member screen — auto-fills name, address, city, state, ZIP, and license class from the chosen provider
* Added: Settings → Callsign Lookup section for picking the provider and (for QRZ) entering credentials
* Disabled by default — no UI changes until an admin selects a provider

= 1.11.0 — 2026-04-27 =
* Added: Email audit log — every outgoing message (welcome, renewal reminder, payment confirmation, admin notifications) is recorded with timestamp, type, subject, and recipient
* Added: Email History panel on the member edit screen showing the full communication trail for each member
* Added: New `wp_mm_email_log` table created automatically on first page load after upgrade
* Changed: Permanent member deletion now also cascade-deletes email log rows

= 1.10.0 — 2026-04-27 =
* Added: Public events calendar shortcode [mm_events_calendar] — month-grid view with prev/next navigation, mobile-friendly layout, and inline event detail cards
* Added: MM_Events::get_in_range() helper for any future iCal / RSS feeds

= 1.9.6 — 2026-04-27 =
* Added: Google Maps URL field per event — paste a maps share link and a 📍 icon appears beside the location in the events list, dashboard, and take-attendance header (opens in a new tab)
* Database: New `map_url` column on `wp_mm_events` (added automatically on first page load after upgrade)

= 1.9.5 — 2026-04-27 =
* Added: Expanded dashboard with six new sections — This Year at a Glance, Renewal Forecast (7/14/30/60 day buckets), Upcoming Events, Recent Activity feed, Current Officers, and Member Composition (by type, license class, and credentials)
* Added: + Add Event button in the Dashboard Quick Actions

= 1.9.4 — 2026-04-27 =
* Added: Permanent member deletion via "Danger Zone" on the member edit screen (MM Administrator only)
* Added: Cascade delete of transactions, attendance records, and reminder log entries when a member is removed
* Added: Type-the-name confirmation prompt to guard against accidental deletion

= 1.9.3 — 2026-04-27 =
* Added: Self-hosted update checker — surfaces new versions via the standard WordPress "Update available" notice and "View details" popup
* Added: package-plugin.ps1 now also generates membership-manager.json with each build (upload it next to the zip)

= 1.9.2 — 2026-04-26 =
* Changed: Settings page is now MM Administrator only — `mm_manage_settings` capability removed from the MM Manager role
* Changed: Cap revocation runs automatically on existing MM Manager users via MM_Roles::add_roles() during the upgrade
* Documentation: Refreshed the User Manual role matrix and added a Capability Reference table

= 1.9.1 — 2026-04-26 =
* Added: Officer titles per member (President, Past President, Vice-President, Secretary, Treasurer, Director, Membership Coordinator, Prize Chairman) selectable from a dropdown on the member edit screen
* Added: Members with an officer title appear at the top of the admin members list and public roster, in title order, with a title badge beside their name
* Database: New `title` column on `wp_mm_members` (added automatically on first page load after upgrade)

= 1.9.0 — 2026-04-26 =
* Added: Member approval queue ("Pending Approvals" admin page with menu count badge) — new signups now wait for admin review before appearing on the roster
* Added: New `pending_approval` member status, applied automatically on new signups (public form + PayPal completion)
* Added: Approval bypass when a new member is linked to an existing WordPress user account
* Added: Admin Notifications settings section — choose which WP/MM admins receive email when a new member registers or when a payment is recorded
* Added: Cash/cheque payments now also notify opted-in admins (in addition to the member receiving a confirmation)
* Added: Approve/Deny actions, MM_Members::approve(), MM_Members::deny(), MM_Emails::send_admin_pending_member_notification(), MM_Emails::send_admin_payment_notification()
* Changed: Public registration success message now reads "Your application is now pending approval" instead of "Your membership is now active"
* Changed: Dashboard now shows separate "Pending Payment" and "Awaiting Approval" stat cards

= 1.8.6 — 2026-04-25 =
* Fixed: Plugin no longer behaves as a fresh install when updated by deactivating and reactivating — activate() now calls add_missing_columns() so new DB columns are always applied regardless of how the plugin files were replaced

= 1.8.5 — 2026-04-25 =
* Changed: Registration page logged-in message now reads "You are currently an active member. Click HERE to see your member profile." with a link to the configured My Membership page
* Added: My Membership Page option in Settings → Page Assignments
* Changed: Silent Key skull-and-crossbones icon replaced with "SK" across the public roster and admin member-edit badge
* Changed: Plugin URI updated to https://www.wb3isp.com
* Added: package-plugin.ps1 — PowerShell packaging script that auto-reads the version and creates a distributable zip

= 1.8.0 — 2026-04-25 =
* Added: Events & Meetings calendar — secretary can create meetings/events with type, date, time, location, and virtual URL
* Added: Per-event attendance form — mark members present/absent, in-person or virtual, with platform and optional notes
* Added: Annual attendance report with event summary, per-member rankings, CSV download, and year filter
* Added: Year-end prize drawing pool — one entry per event attended, visualised as individual name chips
* Added: DXCC Certified checkbox on member edit screen (Credentials section)
* Added: Attendance history table on member edit screen showing all events a member has attended
* Added: MM_Attendance::meets_full_requirements() — checks DXCC certification + 2+ events for Full Member eligibility
* Changed: Database — new wp_mm_events and wp_mm_attendance tables; new dxcc_certified column on wp_mm_members

= 1.7.0 — 2026-04-19 =
* Added: Settings export/import — download all plugin settings as a JSON file and restore them on any site; PayPal credentials excluded by default with an opt-in for MM Administrators

= 1.6.5 — 2025-06-10 =
* Fixed: Settings accordions correctly default to collapsed; stale sessionStorage states no longer override the default

= 1.6.4 — 2025-06-10 =
* Changed: Settings page accordion sections now default to collapsed

= 1.6.3 — 2025-06-10 =
* Added: Roster grace period setting — configurable days after expiry before expired members are removed from the public roster (default 90)

= 1.6.2 — 2025-06-10 =
* Added: Configurable PayPal return pages for payment success, failure, and cancellation outcomes in Settings → Page Assignments

= 1.6.1 — 2025-06-10 =
* Changed: Settings page refactored into collapsible accordion sections; open state persisted in sessionStorage

= 1.6.0 — 2025-06-10 =
* Added: Cash/cheque payment recording on member edit screen — records transaction, activates membership, advances expiry, sends confirmation email
* Changed: Transaction history reference column now shows notes for cash/cheque payments

= 1.5.9 — 2025-06-10 =
* Fixed: Stray square character appearing next to Memberships top-level menu icon caused by unscoped submenu icon CSS

= 1.5.8 — 2025-06-10 =
* Added: Dashicon icons on all admin submenu items (Dashboard, Members, Levels, Transactions, Import/Export, Settings, User Manual)

= 1.5.7 — 2025-06-10 =
* Fixed: Archived members showing Silent Key styling instead of Archived status badge
* Fixed: Saving member edit form was overwriting archived status back to level-based status

= 1.5.6 — 2025-06-10 =
* Fixed: User Manual admin menu label showed only emoji and blank text; replaced with plain text

= 1.5.5 — 2025-06-10 =
* Changed: Archive/Unarchive buttons moved from action icons column to the member edit screen

= 1.5.4 — 2025-06-10 =
* Added: Archived member status (📦 Archive / ↩️ Unarchive actions) — visible in admin, excluded from all renewal reminders

= 1.5.3 — 2025-06-10 =
* Fixed: Send Reminder icon now shows for expired members even when expiry_date is NULL
* Fixed: Backend handler sends email with "has expired" subject when no expiry_date stored

= 1.5.2 — 2025-06-10 =
* Fixed: Send Reminder icon missing for expired members — member_type NULL coerced to string in view and handler; upgrade runner backfills NULL rows to 'standard'

= 1.5.1 — 2025-06-10 =
* Added: In-plugin User Manual (Memberships → 📖 User Manual)
* Added: Action icon buttons in Members list (✏️ ✅ 🚫 🔔 💳) replacing text links
* Fixed: Send Reminder backend rejected expired members with empty member_type
* Fixed: Renewal reminder email subject now says "has expired" for past-due members
* Removed: Silent Key action icon from Members list (use member edit screen instead)
* Changed: Author updated to Jeff Dale WB3ISP

= 1.5.0 — 2025-06-10 =
* Changed: Public roster Location column removed
* Added: Sortable columns on public roster (Name, Callsign, License)
* Added: Name/callsign search box on public roster with Clear link
* Fixed: Email contact icon now opens a new compose window with address pre-filled in To field; tooltip shows the address on hover

= 1.4.3 — 2025-06-10 =
* Fixed: Send Renewal Reminder link missing for expired members (empty member_type string edge case)
* Fixed: Orphaned leading pipe character in actions column for expired members
* Added: Sortable columns in admin members list (Name, Callsign, Status, Expires)
* Added: Filter/sort state preserved; Clear filter button

= 1.4.2 — 2025-06-10 =
* Changed: License classes are now Novice, Technician, General, Advanced, Extra
* Updated CSV import normalisation to match new classes

== Upgrade Notice ==

= 1.15.0 =
Adds an optional renewal **price summary** step on the page you assign as **Public Renewal Page** (the page with `[mm_renew_form]`). No database changes. If that page is not assigned, renewal behaviour is unchanged from 1.14.0. Safe to update at any time.

= 1.14.0 =
Adds self-service renewal for members without a WordPress account. New `renewal_token` column added automatically to `wp_mm_members` and backfilled for existing members on first page load after upgrade. Drop the new [mm_renew_form] shortcode on a page and link it from your menu. Existing renewal-reminder emails will now contain a one-click renewal link that works without login. Safe to update at any time.

= 1.13.1 =
Patch release. Callsign lookup output is now title-cased instead of ALL CAPS. No database changes. Safe to update at any time.

= 1.13.0 =
Adds Stripe Checkout as a second payment gateway alongside PayPal. New `payment_gateway` column added automatically to `wp_mm_transactions` on first page load. Existing transactions default to 'paypal'. Configure Stripe at Memberships → Settings → Stripe Integration. Safe to update at any time.

= 1.12.0 =
Adds optional callsign auto-fill on the registration form using Callook.info (free) or QRZ.com (paid). Disabled by default — no behavioural change until an admin opts in via Settings → Callsign Lookup. No database changes.

= 1.11.0 =
Adds the email audit log. New `wp_mm_email_log` table created automatically on first page load after upgrade. The Email History panel appears on every member's edit screen showing all messages sent to or about that member. Safe to update at any time.

= 1.10.0 =
Adds the public events calendar shortcode `[mm_events_calendar]`. No database changes. Drop the shortcode on any page and assign that page in your menu — visitors see a month-grid calendar of upcoming events.

= 1.9.6 =
Adds a Google Maps URL field per event. New `map_url` column added automatically to `wp_mm_events` on first page load. Existing events default to no map URL until edited. Safe to update at any time.

= 1.9.5 =
Expanded dashboard with renewal forecast, year-to-date totals, upcoming events, recent activity, officers, and member composition panels. No database changes. Safe to update at any time.

= 1.9.4 =
Adds permanent member deletion (MM Administrator only). No database changes. Safe to update at any time.

= 1.9.3 =
Adds a self-hosted update checker. From this version onwards, WordPress will automatically detect new releases hosted at the project download URL and prompt users to update. No database changes. Safe to update at any time.

= 1.9.2 =
Tightens MM Manager permissions. The Settings page is now MM Administrator only. Existing MM Manager users automatically lose the `mm_manage_settings` capability on the first page load after upgrade. No database changes.

= 1.9.1 =
Adds officer titles. New `title` column added automatically to `wp_mm_members` on first page load. Existing members have an empty title. Safe to update at any time.

= 1.9.0 =
Adds member approval queue and admin notification recipients. No database schema changes — the new `pending_approval` status uses the existing `status` column. Existing active members are unaffected. After upgrading, visit Memberships → Settings → Admin Notifications and tick the boxes for which admins should receive new-member and new-payment emails.

= 1.8.6 =
Bug fix for manual installs. No database schema changes — safe to update at any time.

= 1.8.5 =
No database changes. Safe to update at any time.

= 1.8.0 =
Adds two new database tables (wp_mm_events, wp_mm_attendance) and a new dxcc_certified column on wp_mm_members. All changes apply automatically on the first page load after update — no manual steps required.

= 1.5.0 =
No database changes. Safe to update at any time.

= 1.4.3 =
No database changes. Safe to update at any time.

= 1.4.2 =
No database changes. Existing license_class values (Technician, General, Extra) remain valid.


* Fixed: Missing upgrade stubs for 1.3.6, 1.4.0, and 1.4.1 caused upgrade() to re-run on every page load

= 1.4.0 — 2025-06-09 =
* Added: License Class field (Technician / General / Amateur Extra / Other) on registration form, member edit, and roster
* Added: ARRL Member Yes/No field on registration form, member edit, and roster
* Both new fields included in CSV export/import

= 1.3.6 — 2025-06-08 =
* Added: Editable email templates in Settings → Email Templates (MM Administrator only)
* Templates support {token} placeholders; blank templates fall back to built-in defaults
* Preview button renders a live HTML preview of saved templates

= 1.3.5 — 2025-06-08 =
* Fixed: WordPress administrators could not see the Memberships menu — MM caps now granted to the WP administrator role on activation/upgrade

= 1.3.4 — 2025-06-08 =
* Added: Three plugin roles — MM Administrator, MM Manager, MM Member
* MM Manager has full access except PayPal settings and Level management
* MM Member can edit their own privacy/roster preferences on the front end
* Role assignment UI in Settings (MM Administrator only)
* Registration form: all fields now required

== Upgrade Notice ==

= 1.4.1 =
Bug fix release. No database changes. Safe to update at any time.

= 1.4.0 =
Adds License Class and ARRL Member fields. Two new columns added to mm_members automatically on first page load after update.


* Fixed: "Unknown column 'level_type' in field list" — dbDelta silently skips adding columns to existing tables when its strict formatting requirements aren't met. All new columns (level_type, member_type, show_on_roster, privacy_*, silent_key_date, notes) are now added via explicit ALTER TABLE ADD COLUMN statements checked with SHOW COLUMNS, which are guaranteed to work on any MySQL/MariaDB version
* Fixed: add_missing_columns() now runs unconditionally on every upgrade() call, and also before every level insert, so no migration can be silently skipped

= 1.3.2 — 2025-06-08 =
* Fixed: "Could not save membership level" error caused by wpdb guessing column formats incorrectly — explicit format array (%s/%d) now passed to all inserts
* Fixed: Table existence checked before insert; if activation hook never ran (file-drop installs), tables are created on demand
* Improved: Actual MySQL error message is now shown in the admin notice instead of the generic "please try again" message, making future issues easier to diagnose

= 1.3.1 — 2025-06-08 =
* Fixed: New membership levels were silently not saved due to a missing `duration` field in the Add Level form — the DB column has no default in strict MySQL mode, causing the INSERT to fail with no visible error
* Fixed: Duplicate level names could produce identical slugs, hitting the UNIQUE constraint on the `slug` column and silently discarding the insert — slug uniqueness is now enforced in PHP before the INSERT
* Fixed: `flush_rewrite_rules()` was being called inside the upgrade routine on every page load when a version mismatch existed, causing performance issues and potential redirect loops
* Fixed: Version constant bumped to `1.3.1` but activator was missing the corresponding `upgrade_131()` stub, causing the version mismatch check to fire on every request
* Improved: Level edit form now shows Duration field (visible, editable) and hides the Price field automatically for Lifetime and Silent Key level types

= 1.3.0 — 2025-06-01 =
* New: CSV bulk import with duplicate detection (match by email, callsign+name, or name)
* New: CSV export with optional status/type filters
* New: Downloadable import template CSV with example row and column reference
* New: Import / Export admin page (Memberships → Import / Export)
* New: Import results panel showing imported / updated / skipped counts and per-row warnings
* Improved: "Import / Export" quick-link added to Members list header
* Internal: Plugin versioning system with automatic DB upgrade runner on plugins_loaded
* Internal: readme.txt and CHANGELOG.md added

= 1.2.0 — 2025-05-15 =
* New: WordPress user account linking — members can exist without a WP account
* New: Optional "Create WordPress user" checkbox when adding members via admin
* New: All users dropdown on member edit screen for linking existing accounts
* New: Roster visibility toggle per member (AJAX, no page reload)
* New: Per-member contact privacy flags — email, phone, address shown/hidden individually
* New: Member-controlled privacy preferences on [mm_my_membership] page
* New: Admin Notes field on member edit screen
* Changed: wp_user_id now defaults to 0 (no linked WP account) rather than being required

= 1.1.0 — 2025-05-01 =
* New: Lifetime membership level type — no cost, no expiry, no renewal reminders
* New: Silent Key membership category — deceased members honoured on the roster
* New: "Silent Key" quick-action button in the Members list
* New: Silent Key date field on member edit screen
* New: Dedicated "In Memoriam" section at the bottom of the public roster
* New: level_type field on membership levels (standard | lifetime | silent_key)
* New: member_type column on members table for fast filtering
* New: Roster visibility toggle per member (admin-controlled)
* Changed: Status enum extended to include 'silent_key'
* Changed: Lifetime members are excluded from expiry cron and renewal reminders
* Changed: Member edit form split into two-column layout with section headings

= 1.0.0 — 2025-04-15 =
* Initial release
* Membership levels with annual pricing
* Member registration with custom fields (First Name, Last Name, Callsign, Email, Phone, Address 1, Address 2, City, State, ZIP)
* PayPal REST API integration (sandbox + live modes)
* Automatic Dec 31 anniversary expiry
* Renewal reminder emails at 60, 30, 15, 7, and 1 day(s) before expiration
* Per-page access control meta box
* Active member roster ([mm_member_roster]) — active members only
* [mm_registration_form] and [mm_my_membership] shortcodes
* Full admin backend: members list, edit, transactions, settings
* HTML email templates: welcome, renewal reminder, payment confirmation

== Upgrade Notice ==

= 1.3.3 =
Fixes "Unknown column 'level_type'" error on existing installs. The missing columns
are added automatically via ALTER TABLE on the next page load after update.

= 1.3.2 =
Bug fix release for "Could not save membership level" error. No database changes.

= 1.3.1 =
Bug fix release. New membership levels were silently failing to save due to a missing
form field and a slug uniqueness issue. Update immediately if you are unable to add
new levels. No database changes — safe to update at any time.

= 1.3.0 =
Database schema upgrades now apply automatically on plugin update — no deactivation
needed. This release adds CSV import/export (Memberships → Import / Export).

= 1.2.0 =
WordPress user linking is now optional. Existing members retain their wp_user_id.
New privacy and roster visibility fields are added automatically via dbDelta.

= 1.1.0 =
New columns added to mm_levels and mm_members tables via dbDelta on activation.
Existing data is unaffected — all new columns have safe default values.
