=== Lektivo ===
Contributors: ephracis
Tags: courses, classes, schedule, instructors, booking
Requires at least: 7.0
Tested up to: 7.1
Requires PHP: 8.3
Stable tag: 1.3.3
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Manage courses, instructors, locations, and schedules with Gutenberg blocks. Booking and payment stay with your external provider.

== Description ==

Lektivo is for businesses that offer courses or classes — dance studios, yoga studios, language schools, cooking classes, and similar.

It adds custom post types for courses, instructors, locations, and schedules, plus Gutenberg blocks for course lists, schedule viewing, and instructor grids. Professional adds testimonials, location maps, archive filters, and an interactive timetable.

Lektivo handles **content and structure**. Booking and payment stay with your external provider (dans.se, Bookeo, WooCommerce Bookings, and the like) through configurable booking URLs.

= Free vs Professional =

**Lektivo Free** includes the course, instructor, location, and schedule post types, taxonomies, meta fields, core admin, the basic Course List and Instructor Grid blocks, and a day/time-ordered Schedule Viewer class list. Course, instructor, location, and schedule creation is unlimited.

**Lektivo Professional** (a separate paid build distributed outside wordpress.org) adds:

* Interactive timetable Schedule Viewer (location panels, weekday grid, mobile day cards)
* Course and schedule archive filters (Professional only, including trials)
* Location Map block and map embeds
* Testimonials (CPT, admin meta, theme showcase support)
* Related-course and instructor-course showcase helpers for rich theme cards
* Freemius updates and priority support

Style sample packs and testimonial sample data (latin-heat and other Dance lab packs) live in the companion Dance theme and are tracked as a Dance follow-up, not this plugin ZIP.

= Privacy note =

Lektivo uses the Freemius SDK for licensing and optional opt-in analytics. Freemius only connects after you choose to opt in or start an upgrade. The free plugin on WordPress.org does not phone home without consent.

== Installation ==

1. From **Plugins → Add New**, search for Lektivo and click Install, or upload the `lektivo` folder to `/wp-content/plugins/`.
2. Activate Lektivo through the Plugins screen.
3. Add courses, instructors, locations, and schedules from the Lektivo admin menus.
4. Place Lektivo blocks in the block editor, or use theme templates that support Lektivo.

== Frequently Asked Questions ==

= Does Lektivo handle booking or payments? =

No. Each course or schedule entry has a booking URL that links to your external booking provider.

= Are there free-plan creation limits? =

No. The free plugin on WordPress.org lets you create as many courses, instructors, locations, and schedules as you need.

= Where do I get Lektivo Professional? =

Professional is a separate paid build (interactive timetable, archive filters, location maps, testimonials, course showcases, and updates). Upgrade from the Freemius pricing screen in wp-admin after activating the plugin, or visit the product page on jugasit.com.

= Will upgrading keep my content? =

Yes. Your courses, instructors, locations, and schedules stay in place. Professional (including trials) unlocks the interactive Schedule Viewer, course and schedule archive filters, location maps, testimonials, and rich course showcase helpers. On Free, filter values in the address bar have no effect.

= Does the free plugin send data to Freemius without asking? =

No. Connection and optional analytics are opt-in. You stay in control from the Freemius screens in wp-admin.

== Changelog ==

= 1.3.3 =
* CI: `package:wporg-free` fails when the free ZIP contains licence or plan checks (`bin/check-free-gates.php`).
* Release: `release:wporg` always ships our own free build from `package:wporg-free` and no longer downloads the Freemius-generated free ZIP; `bin/freemius-download-free.php` is removed (#65).
* Schedule Viewer layout and filter controls now come from a Professional-only editor script; the free editor shows only the semester picker.
* “Book now” and “View on Map” links that open a new tab include a translated screen-reader cue “(opens in a new tab)” and a single visible ↗ cue (the legacy “Book now →” arrow is dropped so the link is not “Book now → ↗”) (#62).
* Course, instructor and location card titles (and Schedule Viewer location headings) default to h2 so archives no longer skip from h1 to h3; Course List lifecycle sections use h2 with nested card h3, and static booking status no longer uses aria-live. Class-based CSS keeps the prior h3 card title size, weight and spacing so theme h2 styles do not enlarge the cards (#64).
* Pricing tier, specialty and room fields in the admin meta boxes expose programmatic labels on existing rows and on rows added with “Add Tier” / “Add Specialty” / “Add Room” (#63).
* Course and schedule filters update results in place when JavaScript is available (URL stays shareable; result count is announced to assistive tech), keep Apply for no-JS only, group fields with a fieldset legend, keep a visible focus outline on filter controls, and the course card Show more toggle exposes aria-expanded / aria-controls with a 24×24 hit target (#59).
* Course, location and instructor cards: thumbnails are decorative (not nameless duplicate links), each course card keeps a single permalink (title) with a visible focus style, decorative info icons are hidden from assistive technology, and the plugin ships its own `.screen-reader-text` styles (#61).
* Schedule Viewer timetable class cells expose weekday and time to assistive technology, title links meet a 24×24 minimum hit target, sticky columns no longer cover focused links, and a “Skip schedule” link bypasses long timetable regions (#60).
* WordPress.org free build no longer ships licence-gated code (Guideline 5): course archive filtering, licence notices and the Course Filters / Location Map shortcodes are Professional-only (#65).
= 1.3.2 =
* Previous-release → candidate-build upgrade CI job
  (`test:upgrade-from-previous`): installs the last GitLab Release plugin
  and Dance theme ZIPs with `sv_SE` and seeded content, replaces both with
  the new builds in the same folders, asserts an active plugin, clean
  debug.log, rendered home/courses/schedules, Freemius SDK `languages/`
  in the production ZIP, and desktop plus 390px screenshots per style
  variation. Dance theme ZIPs fall back to
  `tests/fixtures/upgrade/` when cross-project release downloads are
  unavailable (#55).
* Automated folder-name upgrade smoke test: install under `coursekit/`,
  upload the canonical `lektivo/` ZIP beside it, assert a single active
  plugin, preserved course data and options, retirement notice option, and
  no PHP fatals (#55).
* Release Checklist and `docs/RELEASE.md` require “Upgrade test from
  previous release passed” before tagging (#55).
* Document the `coursekit/` → `lektivo/` upgrade path in the release
  checklist and readme upgrade notice (#55).
* When a site still runs Lektivo from `wp-content/plugins/coursekit/`
  (common after Freemius in-place updates from the CourseKit product name)
  and a manual zip upload installs `lektivo/` beside it, the canonical
  install deactivates the leftover `coursekit/` copy and shows an admin
  notice so administrators can delete the unused folder. Content and
  settings are shared WordPress data and stay intact (#55).
= 1.3.1 =
* Localize visitor-facing weekday names on `sv_SE` sites: long labels
  (`Torsdag`), short schedule abbreviations (`tor`), and plural session
  forms (`Torsdagar`). Helpers prefer the lektivo text domain then
  WordPress `$wp_locale`; Schedule Viewer fallbacks no longer hard-code
  English (#57).
* Ship Swedish `lektivo` translations (`languages/lektivo-sv_SE.po` /
  `.mo`) inside the plugin ZIP so `sv_SE` sites (for example
  ritmostudio.se) show visitor-facing strings in Swedish without a
  wordpress.org language pack after the CourseKit → Lektivo rename.
  Packaging no longer excludes the plugin `languages/` tree; CI verifies
  the ZIP contains the `.mo` and key visitor strings translate (#56,
  related lektivo-dance#71).
* Production packaging keeps `vendor/freemius/wordpress-sdk/languages/` in
  the Freemius ZIP (`.distignore` no longer strips nested `languages`
  paths) and CI fails if that folder is missing from the built ZIP (#53).
= 1.3.0 =
* Shortcode wrappers for every Lektivo block so Classic Editor and HTML
  widgets can embed the same markup as Gutenberg: `[lektivo_course_list]`,
  `[lektivo_instructor_grid]`, `[lektivo_schedule_viewer]`,
  `[lektivo_course_filters]`, and `[lektivo_location_map]`. Professional
  gates match the blocks (filters stay empty without a licence; Schedule
  Viewer keeps the free class list vs Pro timetable). Use
  `layout="compact"` on the schedule shortcode for the compact day-card
  list (#17).
* Course List **Term** control: defaults to the current bookable semester
  (same `Helpers::get_current_semester()` logic as Schedule Viewer), with
  options for all terms or a fixed semester. Course cards show day and time
  from the schedule in the displayed term. Blocks saved before this keep
  listing all terms until changed (#38).
* Schedule Viewer block settings for **Layout** (Responsive vs Compact list)
  and **Show filters**, so a page can embed the week as day cards on every
  screen size without the filter form. Existing embeds keep the responsive
  timetable and filters (#41).
* Lektivo always uses the site language (Settings → General) for its own
  strings — in wp-admin, the block editor, REST (including `_locale=user`),
  and on the front end — even when the logged-in user’s profile language
  differs. Language-pack `.mo` files under `wp-content/languages/plugins/`
  are rewritten onto the site locale the same way as editor Jed JSON. A
  check ensures the Swedish PO has no empty or fuzzy translations.
  Theme-owned copy such as schedule headings stays in Lektivo Dance (#46).
* The Freemius Account page is no longer listed under Courses, so customers
  do not see the Jugas IT owner's details and keys. Activation now returns
  you to the Courses list, and the licence notices and Plugins row no longer
  link to the Account page (#50).
* Course and schedule archive filters (`lektivocf_*` / `lektivosf_*` URL
  params and filter controls) require Lektivo Professional, including
  Freemius trials. Free sites ignore filter values typed into the address by
  hand (#15).
* Course archive filter query params use the `lektivocf_*` prefix (aligned
  with schedule `lektivosf_*`). Bookmarked or shared links that still use
  `ckf_*` keep working as aliases on Professional; new filter forms emit only
  `lektivocf_*` (#40).
* Admin list table column keys for difficulty colour / sort order and
  semester booking-opens use the `lektivo_` prefix instead of `ck_` (#40).
  Sites with custom CSS that targeted those admin column classes need the
  same rename (`ck_color` → `lektivo_color`, `ck_sort_order` →
  `lektivo_sort_order`, `ck_booking_opens` → `lektivo_booking_opens`).
  Front-end `lektivo-*` / `wp-block-lektivo-*` class names and
  `--lektivo-diff-*` CSS custom properties are unchanged.
* JavaScript translation files named after a script handle
  (`lektivo-{locale}-{handle}.json`) are rewritten onto the site language the
  same way as the MD5 file-path JSON names, so Swedish (or any site locale)
  loads for every Jed file style (#52).
* Semester admin form field IDs use the `lektivo-semester-*` prefix instead of
  the leftover `ck-semester-*` CourseKit IDs. Term meta keys are unchanged, so
  existing semester data is unaffected (#40).
* Course archive cards use the same booking button markup and default booking
  policy as the Course List block and `[lektivo_course_list]` shortcode, so a
  custom course page and the archive show the same book / status state for the
  same courses without extra configuration (#45).
* Schedule Viewer no longer forces the page wider than a phone screen:
  filters stack below 600 px, day labels sit above class times on narrow
  viewports, and long course or instructor names wrap instead of truncating
  (#39).
* Tag pipelines on the canonical GitLab project fail again when Freemius
  credentials are missing, after the project path rename to
  `jugasit/lektivo/lektivo` (forks still dry-run).
= 1.2.1 =
* Updated Freemius SDK to the latest version.
= 1.2.0 =
* WordPress.org free packaging and gated SVN deploy: local free ZIP
  (`bin/package-free-plugin.sh` / `package:wporg-free`), Freemius free
  download helper, `release:wporg` (skips cleanly without
  `WPORG_SVN_USERNAME` / `WPORG_SVN_PASSWORD`), Plugin Check job, and
  `.wordpress-org/` asset checklist. Pro Schedule Viewer UI lives in
  `ScheduleViewerPremium.php` so free ZIPs ship without locked Pro code.
* Idempotent CourseKit → Lektivo legacy data migration
  (`Lektivo\Migration\LegacyCourseKit`) and deprecated Dance-theme
  compatibility layer (`coursekit/*` block aliases, `coursekit_*`
  helpers, `ck_*` template hierarchy fallbacks).
* Swedish stopgap language-pack CI artifact (`dist/lektivo-sv_SE.zip`)
  built from `i18n/lektivo-sv_SE.po` for drop-in under
  `wp-content/languages/plugins/` until translate.wordpress.org ships
  translations. Not bundled in the plugin ZIP.
* Renamed the free plugin to **Lektivo** (slug `lektivo`, namespace
  `Lektivo\`, prefixes `lektivo_` / `_lektivo_` / `lektivo/`).
* Expand Professional surface for the first public free release: Course
  Filters, Location Map, schedule filter/grid helpers, FilterForm,
  Testimonials CPT/admin meta, and related/instructor course showcase
  helpers (`src/Pro/CourseShowcase.php`) ship only in `@fs_premium_only`
  paths stripped from the free ZIP. Free keeps CPTs (except testimonials),
  taxonomies, meta, core admin, Course List, Instructor Grid, and a plain
  day/time-ordered Schedule Viewer list with no upgrade nag.
* Plugin Check (`lint:plugin-check`) runs with a 512M PHP/WP-CLI memory
  limit and fails the pipeline on PCP ERROR or WARNING rows.
* Release tooling moved to `tools/release/` (autoload-dev only; not
  shipped in the plugin ZIP).
* Free Schedule Viewer renders the full class-card list without an
  upgrade nag; the interactive grid remains premium-only code.
* Course pricing meta box: new tier rows use a monotonically increasing
  `data-next-index` so remove-then-add no longer reuses a POST index and
  silently drops a tier on save.
* Activation with CourseKit inactive migrates and flushes rewrite rules in the
  activation hook so front-end course URLs work without an admin page load.
  Coexistence detection uses `CourseKit\Plugin` /
  `is_plugin_active( 'coursekit/coursekit.php' )` (not the compat
  `coursekit_fs()` stand-in). Coexistence early-return sets the rewrite flush
  flag so re-activating then deactivating CourseKit still refreshes permalinks.
* `lektivo_render_course_grid()` (and other hard-coded block wrappers) route
  class lists through `lektivo_classes()` so Dance still sees
  `wp-block-coursekit-*` twins until the theme MR lands.
* Free ZIP excludes all dotfiles via `.distignore` `.*` (fixes Plugin Check
  ERROR for shipped `.eslintignore`) and ships `composer.json` alongside
  `vendor/`. Plugin Check CI fails on CSV rows matching
  `line,column,(ERROR|WARNING),` (`set -o pipefail` + gate self-test).
  Activation smoke installs the built `dist/lektivo.zip` instead of a
  working-tree symlink.
* Legacy CourseKit→Lektivo migration: slash block markup for `wp_update_post()`,
  keep multi-batch `block_markup` on the same step, never rename theme
  `coursekit_dance_*` options, run migration on `admin_init` (not front-end
  `plugins_loaded`) with explicit pending/running/done state, and flush
  rewrites after completion.
* Dance compatibility: keep block-theme `template-canvas.php` results, copy
  style handles onto `coursekit/*` alias blocks, and dual-emit `coursekit-*`
  CSS classes via `lektivo_classes()` while Dance (or `coursekit` theme
  support) is active.
* Unique Composer `autoloader-suffix` so CourseKit and Lektivo can coexist
  without a fatal `ComposerAutoloaderInit` collision.
* Freemius menu/`first-path` points at the Courses CPT list so post-activation
  redirects are accessible to admins.
* Packaging honours `.distignore` as the single ZIP exclude source; free ZIP
  strips premium-only regions and `@fs_premium_only` comments.
* Skip full-table legacy scans once `lektivo_db_version` is current (LIKE
  scoped to migrated content types). Tagged `package-plugin` builds fail when
  CHANGELOG lacks `## X.Y.Z` instead of falling back to Unreleased.
* Front-end Lektivo strings follow the site language (Settings → General),
  not the logged-in user’s profile language; wp-admin still uses the profile
  language.
* WordPress.org free package excludes hidden lint/test cache files; `readme.txt`
  Tested up to is 7.1; `src/functions.php` guards against direct file access.
* Plugin Check PrefixAllGlobals warnings: template body variables are scoped
  inside static closures; block-theme footer HTML uses
  `$lektivo_block_footer_html`.
* Admin meta boxes enqueue CSS/JS instead of inline `<style>`/`<script>`.
* Schedule templates and schedule queries only expose linked
  course/instructor/location titles when the linked post exists, has the
  expected type, and is publicly readable (`lektivo_get_linked_post()`).
* Free-plan creation caps (`Licensing::FREE_LIMITS`, `Admin\PostTypeLimit`)
  and related limit notices. Local content creation is unlimited in the
  free WordPress.org build.

== Upgrade Notice ==

= 1.3.2 =
Prefer Freemius updates if Lektivo still lives under coursekit/. A manual zip installs lektivo/ beside it; activate the new copy and delete unused coursekit/ — Lektivo deactivates the duplicate and keeps your content.

= 1.2.0 =
Major rename to Lektivo. Back up your site, deactivate the old plugin, activate Lektivo, and verify content before deleting the old install.
