{"id":367318,"date":"2026-09-11T14:44:17","date_gmt":"2026-09-11T14:44:17","guid":{"rendered":"https:\/\/fr.wordpress.org\/plugins\/sprint-club\/"},"modified":"2026-09-11T14:43:44","modified_gmt":"2026-09-11T14:43:44","slug":"sprint-club","status":"publish","type":"plugin","link":"https:\/\/ml.wordpress.org\/plugins\/sprint-club\/","author":23539978,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.0","stable_tag":"1.0.0","tested":"7.1","requires":"7.0","requires_php":"8.0","requires_plugins":null,"header_name":"Sprint Club","header_author":"Sprint Plugins","header_description":"Central dashboard and member portal for coordinating Sprint Plugins extensions used by clubs and associations.","assets_banners_color":"e8f1f9","last_updated":"2026-09-11 14:43:44","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/sprintplugins.fr\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":24,"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":"sprintplugins","date":"2026-09-11 14:43:44","revision":3691596}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3691648,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3691648,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3691648,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500-fr.png":{"filename":"banner-1544x500-fr.png","revision":3691648,"resolution":"1544x500","location":"assets","locale":"fr","width":1544,"height":500},"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3691648,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250-fr.png":{"filename":"banner-772x250-fr.png","revision":3691648,"resolution":"772x250","location":"assets","locale":"fr","width":772,"height":250},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3691648,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3691680,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"meta\":{\"title\":\"Sprint Club \\u2014 WordPress.org Demo EN\\\/FR\",\"description\":\"Locale-aware English\\\/French Sprint Club demo with compatible free Sprint Plugins and fully synthetic club data.\"},\"preferredVersions\":{\"php\":\"8.3\",\"wp\":\"7.1\"},\"landingPage\":\"\\\/wp-admin\\\/admin.php?page=sprint-club\",\"steps\":[{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-families\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-collect\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-events-volunteers\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-dotations\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-birthdays\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-connect-for-helloasso\"},\"options\":{\"activate\":true}},{\"step\":\"runPHP\",\"code\":\"<?php\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n\\n\\\/\\\/ Sprint Club WordPress.org demo fixture. All people, addresses and transactions are fictitious.\\nwp_set_current_user( 1 );\\n$demo_version = '1.1.0-en-fr';\\nif ( get_option( 'sprint_club_wordpress_org_demo_version' ) === $demo_version ) {\\n    return;\\n}\\n\\nfunction sc_demo_fail( $message ) {\\n    throw new RuntimeException( '[Sprint Club demo] ' . $message );\\n}\\n\\nfunction sc_demo_user( $login, $email, $first, $last, $role = 'subscriber' ) {\\n    $user = get_user_by( 'login', $login );\\n    if ( ! $user ) {\\n        $id = wp_insert_user( array(\\n            'user_login'   => $login,\\n            'user_pass'    => wp_generate_password( 24, true, true ),\\n            'user_email'   => $email,\\n            'first_name'   => $first,\\n            'last_name'    => $last,\\n            'display_name' => trim( $first . ' ' . $last ),\\n            'role'         => $role,\\n        ) );\\n        if ( is_wp_error( $id ) ) {\\n            sc_demo_fail( $id->get_error_message() );\\n        }\\n        return (int) $id;\\n    }\\n    wp_update_user( array(\\n        'ID'           => $user->ID,\\n        'user_email'   => $email,\\n        'first_name'   => $first,\\n        'last_name'    => $last,\\n        'display_name' => trim( $first . ' ' . $last ),\\n        'role'         => $role,\\n    ) );\\n    return (int) $user->ID;\\n}\\n\\nfunction sc_demo_birthdate( $age, $days_ahead ) {\\n    $future = current_datetime()->modify( '+' . absint( $days_ahead ) . ' days' );\\n    $year = (int) $future->format( 'Y' ) - absint( $age );\\n    return sprintf( '%04d-%s', $year, $future->format( 'm-d' ) );\\n}\\n\\nfunction sc_demo_assert_ok( $result, $context ) {\\n    if ( is_wp_error( $result ) ) {\\n        sc_demo_fail( $context . ': ' . $result->get_error_message() );\\n    }\\n    if ( false === $result || null === $result ) {\\n        sc_demo_fail( $context . ' failed.' );\\n    }\\n    return $result;\\n}\\n\\n\\\/\\\/ --- Site identity and demo users -------------------------------------------------\\nupdate_option( 'blogname', 'Sprint Sports Club \\u2014 Demo \\\/ D\\u00e9mo' );\\nupdate_option( 'blogdescription', 'A fictitious club used to demonstrate Sprint Club and compatible Sprint Plugins. \\\/ Club fictif utilis\\u00e9 pour pr\\u00e9senter Sprint Club et les extensions Sprint compatibles.' );\\nupdate_option( 'timezone_string', 'Europe\\\/Paris' );\\nupdate_option( 'date_format', 'j F Y' );\\nupdate_option( 'time_format', 'H:i' );\\n\\nwp_update_user( array(\\n    'ID'           => 1,\\n    'user_email'   => 'camille.martin@example.test',\\n    'first_name'   => 'Camille',\\n    'last_name'    => 'Martin',\\n    'display_name' => 'Camille Martin',\\n) );\\n\\n$camille = 1;\\n$leo     = sc_demo_user( 'leo.martin', 'leo.martin@example.test', 'L\\u00e9o', 'Martin' );\\n$emma    = sc_demo_user( 'emma.martin', 'emma.martin@example.test', 'Emma', 'Martin' );\\n$alex    = sc_demo_user( 'alex.bernard', 'alex.bernard@example.test', 'Alex', 'Bernard' );\\n$mila    = sc_demo_user( 'mila.bernard', 'mila.bernard@example.test', 'Mila', 'Bernard' );\\n$sam     = sc_demo_user( 'sam.durand', 'sam.durand@example.test', 'Sam', 'Durand' );\\n\\nforeach ( array( $camille, $leo, $emma, $alex, $mila, $sam ) as $uid ) {\\n    update_user_meta( $uid, 'phone', '+33 6 00 00 ' . str_pad( (string) $uid, 2, '0', STR_PAD_LEFT ) . ' 00' );\\n    update_user_meta( $uid, 'member_number', 'DEMO-' . str_pad( (string) $uid, 4, '0', STR_PAD_LEFT ) );\\n}\\nupdate_user_meta( $leo, 'birth_date', sc_demo_birthdate( 13, 5 ) );\\nupdate_user_meta( $emma, 'birth_date', sc_demo_birthdate( 16, 18 ) );\\nupdate_user_meta( $alex, 'birth_date', sc_demo_birthdate( 39, 36 ) );\\nupdate_user_meta( $mila, 'birth_date', sc_demo_birthdate( 10, 52 ) );\\nupdate_user_meta( $sam, 'birth_date', sc_demo_birthdate( 28, 74 ) );\\n\\n\\\/\\\/ --- Sprint Families --------------------------------------------------------------\\nif ( ! function_exists( 'sprint_families_create_group' ) || ! function_exists( 'sprint_families_create_relationship' ) ) {\\n    sc_demo_fail( 'Sprint Families API is unavailable.' );\\n}\\n$family_martin = sc_demo_assert_ok( sprint_families_create_group( array(\\n    'name'       => 'Martin Household \\\/ Foyer Martin',\\n    'status'     => 'active',\\n    'created_by' => $camille,\\n) ), 'Create Martin household' );\\n\\nforeach ( array( $leo, $emma ) as $child_id ) {\\n    sc_demo_assert_ok( sprint_families_create_relationship( array(\\n        'group_id'            => $family_martin,\\n        'guardian_user_id'    => $camille,\\n        'member_user_id'      => $child_id,\\n        'relationship_type'   => 'legal_guardian',\\n        'is_legal_guardian'   => 1,\\n        'is_primary'          => 1,\\n        'status'              => 'active',\\n        'source'              => 'demo',\\n        'permission_template' => 'full',\\n        'created_by'          => $camille,\\n    ) ), 'Create Martin relationship' );\\n}\\n\\n$family_bernard = sc_demo_assert_ok( sprint_families_create_group( array(\\n    'name'       => 'Bernard Household \\\/ Foyer Bernard',\\n    'status'     => 'active',\\n    'created_by' => $camille,\\n) ), 'Create Bernard household' );\\nsc_demo_assert_ok( sprint_families_create_relationship( array(\\n    'group_id'            => $family_bernard,\\n    'guardian_user_id'    => $alex,\\n    'member_user_id'      => $mila,\\n    'relationship_type'   => 'legal_guardian',\\n    'is_legal_guardian'   => 1,\\n    'is_primary'          => 1,\\n    'status'              => 'active',\\n    'source'              => 'demo',\\n    'permission_template' => 'full',\\n    'created_by'          => $camille,\\n) ), 'Create Bernard relationship' );\\n\\n\\\/\\\/ --- Sprint Collect ---------------------------------------------------------------\\nif ( ! class_exists( 'Sprint_Collect_Repository' ) || ! function_exists( 'sprint_collect_assign_template' ) ) {\\n    sc_demo_fail( 'Sprint Collect API is unavailable.' );\\n}\\n$collect = new Sprint_Collect_Repository();\\n$type_ids = array();\\nforeach ( $collect->get_document_types() as $type ) {\\n    $type_ids[ (string) $type->slug ] = (int) $type->id;\\n}\\n$required_slugs = array( 'identity-document', 'authorization', 'certificate', 'photo' );\\nforeach ( $required_slugs as $slug ) {\\n    if ( empty( $type_ids[ $slug ] ) ) {\\n        sc_demo_fail( 'Missing Sprint Collect document type: ' . $slug );\\n    }\\n}\\n$template_id = sc_demo_assert_ok( $collect->save_template(\\n    array(\\n        'name'        => '2026\\u20132027 Membership File \\\/ Dossier d\\u2019adh\\u00e9sion 2026\\u20132027',\\n        'description' => 'Demo supporting documents for the current club season. \\\/ Justificatifs de d\\u00e9monstration pour la saison actuelle.',\\n    ),\\n    array(\\n        array( 'document_type_id' => $type_ids['identity-document'], 'required' => 1, 'sort_order' => 0 ),\\n        array( 'document_type_id' => $type_ids['authorization'],      'required' => 1, 'sort_order' => 1 ),\\n        array( 'document_type_id' => $type_ids['certificate'],        'required' => 1, 'sort_order' => 2 ),\\n        array( 'document_type_id' => $type_ids['photo'],              'required' => 0, 'sort_order' => 3 ),\\n    )\\n), 'Create Sprint Collect template' );\\n\\n$collect_states = array(\\n    $leo  => array( 'identity-document' => 'validated', 'authorization' => 'validated', 'certificate' => 'missing',    'photo' => 'received' ),\\n    $emma => array( 'identity-document' => 'validated', 'authorization' => 'correction','certificate' => 'validated',  'photo' => 'validated' ),\\n    $alex => array( 'identity-document' => 'validated', 'authorization' => 'validated', 'certificate' => 'validated',  'photo' => 'validated' ),\\n    $mila => array( 'identity-document' => 'validated', 'authorization' => 'missing',   'certificate' => 'expired',    'photo' => 'received' ),\\n);\\nforeach ( $collect_states as $uid => $states ) {\\n    $dossier_id = sc_demo_assert_ok( sprint_collect_assign_template( $uid, $template_id, 'wordpress-org-demo' ), 'Assign Sprint Collect template' );\\n    foreach ( $collect->get_dossier_documents( $dossier_id ) as $doc ) {\\n        foreach ( $states as $slug => $status ) {\\n            if ( (int) $doc->document_type_id !== (int) $type_ids[ $slug ] ) {\\n                continue;\\n            }\\n            $comment = 'correction' === $status ? 'Please provide a clearer, fully visible document. \\\/ Merci de fournir un document plus lisible et enti\\u00e8rement visible.' : '';\\n            $expires = 'expired' === $status ? current_datetime()->modify( '-7 days' )->format( 'Y-m-d' ) : null;\\n            sc_demo_assert_ok( sprint_collect_update_document_status( (int) $doc->id, $status, $comment, $expires, $camille, 'wordpress-org-demo' ), 'Set Sprint Collect status' );\\n        }\\n    }\\n}\\n\\n\\\/\\\/ --- Sprint Events & Volunteers ---------------------------------------------------\\nif ( ! class_exists( 'SPEB_Event_Registration_Service' ) || ! class_exists( 'SPEB_Registration_Service' ) ) {\\n    sc_demo_fail( 'Sprint Events & Volunteers API is unavailable.' );\\n}\\n$speb = get_option( 'speb_settings', array() );\\n$speb = is_array( $speb ) ? $speb : array();\\n$speb['events'] = wp_parse_args( isset( $speb['events'] ) && is_array( $speb['events'] ) ? $speb['events'] : array(), array() );\\n$speb['events']['registration_emails_enabled'] = 0;\\n$speb['events']['default_display_lead_days'] = 90;\\n$speb['volunteers'] = wp_parse_args( isset( $speb['volunteers'] ) && is_array( $speb['volunteers'] ) ? $speb['volunteers'] : array(), array() );\\n$speb['volunteers']['emails_enabled'] = 0;\\n$speb['volunteers']['guest_registrations'] = 0;\\nupdate_option( 'speb_settings', $speb, false );\\n\\n$event_start = current_datetime()->modify( '+14 days' )->setTime( 10, 0 );\\n$event_end   = $event_start->setTime( 17, 30 );\\n$event_id = wp_insert_post( array(\\n    'post_type'    => 'speb_event',\\n    'post_status'  => 'publish',\\n    'post_title'   => 'Club Open Day \\\/ Journ\\u00e9e portes ouvertes',\\n    'post_content' => 'A friendly day for members and families, with activities, information and volunteer missions. \\\/ Une journ\\u00e9e conviviale pour les adh\\u00e9rents et les familles, avec activit\\u00e9s, informations et missions b\\u00e9n\\u00e9voles.',\\n    'post_author'  => $camille,\\n), true );\\nsc_demo_assert_ok( $event_id, 'Create demo event' );\\nupdate_post_meta( $event_id, '_speb_start_at', $event_start->format( 'Y-m-d H:i:s' ) );\\nupdate_post_meta( $event_id, '_speb_end_at', $event_end->format( 'Y-m-d H:i:s' ) );\\nupdate_post_meta( $event_id, '_speb_status', 'published' );\\nupdate_post_meta( $event_id, '_speb_visibility', 'logged_in' );\\nupdate_post_meta( $event_id, '_speb_location', 'Demo Community Sports Hall \\\/ Salle omnisports de d\\u00e9monstration' );\\nupdate_post_meta( $event_id, '_speb_location_address', 'Toulouse, France' );\\nupdate_post_meta( $event_id, '_speb_registration_enabled', '1' );\\nupdate_post_meta( $event_id, '_speb_registration_capacity', 30 );\\n\\n$event2_start = current_datetime()->modify( '+35 days' )->setTime( 14, 0 );\\n$event2_end   = $event2_start->setTime( 18, 0 );\\n$event2_id = wp_insert_post( array(\\n    'post_type'    => 'speb_event',\\n    'post_status'  => 'publish',\\n    'post_title'   => 'Friendly Tournament \\\/ Tournoi amical',\\n    'post_content' => 'A fictitious club tournament used for the Sprint Club preview. \\\/ Tournoi fictif utilis\\u00e9 pour la pr\\u00e9visualisation de Sprint Club.',\\n    'post_author'  => $camille,\\n), true );\\nsc_demo_assert_ok( $event2_id, 'Create second demo event' );\\nupdate_post_meta( $event2_id, '_speb_start_at', $event2_start->format( 'Y-m-d H:i:s' ) );\\nupdate_post_meta( $event2_id, '_speb_end_at', $event2_end->format( 'Y-m-d H:i:s' ) );\\nupdate_post_meta( $event2_id, '_speb_status', 'scheduled' );\\nupdate_post_meta( $event2_id, '_speb_visibility', 'logged_in' );\\nupdate_post_meta( $event2_id, '_speb_location', 'Demo Community Sports Hall \\\/ Salle omnisports de d\\u00e9monstration' );\\nupdate_post_meta( $event2_id, '_speb_registration_enabled', '1' );\\nupdate_post_meta( $event2_id, '_speb_registration_capacity', 20 );\\n\\n$mission_id = wp_insert_post( array(\\n    'post_type'    => 'speb_mission',\\n    'post_status'  => 'publish',\\n    'post_title'   => 'Welcome Desk & Refreshments \\\/ Accueil et buvette',\\n    'post_content' => 'Help welcome families in the morning or support the refreshments desk in the afternoon. \\\/ Aidez \\u00e0 accueillir les familles le matin ou \\u00e0 tenir la buvette l\\u2019apr\\u00e8s-midi.',\\n    'post_author'  => $camille,\\n), true );\\nsc_demo_assert_ok( $mission_id, 'Create demo volunteer mission' );\\n$event_date = $event_start->format( 'Y-m-d' );\\nupdate_post_meta( $mission_id, '_speb_event_id', $event_id );\\nupdate_post_meta( $mission_id, '_speb_mission_start', $event_date );\\nupdate_post_meta( $mission_id, '_speb_mission_end', $event_date );\\nupdate_post_meta( $mission_id, '_speb_location', 'Demo Community Sports Hall \\\/ Salle omnisports de d\\u00e9monstration' );\\nupdate_post_meta( $mission_id, '_speb_manager_user_id', $camille );\\nupdate_post_meta( $mission_id, '_speb_slots', array(\\n    array( 'id' => 'welcome-morning',       'date' => $event_date, 'start' => '09:30', 'end' => '12:30', 'capacity' => 3 ),\\n    array( 'id' => 'refreshments-afternoon','date' => $event_date, 'start' => '13:30', 'end' => '17:30', 'capacity' => 4 ),\\n) );\\n\\n$event_reg = new SPEB_Event_Registration_Service();\\nforeach ( array( $leo, $emma, $alex, $mila, $sam ) as $uid ) {\\n    sc_demo_assert_ok( $event_reg->register_user( $event_id, $uid, $camille ), 'Register demo event attendee' );\\n}\\n$mission_repo = new SPEB_Mission_Repository();\\n$mission_reg  = new SPEB_Registration_Service( $mission_repo );\\nsc_demo_assert_ok( $mission_reg->register_user( $mission_id, $alex, 'welcome-morning' ), 'Register Alex as volunteer' );\\nsc_demo_assert_ok( $mission_reg->register_user( $mission_id, $sam, 'refreshments-afternoon' ), 'Register Sam as volunteer' );\\n\\n\\n\\\/\\\/ --- Sprint Dotations -------------------------------------------------------------\\nif ( ! class_exists( 'Sprint_Dotations_DB' ) || ! class_exists( 'Sprint_Dotations_Settings' ) ) {\\n    sc_demo_fail( 'Sprint Dotations API is unavailable.' );\\n}\\nSprint_Dotations_Settings::save( array(\\n    'allowed_roles'            => array(),\\n    'role_can_delete_history'  => 0,\\n    'stock_management_enabled' => 1,\\n    'member_requests_enabled'  => 1,\\n    'default_deposit_amount'   => 20,\\n    'default_deposit_type'     => 'Deposit \\\/ Caution',\\n    'document_mode'            => 'builtin',\\n    'gravity_form_id'          => 0,\\n    'gravity_form_url'         => '',\\n) );\\nif ( class_exists( 'Sprint_Dotations_Email' ) ) {\\n    update_option( Sprint_Dotations_Email::OPTION, array_merge( Sprint_Dotations_Email::defaults(), array( 'automatic_enabled' => 0 ) ), false );\\n}\\n\\nglobal $wpdb;\\n$items_table = Sprint_Dotations_DB::items_table();\\n$loans_table = Sprint_Dotations_DB::loans_table();\\n$season_id   = Sprint_Dotations_DB::default_season_id();\\n$now_mysql   = current_time( 'mysql' );\\n$today       = current_datetime()->format( 'Y-m-d' );\\n\\n$demo_items = array(\\n    array( 'name' => 'Club Jersey \\\/ Maillot du club', 'category' => 'Textile',   'reference' => 'DEMO-JERSEY', 'size' => 'M',        'stock_quantity' => 3, 'condition_status' => 'good',     'availability' => 'available', 'notes' => 'Synthetic demo equipment. \\\/ Mat\\u00e9riel fictif de d\\u00e9monstration.' ),\\n    array( 'name' => 'Club Jacket \\\/ Veste du club', 'category' => 'Textile',   'reference' => 'DEMO-JACKET', 'size' => '12 years \\\/ 12 ans', 'stock_quantity' => 2, 'condition_status' => 'good',     'availability' => 'available', 'notes' => 'Synthetic demo equipment. \\\/ Mat\\u00e9riel fictif de d\\u00e9monstration.' ),\\n    array( 'name' => 'Sports Bag \\\/ Sac de sport',  'category' => 'Equipment \\\/ \\u00c9quipement', 'reference' => 'DEMO-BAG',    'size' => 'One size \\\/ Taille unique', 'stock_quantity' => 1, 'condition_status' => 'good',     'availability' => 'available', 'notes' => 'Synthetic demo equipment. \\\/ Mat\\u00e9riel fictif de d\\u00e9monstration.' ),\\n    array( 'name' => 'Training Bib \\\/ Chasuble', 'category' => 'Equipment \\\/ \\u00c9quipement', 'reference' => 'DEMO-BIB',    'size' => 'L',        'stock_quantity' => 2, 'condition_status' => 'to_check', 'availability' => 'available', 'notes' => 'One item is intentionally marked for checking. \\\/ Un article est volontairement marqu\\u00e9 \\u00e0 contr\\u00f4ler.' ),\\n);\\n$item_ids = array();\\nforeach ( $demo_items as $item ) {\\n    $item['created_at'] = $now_mysql;\\n    $item['updated_at'] = $now_mysql;\\n    $wpdb->insert( $items_table, $item );\\n    $item_ids[ $item['reference'] ] = (int) $wpdb->insert_id;\\n}\\n$loan_rows = array(\\n    array(\\n        'item_id' => $item_ids['DEMO-JACKET'], 'user_id' => $leo, 'group_code' => 'MARTIN', 'season_id' => $season_id,\\n        'loaned_at' => current_datetime()->modify( '-20 days' )->format( 'Y-m-d' ), 'expected_return_at' => current_datetime()->modify( '+120 days' )->format( 'Y-m-d' ),\\n        'initial_condition' => 'good', 'status' => 'active', 'deposit_amount' => 0, 'deposit_type' => '', 'deposit_status' => 'none',\\n        'created_by' => $camille, 'created_at' => $now_mysql, 'updated_at' => $now_mysql,\\n    ),\\n    array(\\n        'item_id' => $item_ids['DEMO-BAG'], 'user_id' => $emma, 'group_code' => 'MARTIN', 'season_id' => $season_id,\\n        'loaned_at' => current_datetime()->modify( '-45 days' )->format( 'Y-m-d' ), 'expected_return_at' => current_datetime()->modify( '-3 days' )->format( 'Y-m-d' ),\\n        'initial_condition' => 'good', 'status' => 'active', 'deposit_amount' => 30, 'deposit_type' => 'Deposit \\\/ Caution', 'deposit_status' => 'due',\\n        'created_by' => $camille, 'created_at' => $now_mysql, 'updated_at' => $now_mysql,\\n    ),\\n    array(\\n        'item_id' => $item_ids['DEMO-JERSEY'], 'user_id' => $mila, 'group_code' => 'BERNARD', 'season_id' => $season_id,\\n        'loaned_at' => current_datetime()->modify( '-10 days' )->format( 'Y-m-d' ), 'expected_return_at' => current_datetime()->modify( '+90 days' )->format( 'Y-m-d' ),\\n        'initial_condition' => 'good', 'status' => 'active', 'deposit_amount' => 20, 'deposit_type' => 'Deposit \\\/ Caution', 'deposit_status' => 'held',\\n        'created_by' => $camille, 'created_at' => $now_mysql, 'updated_at' => $now_mysql,\\n    ),\\n);\\nforeach ( $loan_rows as $loan ) {\\n    $wpdb->insert( $loans_table, $loan );\\n}\\n\\n\\\/\\\/ --- Sprint Birthdays -------------------------------------------------------------\\nif ( ! class_exists( 'Sprint_Birthdays' ) ) {\\n    sc_demo_fail( 'Sprint Birthdays API is unavailable.' );\\n}\\n$birthdays = Sprint_Birthdays::defaults();\\n$birthdays['enabled'] = 1;\\n$birthdays['meta_key'] = 'birth_date';\\n$birthdays['date_format'] = 'Y-m-d';\\n$birthdays['roles'] = array( 'subscriber' );\\n$birthdays['member_days_before'] = 0;\\n$birthdays['copy_enabled'] = 0;\\n$birthdays['responsible_notice_enabled'] = 0;\\n$birthdays['admin_notifications'] = 0;\\nupdate_option( 'sprint_birthdays_settings', $birthdays, false );\\n\\n\\\/\\\/ --- Sprint Connect for HelloAsso -------------------------------------------------\\nif ( class_exists( '\\\\\\\\SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Core\\\\\\\\Plugin' ) && class_exists( '\\\\\\\\SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Database\\\\\\\\Repository' ) ) {\\n    $ha_class   = '\\\\\\\\SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Core\\\\\\\\Plugin';\\n    $repo_class = '\\\\\\\\SprintPlugins\\\\\\\\ConnectHelloAsso\\\\\\\\Database\\\\\\\\Repository';\\n    $ha = $ha_class::default_settings();\\n    $profile = array(\\n        'client_id'        => '',\\n        'client_secret'    => '',\\n        'association_slug' => 'sprint-sports-club-demo',\\n        'form_id'          => 1001,\\n        'form_slug'        => 'membership-2026-2027',\\n        'form_type'        => 'Membership',\\n    );\\n    $ha['environment'] = 'production';\\n    $ha['helloasso_connections']['production'] = array_merge( $ha['helloasso_connections']['production'], $profile );\\n    foreach ( $profile as $key => $value ) {\\n        $ha[ $key ] = $value;\\n    }\\n    $ha['cron_enabled'] = 0;\\n    $ha['send_welcome_email'] = 0;\\n    update_option( 'scha_settings', $ha, false );\\n\\n    $form = array(\\n        'id'                => 1001,\\n        'type'              => 'Membership',\\n        'slug'              => 'membership-2026-2027',\\n        'title'             => '2026\\u20132027 Club Membership \\\/ Adh\\u00e9sion au club 2026\\u20132027',\\n        'state'             => 'Public',\\n        'url'               => 'https:\\\/\\\/www.helloasso.com\\\/associations\\\/sprint-sports-club-demo\\\/adhesions\\\/membership-2026-2027',\\n        'widget_button_url' => 'https:\\\/\\\/www.helloasso.com\\\/associations\\\/sprint-sports-club-demo\\\/adhesions\\\/membership-2026-2027',\\n        'created_at'        => gmdate( 'c', time() - DAY_IN_SECONDS * 60 ),\\n        'updated_at'        => gmdate( 'c' ),\\n    );\\n    sc_demo_assert_ok( $repo_class::upsert_form( $form, 'sprint-sports-club-demo' ), 'Create local HelloAsso form' );\\n\\n    $orders = array(\\n        array(\\n            'id' => 5001, 'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027',\\n            'date' => gmdate( 'c', time() - DAY_IN_SECONDS * 12 ), 'amount' => 12000,\\n            'payer' => array( 'email' => 'camille.martin@example.test', 'first_name' => 'Camille', 'last_name' => 'Martin', 'role' => 'payer' ),\\n            'items' => array( array(\\n                'id' => 6001, 'order_id' => 5001, 'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027',\\n                'name' => 'Youth membership \\\/ Adh\\u00e9sion jeune \\u2014 L\\u00e9o Martin', 'tier_type' => 'Registration', 'state' => 'Processed', 'amount' => 12000,\\n                'person' => array( 'email' => 'leo.martin@example.test', 'first_name' => 'L\\u00e9o', 'last_name' => 'Martin', 'role' => 'participant' ),\\n                'custom_fields' => array(),\\n            ) ),\\n            'payments' => array( array(\\n                'id' => 7001, 'order_id' => 5001, 'item_id' => 6001, 'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027',\\n                'state' => 'Authorized', 'cash_out_state' => 'Transferred', 'amount' => 12000, 'date' => gmdate( 'c', time() - DAY_IN_SECONDS * 12 ),\\n                'installment_number' => 1, 'payment_means' => 'Card',\\n                'payer' => array( 'email' => 'camille.martin@example.test', 'first_name' => 'Camille', 'last_name' => 'Martin' ),\\n            ) ),\\n        ),\\n        array(\\n            'id' => 5002, 'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027',\\n            'date' => gmdate( 'c', time() - DAY_IN_SECONDS * 8 ), 'amount' => 12000,\\n            'payer' => array( 'email' => 'camille.martin@example.test', 'first_name' => 'Camille', 'last_name' => 'Martin', 'role' => 'payer' ),\\n            'items' => array( array(\\n                'id' => 6002, 'order_id' => 5002, 'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027',\\n                'name' => 'Youth membership \\\/ Adh\\u00e9sion jeune \\u2014 Emma Martin', 'tier_type' => 'Registration', 'state' => 'Processed', 'amount' => 12000,\\n                'person' => array( 'email' => 'emma.martin@example.test', 'first_name' => 'Emma', 'last_name' => 'Martin', 'role' => 'participant' ),\\n                'custom_fields' => array(),\\n            ) ),\\n            'payments' => array( array(\\n                'id' => 7002, 'order_id' => 5002, 'item_id' => 6002, 'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027',\\n                'state' => 'Authorized', 'cash_out_state' => 'Transferred', 'amount' => 12000, 'date' => gmdate( 'c', time() - DAY_IN_SECONDS * 8 ),\\n                'installment_number' => 1, 'payment_means' => 'Card',\\n                'payer' => array( 'email' => 'camille.martin@example.test', 'first_name' => 'Camille', 'last_name' => 'Martin' ),\\n            ) ),\\n        ),\\n    );\\n    foreach ( $orders as $order ) {\\n        sc_demo_assert_ok( $repo_class::upsert_order( $order, 'sprint-sports-club-demo' ), 'Create local HelloAsso order' );\\n    }\\n    $repo_class::upsert_person( array( 'email' => 'camille.martin@example.test', 'first_name' => 'Camille', 'last_name' => 'Martin', 'role' => 'payer' ), 'sprint-sports-club-demo', array( 'order_id' => 5001, 'role' => 'payer', 'user_id' => $camille ) );\\n    $repo_class::upsert_person( array( 'email' => 'leo.martin@example.test', 'first_name' => 'L\\u00e9o', 'last_name' => 'Martin', 'role' => 'participant' ), 'sprint-sports-club-demo', array( 'order_id' => 5001, 'item_id' => 6001, 'role' => 'participant', 'user_id' => $leo ) );\\n    $repo_class::upsert_person( array( 'email' => 'emma.martin@example.test', 'first_name' => 'Emma', 'last_name' => 'Martin', 'role' => 'participant' ), 'sprint-sports-club-demo', array( 'order_id' => 5002, 'item_id' => 6002, 'role' => 'participant', 'user_id' => $emma ) );\\n\\n    update_user_meta( $leo, 'scha_import_references', array( array(\\n        'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027', 'order_id' => 5001, 'item_id' => 6001,\\n        'payment_ids' => array( 7001 ), 'payment_states' => array( 'Authorized' ), 'person_role' => 'participant', 'imported_at' => current_time( 'mysql' ),\\n    ) ) );\\n    update_user_meta( $emma, 'scha_import_references', array( array(\\n        'form_id' => 1001, 'form_type' => 'Membership', 'form_slug' => 'membership-2026-2027', 'order_id' => 5002, 'item_id' => 6002,\\n        'payment_ids' => array( 7002 ), 'payment_states' => array( 'Authorized' ), 'person_role' => 'participant', 'imported_at' => current_time( 'mysql' ),\\n    ) ) );\\n}\\n\\n\\\/\\\/ --- Sprint Club ------------------------------------------------------------------\\n$club = get_option( 'sprint_club_settings', array() );\\n$club = is_array( $club ) ? $club : array();\\n$club['usage_mode_default'] = 'complete';\\n$club['usage_mode_sync_plugins'] = 1;\\n$club['hide_standalone_menus'] = 1;\\n$club['portal_title'] = 'My demo club \\\/ Mon club d\\u00e9mo';\\n$club['portal_show_overview'] = 1;\\n$club['hide_empty_portal_sections'] = 1;\\n$club['profile_completeness_enabled'] = 1;\\n$club['profile_completeness_native_fields'] = array( 'first_name', 'last_name', 'user_email', 'phone' );\\n$club['profile_completeness_threshold'] = 100;\\n$club['dashboard_display_mode'] = 'complete';\\nupdate_option( 'sprint_club_settings', $club, false );\\n\\n$portal_page = get_page_by_path( 'club-portal' );\\nif ( ! $portal_page ) {\\n    $portal_page_id = wp_insert_post( array(\\n        'post_type'    => 'page',\\n        'post_status'  => 'publish',\\n        'post_title'   => 'Club Portal \\\/ Portail du club',\\n        'post_name'    => 'club-portal',\\n        'post_content' => '[sprint_club_portal]',\\n        'post_author'  => $camille,\\n    ), true );\\n    sc_demo_assert_ok( $portal_page_id, 'Create Sprint Club portal page' );\\n} else {\\n    $portal_page_id = (int) $portal_page->ID;\\n}\\nupdate_option( 'show_on_front', 'page' );\\nupdate_option( 'page_on_front', $portal_page_id );\\n\\nupdate_option( 'sprint_club_wordpress_org_demo_version', $demo_version, false );\\nflush_rewrite_rules( false );\\n?>\"},{\"step\":\"mkdir\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\"},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/sprint-club-demo-language.php\",\"data\":\"<?php\\n\\\/**\\n * Plugin Name: Sprint Club Demo Language Switcher\\n * Description: EN\\\/FR switcher used only inside the WordPress.org Playground demo.\\n *\\\/\\n\\nif ( ! defined( 'ABSPATH' ) ) {\\n    exit;\\n}\\n\\nfunction sprint_club_demo_language_url( $locale ) {\\n    $base = admin_url( 'admin.php?page=sprint-club' );\\n    return wp_nonce_url(\\n        add_query_arg( 'sprint_demo_lang', $locale, $base ),\\n        'sprint_demo_language'\\n    );\\n}\\n\\nfunction sprint_club_demo_install_french_translations() {\\n    \\\/\\\/ The WordPress.org Preview endpoint already launches Playground in the\\n    \\\/\\\/ visitor's locale. This helper is only needed when an English visitor\\n    \\\/\\\/ switches to French from inside the running demo.\\n    require_once ABSPATH . 'wp-admin\\\/includes\\\/translation-install.php';\\n    if ( function_exists( 'wp_download_language_pack' ) ) {\\n        wp_download_language_pack( 'fr_FR' );\\n    }\\n\\n    \\\/\\\/ Ask WordPress for any available translation packs for installed plugins.\\n    \\\/\\\/ Missing\\\/untranslated companion plugins are simply left in English.\\n    require_once ABSPATH . 'wp-admin\\\/includes\\\/update.php';\\n    require_once ABSPATH . 'wp-admin\\\/includes\\\/class-wp-upgrader.php';\\n    wp_update_plugins();\\n    $translations = wp_get_translation_updates();\\n    if ( ! empty( $translations ) ) {\\n        ob_start();\\n        $upgrader = new Language_Pack_Upgrader( new Automatic_Upgrader_Skin() );\\n        $upgrader->bulk_upgrade( $translations );\\n        ob_end_clean();\\n    }\\n}\\n\\nfunction sprint_club_demo_handle_language_switch() {\\n    if ( empty( $_GET['sprint_demo_lang'] ) || ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {\\n        return;\\n    }\\n\\n    check_admin_referer( 'sprint_demo_language' );\\n    $requested = sanitize_text_field( wp_unslash( $_GET['sprint_demo_lang'] ) );\\n    if ( ! in_array( $requested, array( 'en_US', 'fr_FR' ), true ) ) {\\n        return;\\n    }\\n\\n    if ( 'fr_FR' === $requested ) {\\n        update_option( 'WPLANG', 'fr_FR' );\\n        update_user_meta( get_current_user_id(), 'locale', 'fr_FR' );\\n        sprint_club_demo_install_french_translations();\\n    } else {\\n        update_option( 'WPLANG', '' );\\n        update_user_meta( get_current_user_id(), 'locale', 'en_US' );\\n    }\\n\\n    wp_safe_redirect( admin_url( 'admin.php?page=sprint-club&sprint_demo_lang_changed=1' ) );\\n    exit;\\n}\\nadd_action( 'admin_init', 'sprint_club_demo_handle_language_switch', 1 );\\n\\nfunction sprint_club_demo_language_notice() {\\n    if ( ! current_user_can( 'manage_options' ) ) {\\n        return;\\n    }\\n\\n    $locale = determine_locale();\\n    $is_fr  = 0 === strpos( $locale, 'fr_' );\\n    $title  = $is_fr ? 'D\\u00e9mo Sprint Club en fran\\u00e7ais' : 'Sprint Club demo in English';\\n    $text   = $is_fr\\n        ? 'Cette pr\\u00e9visualisation contient uniquement des donn\\u00e9es fictives. Vous pouvez changer de langue \\u00e0 tout moment.'\\n        : 'This preview contains synthetic demo data only. You can switch language at any time.';\\n\\n    echo '<div class=\\\"notice notice-info\\\" style=\\\"padding:12px 16px;display:flex;align-items:center;gap:14px;flex-wrap:wrap\\\">';\\n    echo '<div style=\\\"flex:1;min-width:260px\\\"><strong>' . esc_html( $title ) . '<\\\/strong><br><span>' . esc_html( $text ) . '<\\\/span><\\\/div>';\\n    echo '<div style=\\\"display:flex;gap:8px\\\">';\\n    echo '<a class=\\\"button ' . ( $is_fr ? '' : 'button-primary' ) . '\\\" href=\\\"' . esc_url( sprint_club_demo_language_url( 'en_US' ) ) . '\\\">\\ud83c\\uddec\\ud83c\\udde7 English<\\\/a>';\\n    echo '<a class=\\\"button ' . ( $is_fr ? 'button-primary' : '' ) . '\\\" href=\\\"' . esc_url( sprint_club_demo_language_url( 'fr_FR' ) ) . '\\\">\\ud83c\\uddeb\\ud83c\\uddf7 Fran\\u00e7ais<\\\/a>';\\n    echo '<\\\/div><\\\/div>';\\n}\\nadd_action( 'admin_notices', 'sprint_club_demo_language_notice' );\\n\\nfunction sprint_club_demo_admin_bar_language( $bar ) {\\n    if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {\\n        return;\\n    }\\n    $bar->add_node( array(\\n        'id'    => 'sprint-demo-language',\\n        'title' => '\\ud83c\\udf10 EN \\\/ FR',\\n        'href'  => false,\\n    ) );\\n    $bar->add_node( array(\\n        'parent' => 'sprint-demo-language',\\n        'id'     => 'sprint-demo-language-en',\\n        'title'  => '\\ud83c\\uddec\\ud83c\\udde7 English',\\n        'href'   => sprint_club_demo_language_url( 'en_US' ),\\n    ) );\\n    $bar->add_node( array(\\n        'parent' => 'sprint-demo-language',\\n        'id'     => 'sprint-demo-language-fr',\\n        'title'  => '\\ud83c\\uddeb\\ud83c\\uddf7 Fran\\u00e7ais',\\n        'href'   => sprint_club_demo_language_url( 'fr_FR' ),\\n    ) );\\n}\\nadd_action( 'admin_bar_menu', 'sprint_club_demo_admin_bar_language', 100 );\\n\"},{\"step\":\"login\",\"username\":\"admin\",\"password\":\"password\"},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"sprint-club\"},\"options\":{\"activate\":true}}]}"}},"all_blocks":{"sprint-club\/portal":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"sprint-club\/portal","version":"1.0.0","title":"Sprint Club Portal","category":"widgets","icon":"groups","description":"Displays the Sprint Club personal portal for the logged-in user.","textdomain":"sprint-club","attributes":{"align":{"type":"string","default":"wide"},"title":{"type":"string","default":""},"showOverview":{"type":"string","default":"default"}},"supports":{"align":["wide","full"],"html":false},"editorScript":"file:.\/editor.js"}},"tagged_versions":["1.0.0"],"block_files":[],"assets_screenshots":{"screenshot-1-fr.png":{"filename":"screenshot-1-fr.png","revision":3691648,"resolution":"1","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-1.png":{"filename":"screenshot-1.png","revision":3691648,"resolution":"1","location":"assets","locale":"","width":1440,"height":900},"screenshot-2-fr.png":{"filename":"screenshot-2-fr.png","revision":3691648,"resolution":"2","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3691648,"resolution":"2","location":"assets","locale":"","width":1440,"height":900},"screenshot-3-fr.png":{"filename":"screenshot-3-fr.png","revision":3691648,"resolution":"3","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3691648,"resolution":"3","location":"assets","locale":"","width":1440,"height":900},"screenshot-4-fr.png":{"filename":"screenshot-4-fr.png","revision":3691648,"resolution":"4","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3691648,"resolution":"4","location":"assets","locale":"","width":1440,"height":900},"screenshot-5-fr.png":{"filename":"screenshot-5-fr.png","revision":3691648,"resolution":"5","location":"assets","locale":"fr","width":1440,"height":900},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3691648,"resolution":"5","location":"assets","locale":"","width":1440,"height":900}},"screenshots":{"1":"Central dashboard showing connected Sprint Plugins extensions and shared status information.","2":"Extension workspace grouping the available administration modules.","3":"Member portal providing one entry point to connected association services.","4":"Essential, Complete, and Expert display-mode settings with per-extension exceptions.","5":"Integration diagnostics and secure configuration export or import."}},"plugin_section":[262246],"plugin_tags":[8874,14241,434,1919,4538],"plugin_category":[58],"plugin_contributors":[274079],"plugin_business_model":[],"class_list":["post-367318","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-association","plugin_tags-club","plugin_tags-dashboard","plugin_tags-members","plugin_tags-portal","plugin_category-user-management","plugin_contributors-sprintplugins","plugin_committers-sprintplugins"],"banners":{"banner":"https:\/\/ps.w.org\/sprint-club\/assets\/banner-772x250.png?rev=3691648","banner_2x":"https:\/\/ps.w.org\/sprint-club\/assets\/banner-1544x500.png?rev=3691648","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/sprint-club\/assets\/icon.svg?rev=3691648","icon":"https:\/\/ps.w.org\/sprint-club\/assets\/icon.svg?rev=3691648","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/sprint-club\/assets\/screenshot-1.png?rev=3691648","caption":"Central dashboard showing connected Sprint Plugins extensions and shared status information."},{"src":"https:\/\/ps.w.org\/sprint-club\/assets\/screenshot-2.png?rev=3691648","caption":"Extension workspace grouping the available administration modules."},{"src":"https:\/\/ps.w.org\/sprint-club\/assets\/screenshot-3.png?rev=3691648","caption":"Member portal providing one entry point to connected association services."},{"src":"https:\/\/ps.w.org\/sprint-club\/assets\/screenshot-4.png?rev=3691648","caption":"Essential, Complete, and Expert display-mode settings with per-extension exceptions."},{"src":"https:\/\/ps.w.org\/sprint-club\/assets\/screenshot-5.png?rev=3691648","caption":"Integration diagnostics and secure configuration export or import."}],"raw_content":"<!--section=description-->\n<p>Sprint Club provides a lightweight central workspace for clubs and associations using compatible Sprint Plugins extensions.<\/p>\n\n<p>It does not replace the connected extensions or duplicate their business data. Each extension remains responsible for its own records, permissions, settings and optional commercial add-ons.<\/p>\n\n<p>Main features:<\/p>\n\n<ul>\n<li>Central administration dashboard for compatible Sprint Plugins extensions.<\/li>\n<li>Two-level workspace navigation grouped by activity and extension.<\/li>\n<li>Member portal available through a shortcode or block.<\/li>\n<li>Configurable native profile section with a local profile photo.<\/li>\n<li>Essential, Complete and Expert display modes shared with compatible extensions.<\/li>\n<li>Per-extension display-mode exceptions for administrators.<\/li>\n<li>Integration diagnostics and API compatibility information.<\/li>\n<li>Secure configuration export, preview and import.<\/li>\n<li>Optional WordPress dashboard widget showing extension versions and license states without exposing license keys.<\/li>\n<li>Personal-data export and erasure support for the local profile photo and profile fields declared as erasable.<\/li>\n<\/ul>\n\n<p>Sprint Club can work on its own. Additional sections appear only when compatible extensions are active. Supported integrations include Sprint Connect for HelloAsso, One Email Multiple Users, Sprint Families, Sprint Collect, Sprint Dotations, Sprint Events &amp; Volunteers and Sprint Birthdays.<\/p>\n\n<h3>Privacy<\/h3>\n\n<p>Sprint Club may store a local profile-photo attachment ID, a display-mode preference and interface-state preferences in user metadata. It also stores plugin settings and temporary configuration-import information.<\/p>\n\n<p>The plugin registers WordPress personal-data exporters and erasers. Profile fields supplied by WordPress or another extension remain under the responsibility of their original provider unless that provider declares the field erasable through the Sprint Club integration API.<\/p>\n\n<p>Sprint Club displays information returned by compatible extensions but does not copy their business records into its own tables.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin ZIP from Plugins &gt; Add New &gt; Upload Plugin, or install it from the WordPress.org directory.<\/li>\n<li>Activate Sprint Club.<\/li>\n<li>Open Sprint Club &gt; Settings and select the default display mode.<\/li>\n<li>Add the <code>[sprint_club_portal]<\/code> shortcode or the Sprint Club Portal block to a page.<\/li>\n<li>Activate any compatible Sprint Plugins extensions that should appear in the central workspace.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20sprint%20club%20require%20another%20sprint%20plugins%20extension%3F\"><h3>Does Sprint Club require another Sprint Plugins extension?<\/h3><\/dt>\n<dd><p>No. Sprint Club can provide its member portal and profile features on its own. Compatible extensions add their own dashboard cards, administration links and portal sections when active.<\/p><\/dd>\n<dt id=\"does%20sprint%20club%20copy%20data%20from%20connected%20extensions%3F\"><h3>Does Sprint Club copy data from connected extensions?<\/h3><\/dt>\n<dd><p>No. Connected extensions retain ownership of their data. Sprint Club calls their public APIs and renders the returned information without creating duplicate business tables.<\/p><\/dd>\n<dt id=\"what%20are%20the%20essential%2C%20complete%20and%20expert%20modes%3F\"><h3>What are the Essential, Complete and Expert modes?<\/h3><\/dt>\n<dd><p>They control how much administration detail is displayed. Essential focuses on daily tasks, Complete adds management features, and Expert exposes diagnostics and technical tools. Compatible extensions can inherit the central mode or define an exception.<\/p><\/dd>\n<dt id=\"can%20members%20manage%20another%20account%3F\"><h3>Can members manage another account?<\/h3><\/dt>\n<dd><p>Only when WordPress permissions or a compatible delegation provider explicitly allow it. Sharing an email address alone does not grant access to another account.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20send%20data%20to%20an%20external%20service%3F\"><h3>Does the plugin send data to an external service?<\/h3><\/dt>\n<dd><p>No. Sprint Club itself does not send data to an external service. A connected extension may use its own service and privacy policy independently.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial public release.<\/li>\n<li>Automatically switches the portal to the selected family member without requiring the \u201cShow\u201d button.<\/li>\n<li>Preserves the active portal tab when switching family members.<\/li>\n<li>Added a central administration dashboard and member portal.<\/li>\n<li>Added Essential, Complete and Expert display modes with compatible-extension inheritance.<\/li>\n<li>Added optional integrations for the Sprint Plugins suite.<\/li>\n<li>Added local profile-photo support and configurable profile fields.<\/li>\n<li>Added secure configuration export and import.<\/li>\n<li>Added integration diagnostics and a compact WordPress dashboard widget.<\/li>\n<li>Added WordPress personal-data export and erasure support.<\/li>\n<\/ul>","raw_excerpt":"A central dashboard and member portal for coordinating Sprint Plugins extensions used by clubs and associations.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/367318","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=367318"}],"author":[{"embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/sprintplugins"}],"wp:attachment":[{"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=367318"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=367318"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=367318"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=367318"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=367318"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ml.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=367318"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}