2.1.7
Bug Fixes
* Fixed email settings save failing with “The dmsilm_email_general options page is not in the allowed options list.” — the email settings option groups (general, queue, logging, notifications, advanced) were only registered when the Email settings page rendered, so the WordPress Settings API save request (options.php) never saw them and rejected every save. The groups are now registered on every admin request.
Improvements
* Email settings page now builds its email/queue/template helper objects lazily, so registering settings no longer instantiates the full email subsystem on unrelated admin requests.
2.1.6
Bug Fixes
* Fixed Action Scheduler group name mismatch — ensure_jobs_scheduled(), trigger_job_now(), and cancel_job() were using the wrong group name (‘dmsi-ddls-digital-downloads-licensing-subscriptions’ instead of ‘dmsi-ddls’), causing the daily self-healing check to create duplicate pending actions on every run
* Added one-time cleanup migration to remove duplicate pending actions accumulated from the bug
Improvements
* Consolidated all 12 Action Scheduler group name references into a single AS_GROUP class constant
* Added action_scheduler_retention_period filter (7 days) to prevent unbounded growth of completed/failed action rows
2.1.5
Bug Fixes
* Fixed variable products showing “Not Licensed” in DDLS Products admin when licensing is enabled on a variation — save_variation_licensing_meta() now re-syncs the parent’s dmsilm_products.requires_license flag based on all child variations
Improvements
* Added variation selector to Add/Edit License admin form — when the selected product is variable, a dropdown of its WC variations is shown so licenses can be bound to a specific variation
* Added variation_id to license save and update flows in admin
* Added variation ownership validation to the activate endpoint — if a license is bound to a specific variation and the client sends a mismatching variation_id, activation is rejected with 403 variation_mismatch
2.1.4
Bug Fixes
* Fixed license check-in and verify endpoints incorrectly rejecting existing activations when the activation limit was full — validate() now uses strict over-limit check (count > limit) instead of at-limit (count >= limit), so devices that already hold an activation can successfully check in; new activation requests are still correctly blocked by the separate is_activation_limit_reached() guard
2.1.3
Bug Fixes
* Fixed stale activations job incorrectly treating brand-new activations (last_checked_date IS NULL) as stale regardless of age — now only marks an activation as stale if it was also created before the stale threshold cutoff date
* Fixed reactivation path not refreshing last_checked_date — calling activate on an already-active site now records a check-in, preventing the stale job from deactivating the activation on the next daily run
2.1.2
Bug Fixes
* Fixed Action Scheduler duplicate registrations — dedup guard was checking without group but scheduling with group, causing hundreds of duplicate recurring jobs
* Fixed 4 background jobs (database cleanup, subscription renewal, support expiration, support renewal) that were silently not running since WP Cron context doesn’t fire admin_init
* Fixed stale activations job hook name mismatch that prevented the job from executing
2.1.1
Bundle UX Improvements
* Customer portal: bundle licenses now display as a single card in the list; child keys and details accessible via VIEW DETAILS
* Customer portal VIEW DETAILS: Included Extensions table shows all child licenses with name, key, activations, and expiry; Active Sites section hidden for bundle parents
* Order email: child extension keys now shown with product name labels; parent bundle container key omitted
2.1.0
New: Bundle Products
* Added Bundle product type — group multiple DDLS products into a single purchasable SKU
* New lnl_dmsilm_bundle_items database table maps bundle products to their child products
* Order processor creates one parent license + one child license per bundled product on purchase
* Child licenses use each child product’s own License Type, Activation Limit, and Duration settings
* One subscription per bundle linked to the parent license; child licenses never get individual subscriptions
* Bundle renewal extends all non-cancelled child licenses atomically in a single DB transaction
* Single consolidated renewal email lists all renewed products with new expiry dates
* Admin: Bundled Products multi-select on WooCommerce product edit Licensing tab (visible when type = Bundle)
* Admin: “Bundle · N products” badge in DDLS Products list for bundle products
* Customer portal: child licenses grouped under bundle parent row with “Managed by [Bundle Name]” label
* Customer portal: renewal action on bundle (parent) row only; child rows show no renewal button
* Extension hook compatibility: dmsilm_license_created fires per child with synthetic order item (3 args) so Team Management and other extensions work automatically
2.0.1
New: Per-Variation File Uploads
* Added variation_id column to files table — assign downloadable files to specific WooCommerce product variations
* Variation dropdown on Add File and Edit File forms (auto-populated via AJAX for variable products)
* Files grouped by variation on Version edit page with section headers
* Version variation dropdown filters the files section in real-time
Improvements
* File uploads now stored in protected directory (wp-content/uploads/dmsilm-files/) with .htaccess deny rules
* AJAX-based file upload with progress bar (0-90% upload, 90-100% server processing)
* Modernized Add File page UI with drag & drop zone, file preview, and card layout
* Delete file now also removes the physical file from disk
* Form processing moved to admin_init hook — eliminates white screen flashes on edit/delete/update
Bug Fixes
* Fixed file upload not working when accessed via shell route URLs (only legacy slug was recognized)
* Fixed “Invalid file type” error on MSI uploads (disabled WordPress MIME check for admin uploads)
* Fixed in_array() null haystack error when saving files (platform validation used uninitialized static property)
* Fixed delete file not actually deleting (missing delete(true) for physical file removal)
* Fixed file edit/delete causing white screen flash (redirect inside render callback)
2.0.0
New: Support Plan Product Type
* Added custom WooCommerce product type “Support Plan” for selling support contracts as standalone products
* Admin product data tab with support level, duration, and contract settings
* Order completion handler automatically creates support contracts on purchase
* Support Plan products visible in WooCommerce product type dropdown
New: EULA & Terms System
* Full EULA management system with admin CRUD interface
* 5 bundled legal templates: Standard Software License, SaaS/Subscription ToS, Free/Open Source, Freemium Plugin, API/Developer Service
* Template picker with one-click loading on EULA creation
* Placeholder substitution engine ({company_name}, {product_name}, {governing_law}, etc.)
* Custom template creation — save any EULA as a reusable template
* EULA Templates admin section with list table
* Governing Law field added to General Settings
* Legal disclaimer on all EULA forms
New: Standalone Support Contracts & Portal Binding
* Support contracts can now exist without a parent license (standalone purchases)
* Supported Products multi-select field on product edit page — defines which products a contract covers
* supported_product_ids column added to support_contracts table
* Auto-bind logic links standalone contracts to eligible licenses on creation
* Customer portal binding UI — button, modal, and eligible license list via AJAX
* Binding confirmation email sent on successful contract-to-license binding
* WooCommerce order email includes support contract line for standalone items
New: Support Level “None” & Upgrade Discovery
* “None” option added to support level dropdown (product and variation level)
* Contracts with support_level=’none’ filtered from customer portal display
* Dashboard support count widget excludes ‘none’ contracts
* find_support_products() discovers purchasable support products for upgrade prompts
* Support Purchase URL field on product admin for manual override
* Cache invalidation for support product discovery
New: Version Variation Support
* variation_id column added to versions table (schema migration)
* Version model updated with variation_id property and methods
* Admin Versions UI includes variation dropdown on add/edit forms
* Versions list table shows variation column
* Customer portal downloads filtered by purchased variation
New: Entitled Major Version
* entitled_major_version field on licenses — restricts update entitlement to a specific major version (e.g., 3.x.x only)
* Entitled Major Version setting on product edit page — auto-stamps onto licenses at purchase time
* “Lifetime” option added to Override License Duration dropdown
New: Email Capture on Activation
* email_notice field added to activation API response when customer email is captured
* Informs API callers that the email was stored and how to request removal
New: Admin-Configurable Platform Values
* Platform dropdown values (Windows, Mac, Linux, etc.) are now admin-configurable via Settings > Platforms tab
* Add, edit, and delete platform options from the admin UI
Security: License Cross-Activation Fix
* Added product_id validation to all license API routes (activate, deactivate, validate, check-in, verify)
* Prevents licenses from one product being used to activate a different product
* validate_product_ownership() helper enforces ownership across all endpoints
* Unified licenses AJAX handlers updated with product validation
Improvements
* Added inline file upload on Add Version form — attach files during version creation
* Made support levels list table responsive for smaller screens
Bug Fixes
* Fixed license type info display showing “12 months” instead of “Lifetime” for lifetime licenses
* Fixed support level edit modal always showing permissions/SLA checkboxes as checked
* Fixed support levels “Name” column too narrow (19px) causing vertical text wrapping
* Fixed all Versions admin URLs using legacy page slug — migrated to new shell format
* Fixed Edit and Deprecate/Restore action links broken on Versions grouped view page
* Fixed Versions grouped view filter URLs encoding ampersand with add_query_arg
* Fixed Override License Duration not accepting lifetime value in save handler
* Fixed email logs bulk delete using unregistered page slug
* Fixed license copy icon duplicate and handler mismatch
* Fixed default support level fallback
* Fixed version numbering incorrectly set to 1.3.0 in plugin header and constant
1.2.2
- Added is_lifetime flag to License Types — allows lifetime licenses with no expiry
- Added variable product support: per-variation license configuration and order processing
- Added resolve_effective_settings() for merging product/variation/license-type settings
- Added variation metadata (license type, activation limit, duration) to API responses
- Added license type permissions info display on product edit page
1.2.1
- Renamed customer portal “Support” tab to “Support Contracts” to avoid conflict with Support Desk extension’s “Support” tab
- Customer portal now backed by a real WordPress page — fixes theme page title banner showing “Recent Posts”
- Added “Portal Page Title” setting (replaces “Portal URL Slug”) — title syncs to the portal WP page
- Portal page auto-created on activation and recreated if deleted while portal is enabled
- Removed “My Portal” h1 from portal shell — theme handles the page title via its standard page template
- Moved logout link to bottom of portal sidebar; removed username display from portal header
1.2.0
- Added 3-column admin shell layout with URL router (section/view params) and dmsilm_admin_shell_sections / dmsilm_admin_shell_pages filters for extensions
- Added draggable dashboard widget system
- Added Extension Status dashboard widget showing installed/active extensions
- Added unified extension license page in Core Settings section
- Added consolidated license admin notice (replaces per-extension notices)
- Added CSS design tokens at :root for consistent theming across Core and extensions
- Added shared dmsilm-chartjs script handle registered by Core
- Added License Types — full model, database table, settings tab with CRUD UI, dynamic dropdown on product and variation edit pages
- Added variation metadata (license type, activation limit, duration) to API responses
- Added resolve_effective_settings() for merging product/variation/license-type settings; updated order processor
- Added variable product support: per-variation license configuration and order processing
- Added 3-column customer portal shell with section/view routing
- Added Email UID — unique message ID generation, schema migration, X-DMSI-UID header injection
- Added Email Logs: Email ID column, search-by-UID, per-email detail view
- Added extension settings tab hooks (dmsilm_settings_tabs filter, dmsilm_settings_tab_content_{tab} and dmsilm_settings_save_{tab} actions)
- Added public key-based REST API routes: POST /licenses/activate and /licenses/deactivate
- Added order processor filters: dmsilm_license_data_before_create, dmsilm_subscription_data_before_create, dmsilm_support_contract_data_before_create
- Added Files section placeholder on Add Version form (shows “Save first” message)
- Added Media Library option to file upload page (choose existing files or upload new ones)
- Added License Type permissions info box on product edit page (shows activation limit, duration, updates, premium content, API access)
- Fixed license activation and customer portal product visibility issues
- Fixed settings tabs, capability manager registration, and legacy URL redirects
- Fixed Versions page: “Add Version” and “Add the first version” buttons not navigating to add form
- Fixed Versions page: product groups rendering beside filters instead of below them
- Fixed database upgrade routine not running on plugin file updates (missing plugins_loaded version check)
1.0.1
- Fixed Email Logs: CSV export, column sorting, filters, view modal, close button, modal styling
- Fixed Email Settings: 403 error, undefined method, queue status queries
- Fixed Email Templates: preview/edit routing, template loading, CSS layout, editor toolbar, validator warnings, iframe link navigation
- Fixed Advanced tab: wired Test Mode to email sending
- Fixed JS/AJAX timing for all email admin pages
- Removed non-functional Real Data preview option
- Added portal_url and preferences_url template variables
- Fixed Dashboard: system activity labels, expiring licenses link, renewals due link
- Fixed Licenses: filter dropdowns resetting (nonce conflict), layout overlap, added Reset button
- Fixed Subscriptions: same filter nonce conflict, status tabs, renewal_due pseudo-status, Clear All button
- Fixed Products: filter nonce conflict, removed redundant Filter button
- Fixed Downloads: filter nonce conflict, native date pickers for date filters
- Added Reset button to all list table filters
- Auto-submit on filter dropdown change across all admin list pages
- Fixed Reports: revenue tab not loading from dashboard link
- Fixed Activations: custom nonce conflict, status tabs
- Fixed Customers: custom nonce conflict, search
- Fixed Settings: tab navigation from external links
- Fixed Email Stats: date range selection from external links
- Fixed Email Stats: page not accessible (missing submenu registration, capability mismatch)
- Added Email Stats: full CSS styling (header, widget cards, charts, tables, queue status, responsive)
- Added Email Stats: asset enqueuing (Chart.js, localizations)
- Added Email Stats: AJAX handler registration for refresh and chart data
- Added Email Logs: “View Statistics” link in page header
1.0.0
- Initial public release
- License management with automatic key generation
- Activation tracking and limits
- Subscription tracking system
- Digital download delivery with versioning
- Customer portal for self-service
- 30+ email templates
- REST API for license validation
- Admin dashboard with overview widgets
- 4 user roles with permissions
- Audit logging and GDPR tools