{"id":357960,"date":"2026-08-29T09:34:18","date_gmt":"2026-08-29T09:34:18","guid":{"rendered":"https:\/\/de.wordpress.org\/plugins\/club-sports-manager\/"},"modified":"2026-10-22T09:00:55","modified_gmt":"2026-10-22T09:00:55","slug":"squadeno-club-sports-manager","status":"publish","type":"plugin","link":"https:\/\/da.wordpress.org\/plugins\/squadeno-club-sports-manager\/","author":7754360,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.15.0","stable_tag":"1.15.0","tested":"7.1.3","requires":"6.7","requires_php":"8.1","requires_plugins":null,"header_name":"Squadeno - Club Sports Manager","header_author":"Thomas Schiffler","header_description":"Manage the sports offerings of a club: sports, trainers, training times, venues, holidays, events and an automatic calendar.","assets_banners_color":"14513f","last_updated":"2026-10-22 09:00:55","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/squadeno.com","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":30,"downloads":1699,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.10.0":{"tag":"1.10.0","author":"tschiffler","date":"2026-09-03 09:52:57","revision":3679494},"1.10.1":{"tag":"1.10.1","author":"tschiffler","date":"2026-09-04 06:52:47","revision":3680759},"1.10.2":{"tag":"1.10.2","author":"tschiffler","date":"2026-09-04 13:13:29","revision":3681266},"1.11.0":{"tag":"1.11.0","author":"tschiffler","date":"2026-09-15 06:54:18","revision":3696328},"1.12.0":{"tag":"1.12.0","author":"tschiffler","date":"2026-09-16 11:33:43","revision":3698444},"1.13.0":{"tag":"1.13.0","author":"tschiffler","date":"2026-09-21 12:27:05","revision":3705583},"1.14.0":{"tag":"1.14.0","author":"tschiffler","date":"2026-09-28 12:35:37","revision":3717174},"1.15.0":{"tag":"1.15.0","author":"tschiffler","date":"2026-10-22 09:00:55","revision":3759598},"1.8.0":{"tag":"1.8.0","author":"tschiffler","date":"2026-08-29 09:34:00","revision":3671311},"1.8.1":{"tag":"1.8.1","author":"tschiffler","date":"2026-08-29 17:07:52","revision":3671752},"1.9.0":{"tag":"1.9.0","author":"tschiffler","date":"2026-08-30 09:21:41","revision":3672271}},"upgrade_notice":{"1.15.0":"<p>Online booking for events, training dates and courses arrives, but stays off until you switch it on. Existing training times get a stable identity. Single-day events lose an end date the old form hid, so they no longer show as multi-day; the old date is kept.<\/p>","1.14.0":"<p>The contact area on sport pages now has four settings: the form as before, nothing at all, your note only, or your note with one contact person (photo, name, email). The form stays the default \u2013 nothing changes until you choose otherwise.<\/p>","1.13.0":"<p>Training times can now repeat every few weeks or on a set weekday of the month, and they can end \u2013 on a date or after a set number of sessions. Everything you have stays weekly and open-ended, and there is nothing to do after the update.<\/p>","1.12.0":"<p>Competitive play arrives, but stays off until you switch it on \u2013 an existing site behaves as before. New alongside it: a setup assistant, sortable squad positions, starter age groups, and your own name for sports. It also closes a permission gap around Quick Edit.<\/p>","1.11.0":"<p>Two new blocks put events and venues on pages of their own. The club administration may now maintain events, and a new package covers pages and posts; nobody loses a permission. Please note: a person without an office no longer shows up in the people list on its own.<\/p>","1.10.2":"<p>Two fixes for the calendar: events with an age group of their own now appear when visitors filter by that age group, and an event without times shows up as an all-day date instead of being left out. Nothing to do after the update.<\/p>","1.10.1":"<p>A fix for the People block: the photo leads to the person&#039;s page, the editor shows the block the way the page does, and the list gets the plugin&#039;s spacing and colours on every page. Nothing to do after the update.<\/p>","1.10.0":"<p>Trainer records are people records now: they keep their URLs and each one gets the office &quot;Trainer&quot;. Permissions move to task packages that you assign per person. Please note that editors no longer maintain closures, seasons, sections, age groups and event types \u2014 administrators do.<\/p>","1.9.0":"<p>The calendar gains a training schedule view, and the admin menu has fewer entries. Existing pages, shortcodes and blocks keep working unchanged.<\/p>","1.8.1":"<p>Text only: the directory page reads better and is now available in German. Nothing changes in the plugin, so there is no hurry.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3671311,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3671311,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3671311,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3671311,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3759750,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"landingPage\":\"\\\/\",\"login\":true,\"steps\":[{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"squadeno-club-sports-manager\"},\"options\":{\"activate\":true}},{\"step\":\"setSiteOptions\",\"options\":{\"permalink_structure\":\"\\\/%postname%\\\/\"}},{\"step\":\"runPHP\",\"code\":\"<?php\\nif (! function_exists('csmgr_demo_texte')) {\\nfunction csmgr_demo_texte(): array\\n{\\nreturn [\\n'site' => [\\n'name' => 'Riverside Sports Club',\\n'tagline' => 'A demo club running on Squadeno',\\n],\\n'slugs' => [\\n'csmgr_sportart' => 'sport',\\n'csmgr_trainer' => 'person',\\n'csmgr_event' => 'event',\\n'csmgr_sportstaette' => 'venue',\\n],\\n'venues' => [\\n'hall' => ['name' => 'Riverside Sports Hall', 'street' => 'Mill Lane 12', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5079', 'lng' => '-0.1283', 'facilities' => \\\"Parking\\\\nChanging rooms\\\\nStep-free access\\\", 'intro' => 'The main hall. Three courts, a climbing wall and the equipment store for the gymnastics group.'],\\n'stadium' => ['name' => 'Meadow Park Stadium', 'street' => 'Meadow Road 4', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5100', 'lng' => '-0.1230', 'facilities' => \\\"Tartan track\\\\nFloodlights\\\\nParking\\\", 'intro' => 'Six-lane track with jumping pits, used by the athletics groups from spring to autumn.'],\\n'studio' => ['name' => 'Studio at the Old Mill', 'street' => 'Mill Lane 1', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5065', 'lng' => '-0.1300', 'facilities' => \\\"Mats provided\\\\nShowers\\\", 'intro' => 'A quiet room above the old mill. Yoga, dance and the smaller courses take place here.'],\\n'annex' => ['name' => 'Lakeside Gym', 'street' => 'Lake Road 27', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5040', 'lng' => '-0.1345', 'facilities' => \\\"Weights room\\\\nChanging rooms\\\", 'intro' => 'The smaller hall by the lake, mainly used for conditioning and the winter programme.'],\\n],\\n'people' => [\\n'alex' => ['name' => 'Alex Moreau', 'role' => 'Head coach', 'qualifications' => 'Coaching licence C, first aid', 'since' => '2012', 'intro' => 'Runs the athletics group and looks after the youngest members on sports days.'],\\n'priya' => ['name' => 'Priya Raman', 'role' => 'Coach', 'qualifications' => 'Coaching licence C, athletics', 'since' => '2016', 'intro' => 'Sprint and jump specialist. Trains the youth group towards the regional championships.'],\\n'nora' => ['name' => 'Nora Lindqvist', 'role' => 'Coach', 'qualifications' => 'Gymnastics licence B, first aid', 'since' => '2014', 'intro' => 'Teaches gymnastics from the first forward roll upwards, toddlers included.'],\\n'sam' => ['name' => 'Sam Okafor', 'role' => 'Coach', 'qualifications' => 'Ball sports licence C', 'since' => '2019', 'intro' => 'Plays in the mixed volleyball team and runs its Wednesday session.'],\\n'mila' => ['name' => 'Mila Novak', 'role' => 'Course instructor', 'qualifications' => 'Yoga teacher (200 h)', 'since' => '2021', 'intro' => 'Hatha yoga in the studio, mornings and evenings. Beginners are welcome any week.'],\\n'jonas' => ['name' => 'Jonas Weber', 'role' => 'Coach', 'qualifications' => 'Table tennis licence C', 'since' => '2018', 'intro' => 'Open table tennis training. Happy to lend a bat to anyone who turns up.'],\\n'ana' => ['name' => 'Ana Ferreira', 'role' => 'Coach', 'qualifications' => 'Badminton licence C', 'since' => '2020', 'intro' => 'Badminton for adults, and the beginners hour on Saturday mornings.'],\\n'tomas' => ['name' => 'Tomas Havel', 'role' => 'Coach', 'qualifications' => 'Ball sports licence B', 'since' => '2015', 'intro' => 'Basketball and the winter conditioning sessions at the Lakeside Gym.'],\\n'ida' => ['name' => 'Ida Sorensen', 'role' => 'Course instructor', 'qualifications' => 'Walking guide, first aid', 'since' => '2022', 'intro' => 'Leads the Nordic walking group. Meets at the park entrance, whatever the weather.'],\\n'hedda' => ['name' => 'Hedda Lang', 'role' => 'Membership records', 'qualifications' => 'Bookkeeping course', 'since' => '2017', 'intro' => 'Keeps the membership list and answers questions about fees. Does not run a session.'],\\n],\\n'age' => [\\n'toddlers' => 'Toddlers',\\n'children' => 'Children',\\n'youth' => 'Youth',\\n'adults' => 'Adults',\\n'seniors' => 'Seniors',\\n],\\n'sections' => [\\n'athletics' => 'Athletics',\\n'gymnastics' => 'Gymnastics',\\n'ball' => 'Ball sports',\\n'fitness' => 'Fitness',\\n],\\n'sports' => [\\n'athletics' => ['title' => 'Athletics', 'intro' => 'Track and field for every age group. Outdoors in summer, in the hall over winter.'],\\n'athletics_kids' => ['title' => 'Athletics Kids (6-11)', 'intro' => 'Playful introduction to running, jumping and throwing.'],\\n'athletics_youth' => ['title' => 'Athletics Youth (12-17)', 'intro' => 'Training towards the regional championships, with a competition every few weeks.'],\\n'gymnastics' => ['title' => 'Gymnastics', 'intro' => 'From the first forward roll to the high bar.'],\\n'toddler_gym' => ['title' => 'Parent and Toddler Gymnastics', 'intro' => 'For children aged 2 to 4, together with a parent.'],\\n'apparatus' => ['title' => 'Apparatus Gymnastics Kids', 'intro' => 'Bars, beam and vault for children aged 6 and up.'],\\n'volleyball' => ['title' => 'Volleyball', 'intro' => 'Mixed team, league matches on selected weekends. New players are welcome to join a session.'],\\n'basketball' => ['title' => 'Basketball', 'intro' => 'Recreational basketball, no league, no pressure.'],\\n'badminton' => ['title' => 'Badminton', 'intro' => 'Doubles for anyone who can keep a rally going, plus a beginners hour on Saturdays.'],\\n'table_tennis' => ['title' => 'Table Tennis', 'intro' => 'Open training. Bats are available.'],\\n'yoga' => ['title' => 'Yoga', 'intro' => 'Hatha yoga for beginners and returners. Booked by the course.', 'fee' => '6 \\u20ac per session'],\\n'nordic' => ['title' => 'Nordic Walking', 'intro' => 'An hour through the park at a pace that still allows a conversation.'],\\n'dance' => ['title' => 'Dance for Kids', 'intro' => 'Movement games and choreography for children aged 6 to 10.', 'fee' => '40 \\u20ac per term'],\\n],\\n'notes' => [\\n'rain' => 'when it rains',\\n'cancelled' => 'Hall used for an exam',\\n'extra' => 'Extra session before the tournament',\\n'meet' => 'meet at the main entrance',\\n],\\n'closures' => [\\n'maintenance' => 'Hall maintenance',\\n'holidays' => 'School holidays',\\n'halfterm' => 'Half-term break',\\n],\\n'events' => [\\n'holiday' => ['title' => 'Holiday Sports Day', 'intro' => 'A day of games and athletics while regular training pauses.', 'type' => 'Holiday programme', 'fee' => '5 \\u20ac'],\\n'party' => ['title' => 'Club Summer Party', 'intro' => 'Barbecue, games and the annual awards. Members and guests welcome.', 'type' => 'Club party'],\\n'tournament' => ['title' => 'Volleyball Home Tournament', 'intro' => 'Six teams, one long Saturday. Cake stall run by the youth group.', 'type' => 'Tournament'],\\n'camp' => ['title' => 'Gymnastics Weekend Camp', 'intro' => 'Two days of training, staying overnight in the hall.', 'type' => 'Camp', 'fee' => '45 \\u20ac'],\\n'openday' => ['title' => 'Open Day: Try Every Sport', 'intro' => 'Every group runs a taster session. Turn up in trainers and see what fits.', 'type' => 'Open day'],\\n'badge' => ['title' => 'Sports Badge Test', 'intro' => 'Run, jump, throw: anyone who wants to earn the sports badge books a start time. Two examiners take each slot.', 'type' => 'Assessment'],\\n],\\n'bookings' => [\\n'holiday' => [\\n['Sarah Collins', 'Lily Collins', ''],\\n['Sarah Collins', 'Tom Collins', ''],\\n['Aisha Khan', 'Omar Khan', 'Brings an inhaler'],\\n['Helen Moore', 'Ella Moore', 'Picked up at 2 pm by her grandmother'],\\n],\\n'badge' => [\\n['Sarah Collins', 'Lily Collins', ''],\\n['Sarah Collins', 'Tom Collins', ''],\\n['Mark Wilson', 'Mark Wilson', ''],\\n['Aisha Khan', 'Omar Khan', 'Brings an inhaler'],\\n['Jonas Becker', 'Jonas Becker', ''],\\n['Helen Moore', 'Ella Moore', ''],\\n['Helen Moore', 'Jack Moore', ''],\\n],\\n],\\n'offices' => [\\n'trainer' => ['name' => 'Trainer', 'parent' => '', 'order' => 5],\\n'board' => ['name' => 'Board', 'parent' => '', 'order' => 10],\\n'exec' => ['name' => 'Executive board', 'parent' => 'board', 'order' => 10],\\n'chair' => ['name' => 'Chairwoman', 'parent' => 'exec', 'order' => 10],\\n'deputy' => ['name' => 'Deputy chair', 'parent' => 'exec', 'order' => 20],\\n'treasurer' => ['name' => 'Treasurer', 'parent' => 'exec', 'order' => 30],\\n'council' => ['name' => 'Extended board', 'parent' => 'board', 'order' => 20],\\n'secretary' => ['name' => 'Secretary', 'parent' => 'council', 'order' => 10],\\n'youth' => ['name' => 'Youth officer', 'parent' => 'council', 'order' => 20],\\n'records' => ['name' => 'Membership secretary', 'parent' => 'council', 'order' => 30],\\n],\\n'pages' => [\\n'calendar' => [\\n'slug' => 'calendar',\\n'title' => 'Calendar',\\n'intro' => [\\n'This is a live demo of Squadeno. Every date below comes out of the training schedule the club maintains once. School holidays block the affected sessions automatically. A cancelled session, a moved date or an extra session is handled on the date itself.',\\n'Filter by age group or section, switch to the week view, or look at the <a href=\\\"\\\/training-plan\\\/\\\">training plan<\\\/a>. You are logged in as an administrator, so the <a href=\\\"\\\/wp-admin\\\/\\\">admin area<\\\/a> is open as well. Change a training time there and the calendar follows.',\\n],\\n],\\n'sports' => ['slug' => 'sports', 'title' => 'Sports', 'intro' => ['Everything the club offers. Filter by age group or section; each sport has its own page with times, venue and trainers.']],\\n'people' => ['slug' => 'people', 'title' => 'People', 'intro' => ['One record per person, whatever they do for the club. The heading names the office: the trainers first, then the board. Anyone who coaches and sits on the board appears under both, out of a single record.']],\\n'coaches' => ['slug' => 'trainers', 'title' => 'Trainers', 'intro' => ['Everyone who runs a session, filtered to the office \\\"Trainer\\\". Each profile lists the sports that person looks after and can be written to directly.']],\\n'plan' => ['slug' => 'training-plan', 'title' => 'Training plan', 'intro' => ['The weekly schedule as a table. It is built from the training times, so it stays current on its own.']],\\n'venues' => ['slug' => 'venues', 'title' => 'Venues', 'intro' => ['Where the club trains. Each venue shows its address on a map, together with what takes place there.']],\\n'events' => ['slug' => 'events', 'title' => 'Events', 'intro' => ['Tournaments, camps and parties. Events sit in the same calendar as the training sessions.']],\\n'board' => ['slug' => 'board', 'title' => 'Board', 'intro' => ['Who runs the club, filtered to the board offices. These are the same records as on the people page, so nobody is kept twice. Permissions are a separate matter: a board member maintains the club data, a trainer only their own sports.']],\\n],\\n];\\n}\\n}\\nif (! function_exists('csmgr_demo_struktur')) {\\nfunction csmgr_demo_struktur(): array\\n{\\nreturn [\\n'spielbetrieb' => false,\\n'positionen' => [],\\n'aemter' => [\\n'alex' => ['trainer', 'chair'],\\n'priya' => ['trainer', 'deputy'],\\n'nora' => ['trainer', 'treasurer'],\\n'sam' => ['trainer', 'secretary'],\\n'mila' => ['trainer', 'youth'],\\n'jonas' => ['trainer'],\\n'ana' => ['trainer'],\\n'tomas' => ['trainer'],\\n'ida' => ['trainer'],\\n'hedda' => ['records'],\\n],\\n'sportarten' => [\\n'athletics' => ['sections' => ['athletics'], 'kosten' => 'im_beitrag'],\\n'athletics_kids' => [\\n'parent' => 'athletics', 'age' => ['children'], 'sections' => ['athletics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [\\n['wochentag' => 2, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'hall', 'saison' => 'winter', 'trainer' => 'alex'],\\n['wochentag' => 4, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'stadium', 'saison' => 'sommer', 'trainer' => 'priya', 'ausweich' => 'hall', 'hinweis' => 'rain'],\\n],\\n'trainer' => [['alex', 'haupt'], ['priya', 'co']],\\n],\\n'athletics_youth' => [\\n'parent' => 'athletics', 'age' => ['youth'], 'sections' => ['athletics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 3, 'von' => '18:00', 'bis' => '19:30', 'ort' => 'stadium', 'saison' => 'sommer', 'trainer' => 'priya']],\\n'trainer' => [['priya', 'haupt']],\\n],\\n'gymnastics' => ['sections' => ['gymnastics'], 'kosten' => 'im_beitrag'],\\n'toddler_gym' => [\\n'parent' => 'gymnastics', 'age' => ['toddlers'], 'sections' => ['gymnastics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 5, 'von' => '15:00', 'bis' => '16:00', 'ort' => 'hall', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n'apparatus' => [\\n'parent' => 'gymnastics', 'age' => ['children'], 'sections' => ['gymnastics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 1, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'hall', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n'volleyball' => [\\n'age' => ['adults'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 3, 'von' => '19:00', 'bis' => '20:30', 'ort' => 'hall', 'trainer' => 'sam']],\\n'ausnahmen' => [\\n['tag' => 'wednesday', 'art' => 'faellt_aus', 'notiz' => 'cancelled'],\\n['tag' => 'saturday', 'art' => 'zusatztermin', 'von' => '10:00', 'bis' => '12:00', 'ort' => 'hall', 'notiz' => 'extra'],\\n],\\n'trainer' => [['sam', 'haupt']],\\n],\\n'basketball' => [\\n'age' => ['adults', 'youth'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 5, 'von' => '19:00', 'bis' => '21:00', 'ort' => 'annex', 'trainer' => 'tomas']],\\n'trainer' => [['tomas', 'haupt']],\\n],\\n'badminton' => [\\n'age' => ['adults'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [\\n['wochentag' => 2, 'von' => '19:30', 'bis' => '21:30', 'ort' => 'hall', 'trainer' => 'ana'],\\n['wochentag' => 6, 'von' => '10:00', 'bis' => '11:00', 'ort' => 'hall', 'trainer' => 'ana'],\\n],\\n'trainer' => [['ana', 'haupt']],\\n],\\n'table_tennis' => [\\n'age' => ['adults', 'youth'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 4, 'von' => '20:00', 'bis' => '22:00', 'ort' => 'hall', 'saison' => 'ganzjaehrig', 'trainer' => 'jonas']],\\n'trainer' => [['jonas', 'haupt']],\\n],\\n'yoga' => [\\n'age' => ['adults', 'seniors'], 'sections' => ['fitness'], 'kosten' => 'gebuehr',\\n'zeiten' => [\\n['wochentag' => 1, 'von' => '19:00', 'bis' => '20:15', 'ort' => 'studio', 'trainer' => 'mila'],\\n['wochentag' => 3, 'von' => '08:30', 'bis' => '09:45', 'ort' => 'studio', 'trainer' => 'mila'],\\n],\\n'trainer' => [['mila', 'haupt']],\\n],\\n'nordic' => [\\n'age' => ['adults', 'seniors'], 'sections' => ['fitness'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 6, 'von' => '09:30', 'bis' => '10:30', 'ort' => 'stadium', 'zusatz' => 'meet', 'trainer' => 'ida']],\\n'trainer' => [['ida', 'haupt']],\\n],\\n'dance' => [\\n'age' => ['children'], 'sections' => ['fitness'], 'kosten' => 'gebuehr',\\n'zeiten' => [['wochentag' => 4, 'von' => '15:30', 'bis' => '16:30', 'ort' => 'studio', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n],\\n'schliesszeiten' => [\\n['key' => 'maintenance', 'monatstag' => [20, 24]],\\n['key' => 'holidays', 'tage' => [10, 17]],\\n['key' => 'halfterm', 'tage' => [120, 127]],\\n],\\n'veranstaltungen' => [\\n'holiday' => ['modus' => 'eintaegig', 'von' => 12, 'zeit_von' => '10:00', 'zeit_bis' => '14:00', 'ort' => 'stadium', 'sport' => 'athletics_kids', 'anmeldung' => 'online', 'plaetze' => '20', 'kosten' => 'gebuehr', 'age' => ['children', 'youth'], 'buchung' => ['wer' => 'alle', 'frist' => -2]],\\n'badge' => ['modus' => 'eintaegig', 'von' => 9, 'zeit_von' => '10:00', 'zeit_bis' => '12:00', 'ort' => 'stadium', 'anmeldung' => 'online', 'plaetze' => '2', 'kosten' => 'frei', 'buchung' => ['wer' => 'alle', 'frist' => -1, 'takt' => 15, 'pause' => [60, 15], 'belegt' => [0, 0, 1, 3, 3, 5, 6]]],\\n'party' => ['modus' => 'eintaegig', 'von' => 21, 'zeit_von' => '15:00', 'zeit_bis' => '22:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n'tournament' => ['modus' => 'eintaegig', 'von' => 28, 'zeit_von' => '09:00', 'zeit_bis' => '18:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n'camp' => ['modus' => 'mehrtaegig', 'von' => 35, 'bis' => 36, 'ort' => 'hall', 'anmeldung' => 'erforderlich', 'plaetze' => '24', 'kosten' => 'gebuehr', 'age' => ['children', 'youth']],\\n'openday' => ['modus' => 'eintaegig', 'von' => 45, 'zeit_von' => '11:00', 'zeit_bis' => '17:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n],\\n'seiten' => ['calendar', 'sports', 'people', 'coaches', 'board', 'plan', 'venues', 'events'],\\n'startseite' => 'calendar',\\n'konten' => [\\n'alex' => ['login' => 'demo_vorstand', 'rolle' => 'csmgr_vorstand'],\\n'jonas' => ['login' => 'demo_trainer', 'rolle' => 'csmgr_trainer'],\\n],\\n];\\n}\\n}\\nuse ClubSportsManager\\\\WordPress\\\\Admin\\\\Settings;\\nuse ClubSportsManager\\\\WordPress\\\\Slugs;\\nif (! defined('ABSPATH')) {\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n}\\nrequire_once ABSPATH . 'wp-admin\\\/includes\\\/plugin.php';\\nforeach (array_keys(get_plugins()) as $csmgrPluginFile) {\\nif (str_ends_with($csmgrPluginFile, '\\\/squadeno-club-sports-manager.php') && ! is_plugin_active($csmgrPluginFile)) {\\nactivate_plugin($csmgrPluginFile);\\n}\\n}\\nif (! class_exists(Slugs::class)) {\\nreturn;\\n}\\n$csmgrEinstellungen = [\\nSettings::KEY_KARTEN => 'osm',\\nSettings::KEY_KALENDER_ANSICHTEN => Settings::CALENDAR_VIEWS,\\nSettings::KEY_ICAL_AKTIV => 'ja',\\nSettings::KEY_SPIELBETRIEB => csmgr_demo_struktur()['spielbetrieb'] ? 'ja' : '',\\n];\\nforeach (csmgr_demo_texte()['slugs'] as $csmgrTyp => $csmgrSlug) {\\n$csmgrEinstellungen[Slugs::optionKey($csmgrTyp)] = $csmgrSlug;\\n}\\nupdate_option(Settings::OPTION, array_merge((array) get_option(Settings::OPTION, []), $csmgrEinstellungen));\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php\\nif (! function_exists('csmgr_demo_texte')) {\\nfunction csmgr_demo_texte(): array\\n{\\nreturn [\\n'site' => [\\n'name' => 'Riverside Sports Club',\\n'tagline' => 'A demo club running on Squadeno',\\n],\\n'slugs' => [\\n'csmgr_sportart' => 'sport',\\n'csmgr_trainer' => 'person',\\n'csmgr_event' => 'event',\\n'csmgr_sportstaette' => 'venue',\\n],\\n'venues' => [\\n'hall' => ['name' => 'Riverside Sports Hall', 'street' => 'Mill Lane 12', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5079', 'lng' => '-0.1283', 'facilities' => \\\"Parking\\\\nChanging rooms\\\\nStep-free access\\\", 'intro' => 'The main hall. Three courts, a climbing wall and the equipment store for the gymnastics group.'],\\n'stadium' => ['name' => 'Meadow Park Stadium', 'street' => 'Meadow Road 4', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5100', 'lng' => '-0.1230', 'facilities' => \\\"Tartan track\\\\nFloodlights\\\\nParking\\\", 'intro' => 'Six-lane track with jumping pits, used by the athletics groups from spring to autumn.'],\\n'studio' => ['name' => 'Studio at the Old Mill', 'street' => 'Mill Lane 1', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5065', 'lng' => '-0.1300', 'facilities' => \\\"Mats provided\\\\nShowers\\\", 'intro' => 'A quiet room above the old mill. Yoga, dance and the smaller courses take place here.'],\\n'annex' => ['name' => 'Lakeside Gym', 'street' => 'Lake Road 27', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5040', 'lng' => '-0.1345', 'facilities' => \\\"Weights room\\\\nChanging rooms\\\", 'intro' => 'The smaller hall by the lake, mainly used for conditioning and the winter programme.'],\\n],\\n'people' => [\\n'alex' => ['name' => 'Alex Moreau', 'role' => 'Head coach', 'qualifications' => 'Coaching licence C, first aid', 'since' => '2012', 'intro' => 'Runs the athletics group and looks after the youngest members on sports days.'],\\n'priya' => ['name' => 'Priya Raman', 'role' => 'Coach', 'qualifications' => 'Coaching licence C, athletics', 'since' => '2016', 'intro' => 'Sprint and jump specialist. Trains the youth group towards the regional championships.'],\\n'nora' => ['name' => 'Nora Lindqvist', 'role' => 'Coach', 'qualifications' => 'Gymnastics licence B, first aid', 'since' => '2014', 'intro' => 'Teaches gymnastics from the first forward roll upwards, toddlers included.'],\\n'sam' => ['name' => 'Sam Okafor', 'role' => 'Coach', 'qualifications' => 'Ball sports licence C', 'since' => '2019', 'intro' => 'Plays in the mixed volleyball team and runs its Wednesday session.'],\\n'mila' => ['name' => 'Mila Novak', 'role' => 'Course instructor', 'qualifications' => 'Yoga teacher (200 h)', 'since' => '2021', 'intro' => 'Hatha yoga in the studio, mornings and evenings. Beginners are welcome any week.'],\\n'jonas' => ['name' => 'Jonas Weber', 'role' => 'Coach', 'qualifications' => 'Table tennis licence C', 'since' => '2018', 'intro' => 'Open table tennis training. Happy to lend a bat to anyone who turns up.'],\\n'ana' => ['name' => 'Ana Ferreira', 'role' => 'Coach', 'qualifications' => 'Badminton licence C', 'since' => '2020', 'intro' => 'Badminton for adults, and the beginners hour on Saturday mornings.'],\\n'tomas' => ['name' => 'Tomas Havel', 'role' => 'Coach', 'qualifications' => 'Ball sports licence B', 'since' => '2015', 'intro' => 'Basketball and the winter conditioning sessions at the Lakeside Gym.'],\\n'ida' => ['name' => 'Ida Sorensen', 'role' => 'Course instructor', 'qualifications' => 'Walking guide, first aid', 'since' => '2022', 'intro' => 'Leads the Nordic walking group. Meets at the park entrance, whatever the weather.'],\\n'hedda' => ['name' => 'Hedda Lang', 'role' => 'Membership records', 'qualifications' => 'Bookkeeping course', 'since' => '2017', 'intro' => 'Keeps the membership list and answers questions about fees. Does not run a session.'],\\n],\\n'age' => [\\n'toddlers' => 'Toddlers',\\n'children' => 'Children',\\n'youth' => 'Youth',\\n'adults' => 'Adults',\\n'seniors' => 'Seniors',\\n],\\n'sections' => [\\n'athletics' => 'Athletics',\\n'gymnastics' => 'Gymnastics',\\n'ball' => 'Ball sports',\\n'fitness' => 'Fitness',\\n],\\n'sports' => [\\n'athletics' => ['title' => 'Athletics', 'intro' => 'Track and field for every age group. Outdoors in summer, in the hall over winter.'],\\n'athletics_kids' => ['title' => 'Athletics Kids (6-11)', 'intro' => 'Playful introduction to running, jumping and throwing.'],\\n'athletics_youth' => ['title' => 'Athletics Youth (12-17)', 'intro' => 'Training towards the regional championships, with a competition every few weeks.'],\\n'gymnastics' => ['title' => 'Gymnastics', 'intro' => 'From the first forward roll to the high bar.'],\\n'toddler_gym' => ['title' => 'Parent and Toddler Gymnastics', 'intro' => 'For children aged 2 to 4, together with a parent.'],\\n'apparatus' => ['title' => 'Apparatus Gymnastics Kids', 'intro' => 'Bars, beam and vault for children aged 6 and up.'],\\n'volleyball' => ['title' => 'Volleyball', 'intro' => 'Mixed team, league matches on selected weekends. New players are welcome to join a session.'],\\n'basketball' => ['title' => 'Basketball', 'intro' => 'Recreational basketball, no league, no pressure.'],\\n'badminton' => ['title' => 'Badminton', 'intro' => 'Doubles for anyone who can keep a rally going, plus a beginners hour on Saturdays.'],\\n'table_tennis' => ['title' => 'Table Tennis', 'intro' => 'Open training. Bats are available.'],\\n'yoga' => ['title' => 'Yoga', 'intro' => 'Hatha yoga for beginners and returners. Booked by the course.', 'fee' => '6 \\u20ac per session'],\\n'nordic' => ['title' => 'Nordic Walking', 'intro' => 'An hour through the park at a pace that still allows a conversation.'],\\n'dance' => ['title' => 'Dance for Kids', 'intro' => 'Movement games and choreography for children aged 6 to 10.', 'fee' => '40 \\u20ac per term'],\\n],\\n'notes' => [\\n'rain' => 'when it rains',\\n'cancelled' => 'Hall used for an exam',\\n'extra' => 'Extra session before the tournament',\\n'meet' => 'meet at the main entrance',\\n],\\n'closures' => [\\n'maintenance' => 'Hall maintenance',\\n'holidays' => 'School holidays',\\n'halfterm' => 'Half-term break',\\n],\\n'events' => [\\n'holiday' => ['title' => 'Holiday Sports Day', 'intro' => 'A day of games and athletics while regular training pauses.', 'type' => 'Holiday programme', 'fee' => '5 \\u20ac'],\\n'party' => ['title' => 'Club Summer Party', 'intro' => 'Barbecue, games and the annual awards. Members and guests welcome.', 'type' => 'Club party'],\\n'tournament' => ['title' => 'Volleyball Home Tournament', 'intro' => 'Six teams, one long Saturday. Cake stall run by the youth group.', 'type' => 'Tournament'],\\n'camp' => ['title' => 'Gymnastics Weekend Camp', 'intro' => 'Two days of training, staying overnight in the hall.', 'type' => 'Camp', 'fee' => '45 \\u20ac'],\\n'openday' => ['title' => 'Open Day: Try Every Sport', 'intro' => 'Every group runs a taster session. Turn up in trainers and see what fits.', 'type' => 'Open day'],\\n'badge' => ['title' => 'Sports Badge Test', 'intro' => 'Run, jump, throw: anyone who wants to earn the sports badge books a start time. Two examiners take each slot.', 'type' => 'Assessment'],\\n],\\n'bookings' => [\\n'holiday' => [\\n['Sarah Collins', 'Lily Collins', ''],\\n['Sarah Collins', 'Tom Collins', ''],\\n['Aisha Khan', 'Omar Khan', 'Brings an inhaler'],\\n['Helen Moore', 'Ella Moore', 'Picked up at 2 pm by her grandmother'],\\n],\\n'badge' => [\\n['Sarah Collins', 'Lily Collins', ''],\\n['Sarah Collins', 'Tom Collins', ''],\\n['Mark Wilson', 'Mark Wilson', ''],\\n['Aisha Khan', 'Omar Khan', 'Brings an inhaler'],\\n['Jonas Becker', 'Jonas Becker', ''],\\n['Helen Moore', 'Ella Moore', ''],\\n['Helen Moore', 'Jack Moore', ''],\\n],\\n],\\n'offices' => [\\n'trainer' => ['name' => 'Trainer', 'parent' => '', 'order' => 5],\\n'board' => ['name' => 'Board', 'parent' => '', 'order' => 10],\\n'exec' => ['name' => 'Executive board', 'parent' => 'board', 'order' => 10],\\n'chair' => ['name' => 'Chairwoman', 'parent' => 'exec', 'order' => 10],\\n'deputy' => ['name' => 'Deputy chair', 'parent' => 'exec', 'order' => 20],\\n'treasurer' => ['name' => 'Treasurer', 'parent' => 'exec', 'order' => 30],\\n'council' => ['name' => 'Extended board', 'parent' => 'board', 'order' => 20],\\n'secretary' => ['name' => 'Secretary', 'parent' => 'council', 'order' => 10],\\n'youth' => ['name' => 'Youth officer', 'parent' => 'council', 'order' => 20],\\n'records' => ['name' => 'Membership secretary', 'parent' => 'council', 'order' => 30],\\n],\\n'pages' => [\\n'calendar' => [\\n'slug' => 'calendar',\\n'title' => 'Calendar',\\n'intro' => [\\n'This is a live demo of Squadeno. Every date below comes out of the training schedule the club maintains once. School holidays block the affected sessions automatically. A cancelled session, a moved date or an extra session is handled on the date itself.',\\n'Filter by age group or section, switch to the week view, or look at the <a href=\\\"\\\/training-plan\\\/\\\">training plan<\\\/a>. You are logged in as an administrator, so the <a href=\\\"\\\/wp-admin\\\/\\\">admin area<\\\/a> is open as well. Change a training time there and the calendar follows.',\\n],\\n],\\n'sports' => ['slug' => 'sports', 'title' => 'Sports', 'intro' => ['Everything the club offers. Filter by age group or section; each sport has its own page with times, venue and trainers.']],\\n'people' => ['slug' => 'people', 'title' => 'People', 'intro' => ['One record per person, whatever they do for the club. The heading names the office: the trainers first, then the board. Anyone who coaches and sits on the board appears under both, out of a single record.']],\\n'coaches' => ['slug' => 'trainers', 'title' => 'Trainers', 'intro' => ['Everyone who runs a session, filtered to the office \\\"Trainer\\\". Each profile lists the sports that person looks after and can be written to directly.']],\\n'plan' => ['slug' => 'training-plan', 'title' => 'Training plan', 'intro' => ['The weekly schedule as a table. It is built from the training times, so it stays current on its own.']],\\n'venues' => ['slug' => 'venues', 'title' => 'Venues', 'intro' => ['Where the club trains. Each venue shows its address on a map, together with what takes place there.']],\\n'events' => ['slug' => 'events', 'title' => 'Events', 'intro' => ['Tournaments, camps and parties. Events sit in the same calendar as the training sessions.']],\\n'board' => ['slug' => 'board', 'title' => 'Board', 'intro' => ['Who runs the club, filtered to the board offices. These are the same records as on the people page, so nobody is kept twice. Permissions are a separate matter: a board member maintains the club data, a trainer only their own sports.']],\\n],\\n];\\n}\\n}\\nif (! function_exists('csmgr_demo_struktur')) {\\nfunction csmgr_demo_struktur(): array\\n{\\nreturn [\\n'spielbetrieb' => false,\\n'positionen' => [],\\n'aemter' => [\\n'alex' => ['trainer', 'chair'],\\n'priya' => ['trainer', 'deputy'],\\n'nora' => ['trainer', 'treasurer'],\\n'sam' => ['trainer', 'secretary'],\\n'mila' => ['trainer', 'youth'],\\n'jonas' => ['trainer'],\\n'ana' => ['trainer'],\\n'tomas' => ['trainer'],\\n'ida' => ['trainer'],\\n'hedda' => ['records'],\\n],\\n'sportarten' => [\\n'athletics' => ['sections' => ['athletics'], 'kosten' => 'im_beitrag'],\\n'athletics_kids' => [\\n'parent' => 'athletics', 'age' => ['children'], 'sections' => ['athletics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [\\n['wochentag' => 2, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'hall', 'saison' => 'winter', 'trainer' => 'alex'],\\n['wochentag' => 4, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'stadium', 'saison' => 'sommer', 'trainer' => 'priya', 'ausweich' => 'hall', 'hinweis' => 'rain'],\\n],\\n'trainer' => [['alex', 'haupt'], ['priya', 'co']],\\n],\\n'athletics_youth' => [\\n'parent' => 'athletics', 'age' => ['youth'], 'sections' => ['athletics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 3, 'von' => '18:00', 'bis' => '19:30', 'ort' => 'stadium', 'saison' => 'sommer', 'trainer' => 'priya']],\\n'trainer' => [['priya', 'haupt']],\\n],\\n'gymnastics' => ['sections' => ['gymnastics'], 'kosten' => 'im_beitrag'],\\n'toddler_gym' => [\\n'parent' => 'gymnastics', 'age' => ['toddlers'], 'sections' => ['gymnastics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 5, 'von' => '15:00', 'bis' => '16:00', 'ort' => 'hall', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n'apparatus' => [\\n'parent' => 'gymnastics', 'age' => ['children'], 'sections' => ['gymnastics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 1, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'hall', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n'volleyball' => [\\n'age' => ['adults'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 3, 'von' => '19:00', 'bis' => '20:30', 'ort' => 'hall', 'trainer' => 'sam']],\\n'ausnahmen' => [\\n['tag' => 'wednesday', 'art' => 'faellt_aus', 'notiz' => 'cancelled'],\\n['tag' => 'saturday', 'art' => 'zusatztermin', 'von' => '10:00', 'bis' => '12:00', 'ort' => 'hall', 'notiz' => 'extra'],\\n],\\n'trainer' => [['sam', 'haupt']],\\n],\\n'basketball' => [\\n'age' => ['adults', 'youth'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 5, 'von' => '19:00', 'bis' => '21:00', 'ort' => 'annex', 'trainer' => 'tomas']],\\n'trainer' => [['tomas', 'haupt']],\\n],\\n'badminton' => [\\n'age' => ['adults'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [\\n['wochentag' => 2, 'von' => '19:30', 'bis' => '21:30', 'ort' => 'hall', 'trainer' => 'ana'],\\n['wochentag' => 6, 'von' => '10:00', 'bis' => '11:00', 'ort' => 'hall', 'trainer' => 'ana'],\\n],\\n'trainer' => [['ana', 'haupt']],\\n],\\n'table_tennis' => [\\n'age' => ['adults', 'youth'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 4, 'von' => '20:00', 'bis' => '22:00', 'ort' => 'hall', 'saison' => 'ganzjaehrig', 'trainer' => 'jonas']],\\n'trainer' => [['jonas', 'haupt']],\\n],\\n'yoga' => [\\n'age' => ['adults', 'seniors'], 'sections' => ['fitness'], 'kosten' => 'gebuehr',\\n'zeiten' => [\\n['wochentag' => 1, 'von' => '19:00', 'bis' => '20:15', 'ort' => 'studio', 'trainer' => 'mila'],\\n['wochentag' => 3, 'von' => '08:30', 'bis' => '09:45', 'ort' => 'studio', 'trainer' => 'mila'],\\n],\\n'trainer' => [['mila', 'haupt']],\\n],\\n'nordic' => [\\n'age' => ['adults', 'seniors'], 'sections' => ['fitness'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 6, 'von' => '09:30', 'bis' => '10:30', 'ort' => 'stadium', 'zusatz' => 'meet', 'trainer' => 'ida']],\\n'trainer' => [['ida', 'haupt']],\\n],\\n'dance' => [\\n'age' => ['children'], 'sections' => ['fitness'], 'kosten' => 'gebuehr',\\n'zeiten' => [['wochentag' => 4, 'von' => '15:30', 'bis' => '16:30', 'ort' => 'studio', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n],\\n'schliesszeiten' => [\\n['key' => 'maintenance', 'monatstag' => [20, 24]],\\n['key' => 'holidays', 'tage' => [10, 17]],\\n['key' => 'halfterm', 'tage' => [120, 127]],\\n],\\n'veranstaltungen' => [\\n'holiday' => ['modus' => 'eintaegig', 'von' => 12, 'zeit_von' => '10:00', 'zeit_bis' => '14:00', 'ort' => 'stadium', 'sport' => 'athletics_kids', 'anmeldung' => 'online', 'plaetze' => '20', 'kosten' => 'gebuehr', 'age' => ['children', 'youth'], 'buchung' => ['wer' => 'alle', 'frist' => -2]],\\n'badge' => ['modus' => 'eintaegig', 'von' => 9, 'zeit_von' => '10:00', 'zeit_bis' => '12:00', 'ort' => 'stadium', 'anmeldung' => 'online', 'plaetze' => '2', 'kosten' => 'frei', 'buchung' => ['wer' => 'alle', 'frist' => -1, 'takt' => 15, 'pause' => [60, 15], 'belegt' => [0, 0, 1, 3, 3, 5, 6]]],\\n'party' => ['modus' => 'eintaegig', 'von' => 21, 'zeit_von' => '15:00', 'zeit_bis' => '22:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n'tournament' => ['modus' => 'eintaegig', 'von' => 28, 'zeit_von' => '09:00', 'zeit_bis' => '18:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n'camp' => ['modus' => 'mehrtaegig', 'von' => 35, 'bis' => 36, 'ort' => 'hall', 'anmeldung' => 'erforderlich', 'plaetze' => '24', 'kosten' => 'gebuehr', 'age' => ['children', 'youth']],\\n'openday' => ['modus' => 'eintaegig', 'von' => 45, 'zeit_von' => '11:00', 'zeit_bis' => '17:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n],\\n'seiten' => ['calendar', 'sports', 'people', 'coaches', 'board', 'plan', 'venues', 'events'],\\n'startseite' => 'calendar',\\n'konten' => [\\n'alex' => ['login' => 'demo_vorstand', 'rolle' => 'csmgr_vorstand'],\\n'jonas' => ['login' => 'demo_trainer', 'rolle' => 'csmgr_trainer'],\\n],\\n];\\n}\\n}\\nuse ClubSportsManager\\\\Domain\\\\Booking\\\\Booking;\\nuse ClubSportsManager\\\\Domain\\\\Booking\\\\BookingStatus;\\nuse ClubSportsManager\\\\WordPress\\\\Admin\\\\Settings;\\nuse ClubSportsManager\\\\WordPress\\\\Admin\\\\TermOrderField;\\nuse ClubSportsManager\\\\WordPress\\\\ArchivePages;\\nuse ClubSportsManager\\\\WordPress\\\\Booking\\\\BookingSchema;\\nuse ClubSportsManager\\\\WordPress\\\\Booking\\\\Bookings;\\nuse ClubSportsManager\\\\WordPress\\\\Booking\\\\EventOffers;\\nuse ClubSportsManager\\\\WordPress\\\\Meta\\\\ClosureMeta;\\nuse ClubSportsManager\\\\WordPress\\\\Meta\\\\EventMeta;\\nuse ClubSportsManager\\\\WordPress\\\\Meta\\\\PositionMeta;\\nuse ClubSportsManager\\\\WordPress\\\\Meta\\\\SportartMetaWriter;\\nuse ClubSportsManager\\\\WordPress\\\\Meta\\\\SportstaetteMeta;\\nuse ClubSportsManager\\\\WordPress\\\\Meta\\\\TrainerMeta;\\nuse ClubSportsManager\\\\WordPress\\\\Seeder;\\nuse ClubSportsManager\\\\WordPress\\\\TaxonomyRegistrar;\\nif (! defined('ABSPATH')) {\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n}\\nif (! class_exists(SportartMetaWriter::class)) {\\n$notice = (int) wp_insert_post([\\n'post_type' => 'page',\\n'post_title' => 'Demo data could not be created',\\n'post_name' => 'welcome',\\n'post_status' => 'publish',\\n'post_content' => '<!-- wp:paragraph --><p>The Squadeno plugin was not active when the demo data was about to be created, so this preview is empty. Please report it at <a href=\\\"https:\\\/\\\/squadeno.com\\\">squadeno.com<\\\/a>.<\\\/p><!-- \\\/wp:paragraph -->',\\n], true);\\nupdate_option('show_on_front', 'page');\\nupdate_option('page_on_front', $notice);\\nreturn;\\n}\\n$t = csmgr_demo_texte();\\n$s = csmgr_demo_struktur();\\nSeeder::run();\\n$attribute = static fn(array $daten): string => (string) wp_json_encode(\\n$daten,\\nJSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES\\n);\\n$create = static function (string $type, string $title, string $content = '', int $parent = 0, string $name = '', int $order = 0): int {\\nreturn (int) wp_insert_post([\\n'post_type' => $type,\\n'post_title' => $title,\\n'post_content' => $content,\\n'post_name' => $name,\\n'post_status' => 'publish',\\n'post_parent' => $parent,\\n'menu_order' => $order,\\n], true);\\n};\\n$term = static function (string $name, string $taxonomy): int {\\n$existing = get_term_by('name', $name, $taxonomy);\\nif ($existing instanceof WP_Term) {\\nreturn $existing->term_id;\\n}\\n$created = wp_insert_term($name, $taxonomy);\\nreturn is_array($created) ? (int) $created['term_id'] : 0;\\n};\\n$season = static function (string $key): int {\\nreturn \\\\ClubSportsManager\\\\WordPress\\\\Seeder::defaultSeasonId($key);\\n};\\n$today = current_time('Y-m-d');\\n$stamp = (int) strtotime($today);\\n$day = static fn(int $offset): string => gmdate('Y-m-d', $stamp + ($offset * DAY_IN_SECONDS));\\n$sperrzeiten = array_map(\\nstatic fn(array $sperre): array => isset($sperre['monatstag'])\\n? [\\ngmdate('Y-m-' . sprintf('%02d', $sperre['monatstag'][0]), $stamp),\\ngmdate('Y-m-' . sprintf('%02d', $sperre['monatstag'][1]), $stamp),\\n]\\n: [$day($sperre['tage'][0]), $day($sperre['tage'][1])],\\n$s['schliesszeiten']\\n);\\n$nextly = static function (string $weekday) use ($stamp, $sperrzeiten): string {\\n$datum = (int) strtotime('next ' . $weekday, $stamp);\\nfor ($woche = 0; $woche < 12; $woche++, $datum += 7 * DAY_IN_SECONDS) {\\n$iso = gmdate('Y-m-d', $datum);\\nforeach ($sperrzeiten as [$von, $bis]) {\\nif ($iso >= $von && $iso <= $bis) {\\ncontinue 2;\\n}\\n}\\nreturn $iso;\\n}\\nreturn gmdate('Y-m-d', $datum);\\n};\\n$saison = [\\n'ganzjaehrig' => $season('year-round'),\\n'winter' => $season('winter'),\\n'sommer' => $season('summer'),\\n];\\n$venues = [];\\nforeach ($t['venues'] as $key => $v) {\\n$id = $create('csmgr_sportstaette', $v['name'], $v['intro']);\\nupdate_post_meta($id, SportstaetteMeta::STRASSE, $v['street']);\\nupdate_post_meta($id, SportstaetteMeta::PLZ, $v['zip']);\\nupdate_post_meta($id, SportstaetteMeta::ORT, $v['city']);\\nupdate_post_meta($id, SportstaetteMeta::LAT, $v['lat']);\\nupdate_post_meta($id, SportstaetteMeta::LNG, $v['lng']);\\nupdate_post_meta($id, SportstaetteMeta::AUSSTATTUNG, $v['facilities']);\\n$venues[$key] = $id;\\n}\\n$trainers = [];\\nforeach ($t['people'] as $key => $p) {\\n$id = $create('csmgr_trainer', $p['name'], $p['intro']);\\nupdate_post_meta($id, TrainerMeta::FUNKTION, $p['role']);\\nupdate_post_meta($id, TrainerMeta::QUALIFIKATIONEN, $p['qualifications']);\\nupdate_post_meta($id, TrainerMeta::MITGLIED_SEIT, $p['since']);\\nupdate_post_meta($id, TrainerMeta::EMAIL, $key . '@example.com');\\nupdate_post_meta($id, TrainerMeta::KONTAKT_FORMULAR, 'ja');\\n$trainers[$key] = $id;\\n}\\n$vorhandeneAemter = get_terms(['taxonomy' => 'csmgr_funktion', 'hide_empty' => false, 'fields' => 'ids']);\\nif (is_array($vorhandeneAemter)) {\\nforeach ($vorhandeneAemter as $altesAmt) {\\nwp_delete_term((int) $altesAmt, 'csmgr_funktion');\\n}\\n}\\n$offices = [];\\n$officeSlugs = [];\\nforeach ($t['offices'] as $key => $office) {\\n$parent = $office['parent'] !== '' ? ($offices[$office['parent']] ?? 0) : 0;\\n$result = wp_insert_term($office['name'], 'csmgr_funktion', ['parent' => $parent]);\\nif (is_wp_error($result)) {\\n$existing = get_term_by('name', $office['name'], 'csmgr_funktion');\\n$offices[$key] = $existing instanceof WP_Term ? $existing->term_id : 0;\\n} else {\\n$offices[$key] = (int) $result['term_id'];\\nupdate_term_meta($offices[$key], '_csmgr_reihenfolge', $office['order']);\\n}\\n$officeTerm = get_term($offices[$key], 'csmgr_funktion');\\n$officeSlugs[$key] = $officeTerm instanceof WP_Term ? $officeTerm->slug : '';\\n}\\n$aemterJePerson = $s['aemter'];\\nforeach ($aemterJePerson as $person => $keys) {\\nif (! isset($trainers[$person])) {\\ncontinue;\\n}\\n$termIds = [];\\nforeach ($keys as $key) {\\nif (isset($offices[$key])) {\\n$termIds[] = $offices[$key];\\n}\\n}\\nif ($termIds !== []) {\\nwp_set_object_terms($trainers[$person], $termIds, 'csmgr_funktion');\\n}\\n}\\n$age = [];\\nforeach ($t['age'] as $key => $name) {\\n$age[$key] = $term($name, 'csmgr_altersgruppe');\\n}\\n$section = [];\\nforeach ($t['sections'] as $key => $name) {\\n$section[$key] = $term($name, 'csmgr_sparte');\\n}\\n$positions = [];\\nif ($s['positionen'] !== []) {\\nif (! taxonomy_exists(PositionMeta::TAXONOMY)) {\\n(new TaxonomyRegistrar())->registerAll();\\n}\\nforeach ($s['positionen'] as $key => $reihenfolge) {\\n$positions[$key] = $term($t['positions'][$key], PositionMeta::TAXONOMY);\\nupdate_term_meta($positions[$key], TermOrderField::META_KEY, $reihenfolge);\\n}\\n}\\n$sportDefs = $s['sportarten'];\\n$writer = new SportartMetaWriter();\\n$sports = [];\\nforeach ($sportDefs as $key => $def) {\\n$texte = $t['sports'][$key];\\n$zeiten = [];\\nforeach ($def['zeiten'] ?? [] as $z) {\\n$zeile = [\\n'wochentag' => $z['wochentag'],\\n'zeit_von' => $z['von'],\\n'zeit_bis' => $z['bis'],\\n'ort_id' => $venues[$z['ort']],\\n'trainer_id' => $trainers[$z['trainer']],\\n];\\nif (isset($z['saison'])) {\\n$zeile['saison_id'] = $saison[$z['saison']];\\n}\\nif (isset($z['ausweich'])) {\\n$zeile['ausweich_ort_id'] = $venues[$z['ausweich']];\\n$zeile['ausweich_hinweis'] = $t['notes'][$z['hinweis']];\\n}\\nif (isset($z['zusatz'])) {\\n$zeile['ort_zusatz'] = $t['notes'][$z['zusatz']];\\n}\\n$zeiten[] = $zeile;\\n}\\n$ausnahmen = [];\\nforeach ($def['ausnahmen'] ?? [] as $a) {\\n$zeile = ['datum' => $nextly($a['tag']), 'art' => $a['art'], 'notiz' => $t['notes'][$a['notiz']]];\\nif (isset($a['von'])) {\\n$zeile['zeit_von'] = $a['von'];\\n$zeile['zeit_bis'] = $a['bis'];\\n$zeile['ort_id'] = $venues[$a['ort']];\\n}\\n$ausnahmen[] = $zeile;\\n}\\n$zuordnung = [];\\nforeach ($def['trainer'] ?? [] as [$trainerKey, $rolle]) {\\n$zuordnung[] = ['trainer_id' => $trainers[$trainerKey], 'rolle' => $rolle];\\n}\\n$kader = [];\\nforeach ($def['kader'] ?? [] as $spieler) {\\n[$vorname, $nachname, $nummer, $positionKey] = $spieler;\\n$kader[] = [\\n'vorname' => $vorname,\\n'nachname' => $nachname,\\n'nummer' => $nummer,\\n'position_id' => $positions[$positionKey] ?? 0,\\n'person_id' => isset($spieler[4]) ? ($trainers[$spieler[4]] ?? 0) : 0,\\n];\\n}\\n$id = $create(\\n'csmgr_sportart',\\n$texte['title'],\\n$texte['intro'],\\n$sports[$def['parent'] ?? ''] ?? 0,\\n'',\\n$def['reihenfolge'] ?? 0\\n);\\nif (($def['age'] ?? []) !== []) {\\nwp_set_object_terms($id, array_map(static fn(string $k): int => $age[$k], $def['age']), 'csmgr_altersgruppe');\\n}\\nif (($def['sections'] ?? []) !== []) {\\nwp_set_object_terms($id, array_map(static fn(string $k): int => $section[$k], $def['sections']), 'csmgr_sparte');\\n}\\n$writer->save($id, [\\n'status' => 'aktiv',\\n'kosten_modus' => $def['kosten'],\\n'kosten_betrag' => $texte['fee'] ?? '',\\n'trainingszeiten' => $zeiten,\\n'ausnahmen' => $ausnahmen,\\n'trainer' => $zuordnung,\\n'kader' => $kader,\\n'kader_sichtbarkeit' => $def['kader_sichtbarkeit'] ?? 'intern',\\n'liga' => $texte['liga'] ?? '',\\n'liga_url' => $def['liga_url'] ?? '',\\n], true, Settings::spielbetriebActive());\\n$sports[$key] = $id;\\n}\\nforeach ($s['schliesszeiten'] as $index => $sperre) {\\n[$von, $bis] = $sperrzeiten[$index];\\n$id = $create('csmgr_schliesszeit', $t['closures'][$sperre['key']]);\\nupdate_post_meta($id, ClosureMeta::VON, $von);\\nupdate_post_meta($id, ClosureMeta::BIS, $bis);\\nupdate_post_meta($id, ClosureMeta::GELTUNG, isset($sperre['sportarten']) ? 'ausgewaehlte' : 'alle');\\nforeach ($sperre['sportarten'] ?? [] as $sportKey) {\\nadd_post_meta($id, ClosureMeta::SPORTARTEN, $sports[$sportKey]);\\n}\\n}\\n$eventDefs = $s['veranstaltungen'];\\n$eventIds = [];\\nforeach ($eventDefs as $key => $def) {\\n$texte = $t['events'][$key];\\n$id = $create('csmgr_event', $texte['title'], $texte['intro']);\\nupdate_post_meta($id, EventMeta::ZEIT_MODUS, $def['modus']);\\nupdate_post_meta($id, EventMeta::DATUM_VON, $day($def['von']));\\nupdate_post_meta($id, EventMeta::ANMELDUNG, $def['anmeldung']);\\nupdate_post_meta($id, EventMeta::KOSTEN_MODUS, $def['kosten']);\\nif (isset($def['ort'])) {\\nupdate_post_meta($id, EventMeta::ORT_ID, $venues[$def['ort']]);\\n}\\nif (isset($def['zusatz'])) {\\nupdate_post_meta($id, EventMeta::ORT_ZUSATZ, $t['notes'][$def['zusatz']]);\\n}\\nif (isset($def['gegner'])) {\\nupdate_post_meta($id, EventMeta::BEGEGNUNG, 'begegnung');\\nupdate_post_meta($id, EventMeta::GEGNER, $def['gegner']);\\nupdate_post_meta($id, EventMeta::SPIELORT, $def['spielort']);\\n}\\nif (isset($def['bis'])) {\\nupdate_post_meta($id, EventMeta::DATUM_BIS, $day($def['bis']));\\n}\\nif (isset($def['zeit_von'])) {\\nupdate_post_meta($id, EventMeta::ZEIT_VON, $def['zeit_von']);\\nupdate_post_meta($id, EventMeta::ZEIT_BIS, $def['zeit_bis']);\\n}\\nif (isset($def['sport'])) {\\nupdate_post_meta($id, EventMeta::SPORTART_ID, $sports[$def['sport']]);\\n}\\nif (isset($def['plaetze'])) {\\nupdate_post_meta($id, EventMeta::PLAETZE, $def['plaetze']);\\n}\\nif (isset($texte['fee'])) {\\nupdate_post_meta($id, EventMeta::KOSTEN_BETRAG, $texte['fee']);\\n}\\nif (isset($def['buchung']) && class_exists(EventOffers::class)) {\\n$buchung = $def['buchung'];\\nupdate_post_meta($id, EventMeta::BUCHUNG_WER, $buchung['wer'] ?? 'alle');\\nif (isset($buchung['frist'])) {\\nupdate_post_meta($id, EventMeta::FRIST, $day($def['von'] + $buchung['frist']));\\n}\\nif (isset($buchung['takt'])) {\\nupdate_post_meta($id, EventMeta::TAKT, $buchung['takt']);\\n}\\nif (isset($buchung['pause'])) {\\nupdate_post_meta($id, EventMeta::PAUSE_ALLE, $buchung['pause'][0]);\\nupdate_post_meta($id, EventMeta::PAUSE_DAUER, $buchung['pause'][1]);\\n}\\n}\\nwp_set_object_terms($id, $texte['type'], EventMeta::TAX_TYP);\\n$eventIds[$key] = $id;\\nif (($def['age'] ?? []) !== []) {\\nwp_set_object_terms($id, array_map(static fn(string $k): int => $age[$k], $def['age']), 'csmgr_altersgruppe');\\n}\\n}\\n$buchbar = array_filter($eventDefs, static fn (array $def): bool => ($def['anmeldung'] ?? '') === 'online');\\nif ($buchbar !== [] && class_exists(EventOffers::class)) {\\nupdate_option(Settings::OPTION, array_merge((array) get_option(Settings::OPTION, []), [Settings::KEY_BUCHUNGEN => 'ja']));\\nBookingSchema::ensure();\\n$buchungen = Bookings::store();\\nforeach ($buchbar as $key => $def) {\\n$startzeiten = EventOffers::slots($eventIds[$key]);\\n$ganzes = EventOffers::forEvent($eventIds[$key]);\\nforeach ($t['bookings'][$key] ?? [] as $index => [$name, $teilnehmer, $hinweis]) {\\n$angebot = $startzeiten !== []\\n? ($startzeiten[$def['buchung']['belegt'][$index] ?? -1] ?? null)\\n: $ganzes;\\nif ($angebot === null) {\\ncontinue;\\n}\\n$adresse = strtolower(str_replace(' ', '.', remove_accents($name))) . '@example.org';\\n$buchungen->insert(new Booking($angebot->target, BookingStatus::Confirmed, $name, $adresse, 0, 0, $teilnehmer, $hinweis, time() - (count($t['bookings'][$key]) - $index) * 3600, '', '', $angebot->start, $angebot->end));\\n}\\n}\\n}\\n$absaetze = static function (array $texte): string {\\n$html = '';\\nforeach ($texte as $absatz) {\\n$html .= '<!-- wp:paragraph --><p>' . $absatz . '<\\\/p><!-- \\\/wp:paragraph -->';\\n}\\nreturn $html;\\n};\\n$bausteine = [\\n'calendar' => '<!-- wp:csmgr\\\/kalender {\\\"view\\\":\\\"month\\\"} \\\/-->',\\n'sports' => '<!-- wp:csmgr\\\/sportarten \\\/-->',\\n'people' => '<!-- wp:csmgr\\\/personen ' . $attribute(['group' => true]) . ' \\\/-->',\\n'plan' => '<!-- wp:csmgr\\\/kalender {\\\"view\\\":\\\"list\\\"} \\\/-->',\\n'venues' => '<!-- wp:csmgr\\\/sportstaetten ' . $attribute(['columns' => 2]) . ' \\\/-->',\\n'events' => '<!-- wp:csmgr\\\/events ' . $attribute(['columns' => 2]) . ' \\\/-->',\\n'coaches' => '<!-- wp:csmgr\\\/personen ' . $attribute(['function' => $officeSlugs['trainer'] ?? '']) . ' \\\/-->',\\n'board' => '<!-- wp:csmgr\\\/personen ' . $attribute(['function' => $officeSlugs['board'] ?? '', 'group' => true]) . ' \\\/-->',\\n];\\n$seiten = [];\\nforeach ($t['pages'] as $key => $seite) {\\n$seiten[$key] = $create('page', $seite['title'], $absaetze($seite['intro']) . $bausteine[$key], 0, $seite['slug']);\\n}\\n$csmgrUebersichten = [\\n'csmgr_sportart' => 'sports',\\n'csmgr_trainer' => 'people',\\n'csmgr_sportstaette' => 'venues',\\n'csmgr_event' => 'events',\\n];\\n$csmgrEinstellungen = (array) get_option(Settings::OPTION, []);\\nforeach ($csmgrUebersichten as $csmgrTyp => $csmgrSeite) {\\n$csmgrEinstellungen[ArchivePages::optionKey($csmgrTyp)] = (string) $seiten[$csmgrSeite];\\n}\\nupdate_option(Settings::OPTION, $csmgrEinstellungen);\\nupdate_option('show_on_front', 'page');\\nupdate_option('page_on_front', $seiten[$s['startseite']]);\\nupdate_option('page_for_posts', 0);\\nforeach (['hello-world', 'sample-page', 'privacy-policy'] as $sampleSlug) {\\n$sample = get_page_by_path($sampleSlug, OBJECT, ['post', 'page']);\\nif ($sample instanceof WP_Post) {\\nwp_delete_post($sample->ID, true);\\n}\\n}\\n$navigation = '';\\nforeach ($s['seiten'] as $key) {\\n$url = $key === $s['startseite'] ? '\\\/' : '\\\/' . $t['pages'][$key]['slug'] . '\\\/';\\n$navigation .= '<!-- wp:navigation-link '\\n. $attribute(['label' => $t['pages'][$key]['title'], 'url' => $url, 'kind' => 'custom', 'isTopLevelLink' => true])\\n. ' \\\/-->';\\n}\\nwp_insert_post([\\n'post_type' => 'wp_navigation',\\n'post_title' => 'Demo navigation',\\n'post_status' => 'publish',\\n'post_content' => $navigation,\\n], true);\\n$konten = $s['konten'];\\nforeach ($konten as $person => $konto) {\\nif (! isset($trainers[$person])) {\\ncontinue;\\n}\\n$vorhanden = get_user_by('login', $konto['login']);\\nif ($vorhanden instanceof WP_User) {\\n$userId = $vorhanden->ID;\\n$vorhanden->set_role($konto['rolle']);\\n} else {\\n$userId = wp_insert_user([\\n'user_login' => $konto['login'],\\n'user_email' => $konto['login'] . '@example.com',\\n'user_pass' => wp_generate_password(24, true, true),\\n'display_name' => $t['people'][$person]['name'],\\n'role' => $konto['rolle'],\\n]);\\n}\\nif (! is_wp_error($userId) && (int) $userId > 0) {\\nupdate_post_meta($trainers[$person], '_csmgr_trainer_user_id', (int) $userId);\\n}\\n}\\nupdate_option('blogname', $t['site']['name']);\\nupdate_option('blogdescription', $t['site']['tagline']);\\nflush_rewrite_rules();\\n\"},{\"step\":\"installTheme\",\"themeData\":{\"resource\":\"wordpress.org\\\/themes\",\"slug\":\"squadeno\"},\"options\":{\"activate\":true}},{\"step\":\"runPHP\",\"code\":\"<?php\\nif (! function_exists('csmgr_demo_texte')) {\\nfunction csmgr_demo_texte(): array\\n{\\nreturn [\\n'site' => [\\n'name' => 'Riverside Sports Club',\\n'tagline' => 'A demo club running on Squadeno',\\n],\\n'slugs' => [\\n'csmgr_sportart' => 'sport',\\n'csmgr_trainer' => 'person',\\n'csmgr_event' => 'event',\\n'csmgr_sportstaette' => 'venue',\\n],\\n'venues' => [\\n'hall' => ['name' => 'Riverside Sports Hall', 'street' => 'Mill Lane 12', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5079', 'lng' => '-0.1283', 'facilities' => \\\"Parking\\\\nChanging rooms\\\\nStep-free access\\\", 'intro' => 'The main hall. Three courts, a climbing wall and the equipment store for the gymnastics group.'],\\n'stadium' => ['name' => 'Meadow Park Stadium', 'street' => 'Meadow Road 4', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5100', 'lng' => '-0.1230', 'facilities' => \\\"Tartan track\\\\nFloodlights\\\\nParking\\\", 'intro' => 'Six-lane track with jumping pits, used by the athletics groups from spring to autumn.'],\\n'studio' => ['name' => 'Studio at the Old Mill', 'street' => 'Mill Lane 1', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5065', 'lng' => '-0.1300', 'facilities' => \\\"Mats provided\\\\nShowers\\\", 'intro' => 'A quiet room above the old mill. Yoga, dance and the smaller courses take place here.'],\\n'annex' => ['name' => 'Lakeside Gym', 'street' => 'Lake Road 27', 'zip' => '12345', 'city' => 'Riverside', 'lat' => '51.5040', 'lng' => '-0.1345', 'facilities' => \\\"Weights room\\\\nChanging rooms\\\", 'intro' => 'The smaller hall by the lake, mainly used for conditioning and the winter programme.'],\\n],\\n'people' => [\\n'alex' => ['name' => 'Alex Moreau', 'role' => 'Head coach', 'qualifications' => 'Coaching licence C, first aid', 'since' => '2012', 'intro' => 'Runs the athletics group and looks after the youngest members on sports days.'],\\n'priya' => ['name' => 'Priya Raman', 'role' => 'Coach', 'qualifications' => 'Coaching licence C, athletics', 'since' => '2016', 'intro' => 'Sprint and jump specialist. Trains the youth group towards the regional championships.'],\\n'nora' => ['name' => 'Nora Lindqvist', 'role' => 'Coach', 'qualifications' => 'Gymnastics licence B, first aid', 'since' => '2014', 'intro' => 'Teaches gymnastics from the first forward roll upwards, toddlers included.'],\\n'sam' => ['name' => 'Sam Okafor', 'role' => 'Coach', 'qualifications' => 'Ball sports licence C', 'since' => '2019', 'intro' => 'Plays in the mixed volleyball team and runs its Wednesday session.'],\\n'mila' => ['name' => 'Mila Novak', 'role' => 'Course instructor', 'qualifications' => 'Yoga teacher (200 h)', 'since' => '2021', 'intro' => 'Hatha yoga in the studio, mornings and evenings. Beginners are welcome any week.'],\\n'jonas' => ['name' => 'Jonas Weber', 'role' => 'Coach', 'qualifications' => 'Table tennis licence C', 'since' => '2018', 'intro' => 'Open table tennis training. Happy to lend a bat to anyone who turns up.'],\\n'ana' => ['name' => 'Ana Ferreira', 'role' => 'Coach', 'qualifications' => 'Badminton licence C', 'since' => '2020', 'intro' => 'Badminton for adults, and the beginners hour on Saturday mornings.'],\\n'tomas' => ['name' => 'Tomas Havel', 'role' => 'Coach', 'qualifications' => 'Ball sports licence B', 'since' => '2015', 'intro' => 'Basketball and the winter conditioning sessions at the Lakeside Gym.'],\\n'ida' => ['name' => 'Ida Sorensen', 'role' => 'Course instructor', 'qualifications' => 'Walking guide, first aid', 'since' => '2022', 'intro' => 'Leads the Nordic walking group. Meets at the park entrance, whatever the weather.'],\\n'hedda' => ['name' => 'Hedda Lang', 'role' => 'Membership records', 'qualifications' => 'Bookkeeping course', 'since' => '2017', 'intro' => 'Keeps the membership list and answers questions about fees. Does not run a session.'],\\n],\\n'age' => [\\n'toddlers' => 'Toddlers',\\n'children' => 'Children',\\n'youth' => 'Youth',\\n'adults' => 'Adults',\\n'seniors' => 'Seniors',\\n],\\n'sections' => [\\n'athletics' => 'Athletics',\\n'gymnastics' => 'Gymnastics',\\n'ball' => 'Ball sports',\\n'fitness' => 'Fitness',\\n],\\n'sports' => [\\n'athletics' => ['title' => 'Athletics', 'intro' => 'Track and field for every age group. Outdoors in summer, in the hall over winter.'],\\n'athletics_kids' => ['title' => 'Athletics Kids (6-11)', 'intro' => 'Playful introduction to running, jumping and throwing.'],\\n'athletics_youth' => ['title' => 'Athletics Youth (12-17)', 'intro' => 'Training towards the regional championships, with a competition every few weeks.'],\\n'gymnastics' => ['title' => 'Gymnastics', 'intro' => 'From the first forward roll to the high bar.'],\\n'toddler_gym' => ['title' => 'Parent and Toddler Gymnastics', 'intro' => 'For children aged 2 to 4, together with a parent.'],\\n'apparatus' => ['title' => 'Apparatus Gymnastics Kids', 'intro' => 'Bars, beam and vault for children aged 6 and up.'],\\n'volleyball' => ['title' => 'Volleyball', 'intro' => 'Mixed team, league matches on selected weekends. New players are welcome to join a session.'],\\n'basketball' => ['title' => 'Basketball', 'intro' => 'Recreational basketball, no league, no pressure.'],\\n'badminton' => ['title' => 'Badminton', 'intro' => 'Doubles for anyone who can keep a rally going, plus a beginners hour on Saturdays.'],\\n'table_tennis' => ['title' => 'Table Tennis', 'intro' => 'Open training. Bats are available.'],\\n'yoga' => ['title' => 'Yoga', 'intro' => 'Hatha yoga for beginners and returners. Booked by the course.', 'fee' => '6 \\u20ac per session'],\\n'nordic' => ['title' => 'Nordic Walking', 'intro' => 'An hour through the park at a pace that still allows a conversation.'],\\n'dance' => ['title' => 'Dance for Kids', 'intro' => 'Movement games and choreography for children aged 6 to 10.', 'fee' => '40 \\u20ac per term'],\\n],\\n'notes' => [\\n'rain' => 'when it rains',\\n'cancelled' => 'Hall used for an exam',\\n'extra' => 'Extra session before the tournament',\\n'meet' => 'meet at the main entrance',\\n],\\n'closures' => [\\n'maintenance' => 'Hall maintenance',\\n'holidays' => 'School holidays',\\n'halfterm' => 'Half-term break',\\n],\\n'events' => [\\n'holiday' => ['title' => 'Holiday Sports Day', 'intro' => 'A day of games and athletics while regular training pauses.', 'type' => 'Holiday programme', 'fee' => '5 \\u20ac'],\\n'party' => ['title' => 'Club Summer Party', 'intro' => 'Barbecue, games and the annual awards. Members and guests welcome.', 'type' => 'Club party'],\\n'tournament' => ['title' => 'Volleyball Home Tournament', 'intro' => 'Six teams, one long Saturday. Cake stall run by the youth group.', 'type' => 'Tournament'],\\n'camp' => ['title' => 'Gymnastics Weekend Camp', 'intro' => 'Two days of training, staying overnight in the hall.', 'type' => 'Camp', 'fee' => '45 \\u20ac'],\\n'openday' => ['title' => 'Open Day: Try Every Sport', 'intro' => 'Every group runs a taster session. Turn up in trainers and see what fits.', 'type' => 'Open day'],\\n'badge' => ['title' => 'Sports Badge Test', 'intro' => 'Run, jump, throw: anyone who wants to earn the sports badge books a start time. Two examiners take each slot.', 'type' => 'Assessment'],\\n],\\n'bookings' => [\\n'holiday' => [\\n['Sarah Collins', 'Lily Collins', ''],\\n['Sarah Collins', 'Tom Collins', ''],\\n['Aisha Khan', 'Omar Khan', 'Brings an inhaler'],\\n['Helen Moore', 'Ella Moore', 'Picked up at 2 pm by her grandmother'],\\n],\\n'badge' => [\\n['Sarah Collins', 'Lily Collins', ''],\\n['Sarah Collins', 'Tom Collins', ''],\\n['Mark Wilson', 'Mark Wilson', ''],\\n['Aisha Khan', 'Omar Khan', 'Brings an inhaler'],\\n['Jonas Becker', 'Jonas Becker', ''],\\n['Helen Moore', 'Ella Moore', ''],\\n['Helen Moore', 'Jack Moore', ''],\\n],\\n],\\n'offices' => [\\n'trainer' => ['name' => 'Trainer', 'parent' => '', 'order' => 5],\\n'board' => ['name' => 'Board', 'parent' => '', 'order' => 10],\\n'exec' => ['name' => 'Executive board', 'parent' => 'board', 'order' => 10],\\n'chair' => ['name' => 'Chairwoman', 'parent' => 'exec', 'order' => 10],\\n'deputy' => ['name' => 'Deputy chair', 'parent' => 'exec', 'order' => 20],\\n'treasurer' => ['name' => 'Treasurer', 'parent' => 'exec', 'order' => 30],\\n'council' => ['name' => 'Extended board', 'parent' => 'board', 'order' => 20],\\n'secretary' => ['name' => 'Secretary', 'parent' => 'council', 'order' => 10],\\n'youth' => ['name' => 'Youth officer', 'parent' => 'council', 'order' => 20],\\n'records' => ['name' => 'Membership secretary', 'parent' => 'council', 'order' => 30],\\n],\\n'pages' => [\\n'calendar' => [\\n'slug' => 'calendar',\\n'title' => 'Calendar',\\n'intro' => [\\n'This is a live demo of Squadeno. Every date below comes out of the training schedule the club maintains once. School holidays block the affected sessions automatically. A cancelled session, a moved date or an extra session is handled on the date itself.',\\n'Filter by age group or section, switch to the week view, or look at the <a href=\\\"\\\/training-plan\\\/\\\">training plan<\\\/a>. You are logged in as an administrator, so the <a href=\\\"\\\/wp-admin\\\/\\\">admin area<\\\/a> is open as well. Change a training time there and the calendar follows.',\\n],\\n],\\n'sports' => ['slug' => 'sports', 'title' => 'Sports', 'intro' => ['Everything the club offers. Filter by age group or section; each sport has its own page with times, venue and trainers.']],\\n'people' => ['slug' => 'people', 'title' => 'People', 'intro' => ['One record per person, whatever they do for the club. The heading names the office: the trainers first, then the board. Anyone who coaches and sits on the board appears under both, out of a single record.']],\\n'coaches' => ['slug' => 'trainers', 'title' => 'Trainers', 'intro' => ['Everyone who runs a session, filtered to the office \\\"Trainer\\\". Each profile lists the sports that person looks after and can be written to directly.']],\\n'plan' => ['slug' => 'training-plan', 'title' => 'Training plan', 'intro' => ['The weekly schedule as a table. It is built from the training times, so it stays current on its own.']],\\n'venues' => ['slug' => 'venues', 'title' => 'Venues', 'intro' => ['Where the club trains. Each venue shows its address on a map, together with what takes place there.']],\\n'events' => ['slug' => 'events', 'title' => 'Events', 'intro' => ['Tournaments, camps and parties. Events sit in the same calendar as the training sessions.']],\\n'board' => ['slug' => 'board', 'title' => 'Board', 'intro' => ['Who runs the club, filtered to the board offices. These are the same records as on the people page, so nobody is kept twice. Permissions are a separate matter: a board member maintains the club data, a trainer only their own sports.']],\\n],\\n];\\n}\\n}\\nif (! function_exists('csmgr_demo_struktur')) {\\nfunction csmgr_demo_struktur(): array\\n{\\nreturn [\\n'spielbetrieb' => false,\\n'positionen' => [],\\n'aemter' => [\\n'alex' => ['trainer', 'chair'],\\n'priya' => ['trainer', 'deputy'],\\n'nora' => ['trainer', 'treasurer'],\\n'sam' => ['trainer', 'secretary'],\\n'mila' => ['trainer', 'youth'],\\n'jonas' => ['trainer'],\\n'ana' => ['trainer'],\\n'tomas' => ['trainer'],\\n'ida' => ['trainer'],\\n'hedda' => ['records'],\\n],\\n'sportarten' => [\\n'athletics' => ['sections' => ['athletics'], 'kosten' => 'im_beitrag'],\\n'athletics_kids' => [\\n'parent' => 'athletics', 'age' => ['children'], 'sections' => ['athletics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [\\n['wochentag' => 2, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'hall', 'saison' => 'winter', 'trainer' => 'alex'],\\n['wochentag' => 4, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'stadium', 'saison' => 'sommer', 'trainer' => 'priya', 'ausweich' => 'hall', 'hinweis' => 'rain'],\\n],\\n'trainer' => [['alex', 'haupt'], ['priya', 'co']],\\n],\\n'athletics_youth' => [\\n'parent' => 'athletics', 'age' => ['youth'], 'sections' => ['athletics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 3, 'von' => '18:00', 'bis' => '19:30', 'ort' => 'stadium', 'saison' => 'sommer', 'trainer' => 'priya']],\\n'trainer' => [['priya', 'haupt']],\\n],\\n'gymnastics' => ['sections' => ['gymnastics'], 'kosten' => 'im_beitrag'],\\n'toddler_gym' => [\\n'parent' => 'gymnastics', 'age' => ['toddlers'], 'sections' => ['gymnastics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 5, 'von' => '15:00', 'bis' => '16:00', 'ort' => 'hall', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n'apparatus' => [\\n'parent' => 'gymnastics', 'age' => ['children'], 'sections' => ['gymnastics'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 1, 'von' => '17:00', 'bis' => '18:30', 'ort' => 'hall', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n'volleyball' => [\\n'age' => ['adults'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 3, 'von' => '19:00', 'bis' => '20:30', 'ort' => 'hall', 'trainer' => 'sam']],\\n'ausnahmen' => [\\n['tag' => 'wednesday', 'art' => 'faellt_aus', 'notiz' => 'cancelled'],\\n['tag' => 'saturday', 'art' => 'zusatztermin', 'von' => '10:00', 'bis' => '12:00', 'ort' => 'hall', 'notiz' => 'extra'],\\n],\\n'trainer' => [['sam', 'haupt']],\\n],\\n'basketball' => [\\n'age' => ['adults', 'youth'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 5, 'von' => '19:00', 'bis' => '21:00', 'ort' => 'annex', 'trainer' => 'tomas']],\\n'trainer' => [['tomas', 'haupt']],\\n],\\n'badminton' => [\\n'age' => ['adults'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [\\n['wochentag' => 2, 'von' => '19:30', 'bis' => '21:30', 'ort' => 'hall', 'trainer' => 'ana'],\\n['wochentag' => 6, 'von' => '10:00', 'bis' => '11:00', 'ort' => 'hall', 'trainer' => 'ana'],\\n],\\n'trainer' => [['ana', 'haupt']],\\n],\\n'table_tennis' => [\\n'age' => ['adults', 'youth'], 'sections' => ['ball'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 4, 'von' => '20:00', 'bis' => '22:00', 'ort' => 'hall', 'saison' => 'ganzjaehrig', 'trainer' => 'jonas']],\\n'trainer' => [['jonas', 'haupt']],\\n],\\n'yoga' => [\\n'age' => ['adults', 'seniors'], 'sections' => ['fitness'], 'kosten' => 'gebuehr',\\n'zeiten' => [\\n['wochentag' => 1, 'von' => '19:00', 'bis' => '20:15', 'ort' => 'studio', 'trainer' => 'mila'],\\n['wochentag' => 3, 'von' => '08:30', 'bis' => '09:45', 'ort' => 'studio', 'trainer' => 'mila'],\\n],\\n'trainer' => [['mila', 'haupt']],\\n],\\n'nordic' => [\\n'age' => ['adults', 'seniors'], 'sections' => ['fitness'], 'kosten' => 'im_beitrag',\\n'zeiten' => [['wochentag' => 6, 'von' => '09:30', 'bis' => '10:30', 'ort' => 'stadium', 'zusatz' => 'meet', 'trainer' => 'ida']],\\n'trainer' => [['ida', 'haupt']],\\n],\\n'dance' => [\\n'age' => ['children'], 'sections' => ['fitness'], 'kosten' => 'gebuehr',\\n'zeiten' => [['wochentag' => 4, 'von' => '15:30', 'bis' => '16:30', 'ort' => 'studio', 'trainer' => 'nora']],\\n'trainer' => [['nora', 'haupt']],\\n],\\n],\\n'schliesszeiten' => [\\n['key' => 'maintenance', 'monatstag' => [20, 24]],\\n['key' => 'holidays', 'tage' => [10, 17]],\\n['key' => 'halfterm', 'tage' => [120, 127]],\\n],\\n'veranstaltungen' => [\\n'holiday' => ['modus' => 'eintaegig', 'von' => 12, 'zeit_von' => '10:00', 'zeit_bis' => '14:00', 'ort' => 'stadium', 'sport' => 'athletics_kids', 'anmeldung' => 'online', 'plaetze' => '20', 'kosten' => 'gebuehr', 'age' => ['children', 'youth'], 'buchung' => ['wer' => 'alle', 'frist' => -2]],\\n'badge' => ['modus' => 'eintaegig', 'von' => 9, 'zeit_von' => '10:00', 'zeit_bis' => '12:00', 'ort' => 'stadium', 'anmeldung' => 'online', 'plaetze' => '2', 'kosten' => 'frei', 'buchung' => ['wer' => 'alle', 'frist' => -1, 'takt' => 15, 'pause' => [60, 15], 'belegt' => [0, 0, 1, 3, 3, 5, 6]]],\\n'party' => ['modus' => 'eintaegig', 'von' => 21, 'zeit_von' => '15:00', 'zeit_bis' => '22:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n'tournament' => ['modus' => 'eintaegig', 'von' => 28, 'zeit_von' => '09:00', 'zeit_bis' => '18:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n'camp' => ['modus' => 'mehrtaegig', 'von' => 35, 'bis' => 36, 'ort' => 'hall', 'anmeldung' => 'erforderlich', 'plaetze' => '24', 'kosten' => 'gebuehr', 'age' => ['children', 'youth']],\\n'openday' => ['modus' => 'eintaegig', 'von' => 45, 'zeit_von' => '11:00', 'zeit_bis' => '17:00', 'ort' => 'hall', 'anmeldung' => 'keine', 'kosten' => 'frei'],\\n],\\n'seiten' => ['calendar', 'sports', 'people', 'coaches', 'board', 'plan', 'venues', 'events'],\\n'startseite' => 'calendar',\\n'konten' => [\\n'alex' => ['login' => 'demo_vorstand', 'rolle' => 'csmgr_vorstand'],\\n'jonas' => ['login' => 'demo_trainer', 'rolle' => 'csmgr_trainer'],\\n],\\n];\\n}\\n}\\nif (! function_exists('csmgr_demo_theme_texte')) {\\nfunction csmgr_demo_theme_texte(): array\\n{\\nreturn [\\n'menu' => 'Primary menu',\\n'sidebar' => ['menu' => 'Sidebar', 'title' => 'At a glance'],\\n'home' => [\\n'slug' => 'welcome',\\n'title' => 'Welcome to the Riverside Sports Club',\\n'intro' => [\\n'This site shows the Squadeno theme together with the Club Sports Manager plugin. The sports, training times, coaches and venues come from the plugin, the presentation from the theme. Nothing was set up beyond what any club finds in the Customizer.',\\n'You are logged in as an administrator. Change a training time in the admin area and watch the calendar and the sport pages follow. Or rearrange the slider, the tiles and the colours in the Customizer.',\\n],\\n],\\n'about' => [\\n'slug' => 'about',\\n'title' => 'About us',\\n'intro' => [\\n'The Riverside Sports Club has been going since 1912. It started as a gymnastics group meeting at the inn on the market square; today there are seven sections and some 800 members. All of it is carried by people who fit it around a job and a family.',\\n'The committee meets on the first Monday of the month and the general meeting is in March. Anyone who would like to help out, as a coach, on the events team or on the hall rota, is welcome.',\\n'This page stands here as an example of an ordinary content page. It shows how the theme sets running text, and it sits in the menu above the two entries below it.',\\n],\\n],\\n'news' => ['slug' => 'news', 'title' => 'News'],\\n'slides' => [\\n['page' => 'sports', 'title' => 'The sports club for Riverside', 'text' => 'Gymnastics, athletics, volleyball and a good deal more. From the toddler group to the seniors.'],\\n['page' => 'calendar', 'title' => 'Every date in one place', 'text' => 'The calendar comes out of the training schedule. School holidays take themselves off it.'],\\n],\\n'tiles' => [\\n'title' => 'The club at a glance',\\n'items' => [\\n['page' => 'sports', 'title' => 'What we offer', 'text' => 'Every group with its times, venue and contact.'],\\n['page' => 'calendar', 'title' => 'Calendar', 'text' => 'Training and events as a month or a week.'],\\n['page' => 'people', 'title' => 'People', 'text' => 'Trainers and board, one record per person.'],\\n['page' => 'venues', 'title' => 'Venues', 'text' => 'Halls and pitches with address and map.'],\\n],\\n],\\n'latest' => 'From the club',\\n'posts' => [\\n['title' => 'A second yoga group on Tuesday mornings', 'excerpt' => 'From next week there is a second yoga session, this time in the morning at the studio.', 'body' => 'The evening group was full, so a second session joins it: Tuesdays at 9 am at the Old Mill studio. Mats are there, no experience needed. Anyone who wants to try it out can simply turn up.'],\\n['title' => 'Summer party at the stadium', 'excerpt' => 'A relay race, a cake stall and the gymnastics groups, all in one afternoon.', 'body' => 'The club celebrates at the stadium on the last Saturday in July. The gymnastics groups show what they can do, athletics invites everyone to a relay race, and there will be cake. Helping hands are welcome, just talk to the section leaders.'],\\n],\\n'cta' => [\\n'page' => 'sports',\\n'title' => 'Fancy joining in?',\\n'text' => 'To try a session, simply come along. Nobody has to sign up first.',\\n'button' => 'See what we offer',\\n],\\n'location' => 'Riverside',\\n'address' => \\\"Riverside Sports Club\\\\nMill Lane 12\\\\n12345 Riverside\\\",\\n];\\n}\\n}\\nif (! defined('ABSPATH')) {\\nrequire_once '\\\/wordpress\\\/wp-load.php';\\n}\\n$t = csmgr_demo_texte();\\n$theme = csmgr_demo_theme_texte();\\n$seitenId = static function (string $slug): int {\\n$seite = get_page_by_path($slug, OBJECT, 'page');\\nreturn $seite instanceof WP_Post ? (int) $seite->ID : 0;\\n};\\n$seiteAnlegen = static function (string $slug, string $titel, array $absaetze = []): int {\\n$inhalt = '';\\nforeach ($absaetze as $absatz) {\\n$inhalt .= '<!-- wp:paragraph --><p>' . $absatz . '<\\\/p><!-- \\\/wp:paragraph -->';\\n}\\n$id = wp_insert_post([\\n'post_type' => 'page',\\n'post_title' => $titel,\\n'post_name' => $slug,\\n'post_status' => 'publish',\\n'post_content' => $inhalt,\\n]);\\nreturn is_wp_error($id) ? 0 : (int) $id;\\n};\\n$startseite = $seiteAnlegen($theme['home']['slug'], $theme['home']['title'], $theme['home']['intro']);\\n$ueberUns = $seiteAnlegen($theme['about']['slug'], $theme['about']['title'], $theme['about']['intro']);\\n$beitraege = $seiteAnlegen($theme['news']['slug'], $theme['news']['title']);\\nif ($startseite > 0) {\\nupdate_option('show_on_front', 'page');\\nupdate_option('page_on_front', $startseite);\\n}\\nif ($beitraege > 0) {\\nupdate_option('page_for_posts', $beitraege);\\n}\\nforeach ($theme['posts'] as $beitrag) {\\nwp_insert_post([\\n'post_type' => 'post',\\n'post_title' => $beitrag['title'],\\n'post_status' => 'publish',\\n'post_excerpt' => $beitrag['excerpt'],\\n'post_content' => '<!-- wp:paragraph --><p>' . $beitrag['body'] . '<\\\/p><!-- \\\/wp:paragraph -->',\\n]);\\n}\\n$menuePunkte = [\\n[\\n'seite' => $ueberUns,\\n'kinder' => [\\n$seitenId($t['pages']['people']['slug']),\\n$seitenId($t['pages']['coaches']['slug']),\\n$seitenId($t['pages']['board']['slug']),\\n$seitenId($t['pages']['venues']['slug']),\\n],\\n],\\n['seite' => $seitenId($t['pages']['sports']['slug'])],\\n['seite' => $seitenId($t['pages']['calendar']['slug'])],\\n['seite' => $seitenId($t['pages']['events']['slug'])],\\n['seite' => $beitraege],\\n];\\n$eintragen = static function (int $menue, int $seite, int $eltern = 0): int {\\n$id = wp_update_nav_menu_item($menue, 0, [\\n'menu-item-object-id' => $seite,\\n'menu-item-object' => 'page',\\n'menu-item-type' => 'post_type',\\n'menu-item-status' => 'publish',\\n'menu-item-parent-id' => $eltern,\\n]);\\nreturn is_wp_error($id) ? 0 : (int) $id;\\n};\\n$vorhanden = wp_get_nav_menu_object($theme['menu']);\\n$menue = $vorhanden instanceof WP_Term ? (int) $vorhanden->term_id : wp_create_nav_menu($theme['menu']);\\nif (! is_wp_error($menue)) {\\nforeach ($menuePunkte as $punkt) {\\nif ($punkt['seite'] <= 0) {\\ncontinue;\\n}\\n$eltern = $eintragen((int) $menue, $punkt['seite']);\\nforeach ($punkt['kinder'] ?? [] as $kind) {\\nif ($kind > 0 && $eltern > 0) {\\n$eintragen((int) $menue, $kind, $eltern);\\n}\\n}\\n}\\nset_theme_mod('nav_menu_locations', ['primary' => (int) $menue]);\\n}\\n$slot = 1;\\nforeach ($theme['slides'] as $slide) {\\n$ziel = $seitenId($t['pages'][$slide['page']]['slug']);\\nset_theme_mod('squadeno_slide_' . $slot . '_mode', 'image');\\nset_theme_mod('squadeno_slide_' . $slot . '_title', $slide['title']);\\nset_theme_mod('squadeno_slide_' . $slot . '_text', $slide['text']);\\nif ($ziel > 0) {\\nset_theme_mod('squadeno_slide_' . $slot . '_link', (string) get_permalink($ziel));\\n}\\n$slot++;\\n}\\nset_theme_mod('squadeno_tiles_enabled', true);\\nset_theme_mod('squadeno_tiles_title', $theme['tiles']['title']);\\n$kachel = 1;\\nforeach ($theme['tiles']['items'] as $eintrag) {\\n$ziel = $seitenId($t['pages'][$eintrag['page']]['slug']);\\nif ($ziel > 0) {\\nset_theme_mod('squadeno_tile_' . $kachel . '_page', $ziel);\\nset_theme_mod('squadeno_tile_' . $kachel . '_title', $eintrag['title']);\\nset_theme_mod('squadeno_tile_' . $kachel . '_text', $eintrag['text']);\\n$kachel++;\\n}\\n}\\nset_theme_mod('squadeno_latest_title', $theme['latest']);\\n$cta = $seitenId($t['pages'][$theme['cta']['page']]['slug']);\\nset_theme_mod('squadeno_cta_title', $theme['cta']['title']);\\nset_theme_mod('squadeno_cta_text', $theme['cta']['text']);\\nset_theme_mod('squadeno_cta_button', $theme['cta']['button']);\\nif ($cta > 0) {\\nset_theme_mod('squadeno_cta_page', $cta);\\n}\\n$bereiche = get_option('sidebars_widgets');\\nif (is_array($bereiche)) {\\nforeach ($bereiche as $bereich => $widgets) {\\nif ('wp_inactive_widgets' !== $bereich && is_array($widgets)) {\\n$bereiche[$bereich] = [];\\n}\\n}\\nupdate_option('sidebars_widgets', $bereiche);\\n}\\n$seitenleiste = wp_get_nav_menu_object($theme['sidebar']['menu']);\\nif (! $seitenleiste instanceof WP_Term) {\\n$seitenleiste = wp_create_nav_menu($theme['sidebar']['menu']);\\nforeach (is_wp_error($seitenleiste) ? [] : ['events', 'board', 'coaches', 'plan'] as $seite) {\\n$id = $seitenId($t['pages'][$seite]['slug']);\\nif ($id > 0) {\\n$eintragen((int) $seitenleiste, $id);\\n}\\n}\\n} else {\\n$seitenleiste = (int) $seitenleiste->term_id;\\n}\\nif (! is_wp_error($seitenleiste) && is_array($bereiche)) {\\nupdate_option('widget_nav_menu', [\\n2 => ['title' => $theme['sidebar']['title'], 'nav_menu' => (int) $seitenleiste],\\n'_multiwidget' => 1,\\n]);\\n$bereiche['homepage-column'] = ['nav_menu-2'];\\nupdate_option('sidebars_widgets', $bereiche);\\n}\\nset_theme_mod('squadeno_archive_location', $theme['location']);\\nset_theme_mod('squadeno_contact_address', $theme['address']);\\nflush_rewrite_rules();\\n\"}]}"}},"all_blocks":{"csmgr\/events":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"csmgr\/events","version":"1.0.0","title":"Events","category":"squadeno-club-sports-manager","icon":"calendar-alt","description":"The events of the club on a page of their own: upcoming ones, a single type, or a look back.","keywords":["events","dates","holiday club","club sports"],"textdomain":"squadeno-club-sports-manager","attributes":{"scope":{"type":"string","default":"upcoming"},"type":{"type":"string","default":""},"age":{"type":"string","default":""},"sport":{"type":"string","default":"0"},"filters":{"type":"boolean","default":true},"layout":{"type":"string","default":"cards"},"columns":{"type":"number","default":3},"limit":{"type":"number","default":0}},"supports":{"html":false},"editorScript":"file:.\/index.js"},"csmgr\/personen":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"csmgr\/personen","version":"1.0.0","title":"People","category":"squadeno-club-sports-manager","icon":"groups","description":"Trainers, board members and other people of the club, optionally grouped by committee.","keywords":["people","board","trainers","club sports"],"textdomain":"squadeno-club-sports-manager","attributes":{"function":{"type":"string","default":""},"group":{"type":"boolean","default":false},"withoutOffice":{"type":"boolean","default":false},"layout":{"type":"string","default":"cards"},"columns":{"type":"number","default":3}},"supports":{"html":false},"editorScript":"file:.\/index.js"},"csmgr\/kalender":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"csmgr\/kalender","version":"1.0.0","title":"Club Calendar","category":"squadeno-club-sports-manager","icon":"calendar-alt","description":"Calendar with all dates of the club.","keywords":["calendar","dates","club sports"],"textdomain":"squadeno-club-sports-manager","attributes":{"view":{"type":"string","default":"month"}},"supports":{"html":false},"editorScript":"file:.\/index.js"},"csmgr\/sportstaetten":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"csmgr\/sportstaetten","version":"1.0.0","title":"Venues","category":"squadeno-club-sports-manager","icon":"location","description":"The venues of the club with their addresses.","keywords":["venues","places","gym","club sports"],"textdomain":"squadeno-club-sports-manager","attributes":{"layout":{"type":"string","default":"cards"},"columns":{"type":"number","default":3},"limit":{"type":"number","default":0}},"supports":{"html":false},"editorScript":"file:.\/index.js"},"csmgr\/sportarten":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"csmgr\/sportarten","version":"1.0.0","title":"Sports Overview","category":"squadeno-club-sports-manager","icon":"grid-view","description":"Grid of all sports of the club.","keywords":["sports","club sports","offers"],"textdomain":"squadeno-club-sports-manager","supports":{"html":false},"editorScript":"file:.\/index.js"},"csmgr\/naechste-termine":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"csmgr\/naechste-termine","version":"1.0.0","title":"Upcoming Dates","category":"squadeno-club-sports-manager","icon":"clock","description":"The upcoming dates of a sport.","keywords":["dates","training","club sports"],"textdomain":"squadeno-club-sports-manager","attributes":{"sport":{"type":"number","default":0},"limit":{"type":"number","default":5}},"supports":{"html":false},"editorScript":"file:.\/index.js"}},"tagged_versions":["1.10.0","1.10.1","1.10.2","1.11.0","1.12.0","1.13.0","1.14.0","1.15.0","1.8.0","1.8.1","1.9.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3759598,"resolution":"1","location":"assets","locale":"","width":1280,"height":1871},"screenshot-10.png":{"filename":"screenshot-10.png","revision":3759598,"resolution":"10","location":"assets","locale":"","width":1280,"height":1675},"screenshot-11.png":{"filename":"screenshot-11.png","revision":3698444,"resolution":"11","location":"assets","locale":"","width":1280,"height":2804},"screenshot-12.png":{"filename":"screenshot-12.png","revision":3759598,"resolution":"12","location":"assets","locale":"","width":1280,"height":985},"screenshot-13.png":{"filename":"screenshot-13.png","revision":3759598,"resolution":"13","location":"assets","locale":"","width":1280,"height":2598},"screenshot-14.png":{"filename":"screenshot-14.png","revision":3759598,"resolution":"14","location":"assets","locale":"","width":1280,"height":2100},"screenshot-15.png":{"filename":"screenshot-15.png","revision":3759598,"resolution":"15","location":"assets","locale":"","width":1280,"height":900},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3759598,"resolution":"2","location":"assets","locale":"","width":1280,"height":2345},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3759598,"resolution":"3","location":"assets","locale":"","width":1280,"height":1940},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3759598,"resolution":"4","location":"assets","locale":"","width":1280,"height":1342},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3759598,"resolution":"5","location":"assets","locale":"","width":1280,"height":2000},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3759598,"resolution":"6","location":"assets","locale":"","width":1280,"height":1328},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3759598,"resolution":"7","location":"assets","locale":"","width":1280,"height":3272},"screenshot-8.png":{"filename":"screenshot-8.png","revision":3759598,"resolution":"8","location":"assets","locale":"","width":1280,"height":1464},"screenshot-9.png":{"filename":"screenshot-9.png","revision":3759598,"resolution":"9","location":"assets","locale":"","width":1280,"height":1566}},"screenshots":{"1":"All sports at a glance on the website, with filters for age group and section.","2":"A sport's own page: training times, contacts and the automatically generated upcoming dates \u2013 holidays are blocked automatically.","3":"The club-wide calendar (month or week view) with trainings, events and closures. School holidays block the affected sessions, a cancelled one stays visible and struck through.","4":"The admin overview introduces new users to the plugin and where to maintain content.","5":"Maintaining a sport: the weekly training time, the dates computed from it with cancel and reschedule right there, fields for an extra or a moved session, and the trainers with their permission level.","6":"All sports in the admin area, with their sub-groups, age groups and sections.","7":"Permissions in two layers: six task packages ticked per person and a permission level per sport for trainers, plus the role \"Club management\" for whoever looks after the website.","8":"The trainers as their own page, filtered to the office \"Trainer\".","9":"The board as its own page, filtered to the board offices and grouped by committee. These are the same records as the trainers, so nobody is kept twice.","10":"The events of the club on a page of their own, with the date above each one and filters for event type and age group.","11":"A team with its squad: positions in the order you keep them, shirt numbers, the league with a link to the association's table, and fixtures among the training dates. Only visible once competitive play is switched on.","12":"The setup assistant: it asks what your club has, one thing at a time, and creates exactly that. Name your teams and you enter the training time once - it goes to all of them.","13":"Online booking with start times: people choose a time on the event page, and full ones are greyed out.","14":"Setting up an event: three sections, one switch for online booking, and start times every 15 minutes with a pause every hour. The box on the right shows each start time and how full it is.","15":"The bookings of an event with start time, participant and note, plus cancellation and CSV export."}},"plugin_section":[],"plugin_tags":[416,14241,4776,77,4775],"plugin_category":[37,40],"plugin_contributors":[277212],"plugin_business_model":[],"class_list":["post-357960","plugin","type-plugin","status-publish","hentry","plugin_tags-calendar","plugin_tags-club","plugin_tags-sports","plugin_tags-teams","plugin_tags-training","plugin_category-arts-and-entertainment","plugin_category-calendar-and-events","plugin_contributors-tschiffler","plugin_committers-tschiffler"],"banners":{"banner":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/banner-772x250.png?rev=3671311","banner_2x":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/banner-1544x500.png?rev=3671311","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/icon-128x128.png?rev=3671311","icon_2x":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/icon-256x256.png?rev=3671311","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-1.png?rev=3759598","caption":"All sports at a glance on the website, with filters for age group and section."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-2.png?rev=3759598","caption":"A sport's own page: training times, contacts and the automatically generated upcoming dates \u2013 holidays are blocked automatically."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-3.png?rev=3759598","caption":"The club-wide calendar (month or week view) with trainings, events and closures. School holidays block the affected sessions, a cancelled one stays visible and struck through."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-4.png?rev=3759598","caption":"The admin overview introduces new users to the plugin and where to maintain content."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-5.png?rev=3759598","caption":"Maintaining a sport: the weekly training time, the dates computed from it with cancel and reschedule right there, fields for an extra or a moved session, and the trainers with their permission level."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-6.png?rev=3759598","caption":"All sports in the admin area, with their sub-groups, age groups and sections."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-7.png?rev=3759598","caption":"Permissions in two layers: six task packages ticked per person and a permission level per sport for trainers, plus the role \"Club management\" for whoever looks after the website."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-8.png?rev=3759598","caption":"The trainers as their own page, filtered to the office \"Trainer\"."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-9.png?rev=3759598","caption":"The board as its own page, filtered to the board offices and grouped by committee. These are the same records as the trainers, so nobody is kept twice."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-10.png?rev=3759598","caption":"The events of the club on a page of their own, with the date above each one and filters for event type and age group."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-11.png?rev=3698444","caption":"A team with its squad: positions in the order you keep them, shirt numbers, the league with a link to the association's table, and fixtures among the training dates. Only visible once competitive play is switched on."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-12.png?rev=3759598","caption":"The setup assistant: it asks what your club has, one thing at a time, and creates exactly that. Name your teams and you enter the training time once - it goes to all of them."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-13.png?rev=3759598","caption":"Online booking with start times: people choose a time on the event page, and full ones are greyed out."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-14.png?rev=3759598","caption":"Setting up an event: three sections, one switch for online booking, and start times every 15 minutes with a pause every hour. The box on the right shows each start time and how full it is."},{"src":"https:\/\/ps.w.org\/squadeno-club-sports-manager\/assets\/screenshot-15.png?rev=3759598","caption":"The bookings of an event with start time, participant and note, plus cancellation and CSV export."}],"raw_content":"<!--section=description-->\n<p>https:\/\/youtu.be\/ZfSqKPM_CCQ<\/p>\n\n<p>In most clubs the training schedule exists. Getting it onto the website and keeping it there is the work. The plan lives in a PDF that is two seasons old, the summer break is still not on the calendar, and the person who knows when the hall is free is away for three weeks.<\/p>\n\n<p>Squadeno turns that around. You maintain the training schedule once, say Tuesday at 6 pm in the gym hall from September to June, and every single date is generated from it. Closures and school holidays block the affected sessions automatically. A cancelled session, a moved date or an extra session is handled on the date itself, and the change shows up everywhere that sport appears.<\/p>\n\n<p><strong>Who it is for:<\/strong> clubs with more than a handful of offerings that want every sport to have its own findable page, and that want trainers to keep their own dates current without being handed the keys to the whole site.<\/p>\n\n<h4>What you get<\/h4>\n\n<ul>\n<li><strong>Sports<\/strong> with their own SEO-friendly page; optional subgroups\/disciplines (e.g. athletics \u2192 kids\/youth).<\/li>\n<li><strong>Training times<\/strong> per sport \u2013 weekly, every few weeks or on a set weekday of the month, running open-ended or as a course with an end date or a number of sessions; season- and weather-aware; location as a venue or free text.<\/li>\n<li><strong>Automatic calendar<\/strong>: dates are generated from the training schedule, holidays\/closures block automatically, exceptions (cancelled\/rescheduled\/extra date) and events are added on top.<\/li>\n<li><strong>People<\/strong> with their own profile page: one record per person, whatever they do for the club. An office says what that is (trainer, chair, treasurer), offices can sit in committees, and a person may hold several.<\/li>\n<li><strong>The board on the website<\/strong>: an ordinary page with one block on it, filtered to the board offices and grouped by committee. Anyone who coaches and holds an office is entered once and appears in both places.<\/li>\n<li><strong>Venues<\/strong> with address, GPS, photo and an automatic \"what takes place here\" list.<\/li>\n<li><strong>Events<\/strong> in three short sections (when and where, registration, costs), on the calendar and, with the Events block, on a page of their own.<\/li>\n<li><strong>Competitive play<\/strong> (optional, off by default): teams are subgroups of a sport, each with a <strong>squad<\/strong> (positions kept as club data, order by drag and drop), a <strong>league<\/strong> with a link to the association's table, and <strong>fixtures<\/strong> on a date (opponent, home or away) that reach the calendar subscription. Player profiles are internal per team unless you publish them. There is no field for a result and no table of your own: the association's table is the one that counts, and a match report belongs in a post.<\/li>\n<li><strong>Online booking<\/strong> (optional, off by default): events, single training dates and whole course runs can be booked online, free of charge. An event can also offer start times, for example every 15 minutes with a pause every hour, with the places counted per start time. Members book with their account, guests confirm by email. Places are counted live and a full date takes no more bookings. Participants can cancel up to a deadline you set. Registrations that come in on paper or by phone are entered in the same list and take a place too. The organizer gets the list of participants the day before, the bookings can be exported as CSV, and personal data is deleted automatically after the date.<\/li>\n<li><strong>Permissions in two layers<\/strong>: six task packages ticked per person (own profile, trainer, club administration, dates, public relations, pages and posts), plus a permission level per sport for trainers. Only the club administration may delete, and nobody can pass on a package they do not hold. On top of that the role \"Club management\" stands above it, adding the design and this plugin's settings \u2013 but never on plugins, themes, user accounts or the WordPress settings.<\/li>\n<li><strong>Call it what your club calls it<\/strong>: a football club does not create \"sports\", it creates teams \u2013 and a gymnastics club creates squads. Enter your own wording in the settings and the menu, the list, every button and every message follow. The web addresses stay as they are.<\/li>\n<li><strong>SEO<\/strong>: clean URLs, internal linking and structured data (schema.org).<\/li>\n<li><strong>Performance<\/strong>: computed dates are cached (transient) and refreshed automatically on changes.<\/li>\n<\/ul>\n\n<p>The source language of the plugin is English; a complete German translation (de_DE) is bundled. Public URL slugs default to English and can be localized per site in the settings.<\/p>\n\n<h4>Putting it on the page<\/h4>\n\n<p>Shortcodes (blocks with the same output are also available):<\/p>\n\n<ul>\n<li><code>[csmgr_sports]<\/code> \u2013 overview of all sports with filters (age group\/section).<\/li>\n<li><code>[csmgr_calendar view=\"month\"]<\/code> \u2013 club-wide calendar (<code>view=\"month\"<\/code>, <code>view=\"week\"<\/code> or <code>view=\"list\"<\/code> for the weekly training schedule).<\/li>\n<li><code>[csmgr_upcoming sport=\"ID\" limit=\"5\"]<\/code> \u2013 upcoming dates of a sport.<\/li>\n<li><code>[csmgr_people group=\"true\"]<\/code> \u2013 the people of the club, optionally filtered to one office (<code>function=\"board\"<\/code>) and grouped by committee. Add <code>without_office=\"true\"<\/code> to include records that hold no office.<\/li>\n<li><code>[csmgr_events]<\/code> \u2013 the events of the club: upcoming ones by default, <code>scope=\"past\"<\/code> for a look back, narrowed by <code>type=\"\"<\/code>, <code>age=\"\"<\/code> or <code>sport=\"ID\"<\/code>.<\/li>\n<li><code>[csmgr_venues]<\/code> \u2013 the venues of the club with their addresses.<\/li>\n<\/ul>\n\n<h4>More about the plugin<\/h4>\n\n<p>The project page at <a href=\"https:\/\/squadeno.com\">squadeno.com<\/a> covers the same ground in more detail, with larger screenshots and a German version of the text.<\/p>\n\n<h4>Privacy by default<\/h4>\n\n<p>Maps are optional and switched off by default: no map data is requested from any external server until an administrator selects the map provider \"OpenStreetMap\" in the settings. The Leaflet map library (BSD-2-Clause) is bundled with the plugin and served locally; it is not loaded from a CDN.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin relies on the following third-party services. It does not send any data unless the corresponding feature is used.<\/p>\n\n<p><strong>OpenStreetMap map tiles (optional, disabled by default)<\/strong> \u2013 Maps are off out of the box: the setting \"Map provider\" (Club Sports \u2192 Settings \u2192 Calendar &amp; maps) defaults to \"none\", and while it is set to \"none\" the plugin contacts no map server at all, neither on the public website nor in the WordPress admin area. Only after an administrator explicitly selects \"OpenStreetMap\" does the browser load map tiles directly from the OpenStreetMap tile servers (<code>tile.openstreetmap.org<\/code>), on venue pages with coordinates and on the venue editor screen in wp-admin. This transmits the IP address and the requested map section to OpenStreetMap. As a fallback for visitors without JavaScript, an embedded map from <code>openstreetmap.org<\/code> may be displayed. No tracking, analytics or usage data is collected by the plugin or sent anywhere.\nTerms\/attribution: https:\/\/www.openstreetmap.org\/copyright \u2013 Privacy policy: https:\/\/wiki.osmfoundation.org\/wiki\/Privacy_Policy<\/p>\n\n<p><strong>Nominatim geocoding (OpenStreetMap) (optional, disabled by default)<\/strong> \u2013 Part of the same opt-in: only when the map provider \"OpenStreetMap\" is enabled, and only when an editor clicks the \"Determine coordinates from address\" button in the venue editor, the address typed into the form is sent to the Nominatim service (<code>nominatim.openstreetmap.org<\/code>) to look up coordinates. It is never called on the public website, and it is not called at all while maps are disabled.\nUsage policy: https:\/\/operations.osmfoundation.org\/policies\/nominatim\/ \u2013 Privacy policy: https:\/\/wiki.osmfoundation.org\/wiki\/Privacy_Policy<\/p>\n\n<p><strong>Cloudflare Turnstile (optional, disabled by default)<\/strong> \u2013 Only if an administrator enables Turnstile in the settings, the plugin loads the Turnstile widget script from Cloudflare (<code>challenges.cloudflare.com<\/code>) on pages that show a trainer contact form, and on submission sends the challenge token together with the visitor's IP address to Cloudflare for bot verification. When Turnstile is disabled, no data is sent to Cloudflare.\nTerms of use: https:\/\/www.cloudflare.com\/website-terms\/ \u2013 Privacy policy: https:\/\/www.cloudflare.com\/privacypolicy\/<\/p>\n\n<h3>Source code and build<\/h3>\n\n<p>The plugin ships with the complete, human-readable source of every generated file, plus the tooling needed to regenerate it. Nothing is obfuscated.<\/p>\n\n<p><strong>Editor blocks<\/strong> \u2013 <code>build\/&lt;block&gt;\/index.js<\/code> is generated and minified. The readable ES module source of each of these files is included in the plugin under <code>blocks\/&lt;block&gt;\/index.js<\/code> (same folder name, e.g. <code>blocks\/kalender\/index.js<\/code> \u2192 <code>build\/kalender\/index.js<\/code>).<\/p>\n\n<p>Build tooling: <a href=\"https:\/\/www.npmjs.com\/package\/@wordpress\/scripts\">@wordpress\/scripts<\/a> (webpack + Babel), declared in the bundled <code>package.json<\/code> \/ <code>package-lock.json<\/code>. To regenerate the files from the shipped sources, run inside the plugin folder:<\/p>\n\n<pre><code>npm ci\nnpm run build\n<\/code><\/pre>\n\n<p>which executes <code>wp-scripts build --webpack-src-dir=blocks<\/code> and writes <code>build\/<\/code>.<\/p>\n\n<p><strong>PHP<\/strong> \u2013 All PHP is plain, unminified source under <code>src\/<\/code> (PSR-4, autoloaded via Composer; see the bundled <code>composer.json<\/code> \/ <code>composer.lock<\/code>). <code>assets\/js\/*.js<\/code> and <code>assets\/css\/*.css<\/code> are hand-written and unminified as well.<\/p>\n\n<p><strong>Third-party libraries<\/strong><\/p>\n\n<ul>\n<li>Leaflet 1.9.4 \u2013 interactive maps, BSD-2-Clause. Bundled locally in <code>assets\/vendor\/leaflet\/<\/code> (upstream distribution build, including <code>LICENSE<\/code>); it is never loaded from a CDN. Project and full source: https:\/\/github.com\/Leaflet\/Leaflet \u2013 release used: https:\/\/github.com\/Leaflet\/Leaflet\/releases\/tag\/v1.9.4<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Copy the <code>squadeno-club-sports-manager<\/code> folder into <code>wp-content\/plugins\/<\/code> (or upload the plugin ZIP).<\/li>\n<li>Activate it under \"Plugins\" in the WordPress admin. This creates the content types, roles and default seasons.<\/li>\n<li>Maintain sports, trainers, venues and events under the new menu entries.<\/li>\n<li>Create pages with the shortcodes or blocks (overview, calendar).<\/li>\n<li>Optional: adjust the URL bases (slugs) in the plugin settings, then open Settings \u2192 Permalinks once to refresh the links.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"can%20trainers%20maintain%20their%20own%20content%3F\"><h3>Can trainers maintain their own content?<\/h3><\/dt>\n<dd><p>Yes. Trainers edit only the sports assigned to them as well as their own profile. Each assignment carries one of three levels: full editing of that sport, read-only (listed as a contact, no admin access), or dates only, which allows cancelling, moving and adding a single session and nothing else.<\/p><\/dd>\n<dt id=\"does%20it%20cover%20the%20board%20as%20well%2C%20not%20just%20the%20trainers%3F\"><h3>Does it cover the board as well, not just the trainers?<\/h3><\/dt>\n<dd><p>Yes. There is one record per person, and an office says what someone does: trainer, chair, treasurer, membership records. Offices can sit in committees, so an executive and an extended board come out in the maintained order rather than alphabetically. A board page is an ordinary page with the people block on it, filtered to the board offices.<\/p><\/dd>\n<dt id=\"who%20is%20allowed%20to%20maintain%20what%3F\"><h3>Who is allowed to maintain what?<\/h3><\/dt>\n<dd><p>Permissions come in two layers. Club-wide there are five task packages, ticked in the person record: own profile, trainer, club administration, dates, and public relations. On top of that a trainer is tied to their assigned sports, with a level per assignment. Only the club administration may delete content, and nobody can pass on a package they do not hold themselves.<\/p><\/dd>\n<dt id=\"our%20club%20plays%20in%20a%20league.%20does%20the%20plugin%20cover%20that%3F\"><h3>Our club plays in a league. Does the plugin cover that?<\/h3><\/dt>\n<dd><p>Yes, once you switch on competitive play in the settings. A team is a subgroup of a sport, so the first team and the under-13s each get their own page, training times, squad, league and fixtures, and everything shows up in the same calendar. A squad row is a name, a shirt number and a position; linking it to a person record is optional, and whether those profiles are public is decided per team, with internal as the default. The plugin keeps dates, not reporting: there is no result field and no table of your own.<\/p><\/dd>\n<dt id=\"how%20are%20holidays%20handled%3F\"><h3>How are holidays handled?<\/h3><\/dt>\n<dd><p>Central closures automatically hide regular trainings within the period; events (e.g. holiday care) remain visible.<\/p><\/dd>\n<dt id=\"is%20the%20plugin%20compatible%20with%20caching%20plugins%3F\"><h3>Is the plugin compatible with caching plugins?<\/h3><\/dt>\n<dd><p>Yes. Dates are cached internally and invalidated whenever something changes, so a page or object cache on top only makes it faster. The one exception is online booking: a page that shows a booking form carries the places still free and a security token, so leave those event and sport pages out of a full-page cache.<\/p><\/dd>\n<dt id=\"can%20people%20book%20a%20place%20online%3F\"><h3>Can people book a place online?<\/h3><\/dt>\n<dd><p>Yes, once you switch on \"Online booking\" under Club Sports \u2192 Settings \u2192 Bookings. On an event you then set \"Registration\" to \"online booking on this page\"; on a sport you set \"Registration\" the same way, then choose single dates or whole course runs and the number of places. You also decide who may book: members with an account, or everyone. Guests get an email with a link and confirm the booking on the page; until they do, their place is held for one hour. Bookings are always free of charge. If there is a fee, it stays on the page as before and is paid outside the plugin.<\/p><\/dd>\n<dt id=\"does%20the%20organizer%20get%20a%20list%20of%20participants%3F\"><h3>Does the organizer get a list of participants?<\/h3><\/dt>\n<dd><p>Yes. Enter the organizer's email address on the event or the sport. On the evening before every date that address gets the list of confirmed bookings, at 6 pm unless you choose another time under Settings \u2192 Bookings. After that every new booking and every cancellation follows by email. A tick below the address sends an email for every booking from the start. WordPress sends scheduled emails when someone visits the site, so on a quiet site the list can arrive a little later; a real cron job on the server makes it punctual.<\/p><\/dd>\n<dt id=\"how%20is%20the%20booking%20form%20protected%20against%20abuse%3F\"><h3>How is the booking form protected against abuse?<\/h3><\/dt>\n<dd><p>Like the contact forms: a security token, a hidden honeypot field, a time trap and, if you switch it on, Cloudflare Turnstile. On top of that each IP address and each email address can only try a few times per hour, so nobody can use the form to send mail to strangers or fill a date with invented names. If everyone may book, Turnstile is a good idea. Behind a proxy or CDN that hides the visitor's address, the limits apply to the proxy, so turn Turnstile on there as well.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20the%20booking%20data%3F\"><h3>What happens to the booking data?<\/h3><\/dt>\n<dd><p>The plugin stores the name and email address of the person who booked and, for offers open to everyone, the name of the participant and an optional note. The data is deleted automatically a number of days after the booked date (30 by default, adjustable in the settings); a guest booking that was never confirmed goes after one day. This only happens while the plugin is active: a deactivated plugin deletes nothing, and the data stays until you activate it again or delete it yourself. Requests for export or erasure go through Tools \u2192 Export Personal Data and Erase Personal Data. The plugin suggests a paragraph for your privacy policy. Bookings live in a table of their own (<code>wp_csmgr_buchungen<\/code>). Like every other piece of club data it stays in place when the plugin is deleted, so that moving to a new version never loses anything; if you remove the plugin for good, delete that table with your database tool.<\/p><\/dd>\n<dt id=\"in%20which%20languages%20is%20the%20plugin%20available%3F\"><h3>In which languages is the plugin available?<\/h3><\/dt>\n<dd><p>The interface is English by default and ships with a complete German translation (de_DE). Additional languages can be added via the bundled .pot file.<\/p><\/dd>\n<dt id=\"can%20the%20urls%20be%20in%20another%20language%3F\"><h3>Can the URLs be in another language?<\/h3><\/dt>\n<dd><p>Yes. The URL bases (e.g. <code>sports<\/code>, <code>people<\/code>) default to English and can be changed per site in the plugin settings, for example to German (<code>sportarten<\/code>, <code>personen<\/code>). After changing them, open Settings \u2192 Permalinks once.<\/p><\/dd>\n<dt id=\"someone%20should%20maintain%20everything%20about%20the%20club%2C%20including%20the%20pages.%20which%20packages%3F\"><h3>Someone should maintain everything about the club, including the pages. Which packages?<\/h3><\/dt>\n<dd><p>For a board member, tick \"Club administration\" and \"Pages and posts\". Together they cover the club data (sports, people, venues, events, base data) and the website pages that present it, including the overview pages.<\/p>\n\n<p>If that person also looks after the website itself, give them the role \"Squadeno: Club management\" under Users instead. It adds the design of the theme and this plugin's settings. In both cases plugins, themes, user accounts and the WordPress settings stay with the administrator, so nobody can lock you out or change what the site runs on.<\/p><\/dd>\n<dt id=\"how%20do%20new%20trainers%20get%20an%20account%3F\"><h3>How do new trainers get an account?<\/h3><\/dt>\n<dd><p>An administrator creates it, or the person registers themselves and you link the account in their person record afterwards. No role of this plugin can create accounts or hand out roles: in WordPress, whoever may do both can make themselves an administrator.<\/p><\/dd>\n<dt id=\"how%20do%20i%20give%20someone%20permissions%20without%20putting%20them%20on%20the%20website%3F\"><h3>How do I give someone permissions without putting them on the website?<\/h3><\/dt>\n<dd><p>Create a person record for them, link their user account and tick the packages \u2013 but give the record no office. A record without an office stays out of the people list, so nobody shows up on the website who is not meant to be there. If you would rather have no record at all, an administrator can add a second role on the command line: <code>wp user add-role &lt;user&gt; csmgr_vorstand<\/code>.<\/p><\/dd>\n<dt id=\"i%20built%20my%20own%20overview%20page%20with%20a%20block.%20can%20i%20use%20it%20instead%20of%20the%20plugin%27s%20list%3F\"><h3>I built my own overview page with a block. Can I use it instead of the plugin's list?<\/h3><\/dt>\n<dd><p>Yes, and you should. The plugin creates a plain list of every entry at <code>\/sports\/<\/code>, <code>\/people\/<\/code> and so on. Most clubs would rather have their own page with the matching block, an introduction and their own wording. Pick that page under Settings \u2192 Overview pages, and the plain list redirects to it permanently, while links pointing at the list point to your page instead. Visitors then have one overview instead of two, and themes that build a breadcrumb follow along. Leave the setting empty and nothing changes.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.15.0<\/h4>\n\n<ul>\n<li>New: <strong>online booking<\/strong>, optional and off by default. Switch it on under Settings \u2192 Bookings, and the setup assistant asks about it too.<\/li>\n<li>Events: set \"Registration\" to \"online booking on this page\". The event page shows the places still free and a booking form instead of the PDF. Places and deadline are the fields you already know; the deadline is when booking closes. Everyone may book unless you choose members only.<\/li>\n<li>Registrations on paper, by phone or by email can be entered in the booking list of an event or a training. They take a place like an online booking. Events with a form to download and trainings without online booking then show on their page how many places are left. A tick sends the participant the confirmation email with the cancellation link.<\/li>\n<li>List of participants: the organizer's email address on an event or sport gets the list on the evening before every date, at 6 pm or the time you set under Settings \u2192 Bookings. After that each booking and cancellation follows by email; a tick sends an email for every booking from the start.<\/li>\n<li>Start times within an event: give an event on one day an interval, for example every 10 minutes from 13:00 to 15:00, and optionally a pause rule. The places then count per start time, people choose one on the event page, and anyone may book several. The list of participants shows every start time, the free ones as well. If the event moves, the bookings move with it.<\/li>\n<li>The event form is easier to fill in: it has sections for date and place, registration and costs, and shows only the fields that apply. There is no separate \"time mode\" any more. Without an end date an event lasts one day; a tick marks a multi-day event that runs through with an overnight stay. The amount only appears with a fee.<\/li>\n<li>Training: a sport or subgroup can be bookable by single date (with a horizon, four weeks ahead by default) or by whole course run. A course run is formed by training times with the same start date, for example Tuesday and Thursday from 6 October. Like an event, a training can also take registrations only by hand, without a form on the page.<\/li>\n<li>Members book with the details of their account. Offers open to everyone ask for the participant's name as well (parents book once per child) and offer a note field. Guests confirm their booking with a link from an email; the link opens the page, and a button confirms. Nothing happens by just opening the link.<\/li>\n<li>Self-cancellation up to a number of hours before the start (24 by default), or switched off. For a course the deadline counts from its first date.<\/li>\n<li>If a date is cancelled, an event goes to the trash or a course run disappears, the bookings lapse and the participants are told. If the date moves, the booking stays and they get the new time.<\/li>\n<li>Under Club Sports \u2192 Bookings the organizers see every booking, cancel one with an email to the participant, and export the list as CSV. Events show how many places are taken, and warn if there are more bookings than places after the number was lowered.<\/li>\n<li>New permission \"manage bookings\" for the administrator, club management, club administration, dates and trainers. A trainer sees the bookings of the sports they manage or cancel dates for.<\/li>\n<li>Personal data is deleted automatically after the date (30 days by default) and can be exported or erased with the WordPress privacy tools. A guest booking that was never confirmed is deleted after one day.<\/li>\n<li>Booking emails are always sent as plain text. Password-protected events and sports can only be booked once the password has been entered.<\/li>\n<li>Calendar export (iCal): a multi-day event without the overnight tick now repeats every day from its start to its end date. Before, an event with no time mode set was exported as one block.<\/li>\n<li>Training times keep an identity of their own when you add, remove or reorder rows. Nothing changes on your pages; the update adds it to the training times you already have.<\/li>\n<li>Fix: the age group and section filters of the sports overview now find a sport when only one of its subgroups carries the age group or section. The card of a sport also lists the age groups and sections of its subgroups.<\/li>\n<li>Calendar: the month view can show each entry on two lines, start and end time above the title. Choose it under Settings \u2192 Calendar &amp; maps; one line stays the default.<\/li>\n<li>Sections can have a colour. Calendar entries of their sports then appear in it, and a subgroup takes the colour of the sport above it. Events keep their own colour and get a stripe in the section colour.<\/li>\n<li>Event types can have a colour too. Events of that type appear in it in the calendar, and the type badge on the event page and in the event list takes it on. Without a colour an event keeps the event colour from the settings.<\/li>\n<li>Sections can get a name of your own, like sports. Enter \"Category\" under Settings \u2192 Naming &amp; URL addresses, and the menu and the filters say \"All categories\". The web address stays the same.<\/li>\n<li>Fix: choosing a section or age group in a calendar on the front page filtered nothing and opened the archive of that section instead. It now filters the calendar.<\/li>\n<li>Fix: on a phone, the calendar and the table of training times no longer make the whole page wider than the screen. They scroll within their own box.<\/li>\n<li>Fix: the filters above the calendar and the overviews now also work on sites with a strict Content Security Policy. Choosing an entry used to do nothing there.<\/li>\n<li>Fix: with debugging switched on, every page of the admin area logged the notice \"Translation loading \u2026 triggered too early\".<\/li>\n<\/ul>\n\n<h4>1.14.0<\/h4>\n\n<ul>\n<li>New: <strong>the contact area on sport pages is yours to choose<\/strong>. Not every club wants a contact form. Under Settings \u2192 Contact you now pick one of four: the contact form as before, no contact area at all, your note on its own, or your note beside one person.<\/li>\n<li>\"Note + contact person\" shows that person with their photo, name and email address \u2013 the same person on every sport page, chosen once in the settings. Photo and address stay with the person.<\/li>\n<li>The form remains the default, so an existing site looks exactly as it did after the update.<\/li>\n<li>Person pages are unaffected: each person still decides for themselves whether their page offers a form.<\/li>\n<\/ul>\n\n<h4>1.13.0<\/h4>\n\n<ul>\n<li>New: <strong>training times that are not weekly<\/strong>. A training time can now repeat every two, three or four weeks, or fall on a set weekday of the month \u2013 the second Saturday, the last Friday. Weekly stays the default, and nothing changes for the training times you already have.<\/li>\n<li>New: <strong>training times that end<\/strong>. A course can run until a date, or for a set number of sessions \u2013 ten and then no more. After that the calendar is quiet.<\/li>\n<li>A closure does not use up one of those sessions: if the hall is shut over the holidays, the course runs a week longer. A session you cancel yourself does count \u2013 it was on the calendar. Add an extra date to make it up.<\/li>\n<li>\"Starts on\" is the day a training time first takes place. For \"every \u2026 weeks\" it also decides which weeks count, and nothing is generated before it.<\/li>\n<li>A training time whose end has passed drops out of the table on the sport's page. The entry itself stays, so you can run the course again next term.<\/li>\n<li>Two patterns on the same weekday (\"first and third Saturday\") are two rows.<\/li>\n<li>The rhythm and the period appear on the sport's page next to the day, and the calendar, the upcoming dates and the calendar subscription all follow them.<\/li>\n<li>Events are unchanged: a series of events is still a series of entries. That is a separate matter, and it is being looked at.<\/li>\n<\/ul>\n\n<h4>1.12.0<\/h4>\n\n<ul>\n<li>New: <strong>competitive play<\/strong> \u2013 optional and off by default. Switch it on and teams become subgroups of a sport, each with a squad, a league field with a link to the association's table, and fixtures on a date with an opponent and home or away. Player profiles stay internal per team unless you publish them. There is no field for a result and no table of your own: the association's table is the one that counts, and a match report belongs in a post.<\/li>\n<li>New: <strong>positions<\/strong> as base data you can sort \u2013 goalkeeper before defence, not alphabetically. They apply to the whole club and order every squad.<\/li>\n<li>New: a <strong>setup assistant<\/strong> that asks what your club needs and creates it. It takes three paths: dates and training times only, competitive play with one sport (your age groups become the sports), or competitive play with several sports. It only ever creates what you typed, and it disappears once your club is set up.<\/li>\n<li>New: <strong>call your sports what your club calls them<\/strong>. A football club enters \"Team\", a gymnastics club \"Squad\", a dance school \"Course\" \u2013 the menu, the list and every button follow. Your web addresses stay as they are.<\/li>\n<li>New: <strong>age groups now come with a starter set<\/strong> (children, youth, adults, seniors), sorted from young to old rather than alphabetically. Rename or remove what does not fit. Existing sites keep the age groups they have.<\/li>\n<li>The people page now shows \"Plays in these teams\" for anyone linked from a squad \u2013 as long as that team publishes its profiles.<\/li>\n<li>Squad rows can be reordered by dragging them, and choosing a person record is enough: you no longer have to type the name as well.<\/li>\n<li>After fourteen days of real use, the plugin asks once whether you would leave a review. Click it away and it stays away.<\/li>\n<li>Fixed: a trainer on the lowest permission level (\"dates only\") can no longer change the section and age group of a sport, set a password on it, take over its authorship or open its comments through Quick Edit. The restrictions were tied to the editor form and the block editor, and Quick Edit goes a third way. Hiding the \"Quick Edit\" link was never the protection. Reported by <a href=\"https:\/\/github.com\/justdoston\">Doston Abdullayev<\/a>.<\/li>\n<li>Nothing changes for an existing site unless you switch competitive play on.<\/li>\n<\/ul>\n\n<h4>1.11.0<\/h4>\n\n<ul>\n<li>New user role \"Squadeno: Club management\". It stands above the club administration package: the same club data, plus the pages and posts, the design of the theme and the settings of this plugin. Assign it under Users like any other role. It cannot install or activate plugins and themes, cannot open the WordPress settings, and cannot create user accounts or hand out roles \u2013 each of those would be a way to take the site over.<\/li>\n<li>The plugin's settings page no longer asks for the WordPress capability \"manage_options\" but for one of its own. That capability is what the new role carries; administrators keep access exactly as before. This also means the settings page can be opened without opening every other setting of the site along with it.<\/li>\n<li>The club administration may now maintain events as well. Until now events were in the \"Dates\" package only, so the package that says \"club administration\" could not put the summer party on the calendar. \"Dates\" stays as the small package for someone who maintains nothing but dates.<\/li>\n<li>New permission package \"Pages and posts\": the WordPress editor role, ticked like the others on the person record. Overview pages are ordinary WordPress pages, so somebody has to be allowed to edit them. Club administration plus this package covers the club and the pages that show it, but still no plugins, themes, users or settings.<\/li>\n<li>Please note: a person without an office no longer appears in the people list on its own. An office is what makes someone public, and a record without one is usually an internal one \u2013 for instance an account that only exists so that someone can be given permissions. Tick \"Include people without an office\" in the block (or <code>without_office=\"true\"<\/code>) to get the old behaviour back.<\/li>\n<li>New block \"Events\" and shortcode [csmgr_events]: the events of the club on a page of their own, outside the calendar. Upcoming events by default, a look back at past ones if you want, as cards or as a list.<\/li>\n<li>An event that is still running stays on the page until its last day. A holiday club from Monday to Friday used to disappear on Tuesday from any list that went by the starting date.<\/li>\n<li>The Events block can be narrowed down: one event type, one age group, or the events of a single sport \u2013 including the sport whose page the block sits on. What you fix in the block, visitors cannot change.<\/li>\n<li>Visitors can filter the list by event type and age group without JavaScript, and the filtered view has an address of its own that can be bookmarked and shared.<\/li>\n<li>The age group works the way it does in the calendar: the one on the event counts more than the one on its sport, and an event without one of its own keeps the one from its sport.<\/li>\n<li>New block \"Venues\" and shortcode [csmgr_venues]: the venues of the club with their addresses, as cards or as a list.<\/li>\n<li>Both blocks fit the overview pages: pick your own page under Settings \u2192 Overview pages, put the matching block on it, and the plain archive redirects there.<\/li>\n<\/ul>\n\n<h4>1.10.2<\/h4>\n\n<ul>\n<li>Fixed: the calendar's age group filter now finds events. A holiday club with the age groups \"children\" and \"youth\" disappeared from the calendar as soon as the filter was set to exactly those age groups, because the filter only looked at the age group of the sport, and club-wide events were left out whenever a filter was set.<\/li>\n<li>The age group on an event now counts more than the one on its sport: a children's event at an adults' sport shows up under \"children\", and the parents' evening at the children's gymnastics does not. An event without an age group of its own keeps the one from its sport.<\/li>\n<li>Fixed: an event without times now shows up in the calendar as an all-day date. A holiday club that runs from Monday to Friday has no times, and until now such an event was missing from every calendar view. The week view, the training schedule and \"Upcoming dates\" label it \"All day\", the month grid shows the name without a time, and the calendar export makes it a date entry.<\/li>\n<\/ul>\n\n<h4>1.10.1<\/h4>\n\n<ul>\n<li>In the People block, the photo leads to the person's page as well. Visitors click the picture, not the name below it.<\/li>\n<li>The editor shows the blocks the way the page does. Until now the plugin's stylesheet was missing in the editor, so a block of people stood there as a plain list and the settings for layout and columns had no visible effect.<\/li>\n<li>The People list now carries the plugin's spacing and colours on any page. On a page that holds nothing else from the plugin, the cards came without spacing, surface or frame.<\/li>\n<li>The People block explains its settings: \"Columns\" sets how wide a card is, not how many are shown, and \"Layout\" says what cards and list look like.<\/li>\n<\/ul>\n\n<h4>1.10.0<\/h4>\n\n<ul>\n<li>New setting \"Overview pages\": pick the page that visitors should see as the overview of sports, people, events or venues. The plugin's own archive then redirects there, and links to the archive point to your page, so there is one overview instead of two. Leave it empty to keep the built-in archive.<\/li>\n<li>The URL base for people is \"people\" now. Sites that update keep their existing base, so no link breaks.<\/li>\n<li>The update fills in the offices: every existing person record gets the office \"Trainer\", because until now every record of this type was one.<\/li>\n<li>A starter set of offices comes with the plugin: trainer, executive board with chair, deputy chair and treasurer, extended board with secretary and board member. Rename or remove what does not fit.<\/li>\n<li>One record per person: the entry type is called People now, and it carries trainers, board members and anyone else with a role in the club.<\/li>\n<li>New base data: functions. The offices of the club, with committees as parent entries and a sort order, because alphabetically the treasurer comes before the chair.<\/li>\n<li>New block \"People\" and shortcode [csmgr_people]: shows the board or any other group, as cards or as a list, grouped by committee if you want.<\/li>\n<li>Permissions are assigned per person now: own profile, trainer, club administration, dates, and public relations.<\/li>\n<li>Permission changes now also reach sites that update the plugin, not only fresh installations.<\/li>\n<li>Closures and seasons come with their own permissions instead of borrowing those for blog posts.<\/li>\n<li>Trainers can assign sections and age groups to the sports they maintain.<\/li>\n<li>Editors no longer maintain closures, seasons, sections, age groups and event types. Administrators do, and site owners who need help there can grant the capabilities with a role plugin.<\/li>\n<li>Fixed: everyone with the right permission can add content again. Bundling the club data under one menu had blocked \"Add new\" for sports, people, events, venues, closures and seasons for everyone except administrators.<\/li>\n<li>Fixed: the seasons open from the base data tabs again instead of showing an error page.<\/li>\n<li>Fixed: trainers with the \"cancel dates\" level can no longer change the sections and age groups of a sport. The restriction only applied to the classic form, not to the editor's own save path.<\/li>\n<li>Fixed: adding content follows the task packages. Until now every package could publish sports and people it was not allowed to edit or remove afterwards. Adding people, sports, venues and seasons belongs to club administration, events and closures to the dates package.<\/li>\n<li>People without an office get their own heading in the People block instead of appearing under the last committee.<\/li>\n<li>The roles this plugin adds carry its name now: \"Squadeno: Trainer\" instead of \"Trainer\". In the user list they sat next to \"Editor\" with nothing saying where they came from.<\/li>\n<li>The default seasons are created in the language of the site. Seasons that already exist keep their names.<\/li>\n<li>The week view of the calendar is a list now, one day per row with its date, and today is highlighted. As a grid it only scrolled sideways on narrow screens.<\/li>\n<li>A note on a training time appears under the name of the sport, not next to the venue, where it read like a detail of the venue.<\/li>\n<li>The person page shows the offices directly under the name, each with its committee, and an office comes before the trainer role.<\/li>\n<li>\"Roles &amp; permissions\" now also explains how permissions are assigned, in four steps with a diagram.<\/li>\n<\/ul>\n\n<h4>1.9.0<\/h4>\n\n<ul>\n<li>New calendar view <code>view=\"list\"<\/code>: the training schedule of the week, grouped by weekday.<\/li>\n<li>Sub-categories in the calendar now name their main category, for example Gymnastics &gt; Kids gymnastics.<\/li>\n<li>Settings: pick which calendar views the site offers (month, week, list).<\/li>\n<li>Cancelled dates in the training schedule are struck through instead of carrying a label.<\/li>\n<li>Admin area: closures, seasons, sections, age groups and event types moved into one menu entry \"Base data\" with tabs.<\/li>\n<li>The plugin directory page now offers a live preview: try the plugin with demo data, right in the browser.<\/li>\n<\/ul>\n\n<h4>1.8.1<\/h4>\n\n<ul>\n<li>Clearer wording on the plugin directory page, with a link to the project page.<\/li>\n<li>German translation of the directory page, in both the informal and the formal address.<\/li>\n<li>No changes to the plugin itself.<\/li>\n<\/ul>\n\n<h4>1.8.0<\/h4>\n\n<ul>\n<li>First public release in the WordPress plugin directory.<\/li>\n<\/ul>","raw_excerpt":"Training times, people and events for sports clubs, with online booking, teams and fixtures. The calendar keeps itself current.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/357960","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=357960"}],"author":[{"embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/tschiffler"}],"wp:attachment":[{"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=357960"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=357960"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=357960"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=357960"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=357960"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/da.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=357960"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}