{"id":308728,"date":"2026-10-18T18:12:21","date_gmt":"2026-10-18T18:12:21","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/swiss-floorball-api\/"},"modified":"2026-10-18T20:25:51","modified_gmt":"2026-10-18T20:25:51","slug":"swiss-floorball-api","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/swiss-floorball-api\/","author":21051162,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.1.2","stable_tag":"2.1.2","tested":"7.1.3","requires":"5.0","requires_php":"7.4","requires_plugins":null,"header_name":"Swiss Floorball API","header_author":"Flavio Waser","header_description":"Ein kleines Plugin, welches erm\u00f6glicht, die aktuellen Daten der Swiss Floorball API abzufragen und auf der Webseite darzustellen.","assets_banners_color":"c7a4a5","last_updated":"2026-10-18 20:25:51","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/www.paypal.me\/flaviowaser","header_plugin_uri":"https:\/\/flaviowaser.ch","header_author_uri":"https:\/\/flaviowaser.ch\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":48,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"2.1.1":{"tag":"2.1.1","author":"flaviowaser","date":"2026-10-18 18:12:08","revision":3753435},"2.1.2":{"tag":"2.1.2","author":"flaviowaser","date":"2026-10-18 20:25:51","revision":3753590}},"upgrade_notice":{"2.0.0":"<p>Breaking changes: the API source, the calendar feed, shortcode attributes and the table markup changed. Read the upgrade guide at https:\/\/github.com\/flawas\/floorball-api-for-swiss-unihockey\/blob\/main\/docs\/migration.md before updating.<\/p>"},"ratings":[],"assets_icons":{"icon-256x256.png":{"filename":"icon-256x256.png","revision":3753590,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-772x250.jpeg":{"filename":"banner-772x250.jpeg","revision":3753590,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250},"banner-914x298.png":{"filename":"banner-914x298.png","revision":3753590,"resolution":"914x298","location":"assets","locale":"","width":914,"height":298}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["2.1.1","2.1.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3753590,"resolution":"1","location":"assets","locale":"","width":1280,"height":800},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3753590,"resolution":"2","location":"assets","locale":"","width":1280,"height":800},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3753590,"resolution":"3","location":"assets","locale":"","width":1280,"height":800},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3753590,"resolution":"4","location":"assets","locale":"","width":1280,"height":800},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3753590,"resolution":"5","location":"assets","locale":"","width":1280,"height":800},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3753590,"resolution":"6","location":"assets","locale":"","width":1280,"height":800},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3753590,"resolution":"7","location":"assets","locale":"","width":1280,"height":800},"screenshot-8.png":{"filename":"screenshot-8.png","revision":3753590,"resolution":"8","location":"assets","locale":"","width":1280,"height":800},"screenshot-9.png":{"filename":"screenshot-9.png","revision":3753590,"resolution":"9","location":"assets","locale":"","width":330,"height":484}},"screenshots":{"1":"Dashboard: Club, season and API source at a glance, with navigation tiles.","2":"Settings: API source (free or Partner API), club, season, theme and table style.","3":"Club overview: Teams and games of your club.","4":"Club games: Games week by week with previous\/next buttons (<code>[swfl-club-games]<\/code>).","5":"Rankings: League table of a group (<code>[swfl-rankings]<\/code>).","6":"League games: Games of a league and group, round by round (<code>[swfl-league-games]<\/code>).","7":"Mobiliar topscorers: Cards for the league or one club (<code>[swfl-mobiliar-topscorer]<\/code>).","8":"Calendar: Upcoming games with an iCalendar subscription link (<code>[swfl-calendars]<\/code>).","9":"Mobile: The widgets follow the width of their container, without horizontal scrolling."}},"plugin_section":[],"plugin_tags":[1556,287405,4776,287406,287407],"plugin_category":[37],"plugin_contributors":[287408],"plugin_business_model":[],"class_list":["post-308728","plugin","type-plugin","status-publish","hentry","plugin_tags-api","plugin_tags-floorball","plugin_tags-sports","plugin_tags-swiss-floorball","plugin_tags-unihockey","plugin_category-arts-and-entertainment","plugin_contributors-flaviowaser","plugin_committers-flaviowaser"],"banners":{"banner":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/banner-772x250.jpeg?rev=3753590","banner_2x":false,"banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/icon-256x256.png?rev=3753590","icon_2x":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/icon-256x256.png?rev=3753590","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-1.png?rev=3753590","caption":"Dashboard: Club, season and API source at a glance, with navigation tiles."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-2.png?rev=3753590","caption":"Settings: API source (free or Partner API), club, season, theme and table style."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-3.png?rev=3753590","caption":"Club overview: Teams and games of your club."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-4.png?rev=3753590","caption":"Club games: Games week by week with previous\/next buttons (<code>[swfl-club-games]<\/code>)."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-5.png?rev=3753590","caption":"Rankings: League table of a group (<code>[swfl-rankings]<\/code>)."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-6.png?rev=3753590","caption":"League games: Games of a league and group, round by round (<code>[swfl-league-games]<\/code>)."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-7.png?rev=3753590","caption":"Mobiliar topscorers: Cards for the league or one club (<code>[swfl-mobiliar-topscorer]<\/code>)."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-8.png?rev=3753590","caption":"Calendar: Upcoming games with an iCalendar subscription link (<code>[swfl-calendars]<\/code>)."},{"src":"https:\/\/ps.w.org\/swiss-floorball-api\/assets\/screenshot-9.png?rev=3753590","caption":"Mobile: The widgets follow the width of their container, without horizontal scrolling."}],"raw_content":"<!--section=description-->\n<p><strong>Independent project:<\/strong> This plugin is developed and maintained by Flavio Waser as a personal open-source project. It is not affiliated with, endorsed by or sponsored by Swiss Unihockey (swissunihockey.ch). It only reads data from their public API.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li><strong>Plug &amp; Play:<\/strong> Configure your Club ID once and get instant access to your club's data.<\/li>\n<li><strong>Calendars:<\/strong> Display upcoming games for teams or entire clubs. Includes <strong>ICS subscription links<\/strong> so visitors can add games to their personal calendars.<\/li>\n<li><strong>Rankings:<\/strong> Always up-to-date league tables for any group or league.<\/li>\n<li><strong>Statistics:<\/strong> Show topscorer lists and detailed game events.<\/li>\n<li><strong>Player Profiles:<\/strong> Showcase individual player stats and national team members.<\/li>\n<li><strong>Responsive:<\/strong> Built-in responsive tables that look great on mobile devices, with dark mode support.<\/li>\n<li><strong>Cached:<\/strong> API responses are cached for one hour, so your pages stay fast and the API is not hammered.<\/li>\n<li><strong>ID finder:<\/strong> Admin helper pages let you browse leagues, clubs, games and seasons and copy the IDs you need for shortcodes.<\/li>\n<\/ul>\n\n<h4>Shortcodes<\/h4>\n\n<p>Use these shortcodes in any Page or Post to display data. Attributes that are left out fall back to the values from the settings page where applicable.<\/p>\n\n<p><strong>Club &amp; Team Data<\/strong><\/p>\n\n<ul>\n<li><code>[swfl-club-teams]<\/code> \u2013 List all teams in your club.<\/li>\n<li><code>[swfl-club-games]<\/code> \u2013 Club games, shown week by week with previous\/next week buttons. Attributes: <code>season<\/code>, <code>limit<\/code>, <code>offset<\/code>, <code>show_upcoming<\/code>, <code>show_pagination<\/code>.<\/li>\n<li><code>[swfl-team-games]<\/code> \u2013 Games of one team, paged around the next game. Attributes: <code>team_id<\/code>, <code>season<\/code>, <code>page_size<\/code> (default 4), <code>limit<\/code>, <code>offset<\/code>, <code>show_upcoming<\/code>, <code>show_pagination<\/code>.<\/li>\n<li><code>[swfl-club-team-games]<\/code> \u2013 All teams of a club with a team selector and their games. Attributes: <code>season<\/code>, <code>page_size<\/code> (default 4).<\/li>\n<li><code>[swfl-next-game]<\/code> \u2013 The next upcoming game of the club, or of one team. Attributes: <code>team_id<\/code>, <code>season<\/code>, <code>limit<\/code> (default 1, max 10).<\/li>\n<li><code>[swfl-last-result]<\/code> \u2013 The most recent result of the club, or of one team. Attributes: <code>team_id<\/code>, <code>season<\/code>, <code>limit<\/code> (default 1, max 10).<\/li>\n<li><code>[swfl-teams]<\/code> \u2013 List teams.<\/li>\n<li><code>[swfl-clubs]<\/code> \u2013 List all clubs.<\/li>\n<li><code>[swfl-calendars]<\/code> \u2013 Show upcoming games + ICS link. Attributes: <code>team_id<\/code>, <code>club_id<\/code>, <code>season<\/code>, <code>league<\/code>, <code>game_class<\/code>, <code>group<\/code>.<\/li>\n<\/ul>\n\n<p><strong>Example:<\/strong><\/p>\n\n<pre><code>[swfl-calendars team_id=\"427892\"]\n[swfl-club-team-games season=\"2025\" page_size=\"6\"]\n<\/code><\/pre>\n\n<p>The games, ranking and Mobiliar topscorer shortcodes mirror the official <a href=\"https:\/\/github.com\/swissunihockey\/swissunihockey-webcomponents\">Swiss Unihockey web components<\/a> (<code>uniho-*<\/code>). Attribute names with hyphens (<code>game-class<\/code>, <code>club-id<\/code>, <code>page-size<\/code>) work as well as the underscore forms. Week and page navigation need JavaScript; without it the initial view is still shown.<\/p>\n\n<p><strong>League &amp; Rankings<\/strong><\/p>\n\n<ul>\n<li><code>[swfl-league-games]<\/code> \u2013 Games of a league and group, navigable round by round; playoff series are grouped. Attributes: <code>game_class<\/code>, <code>league<\/code>, <code>season<\/code>, <code>group<\/code>.<\/li>\n<li><code>[swfl-rankings]<\/code> \u2013 Show the ranking table. Attributes: <code>season<\/code>, <code>league<\/code>, <code>game_class<\/code>, <code>group<\/code> (name, e.g. <code>Gruppe 1<\/code>).<\/li>\n<li><code>[swfl-mobiliar-topscorer]<\/code> \u2013 The Mobiliar topscorers as cards: of the whole league, or of one club when <code>club_id<\/code> is set. Attributes: <code>club_id<\/code>, <code>season<\/code>.<\/li>\n<li><code>[swfl-topscorers]<\/code> \u2013 Show the topscorer list. Attributes: <code>season<\/code>, <code>league<\/code>, <code>game_class<\/code>, <code>group<\/code>, <code>limit<\/code>, <code>offset<\/code>, <code>show_pagination<\/code>.<\/li>\n<li><code>[swfl-groups]<\/code> \u2013 List available groups. Attributes: <code>season<\/code>, <code>league<\/code>, <code>game_class<\/code>.<\/li>\n<li><code>[swfl-cups]<\/code> \u2013 List available cups.<\/li>\n<\/ul>\n\n<p><strong>Example:<\/strong><\/p>\n\n<pre><code>[swfl-rankings season=\"2025\" league=\"3\" game_class=\"21\" group=\"Gruppe 1\"]\n<\/code><\/pre>\n\n<p><strong>Players &amp; Games<\/strong><\/p>\n\n<ul>\n<li><code>[swfl-player]<\/code> \u2013 Show player profile. Attributes: <code>player_id<\/code>.<\/li>\n<li><code>[swfl-game-events]<\/code> \u2013 Show events (goals, penalties) of a game. Attributes: <code>game_id<\/code>.<\/li>\n<li><code>[swfl-national-players]<\/code> \u2013 List national team players.<\/li>\n<\/ul>\n\n<h4>Customization<\/h4>\n\n<p>The plugin uses standard CSS classes prefixed with <code>.sfa-<\/code>, scoped to <code>.swiss-floorball-plugin<\/code>. You can override them in your theme's <code>style.css<\/code> or the Customizer to match your site's branding.<\/p>\n\n<h4>Icons<\/h4>\n\n<p>The plugin ships inline SVG icons from <a href=\"https:\/\/github.com\/google\/material-design-icons\">Material Symbols<\/a> (Outlined, filled, Apache License 2.0, see <code>THIRD-PARTY.md<\/code>). No fonts or external requests are used.<\/p>\n\n<ul>\n<li><strong>Setting:<\/strong> Show or hide all icons under <strong>Settings &gt; Swiss Floorball API &gt; Show icons<\/strong> (option <code>swfl_show_icons<\/code>, on by default).<\/li>\n<li><strong>Styling:<\/strong> Use the CSS variables <code>--swfl-icon-size<\/code> and <code>--swfl-icon-color<\/code> on <code>.swiss-floorball-plugin<\/code>.<\/li>\n<li><strong>Filter:<\/strong> <code>swfl_icon_svg<\/code> lets you replace an icon. The result is sanitized with <code>wp_kses()<\/code> (only <code>svg<\/code>, <code>path<\/code> and <code>span<\/code> with a fixed set of attributes are kept).<\/li>\n<\/ul>\n\n<p>Example:<\/p>\n\n<pre><code>add_filter( 'swfl_icon_svg', function ( $svg, $name, $args ) {\n    if ( 'refresh' === $name ) {\n        return '&lt;svg viewBox=\"0 0 24 24\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\"&gt;&lt;path d=\"M12 4V1L8 5l4 4V6a6 6 0 1 1-6 6H4a8 8 0 1 0 8-8z\"\/&gt;&lt;\/svg&gt;';\n    }\n    return $svg;\n}, 10, 3 );\n<\/code><\/pre>\n\n<h4>Developer Hooks<\/h4>\n\n<ul>\n<li><code>swfl_icon_svg<\/code> (Filter) \u2013 Replace the SVG markup of an icon.<\/li>\n<li><code>swfl_request_timeout<\/code> (Filter) \u2013 Override the API request timeout (seconds). Takes precedence over the setting.<\/li>\n<li><code>swfl_rest_rate_limit<\/code> (Filter) \u2013 Override the REST requests per minute and visitor (0 = off). Takes precedence over the setting.<\/li>\n<\/ul>\n\n<h4>External Services<\/h4>\n\n<p>This plugin connects to the <strong>Swiss Unihockey API<\/strong>: by default the free <code>wc.swissunihockey.ch<\/code>, optionally the Partner API on <code>office.swissunihockey.ch<\/code>, depending on the <em>API source<\/em> setting.\nIt is used to retrieve floorball data such as club games, team rosters, league rankings, player statistics, and calendar feeds.<\/p>\n\n<p><strong>What data is sent and when:<\/strong><\/p>\n\n<ul>\n<li>Your configured Club ID and Season are sent to the API every time a shortcode is rendered or an admin page is loaded.<\/li>\n<li>No personal data of website visitors is transmitted. All requests are read-only.<\/li>\n<li>With the <em>Partner API<\/em> selected, your API key and secret are sent to <code>office.swissunihockey.ch<\/code> to request a short-lived auth token.<\/li>\n<\/ul>\n\n<p><strong>Service provider:<\/strong> Swiss Unihockey (Floorball Schweiz)\n<a href=\"https:\/\/www.swissunihockey.ch\/index.php?cID=1186\">Terms of Service<\/a> | <a href=\"https:\/\/swissunihockey.tlex.ch\/app\/de\/texts_of_law\/2-7\">Privacy Policy<\/a><\/p>\n\n<p><strong>Images from Cloudinary:<\/strong> Team logos and player portraits come from the API as image URLs on <code>res.cloudinary.com<\/code> (Swiss Unihockey's image host). The visitor's browser loads these images directly from Cloudinary when a page with such a shortcode is shown, so the visitor's IP address and browser data are transmitted to Cloudinary. The plugin does not store or process this data.\nService provider: Cloudinary Ltd. | <a href=\"https:\/\/cloudinary.com\/tou\">Terms of Service<\/a> | <a href=\"https:\/\/cloudinary.com\/privacy\">Privacy Policy<\/a><\/p>\n\n<p><strong>Links to other services:<\/strong> Venue cells link to <a href=\"https:\/\/www.openstreetmap.org\/\">OpenStreetMap<\/a> (built from the venue coordinates) and game links point to <code>myapp.swissunihockey.ch<\/code>. No data is sent to these sites unless a visitor clicks the link.<\/p>\n\n<p><strong>Disclaimer:<\/strong> This is an independent plugin. It is not affiliated with, endorsed by or sponsored by Swiss Unihockey (Floorball Schweiz). \"Swiss Unihockey\" is the name of its respective owner and is used only to describe the data source. \"Swiss floorball\" in the plugin name describes the sport in Switzerland and does not refer to any organisation.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Download the plugin from the <a href=\"https:\/\/github.com\/flawas\/floorball-api-for-swiss-unihockey\/releases\">releases page on GitHub<\/a> and upload the <code>swiss-floorball-api.zip<\/code> file to your WordPress plugins (or install it from the WordPress.org plugin directory).<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> menu in WordPress.<\/li>\n<li>Navigate to <strong>Settings &gt; Swiss Floorball API<\/strong>.<\/li>\n<li>Enter your <strong>Club ID<\/strong> (e.g., <code>427892<\/code>) and the <strong>Current Season<\/strong> (e.g., <code>2025<\/code>).<\/li>\n<li>Click <strong>Save Changes<\/strong>.<\/li>\n<li>Add the shortcodes to your pages or posts.<\/li>\n<\/ol>\n\n<h4>API sources<\/h4>\n\n<p>The free API is a read-only whitelist. Shortcodes that need an endpoint it does not offer show a notice that points to the settings.<\/p>\n\n<ul>\n<li>Games, rankings, teams, clubs, seasons, cups, game details \u2013 available with: Free, Partner<\/li>\n<li>Leagues and groups (ID finder) \u2013 available with: Partner<\/li>\n<li>Topscorers, player profiles, national players, game events \u2013 available with: Partner<\/li>\n<li>Calendars \/ ICS links (generated by the plugin from the games) \u2013 available with: Free, Partner<\/li>\n<\/ul>\n\n<h4>Settings<\/h4>\n\n<ul>\n<li><strong>API source<\/strong> \u2013 Where the data comes from (<code>swfl_api_source<\/code>). <strong>Free API<\/strong> (default, <code>wc.swissunihockey.ch<\/code>) or <strong>Partner API<\/strong> (<code>office.swissunihockey.ch<\/code>, needs credentials). See <em>API sources<\/em> above for what each source provides.<\/li>\n<li><strong>Partner API key \/ secret<\/strong> \u2013 Credentials for the Partner API (<code>swfl_api_key<\/code>, <code>swfl_api_secret<\/code>). Only used when the source is <em>Partner API<\/em>; the plugin exchanges them for a short-lived auth token.<\/li>\n<li><strong>Swiss Floorball Club Number<\/strong> \u2013 Your club's ID (<code>swfl_club_number<\/code>), used by the club shortcodes.<\/li>\n<li><strong>Swiss Floorball Club Name<\/strong> \u2013 Filled in automatically from the API when you save a new Club ID (<code>swfl_club_name<\/code>).<\/li>\n<li><strong>Swiss Floorball Aktuelle Saison<\/strong> \u2013 Season as a year, e.g. <code>2025<\/code> (<code>swfl_actual_season<\/code>). Used when a shortcode has no <code>season<\/code> attribute.<\/li>\n<li><strong>Show icons<\/strong> \u2013 Show or hide all icons in the admin and in the shortcodes (on by default).<\/li>\n<li><strong>Theme<\/strong> \u2013 Controls whether the plugin uses light, dark, or auto (follows system preference) colors (<code>swfl_theme<\/code>). Option values are <code>auto<\/code> (default), <code>light<\/code>, or <code>dark<\/code>.<\/li>\n<li><strong>Seed colour<\/strong> \u2013 Optional brand colour as hex, e.g. <code>#0066cc<\/code> (<code>swfl_seed_color<\/code>). Primary and secondary colours are derived from it for light and dark mode with AA text contrast. Leave empty for the default colours.<\/li>\n<li><strong>Table style<\/strong> \u2013 <code>flat<\/code> (default: no border or shadow, thin row dividers, accent line under the header) or <code>classic<\/code> (the previous boxed look) (<code>swfl_table_style<\/code>).<\/li>\n<li><strong>Table colours<\/strong> \u2013 Optional hex colours picked with the WordPress colour picker: accent (header line), header text, row divider and row highlight (<code>swfl_table_{accent,header,divider,highlight}_color<\/code>). Accent and header text are adjusted to keep AA contrast in light and dark mode. Empty uses the defaults derived from the seed colour.<\/li>\n<li><strong>Striped table rows<\/strong> \u2013 Subtle zebra striping for table rows (<code>swfl_table_striped<\/code>, off by default).<\/li>\n<li><strong>API request timeout (seconds)<\/strong> \u2013 Seconds to wait for the API, 1-30 (default 3).<\/li>\n<\/ul>\n\n<p>The admin interface is currently in German. Under <strong>Cache Verwaltung<\/strong> the button <strong>Cache leeren<\/strong> removes all cached API responses. Changing the Club Number clears the cache automatically.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20do%20i%20find%20the%20ids%3F\"><h3>Where do I find the IDs?<\/h3><\/dt>\n<dd><p>You can find Team, Club, and League IDs in the URL on the <a href=\"https:\/\/www.swissunihockey.ch\">Swiss Unihockey website<\/a> or by using the plugin backend to explore and copy the IDs.<\/p><\/dd>\n<dt id=\"why%20does%20my%20data%20not%20update%20right%20away%3F\"><h3>Why does my data not update right away?<\/h3><\/dt>\n<dd><p>API responses are cached for one hour. Use the <strong>Cache leeren<\/strong> button on the settings page to refresh immediately.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20free%3F\"><h3>Is this plugin free?<\/h3><\/dt>\n<dd><p>Yep! It's open-source and completely free to use.<\/p><\/dd>\n<dt id=\"is%20this%20an%20official%20swiss%20unihockey%20plugin%3F\"><h3>Is this an official Swiss Unihockey plugin?<\/h3><\/dt>\n<dd><p>No. This is an independent, unofficial plugin by Flavio Waser. It has no connection to Swiss Unihockey (swissunihockey.ch) other than reading their public API.<\/p><\/dd>\n<dt id=\"can%20i%20use%20this%20plugin%20for%20commercial%20purposes%3F\"><h3>Can I use this plugin for commercial purposes?<\/h3><\/dt>\n<dd><p>Yes. As GPLv2-or-later licensed software, it can be used on any site, including commercial ones. It was built for floorball clubs and teams in Switzerland to show their data on their websites, but nothing stops other use.<\/p><\/dd>\n<dt id=\"any%20further%20questions%3F\"><h3>Any further questions?<\/h3><\/dt>\n<dd><p>Just drop me a message on <a href=\"https:\/\/github.com\/flawas\">GitHub<\/a>, <a href=\"https:\/\/www.linkedin.com\/in\/flawas\/\">LinkedIn<\/a>, or via my <a href=\"https:\/\/flaviowaser.ch\/contact\/\">homepage<\/a>.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.1.2 \u2013 2026-10-18<\/h4>\n\n<ul>\n<li>Change: The WordPress.org plugin page is easier to read. <code>readme.txt<\/code> is generated with the sections WordPress.org knows (Description, Installation, FAQ, Screenshots, Changelog, Upgrade Notice); tables became lists, code examples are indented blocks and emoji are gone. The nine screenshots are now published, and the release deploys <code>assets\/<\/code> (banner, icon, screenshots). The icon and the 772x250 banner now have their correct sizes. No code changes<\/li>\n<\/ul>\n\n<h4>2.1.1 \u2013 2026-10-13<\/h4>\n\n<ul>\n<li>Change: Readme: the short description, FAQ and disclaimer now state clearly that this is an independent, unofficial plugin that is not affiliated with Swiss Unihockey and only reads their public API. No code changes<\/li>\n<\/ul>\n\n<h4>2.1.0 \u2013 2026-10-11<\/h4>\n\n<ul>\n<li>New: Read-only JSON REST routes under <code>swfl\/v1<\/code>: <code>\/games<\/code>, <code>\/next-game<\/code>, <code>\/last-result<\/code>, <code>\/rankings<\/code>, <code>\/topscorers<\/code>. They use the shared cache and the existing rate limit; visitors get a neutral 502 on upstream errors (Issue #22)<\/li>\n<li>New: Shortcodes <code>[swfl-next-game]<\/code> and <code>[swfl-last-result]<\/code> (attributes <code>team_id<\/code>, <code>season<\/code>, <code>limit<\/code>) and the sidebar widget <em>Swiss Floorball: N\u00e4chstes Spiel und letztes Resultat<\/em> with the display mode next \/ last \/ both and an optional team ID. They read the club and season from the settings. A game counts as a result from the day after it was played. Internally the games list options got <code>show_past<\/code>, which keeps past games, newest first (Issue #18)<\/li>\n<li>New: Scheduled cache warm-up. A WP-Cron job refreshes the games, rankings and topscorers of your club in the background (setting <em>Cache warm-up interval (minutes)<\/em>, default 30, 0 turns it off), so the first visitor after a cache expiry no longer waits for the API. The option <em>Warm up after clearing the cache<\/em> refills the cache right after a manual clear. <code>SWFL_Client::fetch_data()<\/code> got a <code>$force_refresh<\/code> argument that skips the cache read but still writes the response back.<\/li>\n<li>New: List options <code>limit<\/code>, <code>offset<\/code>, <code>show_upcoming<\/code> and <code>show_pagination<\/code> for <code>[swfl-club-games]<\/code>, <code>[swfl-team-games]<\/code> (and <code>limit<\/code>, <code>offset<\/code>, <code>show_pagination<\/code> for <code>[swfl-topscorers]<\/code>). Any of them shows a static, trimmed list instead of the week\/pager navigation; pagination uses the <code>swfl_page<\/code> query argument. Example: <code>[swfl-team-games team_id=\"12345\" limit=\"5\" show_upcoming=\"true\"]<\/code><\/li>\n<li>New: Configurable cache lifetime per API endpoint (games, rankings, game events, topscorers, leagues, seasons, clubs, cups, groups, teams, players, national players) on the settings page. Game events default to 120 seconds for near-live updates, rankings and topscorers to 2 hours. <code>SWFL_Client::fetch_data()<\/code> keeps its signature; without an explicit lifetime it uses the setting of the endpoint<\/li>\n<\/ul>\n\n<h4>2.0.3 \u2013 2026-10-11<\/h4>\n\n<ul>\n<li>Security: Values sent to the Swiss Unihockey API are URL-encoded, so a request value such as a group name can no longer add query parameters of its own (parameter injection through the public REST routes and shortcode attributes)<\/li>\n<li>Security: The public REST routes <code>swfl\/v1\/*<\/code> are rate limited per visitor (setting <em>REST requests per minute and visitor<\/em>, default 60, 0 turns it off, filter <code>swfl_rest_rate_limit<\/code>), and the <code>group<\/code> value accepts only letters, digits, spaces and <code>. _ \/ -<\/code> up to 40 characters. This stops anonymous visitors from filling the options table with cached responses<\/li>\n<li>Security: The calendar feed no longer returns the upstream API's error text to visitors; administrators still get it<\/li>\n<li>Fix: WordPress.org plugin review: escape late. Icon markup is passed through <code>wp_kses()<\/code> where it is printed, inline CSS goes through <code>wp_strip_all_tags()<\/code>, and table cells, row attributes, settings inputs and the unknown-response fallback are escaped at the point of output<\/li>\n<li>Remove: The compatibility loader <code>floorball-api-for-swiss-unihockey.php<\/code> is gone, because a plugin must not change the list of active plugins itself. Sites that update from 2.0.1 or older may show the plugin as deactivated once; activate it again, the settings are kept<\/li>\n<li>Change: The cache is cleared through <code>SWFL_Client::clear_cache()<\/code> (one shared implementation instead of two copies)<\/li>\n<\/ul>\n\n<h4>2.0.2 \u2013 2026-10-07<\/h4>\n\n<ul>\n<li>Fix: WordPress.org plugin review: all PHP globals now use the single prefix <code>swfl<\/code>. Options <code>swissfloorball_*<\/code> became <code>swfl_*<\/code> (existing settings are copied automatically on the first request after the update, <code>uninstall.php<\/code> removes both), classes <code>Swiss_Floorball_Api*<\/code> became <code>SWFL_*<\/code>, the constant <code>SWISS_FLOORBALL_API_VERSION<\/code> became <code>SWFL_VERSION<\/code> and the JS global <code>sfaAutosave<\/code> became <code>swflAutosave<\/code>. Custom code that reads <code>swissfloorball_*<\/code> options or calls the old class names must be adjusted<\/li>\n<li>Change: The main file is now <code>swiss-floorball-api.php<\/code> and all other files are named <code>class-swfl-*.php<\/code> \/ <code>swfl-*<\/code>. The old main file stays as a small loader that moves the activation to the new file; if the plugin shows as deactivated after a manual single update, activate it once. Admin page URLs changed to <code>admin.php?page=swiss-floorball-api\u2026<\/code><\/li>\n<li>Fix: <code>uninstall.php<\/code> now also removes the <em>Show icons<\/em> option<\/li>\n<li>Fix: Every registered setting has an explicit type and sanitize callback; the Partner API secret keeps all printable characters instead of going through <code>sanitize_text_field()<\/code><\/li>\n<li>Change: When data cannot be loaded, administrators now also see the reason returned by the API (e.g. <code>No such group with name \"Gruppe 4\".<\/code>); visitors still see the generic message<\/li>\n<\/ul>\n\n<h4>2.0.1 \u2013 2026-10-04<\/h4>\n\n<ul>\n<li>Fix: Plugin Check error: <code>wp_unique_id()<\/code> needs WordPress 5.0.3 but the plugin supports 5.0, replaced by a local counter<\/li>\n<li>Fix: phpcs warnings for the unused <code>$is_backend<\/code> parameters, kept for backwards compatibility<\/li>\n<li>Fix: Plugin guidelines: the bundled Roboto font is documented in <code>THIRD-PARTY.md<\/code> and ships with its licence (<code>fonts\/OFL.txt<\/code>); the external services section now discloses the Cloudinary image host (team logos, player portraits) and links to OpenStreetMap and the Swiss Unihockey app; added a disclaimer that the plugin is not affiliated with Swiss Unihockey or Floorball Schweiz<\/li>\n<li>Change: Internal refactoring without functional change: the large display, widget, admin and public classes are split into smaller topic classes (SonarCloud findings)<\/li>\n<\/ul>\n\n<h4>2.0.0 \u2013 2026-10-02<\/h4>\n\n<ul>\n<li><strong>Breaking:<\/strong> API source switch. <code>api-v2.swissunihockey.ch<\/code> is no longer used; the default is the free API (<code>wc.swissunihockey.ch<\/code>), optionally the Partner API (<code>office.swissunihockey.ch<\/code>, API key + secret). Leagues, groups, topscorers, player profiles, national players and game events need the Partner API. See <a href=\"docs\/migration.md\">docs\/migration.md<\/a><\/li>\n<li><strong>Breaking:<\/strong> Calendar subscriptions: the old calendar export is gone. The plugin serves its own iCalendar feed at <code>\/wp-json\/swfl\/v1\/calendar<\/code>; previously subscribed URLs must be subscribed again<\/li>\n<li><strong>Breaking:<\/strong> <code>[swfl-rankings]<\/code> takes the group name (<code>Gruppe 1<\/code>) instead of a group ID, <code>[swfl-topscorers]<\/code> ignores <code>group<\/code> (endpoint <code>topscorers\/su<\/code>); <code>[swfl-club-games]<\/code> and <code>[swfl-team-games]<\/code> now page by week \/ around the next game and need JavaScript and the public REST routes <code>swfl\/v1\/*<\/code> for navigation<\/li>\n<li><strong>Breaking:<\/strong> New table markup and styling (flat style, <code>.sfa-table-wrap<\/code>, <code>--sfa-sys-*<\/code> tokens). Custom CSS targeting the old markup needs a check; set <em>Table style<\/em> to <code>classic<\/code> for the previous look<\/li>\n<li>Change: Major release. The default API source is now the free Swiss Unihockey API, shortcodes follow the official web components, and the frontend and admin were redesigned (see below). Review the <em>Remove<\/em> entry before updating if you used the former API source<\/li>\n<li>Change: Settings page: the Partner API key and secret fields have clear labels and descriptions and are only shown while <em>Partner API<\/em> is selected as API source. All settings inputs and dropdowns share one compact size<\/li>\n<li>Fix: <code>[swfl-topscorers]<\/code> now uses the <code>topscorers\/su<\/code> API endpoint (the old <code>topscorers<\/code> endpoint returned 404). The <code>group<\/code> attribute is still accepted but ignored; lists for leagues with several groups may be longer (Issue #27)<\/li>\n<li>Fix: Game lists no longer fail on cancelled games without a time (Issue #28)<\/li>\n<li>Fix: The admin script is versioned by its modification time, so browsers no longer serve a stale cached copy after an update<\/li>\n<li>Change: <code>[swfl-mobiliar-topscorer]<\/code> no longer defaults to the configured club. Without <code>club_id<\/code> it shows the Mobiliar topscorers of the whole league; with <code>club_id<\/code> it filters to that club<\/li>\n<li>Change: Responsive widgets without a horizontal scrollbar. Layout now follows the width of the widget (container queries) instead of the screen width, because theme content columns are often much narrower than the viewport: compact cells and wrapping text first, then minor columns (ranking details, league column, venue, logos) are hidden on narrow widths; the paging buttons wrap below the label; the dark wrapper uses less padding on phones. The calendar table headers now read Heim\/Gast (they showed Goal\/Resultat)<\/li>\n<li>New: Frontend icons (controlled by the <em>Show icons<\/em> setting): section titles, previous\/next buttons, calendar title and subscribe link, error notices, and a map link with a pin icon on venue cells (OpenStreetMap, from the venue coordinates of the API)<\/li>\n<li>Remove: The previous API (<code>api-v2.swissunihockey.ch<\/code>) is no longer an API source. Installations that had it selected fall back to the free API; leagues, groups, topscorers, player profiles, national players and game events are then only available through the Partner API. Calendar subscriptions no longer use the old calendar export: the plugin now serves its own iCalendar feed at <code>\/wp-json\/swfl\/v1\/calendar<\/code> (<code>team_id<\/code>, <code>club_id<\/code> or <code>league<\/code> + <code>game_class<\/code> + <code>group<\/code>, optional <code>season<\/code>), built from the games of the free API<\/li>\n<li>Change: New admin dashboard with stat tiles (club, club ID, season, API source) and navigation tiles. The club overview (teams) and the club games list moved from the dashboard to a separate submenu page \"Club-\u00dcbersicht\" (Issue #94)<\/li>\n<li>Change: Admin pages without big boxes: the gradient header became a slim top app bar, cards, table containers and form sections sit flat on the page (no border, shadow or hover lift), quick-access links are compact tonal buttons and the settings overview is a divided key\/value list (Issue #94)<\/li>\n<li>Change: All tables share the look of the games widgets (<code>swfl-club-team-games<\/code>, <code>swfl-league-games<\/code>): section title, scrollable <code>.sfa-table-wrap<\/code>, right-aligned numbers, highlighted rows and bold links. The admin game lists (Spiele, dashboard) now render with the same code and markup as the shortcodes plus a Details link, and the admin tables follow the flat style and the table colour settings (Issue #94)<\/li>\n<li>New: Flat table style (new default) with backend settings for table style (<code>flat<\/code>\/<code>classic<\/code>), accent, header text, divider and highlight colours (colour picker, AA contrast guaranteed) and optional striped rows. The wrapper gets <code>sfa-table--flat|classic<\/code> (and <code>sfa-table--striped<\/code>); choose <code>classic<\/code> to keep the previous boxed look (Issue #94)<\/li>\n<li>New: Shortcodes modelled on the official Swiss Unihockey web components: <code>swfl-league-games<\/code>, <code>swfl-club-team-games<\/code> and <code>swfl-mobiliar-topscorer<\/code>. <code>swfl-club-games<\/code> now shows games week by week, <code>swfl-team-games<\/code> pages around the next game (<code>page_size<\/code>, <code>season<\/code>) and <code>swfl-rankings<\/code> takes the group name (e.g. <code>Gruppe 1<\/code>). Navigation uses a small script and the REST routes <code>swfl\/v1\/team-games<\/code> and <code>swfl\/v1\/league-games<\/code><\/li>\n<li>New: API source setting. The plugin now uses the free Swiss Unihockey API (<code>wc.swissunihockey.ch<\/code>) by default and can be switched to the Partner API (api key + secret, auth token). Endpoints the free API does not offer (leagues, groups, topscorers, players, national players, game events) show a clear notice instead of a generic error<\/li>\n<li>New: Optional Seed colour setting (hex). Derives the primary\/secondary <code>--sfa-sys-color-*<\/code> tokens, including <code>on-*<\/code> colours with AA contrast, for light, dark and auto mode on the frontend and in the admin. Empty keeps the default look; a set value overrides <code>--sfa-primary<\/code> \/ <code>--sfa-secondary<\/code> theme overrides<\/li>\n<li>New: Theme setting (Auto \/ Light \/ Dark, default Auto) under Settings. Dark colours are <code>--sfa-sys-*<\/code> tokens scoped to the plugin containers (<code>data-sfa-theme<\/code> on <code>.swiss-floorball-plugin<\/code> and <code>.sfa-admin-wrap<\/code>); Auto follows <code>prefers-color-scheme<\/code>. The old hard-coded dark block was replaced by these tokens<\/li>\n<li>Fix: Plugin Check readme issues: \"Tested up to\" raised to 7.1 and short description shortened to 150 characters or less<\/li>\n<li>Fix: Resolve phpcs findings in admin and display classes: add translator comments, escape wp_die, use wp_safe_redirect, annotate DB queries (Issue #75)<\/li>\n<li>New: Admin dashboard restyled in Material 3 (header, tonal navigation buttons, outlined form fields, filled\/outlined buttons, cards and tables) using the <code>--sfa-sys-*<\/code> tokens; 48 px touch targets and visible keyboard focus. Markup, shortcodes and options are unchanged<\/li>\n<li>Change: Frontend tables and cards in Material 3 look using only <code>--sfa-sys-*<\/code> tokens: surface-container tables with dividers, hover\/focus state layer, sticky header, <code>.sfa-card<\/code> variants (<code>--elevated<\/code>, <code>--filled<\/code>, <code>--outlined<\/code>), 48 px touch targets and reduced-motion support. On narrow screens tables now scroll horizontally instead of switching to a card layout<\/li>\n<li>New: Visually hidden <code>&lt;caption&gt;<\/code> and <code>scope<\/code> attributes on all plugin tables for accessibility<\/li>\n<li>Change: Frontend buttons (<code>.button<\/code>, <code>.btn<\/code>), calendar subscribe link, info boxes and empty states restyled with the <code>--sfa-sys-*<\/code> tokens (state layers, focus ring, 48px touch targets, motion tokens). Themes that style <code>.button<\/code> inside the plugin will see the new look<\/li>\n<li>Change: Failed API requests render a <code>.sfa-info-box--danger<\/code> banner with <code>role=\"alert\"<\/code> instead of a bare paragraph; the calendar link gets <code>rel=\"noopener noreferrer\"<\/code>; <code>.sfa-empty-state*<\/code> now styled (legacy <code>.empty-state*<\/code> kept)<\/li>\n<li>New: Design-token layer (<code>--sfa-sys-*<\/code> colors, typography, shape, elevation 0-5) for the frontend and admin styles, scoped to <code>.swiss-floorball-plugin<\/code> \/ <code>.sfa-admin-wrap<\/code>. Existing <code>--sfa-*<\/code> variables, Roboto and dark mode are unchanged; no visual change<\/li>\n<li>Fix: Add explicit request timeout (5 seconds, minimum 1 second) to API client calls, filterable via <code>swfl_request_timeout<\/code> hook<\/li>\n<li>Fix: Add <code>apply_filters()<\/code> stub to verify_api.php for test compatibility<\/li>\n<li>New: Icon infrastructure. <code>Swiss_Floorball_Api_Icons::get()<\/code> accepts an argument array (<code>class<\/code>, <code>label<\/code>) and loads Material Symbols SVGs from <code>public\/icons\/<\/code> (<code>calendar_month<\/code>, <code>location_on<\/code>, <code>error<\/code>, <code>info<\/code>, <code>refresh<\/code>)<\/li>\n<li>New: Setting <code>swissfloorball_show_icons<\/code> and filter <code>swfl_icon_svg<\/code><\/li>\n<li>Removed: Unused <code>render_sessions()<\/code> stub that issued a discarded API request<\/li>\n<li>New: Setting <code>swissfloorball_request_timeout<\/code> (API request timeout, 1-30 seconds) on the settings page; it provides the default of the <code>swfl_request_timeout<\/code> filter, which still overrides it<\/li>\n<li>Security: Replace cache-cleared notice GET parameters with transient-based notification<\/li>\n<li>Change: Default API request timeout lowered from 5 to 3 seconds<\/li>\n<li>Fix: Render player details via shortcode <code>[swfl-player]<\/code> without dumping raw API response \u2014 now displays structured data as a table with all values escaped (PR #61)<\/li>\n<li>Fix: Escape LIKE patterns in transient cleanup queries to prevent wildcard interpretation<\/li>\n<li>Fix: Admin class methods to snake_case; prefix template variables with <code>swfl_<\/code>; apply Yoda conditions (PR #50, WPCS step 4\/5)<\/li>\n<li>Fix: End inline comments with full stops (WPCS 3c2)<\/li>\n<li>Fix: Rename global functions to snake_case (WPCS)<\/li>\n<\/ul>\n\n<h4>1.0.5 \u2013 2026-08-09<\/h4>\n\n<ul>\n<li>Fix: Remove FAQ answer restricting the plugin to \"personal and non-commercial use\" \u2014 this contradicted the GPLv2-or-later license, which grants unrestricted use including commercial. Not allowed under WordPress.org plugin guidelines.<\/li>\n<li>Fix: Add <code>Requires PHP<\/code> and <code>License URI<\/code> fields to the readme header<\/li>\n<li>Fix: Correct Text Domain (header + all translation strings) from <code>floorball-api-for-swiss-unihockey<\/code> to <code>swiss-floorball-api<\/code> to match the assigned WordPress.org slug<\/li>\n<li>Fix: Rename <code>languages\/floorball-api-for-swiss-unihockey.pot<\/code> to <code>languages\/swiss-floorball-api.pot<\/code> to match the text domain<\/li>\n<li>Fix: Update \"Tested up to\" to WordPress 7.0<\/li>\n<li>Fix: Exclude <code>graphify-out\/<\/code>, <code>.vscode<\/code>, <code>.claude<\/code>, and <code>.DS_Store<\/code> from the release ZIP and SVN deploy via <code>.distignore<\/code> \u2014 these dev-tool artifacts (including files with <code>&amp;<\/code>\/<code>()<\/code> in their names) were failing the automated plugin scan<\/li>\n<li>Fix: Update release workflow <code>SLUG<\/code> and build\/zip folder names from <code>floorball-api-for-swiss-unihockey<\/code> to <code>swiss-floorball-api<\/code><\/li>\n<li>Fix: Correct <code>.gitignore<\/code> readme.txt casing for case-sensitive CI filesystems<\/li>\n<\/ul>\n\n<h4>1.0.4 \u2013 2026-05-04<\/h4>\n\n<ul>\n<li>Security: Add nonce and permission checks for all admin <code>$_GET<\/code>\/<code>$_POST<\/code> actions<\/li>\n<li>Security: Sanitize nonce with <code>wp_unslash()<\/code> + <code>sanitize_text_field()<\/code> before <code>wp_verify_nonce()<\/code><\/li>\n<li>Security: Escape all dynamic HTML attributes with <code>esc_attr()<\/code> in settings field renderer<\/li>\n<li>Fix: Add <code>ABSPATH<\/code> guard to main plugin file and admin matches partial<\/li>\n<li>Fix: Rename plugin slug to <code>floorball-api-for-swiss-unihockey<\/code> for WordPress.org listing<\/li>\n<li>Fix: Rename all shortcodes from <code>suh-*<\/code> to <code>swfl-*<\/code> to meet 4-character prefix requirement<\/li>\n<li>Fix: Prefix settings group and section IDs with <code>sfa_<\/code> to avoid naming collisions<\/li>\n<li>Fix: Remove <code>load_plugin_textdomain()<\/code> call \u2014 not needed since WordPress 4.6<\/li>\n<li>Fix: Update \"Tested up to\" to WordPress 6.9<\/li>\n<li>Fix: Remove angle brackets from Donate link URL<\/li>\n<li>Fix: Exclude <code>.distignore<\/code> and <code>README.md<\/code> from release ZIP<\/li>\n<li>Improvement: Document external service (Swiss Unihockey API) with Terms of Service and Privacy Policy links<\/li>\n<\/ul>\n\n<h4>1.0.3 \u2013 2026-05-03<\/h4>\n\n<ul>\n<li>Security: Use <code>$wpdb-&gt;prepare()<\/code> for all database queries to prevent SQL injection<\/li>\n<li>Security: Sanitize all shortcode attributes with <code>absint()<\/code> before passing to API<\/li>\n<li>Security: Escape all <code>admin_url()<\/code> outputs with <code>esc_url()<\/code><\/li>\n<li>Fix: Scoped all CSS to plugin containers to prevent styling conflicts with other theme elements (buttons, notices, etc.)<\/li>\n<li>Fix: Dark mode CSS variables scoped to <code>.swiss-floorball-plugin<\/code> \/ <code>.sfa-admin-wrap<\/code> instead of <code>:root<\/code><\/li>\n<li>Fix: <code>prefers-reduced-motion<\/code> rule scoped to plugin elements only<\/li>\n<li>Fix: <code>scroll-behavior: smooth<\/code> scoped to plugin container instead of <code>html<\/code><\/li>\n<li>Fix: Admin <code>.button-primary<\/code> styles scoped to <code>.sfa-admin-wrap<\/code> to avoid overriding WordPress core buttons<\/li>\n<li>Performance: CSS and JS only enqueued on pages\/posts that contain a plugin shortcode (frontend) or on plugin admin pages (backend)<\/li>\n<li>Improvement: Replaced all inline styles in templates with reusable CSS classes<\/li>\n<li>Improvement: Added <code>aria-label<\/code> to admin search input for accessibility<\/li>\n<li>Improvement: Removed <code>console.log<\/code> statements from admin JavaScript<\/li>\n<li>Improvement: Use <code>const<\/code>\/<code>let<\/code> and <code>.includes()<\/code> in admin JavaScript<\/li>\n<li>Improvement: Use <code>require_once<\/code> instead of <code>require<\/code> for the main plugin class<\/li>\n<li>Fix: Version constant <code>SWISS_FLOORBALL_API_VERSION<\/code> now matches plugin header version<\/li>\n<li>Improvement: Calendar link now has correct desktop styles (was previously only styled on mobile)<\/li>\n<li>Improvement: Replaced remaining inline styles in admin partials (matches, settings, shortcodes) with CSS classes<\/li>\n<li>Improvement: Replaced hardcoded colors in admin CSS with CSS variables<\/li>\n<li>Improvement: Escaped remaining <code>admin_url()<\/code> outputs in shortcodes partial<\/li>\n<\/ul>\n\n<h4>1.0.2 \u2013 2026-01-02<\/h4>\n\n<ul>\n<li>Fix: Mobile table background color<\/li>\n<li>Fix: Mobile table text alignment<\/li>\n<\/ul>\n\n<h4>1.0.1 \u2013 2025-11-30<\/h4>\n\n<ul>\n<li>Feature: Implement detailed matches display in admin<\/li>\n<li>Feature: Scope public CSS<\/li>\n<li>Fix: Add release workflow documentation<\/li>\n<li>Fix: Consistent language and naming in code<\/li>\n<\/ul>\n\n<h4>1.0.0 \u2013 2025-11-29<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<\/ul>","raw_excerpt":"Unofficial plugin: show Swiss floorball games, rankings, topscorers, rosters and player stats from the public Swiss Unihockey API via shortcodes.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/308728","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=308728"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/flaviowaser"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=308728"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=308728"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=308728"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=308728"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=308728"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=308728"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}