{"id":241723,"date":"2026-06-04T05:55:14","date_gmt":"2026-06-04T05:55:14","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/meta-membership\/"},"modified":"2026-06-09T03:52:54","modified_gmt":"2026-06-09T03:52:54","slug":"meta-membership","status":"publish","type":"plugin","link":"https:\/\/bal.wordpress.org\/plugins\/meta-membership\/","author":16091062,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.3","stable_tag":"1.0.3","tested":"7.0","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"Meta Membership","header_author":"Webworkz","header_description":"A clean, modern, flexible, membership plugin","assets_banners_color":"b6cddd","last_updated":"2026-06-09 03:52:54","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/plugins.webworkz.nz","header_author_uri":"https:\/\/webworkz.nz","rating":0,"author_block_rating":0,"active_installs":0,"downloads":196,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"jerrystewart99","date":"2026-06-05 00:02:38"},"1.0.1":{"tag":"1.0.1","author":"jerrystewart99","date":"2026-06-07 07:34:48"},"1.0.2":{"tag":"1.0.2","author":"jerrystewart99","date":"2026-06-08 04:37:08"},"1.0.3":{"tag":"1.0.3","author":"jerrystewart99","date":"2026-06-09 03:52:54"}},"upgrade_notice":{"1.0.0":"<p>Initial release. Migrating from a custom membership setup? Back up your site before enabling.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3560301,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3560301,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3560301,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3565447,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"7.0\"},\"phpExtensionBundles\":[\"kitchen-sink\"],\"features\":{\"intl\":true},\"landingPage\":\"\\\/\",\"login\":true,\"steps\":[{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"meta-membership\"},\"options\":{\"activate\":true}},{\"step\":\"runPHP\",\"code\":\"<?php\\n\\\/**\\n * Meta Membership demo seed.\\n *\\n * Runs after WordPress + Meta Membership are installed and activated.\\n * Populates settings, meta field descriptors, membership types, pages,\\n * and three demo members with relative-dated cycles.\\n *\\n * This is the readable reference copy. It is embedded (escaped) into the\\n * runPHP step of mm-demo.json.\\n *\\\/\\n\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n\\nglobal $wpdb;\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 0. Helpers\\n * ------------------------------------------------------------------------- *\\\/\\n\\n$today = new DateTime( 'now', new DateTimeZone( 'UTC' ) );\\n\\n$date_offset = function ( int $days ) use ( $today ): string {\\n\\t$d = clone $today;\\n\\t$d->modify( ( $days >= 0 ? '+' : '' ) . $days . ' days' );\\n\\treturn $d->format( 'Y-m-d' );\\n};\\n\\n$now_dt = $today->format( 'Y-m-d H:i:s' );\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 1. System settings (wp_options) \\u2014 native arrays, plugin serialises on read\\n * ------------------------------------------------------------------------- *\\\/\\n\\nupdate_option( 'mime_general_settings', [\\n\\t'async_emails'                 => true,\\n\\t'dashboard_page_mode'          => 'auto',\\n\\t'manual_dashboard_page_id'     => 0,\\n\\t'auto_dashboard_pages'         => [],   \\\/\\\/ repopulated below once the page exists\\n\\t'redirect_after_login_page_id' => 0,\\n\\t'default_no_access_message'    => 'You do not have access to this content.',\\n\\t'enable_profile_tab'           => true,\\n\\t'enable_membership_tab'        => true,\\n\\t'enable_payments_tab'          => true,\\n] );\\n\\nupdate_option( 'mime_membership_settings', [\\n\\t'is_renewable'       => true,\\n\\t'renewal_mode'       => 'fixed',\\n\\t'fixed_expiry_day'   => 1,\\n\\t'fixed_expiry_month' => 4,\\n\\t'max_memberships'    => 1,\\n\\t'grace_period'       => 30,\\n] );\\n\\nupdate_option( 'mime_payment_settings', [\\n\\t'gateway_settings' => [\\n\\t\\t'manual' => [\\n\\t\\t\\t'enabled'      => true,\\n\\t\\t\\t'instructions' => \\\"<h4>Manual Payment (direct bank transfer)<\\\/h4>\\\\n&nbsp;\\\\nTo pay by bank transfer, press 'Pay Now' and we'll email you payment instructions.\\\\n\\\",\\n\\t\\t],\\n\\t],\\n\\t'currency' => 'USD',\\n] );\\n\\nupdate_option( 'mime_registration_settings', [\\n\\t'admin_only'       => false,\\n\\t'admin_approval'   => true,\\n\\t'email_validation' => false,\\n] );\\n\\nupdate_option( 'mime_notification_settings', [\\n\\t'admin_recipients' => [ 'admin@demo.com' ],\\n] );\\n\\n\\\/\\\/ Suppress the scheduled notification check during the demo session.\\nupdate_option( 'mime_notifications_last_run', time() );\\n\\n\\\/\\\/ Site identity.\\nupdate_option( 'blogname', 'Meta Membership Demo' );\\nupdate_option( 'blogdescription', 'A live demo of the Meta Membership plugin' );\\n\\n\\\/\\\/ Remove the default Sample Page so it doesn't clutter the auto-generated menu.\\n$sample = get_page_by_path( 'sample-page' );\\nif ( $sample ) {\\n\\twp_delete_post( $sample->ID, true );\\n}\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 2. Meta field descriptors (explicit IDs 3 and 4 \\u2014 type blobs reference them)\\n * ------------------------------------------------------------------------- *\\\/\\n\\n$descriptor_table = $wpdb->prefix . 'mime_meta_field_descriptor';\\n\\n$wpdb->insert( $descriptor_table, [\\n\\t'id'         => 3,\\n\\t'slug'       => 'street-address',\\n\\t'label'      => 'Street Address',\\n\\t'type'       => 'text',\\n\\t'options'    => '[]',\\n\\t'attributes' => wp_json_encode( [ 'placeholder' => '', 'title' => 'your street address' ] ),\\n\\t'created_at' => $now_dt,\\n\\t'updated_at' => null,\\n] );\\n\\n$wpdb->insert( $descriptor_table, [\\n\\t'id'         => 4,\\n\\t'slug'       => 'city',\\n\\t'label'      => 'City',\\n\\t'type'       => 'text',\\n\\t'options'    => '[]',\\n\\t'attributes' => wp_json_encode( [ 'placeholder' => '', 'title' => 'your city' ] ),\\n\\t'created_at' => $now_dt,\\n\\t'updated_at' => null,\\n] );\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 3. Membership types (explicit IDs 1\\\/2\\\/3; field-5 stripped from blobs)\\n * ------------------------------------------------------------------------- *\\\/\\n\\n$types_table = $wpdb->prefix . 'mime_membership_types';\\n\\n\\\/\\\/ Standard \\u2014 fixed\\\/April, $110\\\/$100, 30-day grace, both meta fields on\\n\\\/\\\/ profile + registration, public + approval (inherits system defaults).\\n$standard_settings = [\\n\\t'registration_fee'   => 110,\\n\\t'renewal_fee'        => 100,\\n\\t'prorata'            => 'none',\\n\\t'admin_approval'     => null,\\n\\t'email_validation'   => null,\\n\\t'admin_only'         => null,\\n\\t'renewal_mode'       => null,\\n\\t'is_renewable'       => null,\\n\\t'gateways'           => [ 'manual' ],\\n\\t'currency'           => null,\\n\\t'meta_fields'        => [\\n\\t\\t'profile'      => [\\n\\t\\t\\t'3' => [ 'id' => 3, 'width' => 'full', 'privacy' => 'all', 'can_edit' => true ],\\n\\t\\t\\t'4' => [ 'id' => 4, 'width' => 'full', 'privacy' => 'all', 'can_edit' => true ],\\n\\t\\t],\\n\\t\\t'registration' => [\\n\\t\\t\\t'3' => [ 'id' => 3, 'width' => 'full' ],\\n\\t\\t\\t'4' => [ 'id' => 4, 'width' => 'full' ],\\n\\t\\t],\\n\\t],\\n\\t'fixed_expiry_day'   => 1,\\n\\t'fixed_expiry_month' => 4,\\n\\t'grace_period'       => 30,\\n];\\n\\n\\\/\\\/ Student \\u2014 anniversary, grace 0, $55\\\/$50, admin-only, both meta on profile only.\\n$student_settings = [\\n\\t'registration_fee' => 55,\\n\\t'renewal_fee'      => 50,\\n\\t'prorata'          => 'none',\\n\\t'admin_approval'   => null,\\n\\t'email_validation' => null,\\n\\t'admin_only'       => true,\\n\\t'renewal_mode'     => 'anniversary',\\n\\t'is_renewable'     => null,\\n\\t'gateways'         => [ 'manual' ],\\n\\t'currency'         => null,\\n\\t'meta_fields'      => [\\n\\t\\t'profile'      => [\\n\\t\\t\\t'3' => [ 'id' => 3, 'width' => 'full', 'privacy' => 'all', 'can_edit' => true ],\\n\\t\\t\\t'4' => [ 'id' => 4, 'width' => 'full', 'privacy' => 'all', 'can_edit' => true ],\\n\\t\\t],\\n\\t\\t'registration' => [],\\n\\t],\\n\\t'grace_period'     => 0,\\n];\\n\\n\\\/\\\/ Guest \\u2014 non-renewable, fixed 1 August, grace 0, free, no gateway, no meta.\\n$guest_settings = [\\n\\t'registration_fee'   => 0,\\n\\t'renewal_fee'        => 0,\\n\\t'prorata'            => 'none',\\n\\t'admin_approval'     => null,\\n\\t'email_validation'   => null,\\n\\t'admin_only'         => true,\\n\\t'renewal_mode'       => 'fixed',\\n\\t'is_renewable'       => false,\\n\\t'gateways'           => [],\\n\\t'currency'           => null,\\n\\t'fixed_expiry_day'   => 1,\\n\\t'fixed_expiry_month' => 8,\\n\\t'meta_fields'        => [ 'profile' => [], 'registration' => [] ],\\n\\t'grace_period'       => 0,\\n];\\n\\n$wpdb->insert( $types_table, [\\n\\t'id'         => 1,\\n\\t'slug'       => 'standard',\\n\\t'title'      => 'Standard',\\n\\t'settings'   => wp_json_encode( $standard_settings, JSON_UNESCAPED_SLASHES ),\\n\\t'status'     => 'active',\\n\\t'created_at' => $now_dt,\\n\\t'updated_at' => $now_dt,\\n] );\\n\\n$wpdb->insert( $types_table, [\\n\\t'id'         => 2,\\n\\t'slug'       => 'student',\\n\\t'title'      => 'Student',\\n\\t'settings'   => wp_json_encode( $student_settings, JSON_UNESCAPED_SLASHES ),\\n\\t'status'     => 'active',\\n\\t'created_at' => $now_dt,\\n\\t'updated_at' => $now_dt,\\n] );\\n\\n$wpdb->insert( $types_table, [\\n\\t'id'         => 3,\\n\\t'slug'       => 'guest',\\n\\t'title'      => 'Guest',\\n\\t'settings'   => wp_json_encode( $guest_settings, JSON_UNESCAPED_SLASHES ),\\n\\t'status'     => 'active',\\n\\t'created_at' => $now_dt,\\n\\t'updated_at' => $now_dt,\\n] );\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 4. Pages \\u2014 Home, Member Dashboard, Standard Registration\\n * ------------------------------------------------------------------------- *\\\/\\n\\n$home_content = <<<'HTML'\\n<!-- wp:paragraph -->\\n<p>A live, working demo of <strong>Meta Membership<\\\/strong> \\u2014 running entirely in your browser. Nothing here is installed on a server; close the tab and it's gone. You start signed in as the administrator.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:separator -->\\n<hr class=\\\"wp-block-separator has-alpha-channel-opacity\\\"\\\/>\\n<!-- \\\/wp:separator -->\\n\\n<!-- wp:heading -->\\n<h2 class=\\\"wp-block-heading\\\">Two ways to explore<\\\/h2>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>Use the buttons below to switch between the two sides of the plugin. Each logs you out and takes you to the right login screen.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:shortcode -->\\n[demo_auth_buttons]\\n<!-- \\\/wp:shortcode -->\\n\\n<!-- wp:heading {\\\"level\\\":3} -->\\n<h3 class=\\\"wp-block-heading\\\">As the administrator<\\\/h3>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>See how membership types, fees, renewal cycles, members, and notifications are configured \\u2014 this is where Meta Membership does its work. Log in with <code>admin<\\\/code> \\\/ <code>password<\\\/code>, or use the toolbar at the top of any page.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:heading {\\\"level\\\":3} -->\\n<h3 class=\\\"wp-block-heading\\\">As a member<\\\/h3>\\n<!-- \\\/wp:heading -->\\n\\n<!-- wp:paragraph -->\\n<p>Log in as one of three demo members to see the member dashboard in different lifecycle states (all use the password <code>password<\\\/code>):<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:list -->\\n<ul class=\\\"wp-block-list\\\"><!-- wp:list-item -->\\n<li><code>current<\\\/code> \\u2014 a current Student member<\\\/li>\\n<!-- \\\/wp:list-item -->\\n\\n<!-- wp:list-item -->\\n<li><code>grace<\\\/code> \\u2014 a Standard member in their renewal grace period<\\\/li>\\n<!-- \\\/wp:list-item -->\\n\\n<!-- wp:list-item -->\\n<li><code>expired<\\\/code> \\u2014 a lapsed Guest member<\\\/li>\\n<!-- \\\/wp:list-item --><\\\/ul>\\n<!-- \\\/wp:list -->\\n\\n<!-- wp:paragraph -->\\n<p>Or register a brand-new Standard member from the <a href=\\\"\\\/standard-registration\\\/\\\">registration page<\\\/a> and watch it appear in the admin for approval.<\\\/p>\\n<!-- \\\/wp:paragraph -->\\n\\n<!-- wp:paragraph {\\\"fontSize\\\":\\\"small\\\"} -->\\n<p class=\\\"has-small-font-size\\\"><em>This is a sandbox: it can't send email or take real payments, so confirmation emails and live card payments won't go through \\u2014 but you can see exactly how they're configured.<\\\/em><\\\/p>\\n<!-- \\\/wp:paragraph -->\\nHTML;\\n\\n$home_id = wp_insert_post( [\\n\\t'post_title'   => 'Demo Home',\\n\\t'post_name'    => 'home',\\n\\t'post_content' => $home_content,\\n\\t'post_status'  => 'publish',\\n\\t'post_type'    => 'page',\\n] );\\n\\n$dashboard_id = wp_insert_post( [\\n\\t'post_title'   => 'Member Dashboard',\\n\\t'post_name'    => 'member-dashboard',\\n\\t'post_content' => \\\"<!-- wp:shortcode -->\\\\n[mime_dashboard]\\\\n<!-- \\\/wp:shortcode -->\\\",\\n\\t'post_status'  => 'publish',\\n\\t'post_type'    => 'page',\\n] );\\n\\n$registration_id = wp_insert_post( [\\n\\t'post_title'   => 'Standard Registration',\\n\\t'post_name'    => 'standard-registration',\\n\\t'post_content' => \\\"<!-- wp:shortcode -->\\\\n[mime_registration type=\\\\\\\"standard\\\\\\\"]\\\\n<!-- \\\/wp:shortcode -->\\\",\\n\\t'post_status'  => 'publish',\\n\\t'post_type'    => 'page',\\n] );\\n\\n\\\/\\\/ Static front page = Home.\\nupdate_option( 'show_on_front', 'page' );\\nupdate_option( 'page_on_front', (int) $home_id );\\n\\n\\\/\\\/ Tell MM which page hosts the dashboard shortcode (kills the \\\"no shortcode\\\" warning).\\n\\\/\\\/ Creating the page via wp_insert_post fires save_post, which should trigger MM's\\n\\\/\\\/ own shortcode scan and populate this. Only set it directly as a fallback if MM\\n\\\/\\\/ didn't \\u2014 so we never clobber a richer structure MM may have written.\\n\\\/\\\/\\n\\\/\\\/ Also point the post-login redirect at the dashboard page. The scanner populates\\n\\\/\\\/ auto_dashboard_pages but does NOT update redirect_after_login_page_id (which holds\\n\\\/\\\/ an explicit page id), so without this a member lands on the home page after login\\n\\\/\\\/ instead of their dashboard. We set it directly here.\\n$general = get_option( 'mime_general_settings', [] );\\nif ( empty( $general['auto_dashboard_pages'] ) ) {\\n\\t$general['auto_dashboard_pages'] = [ (int) $dashboard_id ];\\n}\\n$general['redirect_after_login_page_id'] = (int) $dashboard_id;\\nupdate_option( 'mime_general_settings', $general );\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 4b. Demo auth buttons \\u2014 a must-use plugin registering [demo_auth_buttons].\\n *\\n * Renders two links that log the current user out and redirect to a login\\n * screen: one to the Member Dashboard (MM login card), one to wp-admin\\n * (WordPress login). wp_logout_url() embeds a per-session nonce, so the URLs\\n * must be generated live at render time \\u2014 hence a shortcode rather than a\\n * static link in the page content. This is demo-environment plumbing, not\\n * part of Meta Membership.\\n * ------------------------------------------------------------------------- *\\\/\\n\\n$mu_dir = WPMU_PLUGIN_DIR;\\nif ( ! is_dir( $mu_dir ) ) {\\n\\twp_mkdir_p( $mu_dir );\\n}\\n\\n$mu_code = <<<'PHP'\\n<?php\\n\\\/**\\n * Plugin Name: Demo Auth Buttons\\n * Description: Provides [demo_auth_buttons] for the Meta Membership demo.\\n *\\\/\\n\\nif ( ! defined( 'WPINC' ) ) {\\n\\texit;\\n}\\n\\nadd_shortcode( 'demo_auth_buttons', function () {\\n\\n\\t$member_url = wp_logout_url( home_url( '\\\/member-dashboard\\\/' ) );\\n\\t$admin_url  = wp_logout_url( admin_url() );\\n\\n\\t$style = 'display:inline-block;margin:0 10px 10px 0;padding:10px 18px;'\\n\\t\\t. 'background:#2271b1;color:#fff;text-decoration:none;border-radius:4px;'\\n\\t\\t. 'font-weight:600;';\\n\\n\\t$out  = '<div class=\\\"demo-auth-buttons\\\" style=\\\"margin:1rem 0;\\\">';\\n\\t$out .= '<a href=\\\"' . esc_url( $member_url ) . '\\\" style=\\\"' . esc_attr( $style ) . '\\\">Log in as a member<\\\/a>';\\n\\t$out .= '<a href=\\\"' . esc_url( $admin_url ) . '\\\" style=\\\"' . esc_attr( $style ) . '\\\">Log in as admin<\\\/a>';\\n\\t$out .= '<\\\/div>';\\n\\n\\treturn $out;\\n} );\\nPHP;\\n\\nfile_put_contents( $mu_dir . '\\\/demo-auth-buttons.php', $mu_code );\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 5. Demo members \\u2014 wp_user + member + cycle + meta, relative dates\\n * ------------------------------------------------------------------------- *\\\/\\n\\n$members_table = $wpdb->prefix . 'mime_members';\\n$cycles_table  = $wpdb->prefix . 'mime_membership_cycles';\\n$meta_table    = $wpdb->prefix . 'mime_member_meta';\\n\\n\\\/**\\n * Seed one demo member end-to-end.\\n *\\n * @param array $args keys: username, email, first, last, type_id,\\n *                    member_id, cycle_id, start_offset, end_offset,\\n *                    renewal_intent, street, city, meta_base_id\\n *\\\/\\n$seed_member = function ( array $args ) use ( $wpdb, $members_table, $cycles_table, $meta_table, $date_offset, $now_dt ) {\\n\\n\\t\\\/\\\/ wp_user with a known, usable password.\\n\\t$user_id = wp_insert_user( [\\n\\t\\t'user_login' => $args['username'],\\n\\t\\t'user_pass'  => 'password',\\n\\t\\t'user_email' => $args['email'],\\n\\t\\t'first_name' => $args['first'],\\n\\t\\t'last_name'  => $args['last'],\\n\\t\\t'role'       => 'subscriber',\\n\\t] );\\n\\n\\tif ( is_wp_error( $user_id ) ) {\\n\\t\\treturn; \\\/\\\/ skip on collision; demo is best-effort\\n\\t}\\n\\n\\t\\\/\\\/ member row\\n\\t$wpdb->insert( $members_table, [\\n\\t\\t'id'                  => $args['member_id'],\\n\\t\\t'wp_user_id'          => (int) $user_id,\\n\\t\\t'email'               => $args['email'],\\n\\t\\t'first_name'          => $args['first'],\\n\\t\\t'last_name'           => $args['last'],\\n\\t\\t'registration_status' => 'approved',\\n\\t\\t'created_at'          => $now_dt,\\n\\t\\t'updated_at'          => null,\\n\\t] );\\n\\n\\t\\\/\\\/ cycle row with relative dates\\n\\t$wpdb->insert( $cycles_table, [\\n\\t\\t'id'                 => $args['cycle_id'],\\n\\t\\t'member_id'          => $args['member_id'],\\n\\t\\t'membership_type_id' => $args['type_id'],\\n\\t\\t'start_date'         => $date_offset( $args['start_offset'] ),\\n\\t\\t'end_date'           => $date_offset( $args['end_offset'] ),\\n\\t\\t'transaction_ids'    => '[]',\\n\\t\\t'renewal_intent'     => $args['renewal_intent'],\\n\\t\\t'comment'            => 'Demo member',\\n\\t\\t'notifications_sent' => '[]',\\n\\t\\t'created_at'         => $now_dt,\\n\\t\\t'updated_at'         => $now_dt,\\n\\t] );\\n\\n\\t\\\/\\\/ meta values (street 3, city 4) \\u2014 only if provided\\n\\tif ( ! empty( $args['street'] ) ) {\\n\\t\\t$wpdb->insert( $meta_table, [\\n\\t\\t\\t'id'         => $args['meta_base_id'],\\n\\t\\t\\t'member_id'  => $args['member_id'],\\n\\t\\t\\t'schema_id'  => 3,\\n\\t\\t\\t'value'      => $args['street'],\\n\\t\\t\\t'updated_at' => $now_dt,\\n\\t\\t] );\\n\\t\\t$wpdb->insert( $meta_table, [\\n\\t\\t\\t'id'         => $args['meta_base_id'] + 1,\\n\\t\\t\\t'member_id'  => $args['member_id'],\\n\\t\\t\\t'schema_id'  => 4,\\n\\t\\t\\t'value'      => $args['city'],\\n\\t\\t\\t'updated_at' => $now_dt,\\n\\t\\t] );\\n\\t}\\n};\\n\\n\\\/\\\/ current \\u2014 Student (type 2), active: ends ~1 year out\\n$seed_member( [\\n\\t'username' => 'current', 'email' => 'current@demo.com',\\n\\t'first' => 'Jane', 'last' => 'Doe', 'type_id' => 2,\\n\\t'member_id' => 1, 'cycle_id' => 1,\\n\\t'start_offset' => -65, 'end_offset' => 300,\\n\\t'renewal_intent' => 'manual',\\n\\t'street' => '123 Student Villas', 'city' => 'Studentsville',\\n\\t'meta_base_id' => 1,\\n] );\\n\\n\\\/\\\/ grace \\u2014 Standard (type 1, 30-day grace), 7 days past expiry \\u2192 in grace\\n$seed_member( [\\n\\t'username' => 'grace', 'email' => 'grace@demo.com',\\n\\t'first' => 'John', 'last' => 'Smith', 'type_id' => 1,\\n\\t'member_id' => 2, 'cycle_id' => 2,\\n\\t'start_offset' => -372, 'end_offset' => -7,\\n\\t'renewal_intent' => 'manual',\\n\\t'street' => '456 Graceland', 'city' => 'Standard City',\\n\\t'meta_base_id' => 3,\\n] );\\n\\n\\\/\\\/ expired \\u2014 Guest (type 3, non-renewable, grace 0), 30 days past \\u2192 expired\\n$seed_member( [\\n\\t'username' => 'expired', 'email' => 'expired@demo.com',\\n\\t'first' => 'Sally', 'last' => 'Jones', 'type_id' => 3,\\n\\t'member_id' => 3, 'cycle_id' => 3,\\n\\t'start_offset' => -395, 'end_offset' => -30,\\n\\t'renewal_intent' => 'none',\\n\\t'street' => '678 Expired St', 'city' => 'Gueststown',\\n\\t'meta_base_id' => 5,\\n] );\\n\\n\\\/* ---------------------------------------------------------------------------\\n * 6. Pretty permalinks + flush\\n * ------------------------------------------------------------------------- *\\\/\\n\\nupdate_option( 'permalink_structure', '\\\/%postname%\\\/' );\\nflush_rewrite_rules();\\n\"}]}"}},"all_blocks":[],"tagged_versions":["1.0.0","1.0.1","1.0.2","1.0.3"],"block_files":[],"assets_screenshots":{"screenshot-1.jpg":{"filename":"screenshot-1.jpg","revision":3565432,"resolution":"1","location":"assets","locale":"","width":1854,"height":601},"screenshot-2.jpg":{"filename":"screenshot-2.jpg","revision":3565432,"resolution":"2","location":"assets","locale":"","width":1831,"height":838},"screenshot-3.jpg":{"filename":"screenshot-3.jpg","revision":3565432,"resolution":"3","location":"assets","locale":"","width":1550,"height":792},"screenshot-4.jpg":{"filename":"screenshot-4.jpg","revision":3565432,"resolution":"4","location":"assets","locale":"","width":1498,"height":1273},"screenshot-5.jpg":{"filename":"screenshot-5.jpg","revision":3565432,"resolution":"5","location":"assets","locale":"","width":1839,"height":826},"screenshot-6.jpg":{"filename":"screenshot-6.jpg","revision":3565432,"resolution":"6","location":"assets","locale":"","width":1843,"height":760}},"screenshots":{"1":"Membership Types ? each type sets its own fees, renewal mode (fixed-date, anniversary, or non-renewable), and registration policy.","2":"The members list shows each member's live status ? active, in grace period, or expired ? derived from their membership cycle.","3":"The member dashboard: members see their membership type, expiry date, and status, and can renew or cancel.","4":"Per-type settings for fees, pro-rata discounts, registration policy, and payment gateways ? each able to inherit system defaults.","5":"Define custom member fields with per-field width, privacy, and edit permissions, shown on profile and registration forms.","6":"Time-based notifications send expiry reminders to members and admins, configurable per membership type."}},"plugin_section":[],"plugin_tags":[1912,1932,15130,4079,5134],"plugin_category":[41,58],"plugin_contributors":[184797],"plugin_business_model":[],"class_list":["post-241723","plugin","type-plugin","status-publish","hentry","plugin_tags-access-control","plugin_tags-membership","plugin_tags-nonprofit","plugin_tags-subscriptions","plugin_tags-user-registration","plugin_category-communication","plugin_category-user-management","plugin_contributors-jerrystewart99","plugin_committers-jerrystewart99"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/meta-membership\/assets\/icon-128x128.png?rev=3560301","icon_2x":"https:\/\/ps.w.org\/meta-membership\/assets\/icon-256x256.png?rev=3560301","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/meta-membership\/assets\/screenshot-1.jpg?rev=3565432","caption":"Membership Types ? each type sets its own fees, renewal mode (fixed-date, anniversary, or non-renewable), and registration policy."},{"src":"https:\/\/ps.w.org\/meta-membership\/assets\/screenshot-2.jpg?rev=3565432","caption":"The members list shows each member's live status ? active, in grace period, or expired ? derived from their membership cycle."},{"src":"https:\/\/ps.w.org\/meta-membership\/assets\/screenshot-3.jpg?rev=3565432","caption":"The member dashboard: members see their membership type, expiry date, and status, and can renew or cancel."},{"src":"https:\/\/ps.w.org\/meta-membership\/assets\/screenshot-4.jpg?rev=3565432","caption":"Per-type settings for fees, pro-rata discounts, registration policy, and payment gateways ? each able to inherit system defaults."},{"src":"https:\/\/ps.w.org\/meta-membership\/assets\/screenshot-5.jpg?rev=3565432","caption":"Define custom member fields with per-field width, privacy, and edit permissions, shown on profile and registration forms."},{"src":"https:\/\/ps.w.org\/meta-membership\/assets\/screenshot-6.jpg?rev=3565432","caption":"Time-based notifications send expiry reminders to members and admins, configurable per membership type."}],"raw_content":"<!--section=description-->\n<p><strong>Meta Membership<\/strong> is a clean, flexible, developer-friendly plugin for membership sites. Most membership plugins treat payments as the source of truth. Meta Membership is built around the membership cycle \u2014 so renewals, grace periods, and fixed annual expiry dates work the way your organisation actually expects.<\/p>\n\n<p>Designed with clubs, nonprofits, professional associations, and community groups in mind.<\/p>\n\n<p><strong>Try it now \u2014 no install needed.<\/strong> Explore a fully-configured demo site running entirely in your browser, with sample membership types, members in different lifecycle states, and a working registration flow: <a href=\"https:\/\/playground.wordpress.net\/?blueprint-url=https:\/\/plugins.webworkz.nz\/mm-demo.json\">Launch the live demo<\/a><\/p>\n\n<p>It includes:<\/p>\n\n<ul>\n<li>Stripe-based payments and renewals (via <a href=\"https:\/\/wordpress.org\/plugins\/minimal-stripe-wrapper\/\">Minimal Stripe Wrapper<\/a>)<\/li>\n<li>Three renewal modes: fixed expiry date, anniversary, or never expires<\/li>\n<li>Fixed expiry with pro-rata fees for mid-year joiners<\/li>\n<li>Configurable grace periods \u2014 members retain access while they renew<\/li>\n<li>Registration policies: open, admin approval, or admin-only<\/li>\n<li>Page, post, and menu access control by membership status<\/li>\n<li>Built-in notifications for expiry reminders and admin alerts<\/li>\n<li>CSV import and export \u2014 migrate in from another plugin without losing your data<\/li>\n<li>Native MailPoet integration (via free Meta MailPoet add-on)<\/li>\n<\/ul>\n\n<p>Whether you need a simple signup flow or granular control over membership logic, Meta Membership is built to adapt \u2014 without the bloat.<\/p>\n\n<p>For documentation, visit <a href=\"https:\/\/plugins.webworkz.nz\">plugins.webworkz.nz<\/a><\/p>\n\n<h3>Ideal For<\/h3>\n\n<ul>\n<li>Not-for-profit organisations<\/li>\n<li>Professional and hobby clubs<\/li>\n<li>Community and advocacy groups<\/li>\n<li>Event-based member programmes<\/li>\n<li>Developers building custom member workflows<\/li>\n<\/ul>\n\n<h3>Features<\/h3>\n\n<ul>\n<li>Membership cycle as source of truth \u2014 status driven by cycle, not payment history<\/li>\n<li>Three renewal modes: fixed expiry date, anniversary, or never expires<\/li>\n<li>Fixed expiry date support \u2014 e.g. all memberships expire 1 April each year<\/li>\n<li>Pro-rata fees for members joining mid-period on fixed expiry memberships<\/li>\n<li>Configurable grace periods \u2014 expired members retain access while they renew<\/li>\n<li>Multiple membership types with separate fees, renewal rules, and registration policies<\/li>\n<li>Separate registration fee and renewal fee per membership type<\/li>\n<li>Stripe-integrated membership payments (via Minimal Stripe Wrapper)<\/li>\n<li>Manual payment option for members who pay offline<\/li>\n<li>Registration policies: open, admin approval, or admin-only<\/li>\n<li>Email validation option on registration<\/li>\n<li>Page, post, and menu visibility control by membership status<\/li>\n<li>Custom member fields (meta fields) with privacy and editability controls<\/li>\n<li>Two notification systems \u2014 event-triggered system notifications and configurable time-based membership reminders<\/li>\n<li>CSV member import \u2014 migrate from your existing system with membership type, expiry, status, and custom fields<\/li>\n<li>CSV member export<\/li>\n<li>WordPress role sync \u2014 optionally map membership types and statuses to WP roles for compatibility with other plugins<\/li>\n<li>Developer-friendly: modern architecture with PSR-4 autoloading and clean namespacing under MIME<\/li>\n<li>Built with not-for-profit governance workflows in mind<\/li>\n<\/ul>\n\n<h3>Notes for Developers<\/h3>\n\n<p>This plugin is built with a modern architecture using PSR-4 autoloading, namespacing under <code>MIME<\/code>, and follows WordPress best practices for escaping, sanitizing, and hooking. Documentation at https:\/\/plugins.webworkz.nz<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/meta-membership\/<\/code><\/li>\n<li>Activate the plugin through the 'Plugins' menu in WordPress<\/li>\n<li>Create a page and add the <code>[mime_dashboard]<\/code> shortcode \u2014 Meta Membership will detect it automatically<\/li>\n<li>Go to <strong>Meta Membership Settings<\/strong> to configure defaults (General, Payments, Notifications, Member Meta Fields)<\/li>\n<li>Create one or more Membership Types under <strong>Membership &gt; Membership Types<\/strong><\/li>\n<li>Add the <code>[mime_registration type=\"mt-slug\"]<\/code> shortcode to any page to enable a public registration form \u2014 replace mt-slug with your membership type slug (available via copy-to-clipboard in the Membership Types table)<\/li>\n<li>Enable payment gateways in the Payments tab<\/li>\n<\/ol>\n\n<p>Note: Stripe payments require <a href=\"https:\/\/wordpress.org\/plugins\/minimal-stripe-wrapper\/\">Minimal Stripe Wrapper<\/a> to be installed and configured separately.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"is%20this%20plugin%20suitable%20for%20commercial%20use%3F\"><h3>Is this plugin suitable for commercial use?<\/h3><\/dt>\n<dd><p>Yes, absolutely. While it's designed with clubs and not-for-profits in mind, it's flexible enough for commercial sites as well.<\/p><\/dd>\n<dt id=\"how%20is%20this%20different%20from%20other%20membership%20plugins%3F\"><h3>How is this different from other membership plugins?<\/h3><\/dt>\n<dd><p>Most membership plugins determine membership status from payment records. Meta Membership uses the membership cycle as the source of truth \u2014 so grace periods, fixed expiry dates, and renewal behaviour work the way your organisation expects, regardless of payment history.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20handle%20fixed%20annual%20expiry%20dates%3F\"><h3>Does this plugin handle fixed annual expiry dates?<\/h3><\/dt>\n<dd><p>Yes. You can set a fixed expiry date (e.g. 1 April) that applies to all memberships of a given type, with configurable grace periods and optional pro-rata fees for members joining mid-year.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20support%20recurring%20payments%3F\"><h3>Does this plugin support recurring payments?<\/h3><\/dt>\n<dd><p>Not yet. Stripe-based recurring payments \u2014 where a card is charged automatically without member action \u2014 are on the roadmap. Meta Membership handles one-off payments for registration and renewal, supported by automated expiry notifications and grace periods that guide members through the renewal process at the right time.<\/p><\/dd>\n<dt id=\"can%20i%20migrate%20my%20existing%20members%20from%20another%20plugin%3F\"><h3>Can I migrate my existing members from another plugin?<\/h3><\/dt>\n<dd><p>Yes. Meta Membership includes a CSV import tool that handles member records, membership types, expiry dates, registration status, renewal intent, and custom member fields.<\/p><\/dd>\n<dt id=\"can%20i%20extend%20or%20customise%20the%20member%20fields%3F\"><h3>Can I extend or customise the member fields?<\/h3><\/dt>\n<dd><p>Yes. Member profiles are stored in a dedicated database table, and custom fields can be defined with privacy controls, editability settings, and required flags. The plugin is extensible via actions and filters.<\/p><\/dd>\n<dt id=\"will%20my%20members%20need%20to%20reset%20their%20passwords%20after%20migrating%20to%20meta%20membership%3F\"><h3>Will my members need to reset their passwords after migrating to Meta Membership?<\/h3><\/dt>\n<dd><p>No. Meta Membership is designed to work with existing WordPress user accounts. During member import, if an email address matches an existing WordPress user, that account is preserved and linked to the new membership record. Members can log in with their existing credentials immediately after migration \u2014 no password reset required.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20mailpoet%3F\"><h3>Does this work with MailPoet?<\/h3><\/dt>\n<dd><p>Yes, via the free Meta MailPoet add-on, which syncs membership status and type to MailPoet lists automatically. No paid MailPoet tier required.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20ultimate%20member%3F\"><h3>Does this work with Ultimate Member?<\/h3><\/dt>\n<dd><p>Meta Membership can coexist with Ultimate Member. A common setup is to use Ultimate Member for its member profile UX and directory features, while Meta Membership handles membership logic \u2014 with Ultimate Member's own membership features disabled to avoid conflict. Role Sync Rules can be used to keep WP roles aligned with membership status for compatibility.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20multisite-compatible%3F\"><h3>Is this plugin multisite-compatible?<\/h3><\/dt>\n<dd><p>Not yet tested with multisite. It should work on a per-site basis, but has not been formally validated in a network setup.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Added: link to a live, in-browser demo (WordPress Playground) in the plugin description<\/li>\n<li>Remove blank 'Advanced' tab from Member edit screen<\/li>\n<li>fixed: exporter not exporting expired member info<\/li>\n<li>fixed: importer not stripping BOM from import stream breaking import if those bytes were present<\/li>\n<li>fixed: redirect-after-login not being updated after identifying dashboard page<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>fixed: registration form php error<\/li>\n<li>fixed grace period setting not saving on fixed-expiry mode<\/li>\n<li>upgraded 'already registered' message on dashboard<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Access control meta box enabled for all registered post types, including CPT<\/li>\n<li>CSS update to fix dashboard responsiveness<\/li>\n<li>Prevent MM error when a non MM-core payment is made (eg. Worskhop registration payment)<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial public release<\/li>\n<li>Membership cycle architecture with renewal tracking<\/li>\n<li>Three renewal modes: fixed expiry, anniversary, never expires<\/li>\n<li>Pro-rata fee support for fixed expiry memberships<\/li>\n<li>Configurable grace periods<\/li>\n<li>Stripe Checkout integration via Minimal Stripe Wrapper<\/li>\n<li>Manual payment option<\/li>\n<li>Event-triggered system notifications and configurable time-based membership reminders<\/li>\n<li>Page, post, and menu visibility settings by membership status<\/li>\n<li>Registration policy framework: open, admin approval, admin-only<\/li>\n<li>Custom member fields with privacy and editability controls<\/li>\n<li>WordPress role sync rules<\/li>\n<li>CSV member import and export<\/li>\n<li>Member dashboard and registration shortcodes<\/li>\n<li>Developer-friendly modern architecture<\/li>\n<\/ul>","raw_excerpt":"WordPress membership built around the membership cycle, not transactions. Free, flexible, and designed with clubs and nonprofits in mind.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/241723","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=241723"}],"author":[{"embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/jerrystewart99"}],"wp:attachment":[{"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=241723"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=241723"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=241723"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=241723"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=241723"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/bal.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=241723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}