=== Carta Online ===
Contributors: carta-online
Donate link:
Tags: Carta,cursusadministratie,aanbod,inschrijven,evenement
Requires at least: 5.3
Requires PHP: 8.1
Tested up to: 7.0
Stable tag: 2.18.3
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Author: Carta Online
Author URI: https://www.cartaonline.nl
Plugin URI: https://www.cartaonline.nl/carta-wordpress-plugin/

Use the Carta Online WordPress plugin to embed your offerings on your website. 

== Description ==

The Carta Online Course Administrator WordPress plugin enables integration, publishing and processing of your offerings in Carta.
With the co-offerlist shortcode your offerings can easily be integrated in your existing WordPress web pages.

== Installation ==

1. Upload the plugin files to the `/wp-content/plugins/plugin-name` directory, or install the plugin through the WordPress plugins screen directly.
1. Activate the plugin through the 'Plugins' screen in WordPress
1. Use the 'carta online' screen to configure the plugin
1. Direct download link: [Carta Online plugin](https://downloads.wordpress.org/plugin/carta-online.zip "Carta Online WordPress plugin") 

Detailed installation instructions can be found at [Carta Online](https://www.cartaonline.nl/carta-wordpress-plugin/ "Carta Online WordPress plugin")

== Frequently Asked Questions ==

= Does the plugin work without a valid Carta License? =

No, you need to obtain a Carta Online API key to use the plugin. A demo site is available on request. Send an e-mail with your project details to info@cartaonline.nl to obtain a demo API-KEY.

= Can I hide the thumbnail picture or location in the generated list of offerings? =

Yes, You can. You can add styling to the plugin, or to your theme. If needed every rendered object can be styled individually, by type or by usage. Use the styling option display:none to hide the disable unneeded elements.

== Screenshots ==

1. Example of rendered offerings

== Changelog ==

= 2.18.3-beta4 =
- Branding: fixed the offer reconciliation failing when a brand is configured. The v2 offer filter is now sent in the exact shape the API requires, so offers are correctly scoped to the configured brand when fetched.
- Settings: saving one settings tab (Connection / Rendering / Error handling) no longer clears the fields of the other tabs.
- Settings: fixed duplicate admin notices ("Settings saved" / "Cache cleared") shown after saving.

= 2.18.3-beta3 =
- Raised minimum required WordPress version to 5.3.
- Compliance: resolved Plugin Check findings (WordPress coding-standards annotations for direct DB access on plugin-owned cache tables and a read-only nonce check). No functional changes.

= 2.18.3-beta2 =
- co-test: in test mode the shortcode now reads live from the portal entered in the form (URL + API Key take precedence over the saved settings) instead of the local cache.
- co-test: leaving both URL and API Key empty now falls back to the saved settings (runs against the configured portal) instead of refusing with a validation error.
- Cache: default "local cache update frequency" is now 1440 minutes (once per day) and is shown in the settings field when the value is missing after an upgrade.
- Tested with WordPress 7.0.
- Portal URL: a trailing slash in the portal address is now accepted and normalised, so both `https://host` and `https://host/` work.

= 2.18.3-beta1 =
- Cache: multi-value dynamic items (e.g. `Website_Capability`) are now split on `;` into one row per value, so a `dynamic-filter` on a single value matches offerings that list several capabilities. Schema 1.2.0 (surrogate `Id` primary key + UNIQUE(Identifier, Name, Value)); existing cached offerings are re-hydrated automatically on upgrade.
- Cache: `dynamic-filter` shortcode now trims the filter key/value, so a stray space no longer breaks the match.

= 2.18.2 =
- Search: `[co-offerlist search=...]` better wildcard use in URL based matching
 
= 2.18.1 =
 - Cache: dynamic-filter shortcode attribute (e.g. `dynamic-filter="DetailPagina=/slug/"`) is honoured again. Each offering's `payload.dynamicItems` is now flattened into a new junction table `CartaOnline_OfferingDynamicItems` (Identifier, Name, Value) so filters hit an indexed EXISTS clause. Schema 1.1.0 — every existing row is automatically re-hydrated on upgrade so the new table fills without operator action.
 - Search: `[co-offerlist search=...]` works again. Search terms wrapped in literal double quotes (the standard pattern emitted by `carta_online_get_filter_from_url()` and the `?s=...` shortcode template) are now stripped before the SQL LIKE clause, matching the upstream API's behaviour.
 - Renderer: `%dyn_<KeyName>%` placeholders are now wrapped in `<div class="co-offer-dyn-<KeyName>">…</div>` so themes can target individual dynamic items without parsing the rendered string. CSS class is sanitised via `sanitize_html_class()`.

= 2.17.0 =
 - Cache: typed columns Status, DateStart, DateEnd, Location are now promoted from the primary instance of each offering, so shortcode filters like status=Open;AlmostFull match again
 - Added Tools → "Carta Online aanbod" admin page: live SyncState, searchable cache table, payload viewer, one-click "Wipe cache and reconcile now" button
 - Expertise list ([co-expertiselist]) now reads from the local cache (only fields that actually appear in offerings) instead of /stock/fields
 - Field filter is now case-insensitive so dynamic-fields cookies (lower-cased) match the cache's canonical casing
 - Webhook subscriptions: only relevant slots (2 / 3) are listed; slot id detection now reads `identifier` (matches Carta API), claim/release flow works end-to-end
 - Webhook admin moved to Settings menu — fixes the 404 caused by hosting it as a submenu of the existing file-path menu slug
 - API Key field is now masked (type=password) on the Settings page
 - Single "Secret rotated…" admin notice instead of the duplicate it used to render
 - dynamicItems shortcode filter no longer throws — logged via carta_online_report_error and ignored, so pages keep rendering
 - cacheless_request now wraps array responses (e.g. /api/v1/webhook) in a {status, result} envelope; PUT/DELETE methods supported via wp_remote_request
 - Dutch translations added for every new admin string (Webhook subscriptions, Tools page, settings field labels)
 - Internal: shared `co_localcache_promote.php` helper used by both the drain (write side) and the repository (read side) so the typed columns and the rendered cards always agree on the primary instance
 - co-offerlist template now supports generic `%dyn_<KeyName>%` placeholders for every key in an offering's dynamicItems object (T13469). Non-scalar values are skipped.
 - When the API rate limit (HTTP 429) is reached and exponential backoff is exhausted, an admin e-mail is sent (force-routed even when general error reporting is off, throttled to once per hour) asking to contact Carta support to raise the limit.
 - Instance promotion: dateStart/dateEnd/location are now correctly drilled out of `instances[].planning[]` (the OfferModel's actual schedule layer) instead of `instances[]` itself, fixing missing locations on ClassMother/ClassSingle cards. Offerings with no scheduled planning render with empty location/date, which is the truthful representation — no sentinel substitutes.

= 2.16.0 =
 - Added local DB cache: offerings are now served from a WordPress-side store, refreshed continuously by webhooks (slot 3 / Synchronisation complete) and reconciled periodically as a backstop
 - Added Webhook subscriptions admin tab to claim / release slots and rotate the per-install webhook secret
 - Added realtime occupation overlay (js/co_realtime.js): cards stay rendered from the cache, only the live trainee count is refreshed via a single batched admin-ajax call
 - Added 429 / 5xx exponential backoff with jitter on every API call; web context is hard-capped at 1 s so live pages never block
 - Replaced deprecated get_page_by_title() calls with WP_Query (WordPress 6.2+ compliance)
 - All connection-affecting setting changes wipe the cache and trigger a fresh reconciliation; portal/key changes also release claimed webhook slots

= 2.15.1 = 
 - Added planning details for CourseSingle in co-offerlist shortcode with planning-template attribute

= 2.15.0 =
 - Security: Fixed Stored Cross-Site Scripting (XSS) vulnerability via admin settings (CVE-2026-1071)
 - Added input sanitization callback for all plugin settings
 - Added output escaping (esc_attr, esc_textarea) for all settings field rendering
 - Added sanitization for meta box save operations
 - Added output escaping for meta tag generation
 - Hardened all input and output against script injection
 - Tested with Plugin Check (PCP)

= 2.14.1 =
 - Fixed issue with rendertype teacherlist

= 2.14.0 =
 - Added company description to co-credits
 - changed css class name co_credits_description to co_credits_scheme

= 2.13.0 =
 - refactored session use
 - refactored code to be more robust and more WordPress compliant

= 2.12.3 =
 - added type motherplanning to co-detail shortcode

= 2.12.2 =
 - Clear cache after update 
 - Clear cache after save settings 
 - Removed option "Clear cache after save settings", when saving settings cache will always be cleared
 - Set default settings after first install

= 2.12.1 =
 - Cache integrity check improved

= 2.12.0 =
 - Improved robustness, error handling
 - Added short-term cache for non-200 API results
 - Render type money added

= 2.11.4 =
 - Fixed error accessing field subscribeURL via co-detail rendering

= 2.11.3 =
 - Fixed API compatibility with old portals

= 2.11.2 =
 - Improved rendering of price, price components and calculated price

= 2.11.1 =
 - Fixed API backwards compatibility

= 2.11.0 =
 - Rendering (optional) priceComponents

= 2.10.1 =
 - External subscribe link
 - WordPress 6.5

= 2.10.0 = 
 - Naming Consistency
 - Cache folder location
 
= 2.9.4 =
 - co_datatable more robust error handling and cache usage
 
= 2.9.3 =
 - Added API call timeout 
 - Added Grace period timeout 
 - Added Clear API Cache option in settings

= 2.9.2 =
 - WordPress 6.3 tested
 - Backwards compatibility mixed case usage in [co-detail] shortcode

= 2.9.1 =
 - Fixed bug in planning-template returning incorrect location and module information
 - Fixed rendering bug in admin settings on first use

= 2.9.0 =
 - Added support for class filtering via auto-class en class attributes in co-offerlist

= 2.8.2 =
 - Fixed bug in Widget rendering

= 2.8.1 =
 - Minor fixes

= 2.8.0 =
 - Google Tag Manager
 - PHP 8 compatibility

= 2.7.1 =
 - co_datatable kolom voor planning pagina toegevoegd
 
= 2.7.0 =
 - Branding added
 - Fix casing co-detail planning subfields

** Version 2.6.1
 - WordPress 6.1.1 tested
 - Fix default filter for datatables

** Version 2.6.0
 - WordPress 6.1 tested
 - Datatable php plugin support for dynamic fields

** Version 2.5.0
 - WordPress 6.0 tested
 - Datatable php plugin provided on (base-url)/wp-content/plugins/carta-online/includes/co_datatable.php

** Version 2.4.5
 - Compatibility met de nieuwste versie van Yoast SEO verbeterd

** Version 2.4.4
 - corrected typo corrected in label "Enable cache limiter"

** Version 2.4.3
 - Added a setting "session cache limiter"  

** Version 2.4.2
 - Fix back button in browser resulting in: ERR_CACHE_MISS 	

** Version 2.4.1
 - Added %studyLoadTotal% to template co-offerlist
 - Added %studyLoadPerWeek% to template co-offerlist
 - Tested 5.8.2 compatibility

** Version 2.4.0
 - You can now use a co_search attribute (meta field) to overrule default url-based-filtering
 - Tested WordPress 4.8 compatability

** Version 2.3.8
 - Added special render option selectsize to be used for co_filter rendering with all options shown
 - Added template field %seoName% for rendering seo friendly name of class

** Version 2.3.7
 - Added special render option money-thousand-separator with default value null
 - Added special render option money-decimal-separator with default value null

** Version 2.3.6
 - Enabled rendering of (short) day names
 - Corrected class names. Renamed "co-offer-datestartdaynameshort" to "co-offer-planning-datestartdaynameshort"
 - Corrected class names. Renamed "co-offer-datestartdaynamefull"  to "co-offer-planning-datestartdaynamefull" 
 - Added class "co-offer-planning-datestartdaynameshort" in offering template
 - Added class "co-offer-planning-datestartdaynamefull" in offering template
 - Short or long name of day of week added to shortcode offering template
 - Improved exception handling
 - Fixed bug in Google Ads plugin

** Version 2.3.5
 - Rendering Techerlist based on links to teacher detail page added
 - Short or long name of day of week added to shortcode offering planning-template
 - 'Dates in YYYY will be announced later' will be rendered if start date is 01-01 and special render option dateunknownvalue="01-01"

** Version 2.3.4
 - User defined text on submitbutton when rendering offerings using the widget is not correctly generated.
 - Filtering on fields/expertise when field description contains space fixed
  
** Version 2.3.3
 - Improved session management.
 - Google ads tag renering changed. Altered page_view to view_item
 - Workaround for bug in rendering Image URL in CartaOnline
 - Fixed several issues with co-expertiselist

** Version 2.3.2
 - Fixed bug when using a Location filter on a Field page  
 
** Version 2.3.1
 - Compatibility with newest Carta Online portal (20.6)

** version 2.3
 - Tested compatibility 5.5 

** version 2.2.3
 - Introduction of special field pages with filtering on field
 - Introduction of implementation of v2 of api
 - fixed bug in google gtag script
 - new template variables: dateStartDay, dateStartMonth, dateStartYear, fieldOpen, fields, fieldLinks, fieldClose, detail_mainTeacher, detail_Teacherlist, detail_ContactDays, detail_Price, detail_PriceComponents, detail_PriceInfo
 - support for price components
 - new special render options added: carta-field-id
 - fixed bug in [co-test] shortcode

** version 2.2.2
 - Improved version of co-filter

** version 2.2.1
 - Introduced co-filter shortcode
 - Added attribute 'use-filter-selection' to filter offerlist based on selection in 'co-filter'
 
** version 2.2.0
 - Added support for Google Ads dynamic remarketing

** version 2.1.24
 - Compatibility with PHP 7.4 and WordPress 5.3 tested
 - Fixed issue with shortcode attributes containging '-' in PHP 7.x
 - Caching of 404 results added to further reduce load on server when aggressive bots visit website

** version 2.1.23
 - co-test handling of special characters improved

** version 2.1.22
 - co-detail redirect to searchpage if class not found
 - co-test fixed some bugs
 - tested wordpress 4.5.2

** version 2.1.21
 - co-expertiselist

** verison 2.1.20
 - co_expertiselist filter bug fixed
 - field-filter render option added

** version 2.1.19
 - co-offerlist added template substitution for category

** version 2.1.18
 - co-detail added fields minimumNumberOfTrainees and maximumNumberOfTrainees
 - co-detail added type="documentlist". Generates list of hyperlinks to public documents.

** version 2.1.17
 - filter-by-field fixed
 - class naming improved

** version 2.1.16
 - added [co-test] for ad-hoc shortcode testing 

** version 2.1.15
 - added rewrite base for detail pages. /rewrite_base/classid/classname => /details/?class=classid&cn=classname

** version 2.1.14
 - first-only flag added for rendering only info for first planning item
 - page-size may now be max 1000 records
 - max-one flag added for rendering exactly one planning item when multiple daugters are present
 - Nothing will be rendered for 'datelist' detail field if no planning is present
 - Detail field startendtime added  

** version 2.1.13
 - No detail picture rendered in case of empty picture
 - datelist, list of all course dates, added to detail-rendering

** version 2.1.12
 - course description added to URL
 - fix for detail page check

** version 2.1.10
 - dynamic fields in company list
 - Automatic meta tags based on course/class summary in detailpage
 - Automatic page titel base on course/class name in detailpage
 - Added Search attribute for use of shortcode in 'search results' page

** version 2.1.9
 - Added posibility for rendering a collapsable planning-overview within a offerable item

** version 2.1.8
 - Removed unneeded ?> at the end of some php files to improve compatibility

** version 2.1.7
 - special render option added: striped-zeroes
 - Support for alternate subscription page added

** version 2.1.6
 - Fix in widget for unchecking 'url-based-filter'

** version 2.1.5
 - Added options for rendering offerlist:
 - Render template and submit-caption can now be defined for each widget and shortcode

** version 2.1.4
- URL based filtering now using REQUEST_URI

** version 2.1.3 **
- API call result CompanyList 

** version 2.1.2 **
- readme.txt in English + Translation in WordPress 

** version 2.1.1 **
- Minimal Carta Online version required: 17.0.10.4747
- Fix in filtering url-based-content

** version 2.1.0 **
- Added new shortcode co-teacher
- Added url-based-filtering to shortcode co-offerlist and to widget
- Added fields filter to co-offerlist and to widget
- Added co-expertiselist to list all known fields
- Added shortcode co-detail for rendering detail pages based on Carta content
- Added shortcode co-teacher for rendering teacher profile
- Added special content type teacher-profile for attaching WordPress teacher profile to Carta Person
- Compatibiliteit WordPres 4.8
- Consistency naming shortcodes and class names

** version 2.0.10 **
- Local translation precedes online translation

** version 2.0.9 **
- Text only  

** version 2.0.8 **
- Fixed bug in admin page

** version 2.0.6 **
- Text domain changed to plugin name (carta-online)

** version 2.0.4 **
- Meta information translation

** version 2.0.3 **
- Added dutch translation
- Extended filter with fields / specialsm
- SEO friendly generated detail page
- Wordpress 4.7.5 compatibility

** initial version 1.0 **
- [co_offerlist] shortcode en widget

== Upgrade Notice ==

= 2.12.3 =
Added support for motherplanning type in co-detail shortcode. This is a minor update with no breaking changes.

= 2.12.3 =
Added support for motherplanning type in co-detail shortcode. This is a minor update with no breaking changes.

= 2.1.0 =
Breaking change: All class names are now using- in stead of _

= 2.0.3 =
 
= 1.0.1 =
Compatibility issues

= 1.0
No previous versions publicly available