=== Dienstplan ===
Contributors: wpdienstplan
Tags: scheduling, shifts, roster, volunteers, calendar
Requires at least: 6.3
Tested up to: 7.0
Requires PHP: 8.1
Stable tag: 3.4.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

The simple solution for digital shift scheduling.

== Description ==

Dienstplan is a self-hosted shift and duty roster planner for WordPress. It is built for organisations that run on volunteers — fire departments, emergency medical services, first-responder teams, crisis-response groups, sports clubs and similar associations — where the schedule has to work without a full-time coordinator chasing people through group chats.

Members sign up for shifts themselves on a public calendar page. Coordinators see at a glance which shifts are still open, who already volunteered, and who has not yet contributed this period. Every change is recorded so questions like "who removed me from Saturday?" have a clear answer.

The plugin runs entirely on your own WordPress install. Member data, schedules and assignments stay in your database — no SaaS account to maintain, and no recurring fee to use the free version. The only optional outbound connection is the public-holiday lookup via openholidaysapi.org (disabled by default; see **External services** below).

= What you can do with it =

* Define shift types (early/late/night, driver/paramedic, whatever your organisation uses) and assign them to your roster (multiple rosters with the Basis or Pro add-on)
* Build week-by-week or month-view rosters that members open from any page on your site via the `[dienstplan]` shortcode
* Let members sign themselves up for shifts and remove themselves again — coordinators see the change live, no email ping-pong
* Plan events (training nights, fundraisers, on-call duties) with categories and show them on the calendar; per-event signup lists come with the Basis or Pro add-on
* Group-based access restrictions for shifts and events (Basis & Pro add-on)
* Show every user their personal upcoming shifts in a dashboard widget

= Why on WordPress =

Most volunteer-run organisations already operate a WordPress site for public information, news and member communication. Adding a roster directly into that existing site means:

* No new login for your members — they use the WordPress account they already have
* No new tool to introduce to a board or executive committee
* No vendor lock-in — your data sits in your own database, exportable any time
* No data-protection paperwork for an external SaaS — everything stays on your server

= Free, Basis and Pro =

The plugin you install from WordPress.org is the **Free** version and stays free forever. It includes one shift schedule, unlimited members, shift types and future events — no artificial caps on how much you can plan.

Two optional commercial add-ons extend the same plugin in place:

* **Basis** — recurring events, shift trading between members, group-based shift restrictions, weekday restrictions for shift types, automated email notifications, and a community feedback channel. Suitable for established teams that need self-service workflows.
* **Pro** — everything in Basis plus statistics with exportable totals, a tamper-evident audit log, iCal feed and printable PDF export, and event attachments (briefings, route sheets, maps). Suitable for organisations with reporting or audit obligations.

Details, pricing and the upgrade path are documented at [wp-dienstplan.de](https://www.wp-dienstplan.de). The add-on is installed as a separate plugin alongside the Free version — your data, settings and rosters carry over automatically.

= Languages =

The plugin ships with translations for English, German, Spanish, French and Italian. The source strings are English; additional languages can be added via standard `.po`/`.mo` files in the `languages/` directory.

== Installation ==

= From WordPress.org (recommended) =

1. In your WordPress admin, go to **Plugins → Add New**.
2. Search for "Dienstplan".
3. Click **Install Now**, then **Activate**.
4. A new **Dienstplan** entry appears in the admin sidebar — open it to start configuring.

= From a ZIP file =

1. Download the latest ZIP from this plugin page or from your account at wp-dienstplan.de.
2. In your WordPress admin, go to **Plugins → Add New → Upload Plugin**.
3. Upload the ZIP and click **Install Now**, then **Activate**.

= First steps =

1. Open **Dienstplan → Overview** for a guided setup of your shift types and event categories.
2. Place the `[dienstplan]` shortcode on a page where your members should see the calendar.
3. Optionally configure holidays under **Dienstplan → Settings → Holidays** so days off are visible on the roster.

== Frequently Asked Questions ==

= What does my hosting need? =

Standard shared hosting is enough. Required: WordPress 6.3 or later and PHP 8.1 or later. No special server modules and no cron daemon (the plugin uses WordPress's built-in cron). No external accounts are required; the optional holiday feature may call openholidaysapi.org when enabled (see **External services**).

= Where is member data stored? =

Exclusively in your own WordPress database, on your own server, under your full control. The plugin does not transfer member data to us or to any third party. If you enable public holidays in settings, the plugin fetches holiday dates from openholidaysapi.org (configuration values only — see **External services** and **Privacy** below); otherwise no outbound requests are made.

= Can I show the roster on a public page? =

Yes. Use the `[dienstplan]` shortcode on any page or post. You can scope the calendar with optional parameters (e.g. `[dienstplan year="2026" shift_type_id="5"]`) — see the in-admin help for the full reference.

= How does shift signup work? =

Members log into your WordPress site, open the calendar page, and click an open shift to sign up. They can also remove themselves from a shift they already took (the change is recorded). Coordinators see the live state on the same page and in the admin overview.

= Does the plugin support multiple shift types or rosters? =

The free version includes one shift schedule with unlimited shift types and events. The Basis and Pro add-ons add multiple schedules, group-based restrictions, recurring events, statistics and an audit log.

= Will my data survive an upgrade or downgrade between Free, Basis and Pro? =

Yes. The add-on plugs into the same database tables as the free plugin. Activating Basis or Pro does not migrate or rewrite anything — it only unlocks features. Deactivating the add-on hides premium-only admin screens; your data stays in place.

= Is there a public roadmap or way to suggest features? =

Yes — the **Improve plugin** screen inside the admin (available with Basis and Pro) lets you submit suggestions, report bugs, vote on existing ideas and see what is being worked on. Issues without a license can be filed via the WordPress.org support forum for this plugin.

= Where can I get support? =

For questions about the free version, please use the WordPress.org support forum linked from this plugin page. Customers with a Basis or Pro license can additionally email info@wp-dienstplan.de with their license key.

== Screenshots ==

1. The interactive calendar on a public page — members see all upcoming shifts and sign up with a single click.
2. The admin shift planner — assign members to shifts, with an at-a-glance view of who is signed up where.
3. The events list — plan training nights, fundraisers and on-call duties with categories and per-event signup lists.
4. The personal "My shifts" dashboard widget — every member sees their own upcoming commitments at login.
5. The plan comparison screen — see at a glance which features are available in Free, Basis and Pro.

== Changelog ==

= 3.4.2 — 2026-06-09 =

* readme: clarify that openholidaysapi.org is the only optional external service in the free plugin (disabled by default; no member data sent) — aligns Description, FAQ and **External services**

= 3.4.1 — 2026-06-09 =

* Fix: restore the “My duties” dashboard widget (`MyShiftsWidget`) — registration was accidentally dropped in 3.4.0 during the plan-schema refactor

= 3.4.0 — 2026-06-09 =

wp.org compliance (Guideline 5): the free plugin is a fully functional single-schedule roster with no trialware patterns.

* Multi-plan management moved entirely to the Premium add-on (no `dienstplan_plans` table, no PlanRepository, no “More plans – Pro” teaser in the free plugin)
* Plan-bound queries, weekday restrictions and premium event tables (recurrences, tasks, sign-ups, audit log) live in Premium only; free exposes extension hooks with permissive defaults
* `do_action` payloads for shift-type and event saves now pass sanitized POST data (`map_deep(…, 'sanitize_text_field')`)
* Sanitization hardening in holiday settings retained from prior passes

= 3.3.0 — 2026-06-01 =

wp.org compliance (Guideline 5): no feature code gated by license limits in the free plugin.

* Removed all quantity limits (members, plans, shift types, groups, events) from the free plugin
* Groups moved entirely to the Premium add-on — including the database tables and the access-control logic; the free plugin no longer creates or queries any group storage and exposes extension hooks only (dienstplan_filter_accessible_plans, dienstplan_user_can_access_event)
* Event creation dialog simplified to a single-column layout
* Plans comparison page: added "Event sign-ups & tasks" and clearer feature descriptions on the overview
* SQL and i18n hardening from prior releases retained

= 3.2.2 — 2026-05-22 =

Folge-Fix zur 3.2.1: SVG-Icons in Admin-Templates lösen jetzt direkt `wp_kses()` mit der Plugin-Allow-List aus, statt über einen Klassen-Wrapper. Plugin-Check erkennt Custom-Klassen-Methoden nicht als Escape-Funktionen — der direkte `wp_kses()`-Aufruf ist Reviewer- und Plugin-Check-konform.

* `\Dienstplan\Security::ksesSvg($svg)` durch `wp_kses( $svg, \Dienstplan\Security::svgAllowedHtml() )` ersetzt — Allow-List bleibt dieselbe, nur der Wrapper ist weg
* 20 Stellen in `templates/admin/schichtplaner.php`, `templates/admin/events.php`, `templates/admin/partials/tab-groups.php`, `templates/admin/partials/tab-categories.php` (plus zwei Stellen in Premium `templates/admin/partials/tab-emails.php`) entsprechend angepasst

= 3.2.1 — 2026-05-21 =

Adressiert die Punkte aus der zweiten Reviewer-Runde des WordPress-Plugin-Review-Teams. Keine Funktionsänderungen, ausschließlich Härtung der Ausgabe-Routinen und Klarstellung der External-Service-Doku.

* External-Services-Block in readme: Provider, Terms-of-Use-Link und Privacy-Policy-Link der STÜBER SYSTEMS GmbH jetzt im vom Reviewer-Beispiel vorgegebenen Format direkt im selben Absatz wie der Service-Provider
* Sicherheits-Härtung der Inline-CSS-Ausgabe: `wp_add_inline_style()` bekommt das dynamische Kalender-Color-CSS jetzt durch `wp_strip_all_tags()` defensiv vorgefiltert
* ICS-Export der Dashboard-„Meine Schichten"-Widget-Schaltfläche: Zeitkomponenten via expliziter `(int)`-Cast + `sprintf`, finaler Output line-by-line mit `wp_strip_all_tags()` statt direktem `echo`
* Calendar-Shortcode im Admin-Kontext wird jetzt über `do_shortcode('[dienstplan]')` ausgespielt statt über direktes `echo $this->calShortcode->render([])`
* SVG-Icons in Admin-Templates laufen jetzt durch `wp_kses()` mit einer SVG-Allow-List statt mit `phpcs:ignore`-Kommentaren
* Dutzende ternäre `echo`-Ausdrücke in HTML-Attributen explizit mit `esc_attr()` umschlossen
* Inline-Event-Handler in Statistics-Template auf `data-…`-Attribute + `addEventListener` umgestellt (war noch ein „echo `onchange=…`"-Rest)
* Cron-Schedule-Key `'yearly'` → `'dienstplan_yearly'` (eigene Identifier-Prefix-Disziplin auch für Cron-Hooks)
* readme: `Tested up to: 7.0`

= 3.2.0 — 2026-05-06 =

Adressiert die Punkte aus der Erstprüfung des WordPress-Plugin-Review-Teams. Die Änderungen unter der Haube sind weitreichend, im Funktionsumfang ändert sich nichts. Greenfield-Release ohne Migration für Bestandsdaten — die Free-Variante erscheint hier zum ersten Mal in der offiziellen Directory.

* Einheitlicher Plugin-Prefix `dienstplan_` für sämtliche AJAX-Actions, Nonces, Options-Keys, Transients, Cron-Hooks, Asset-Handles, JS-Globals, Widget-IDs, sessionStorage-Keys, PHP-Konstanten **und CSS-Klassennamen** — die alten Kurzformen `dp_`/`dp-` und `wpd_`/`wpd-` sind ersatzlos entfernt
* Top-Level-Menü-Position auf 80 abgesenkt, damit der Plugin-Eintrag unterhalb der WordPress-Kernbereiche (Tools/Settings) landet
* Sämtliche Inline-`<style>`- und `<script>`-Blöcke entfernt — die Inhalte werden jetzt über `wp_enqueue_*` mit `wp_add_inline_style/script` und ggf. `wp_localize_script` ausgespielt
* Frontend-Shortcode `[termine]` heißt jetzt `[dienstplan_termine]` (eindeutiger Plugin-Prefix)
* Externe API `openholidaysapi.org` ist im neuen Abschnitt **External services** mit Datenflüssen, Imprint-Link und Privacy-Policy-Link transparent dokumentiert
* `wpd_is_pro()` / `wpd_is_basis()` heißen jetzt `dienstplan_is_pro()` / `dienstplan_is_basis()` und werden ohne `function_exists()`-Wrap deklariert

= 3.1.0 — 2026-04-30 =

Vorbereitung für die WordPress.org-Erstpublikation. Wegen des Reviewer-Feedbacks wurde diese Version nicht öffentlich ausgeliefert; siehe 3.2.0.

* Fully English source strings; localisations for German, Spanish, French and Italian shipped in `languages/`
* Public hook API (`dienstplan_*` filters and actions) for clean extension by add-ons or custom code — full reference in `docs/hooks-reference.md`
* Tier structure (Free / Basis / Pro) documented in the readme
* Optional commercial add-on (Basis / Pro) available separately at wp-dienstplan.de — installs alongside without data migration

== Upgrade Notice ==

= 3.4.2 =
Documentation-only update: clearer readme wording for the optional OpenHolidays API. No code changes — safe to skip if you are already on 3.4.1.

= 3.4.1 =
Restores the “My duties” dashboard widget missing since 3.4.0. Recommended for all 3.4.0 installs.

= 3.4.0 =
wp.org compliance release: free is a single fully functional schedule; multi-plan, weekday restrictions, event sign-ups/recurrences and audit log require the Premium add-on. Update Premium to 1.4.0 at the same time if you use it.

= 3.3.0 =
wp.org compliance release: free tier is no longer capped by artificial limits; groups require the Premium add-on. Update Premium to 1.3.0 at the same time if you use it.

= 3.2.2 =
Folge-Fix zur 3.2.1: SVG-Icon-Output in Admin-Templates läuft jetzt direkt durch `wp_kses()` statt über einen Klassen-Wrapper. Plugin-Check-konform, keine Funktionsänderungen.

= 3.2.1 =
Sicherheits-Härtung der Ausgabe-Routinen aus der zweiten wp.org-Review-Runde. Keine Funktionsänderungen, keine Daten-Migration nötig.

= 3.2.0 =
Reviewer-konforme Umbenennung: alle internen IDs nutzen jetzt den Prefix `dienstplan_`. Datenbank-Schemata bleiben, Options-Keys werden hart auf den neuen Prefix umgestellt.

= 3.1.0 =
First public WordPress.org release. If you previously used the plugin from wp-dienstplan.de directly, your data, rosters and settings carry over without any migration step.

== External services ==

This plugin connects to the public OpenHolidays API (`https://openholidaysapi.org`) to retrieve official public-holiday dates for the country and (optional) region configured under **Dienstplan → Settings → Holidays**. The dates are stored in your own WordPress options and shown as background highlights on the calendar so members can see at a glance which days are public holidays. The feature is fully optional — clearing the holiday country in settings disables every outbound call.

It sends a request to `https://openholidaysapi.org` in three situations:

* `GET /Countries` — once, when the admin opens the holiday-settings page and the country list has not been cached yet. No payload, only an HTTP request.
* `GET /Subdivisions?countryIsoCode=XX` — once per country, when the admin selects a country in the settings UI to populate the region dropdown. The configured country code is transmitted as a query parameter.
* `GET /PublicHolidays?countryIsoCode=XX&languageIsoCode=YY&validFrom=YYYY-01-01&validTo=YYYY-12-31[&subdivisionCode=ZZZZ]` — once per year via WordPress cron, plus on demand when the admin triggers a manual refresh from the settings page. The configured country code, region code (if any), language code and requested year are sent as query parameters.

In every case, **only configuration values entered by the administrator are transmitted** (country / region / language / year). No member names, email addresses, shift data or audit-log entries are sent.

This service is provided by STÜBER SYSTEMS GmbH as an Open Data project: [terms of use](https://www.stueber.de/en/legal/imprint.php), [privacy policy](https://www.stueber.de/en/legal/privacy.php). Project documentation and the data-source overview are available at [https://www.openholidaysapi.org](https://www.openholidaysapi.org).

== Privacy ==

Dienstplan is built so that all member-related data — names, email addresses (from the WordPress user account), shift assignments, signup history, audit-log entries — stays exclusively in your own WordPress database. The free plugin makes no outbound HTTP requests other than the OpenHolidays calls documented in **External services** above.

= If you use the optional Basis or Pro add-on =

The commercial add-on contacts a separate license server at `lizenz.wp-dienstplan.de` for license activation and a daily license-status check. The data transmitted is limited to the license key, your site URL, and a status flag — no member data, no shift data and no email addresses are sent. License-server activity is logged with industry-standard webserver logs (IP, timestamp, user agent) for fraud prevention; the privacy policy at [wp-dienstplan.de](https://www.wp-dienstplan.de) documents this in detail. The license server is **not** active in the free plugin distributed via WordPress.org.

= Cookies and trackers =

Dienstplan sets no cookies of its own and uses no third-party trackers. The plugin relies on WordPress's standard authentication cookies for member login and on `localStorage` for remembering the last selected roster on multi-roster pages.
