DRM (Digital Rights Management)

The DRM system enforces license compliance through escalating admin notices and a full-page interstitial lock. It only affects the WordPress admin area โ€” front-end functionality is never impacted.

Source: libraries/slswc/client/src/DRM.php

Configuration

Pass a drm array in the $args parameter of Plugin::get_instance():

use SLSWC\Client\Plugin;

$client = Plugin::get_instance(
    'https://your-license-server.com/',
    __FILE__,
    array(
        'license_key' => get_option( 'my_license_key', '' ),
        'drm'         => array(
            'enabled'          => true,
            'product_name'     => 'My Plugin Pro',
            'parent_menu_slug' => 'my-plugin',
            'page_hook_prefix' => 'my-plugin',
            'submenu_slugs'    => array(
                'my-plugin-settings',
                'my-plugin-reports',
            ),
            'license_page_url' => admin_url( 'admin.php?page=my-plugin-settings&tab=license' ),
            'purchase_url'     => 'https://your-store.com/',
            'logo_url'         => plugins_url( 'assets/logo.svg', __FILE__ ),
            'text_domain'      => 'my-plugin',
        ),
    )
);

Configuration Options

KeyTypeDefaultDescription
enabledbooltrueEnable/disable DRM enforcement
identifierstring''Unique identifier (defaults to the updater's text domain)
product_namestring''Product name shown in notices
parent_menu_slugstring''Parent admin menu slug to intercept
page_hook_prefixstring''WordPress sanitized menu title for load- hooks
submenu_slugsarray[]List of submenu slugs to intercept when locked. The parent menu page is only intercepted when this list is not empty
license_page_urlstring''URL to the license activation page
purchase_urlstring''URL where customers can purchase a license
logo_urlstring''Logo image URL for the interstitial page
grace_soft_daysint2Days without a successful license check before grace period notices begin
grace_lock_daysint7Days without a successful license check before the full-page lock activates
text_domainstring'slswc-client'Text domain for translations

Configuration Precedence

The grace period values are resolved in this order, from lowest to highest priority:

  1. The built-in defaults (grace_soft_days 2, grace_lock_days 7).
  2. Values sent by the license server and saved with the license details during the scheduled license check.
  3. Values you pass in the drm array (only keys you set explicitly).
  4. The slswc_drm_config_{identifier} filter.

State Machine

The DRM operates on a state machine with four states:

[License Active] -----> 'ok'           (no notices)
                   |
[No License]  ---->  'grace_silent'  (silent grace period)
                   |
                   +--> 'grace_soft' (warning notices with countdown)
                   |
                   +--> 'locked'     (full-page interstitial)

"Days without license" is counted from the last successful license check (or from when the DRM first ran on a fresh install).

StateDays Without LicenseBehavior
okN/A (license active)No notices. Normal operation.
grace_silent0 to grace_soft_days - 1No "no license" notice. Grace period begins silently.
grace_softgrace_soft_days to grace_lock_days - 1Warning admin notices with countdown.
lockedgrace_lock_days and beyondFull-page interstitial blocks access to the intercepted plugin admin pages. Admin notices still show on other pages.

Admin Notice Escalation

Notices are shown based on the license status:

License StatusNotice TypeDescription
activeNoneLicense is valid
expiringPre-expiry warningLicense will expire soon
expiredExpired noticeLicense has expired
disabledDisabled noticeLicense was revoked
Other (no license)Grace period countdownShows days remaining. Not shown in the grace_silent state

The expiring, expired, and disabled notices are shown regardless of the DRM state. Notices are dismissible for 24 hours. Dismissal state is stored in the {identifier}_drm_show_{flag}_notice option.

Full-Page Interstitial

When the DRM state reaches locked, any attempt to load the plugin's admin pages is intercepted. Instead of the normal page content, a full-page interstitial is rendered with:

  • Product logo
  • Explanation that the license has expired or is missing
  • Link to the license activation page
  • Link to purchase a license
  • "Check License" button (AJAX) to re-verify without page reload

The interstitial intercepts page loads via load-{hook} actions for all configured submenu pages, and for the parent menu page (toplevel_page_{parent_menu_slug}) only when submenu_slugs is not empty. Use the slswc_drm_should_lock_page_{identifier} filter to narrow locking further, for example to specific screens.

Scheduled License Check

The DRM schedules a daily wp-cron job (slswc_drm_license_check_{identifier}) that:

  1. Sends a check_license request to the license server.
  2. Updates the license status based on the response.
  3. Resets the grace period clock if the license is valid.
  4. Clears all notice dismissals on successful activation.

If the server is unreachable, the current state is preserved (the clock does not advance).

AJAX Handlers

Two AJAX endpoints are registered per DRM instance:

ActionDescription
wp_ajax_slswc_drm_dismiss_notice_{identifier}Dismiss a notice for 24 hours
wp_ajax_slswc_drm_refresh_status_{identifier}Re-check the license and return the new DRM state

Both require a valid nonce (slswc_drm_{identifier}) and manage_options capability.

Filters

FilterParametersDescription
slswc_drm_config_{identifier}(array $config)Modify the DRM configuration after initialization
slswc_drm_state_{identifier}(string $state, DRM $drm)Override the computed DRM state
slswc_drm_notice_html_{identifier}(string $html, string $state, string $license_status, DRM $drm)Modify or replace notice HTML
slswc_drm_should_lock_page_{identifier}(bool $should_lock, DRM $drm)Prevent the interstitial from showing on the current page, or narrow locking to specific screens
slswc_drm_interstitial_html_{identifier}(string $html, DRM $drm)Modify or replace the interstitial HTML

Example: Extend the Grace Period

add_filter( 'slswc_drm_config_my-plugin', function ( $config ) {
    $config['grace_soft_days'] = 5;
    $config['grace_lock_days'] = 14;
    return $config;
} );

Example: Disable the Lock for Specific Users

add_filter( 'slswc_drm_should_lock_page_my-plugin', function ( $should_lock, $drm ) {
    if ( current_user_can( 'manage_network' ) ) {
        return false;
    }
    return $should_lock;
}, 10, 2 );

Option Keys

The DRM stores state in WordPress options using the pattern {identifier}_drm_{key}. On multisite, license_activated and last_license_check are stored as network (site) options; the notice dismissal flags are always regular options:

KeyDescription
license_activatedyes or no
last_license_checkUnix timestamp of last successful check (set to the current time on first run)
show_no_license_noticeDismissal expiry timestamp
show_pre_expiry_noticeDismissal expiry timestamp
show_expired_noticeDismissal expiry timestamp
show_disabled_noticeDismissal expiry timestamp