From 5bad6219ebc889aa984eb41021ac405d4efe1d37 Mon Sep 17 00:00:00 2001
From: Glynn Quelch
Date: Thu, 9 Apr 2026 12:12:03 +0100
Subject: [PATCH 1/2] Do not unregister the tikckets block for other post
types, just modify its insertaion so it cant be inserted when nont on an
event post
---
OLD/README.md | 105 ++
OLD/build/blocks/calendar/block.json | 135 +++
OLD/build/blocks/calendar/index-rtl.css | 1 +
OLD/build/blocks/calendar/index.asset.php | 1 +
OLD/build/blocks/calendar/index.css | 1 +
OLD/build/blocks/calendar/index.js | 1 +
OLD/build/blocks/calendar/style-index-rtl.css | 1 +
OLD/build/blocks/calendar/style-index.css | 1 +
OLD/build/blocks/calendar/view.asset.php | 1 +
OLD/build/blocks/calendar/view.js | 1 +
OLD/build/blocks/countdown/block.json | 28 +
OLD/build/blocks/countdown/index.asset.php | 1 +
OLD/build/blocks/countdown/index.js | 1 +
.../blocks/countdown/style-index-rtl.css | 1 +
OLD/build/blocks/countdown/style-index.css | 1 +
OLD/build/blocks/countdown/view.asset.php | 1 +
OLD/build/blocks/countdown/view.js | 1 +
OLD/build/blocks/event-info/block.json | 48 +
OLD/build/blocks/event-info/index-rtl.css | 1 +
OLD/build/blocks/event-info/index.asset.php | 1 +
OLD/build/blocks/event-info/index.css | 1 +
OLD/build/blocks/event-info/index.js | 1 +
OLD/build/blocks/event-tickets/block.json | 27 +
OLD/build/blocks/event-tickets/index-rtl.css | 1 +
.../blocks/event-tickets/index.asset.php | 1 +
OLD/build/blocks/event-tickets/index.css | 1 +
OLD/build/blocks/event-tickets/index.js | 1 +
.../blocks/event-tickets/style-index-rtl.css | 1 +
.../blocks/event-tickets/style-index.css | 1 +
OLD/build/blocks/external-link/block.json | 30 +
OLD/build/blocks/external-link/index-rtl.css | 1 +
.../blocks/external-link/index.asset.php | 1 +
OLD/build/blocks/external-link/index.css | 1 +
OLD/build/blocks/external-link/index.js | 1 +
OLD/build/blocks/inner-blocks/block.json | 18 +
OLD/build/blocks/inner-blocks/index.asset.php | 1 +
OLD/build/blocks/inner-blocks/index.js | 1 +
.../blocks/inner-blocks/style-index-rtl.css | 1 +
OLD/build/blocks/inner-blocks/style-index.css | 1 +
OLD/build/blocks/loop-event-info/block.json | 75 ++
.../blocks/loop-event-info/index-rtl.css | 2 +
.../blocks/loop-event-info/index.asset.php | 1 +
OLD/build/blocks/loop-event-info/index.css | 2 +
OLD/build/blocks/loop-event-info/index.js | 1 +
.../blocks/past-events-notice/block.json | 31 +
.../blocks/past-events-notice/index.asset.php | 1 +
OLD/build/blocks/past-events-notice/index.js | 1 +
OLD/build/blocks/upcoming-events/block.json | 78 ++
.../blocks/upcoming-events/index-rtl.css | 1 +
.../blocks/upcoming-events/index.asset.php | 1 +
OLD/build/blocks/upcoming-events/index.css | 1 +
OLD/build/blocks/upcoming-events/index.js | 1 +
OLD/build/js/admin.asset.php | 1 +
OLD/build/js/admin.js | 1 +
OLD/build/variations/index.asset.php | 1 +
OLD/build/variations/index.js | 1 +
OLD/plugin.php | 98 ++
OLD/postcss.config.js | 19 +
OLD/src/assets/js/admin.js | 58 +
OLD/src/back-compat.php | 28 +
OLD/src/blocks/calendar/block.json | 130 ++
OLD/src/blocks/calendar/calendar.js | 245 ++++
OLD/src/blocks/calendar/color-panel/index.js | 58 +
OLD/src/blocks/calendar/editor.scss | 14 +
OLD/src/blocks/calendar/index.js | 266 +++++
OLD/src/blocks/calendar/style.scss | 275 +++++
OLD/src/blocks/calendar/view.js | 15 +
OLD/src/blocks/common.scss | 60 +
OLD/src/blocks/countdown/block.json | 28 +
OLD/src/blocks/countdown/index.js | 44 +
OLD/src/blocks/countdown/style.scss | 28 +
OLD/src/blocks/countdown/view.js | 62 +
OLD/src/blocks/event-info/block.json | 48 +
OLD/src/blocks/event-info/editor-compat.scss | 30 +
OLD/src/blocks/event-info/editor.scss | 202 ++++
OLD/src/blocks/event-info/index.js | 1055 +++++++++++++++++
OLD/src/blocks/event-tickets/block.json | 27 +
.../blocks/event-tickets/draggable/index.js | 94 ++
OLD/src/blocks/event-tickets/editor.scss | 371 ++++++
OLD/src/blocks/event-tickets/index.js | 371 ++++++
.../search-list-control/index.js | 163 +++
OLD/src/blocks/event-tickets/style.scss | 42 +
.../ticket-data-control/index.js | 766 ++++++++++++
OLD/src/blocks/external-link/block.json | 22 +
OLD/src/blocks/external-link/editor.scss | 3 +
OLD/src/blocks/external-link/index.js | 24 +
OLD/src/blocks/inner-blocks/block.json | 15 +
OLD/src/blocks/inner-blocks/index.js | 25 +
OLD/src/blocks/inner-blocks/style.scss | 3 +
OLD/src/blocks/loop-event-info/block.json | 61 +
OLD/src/blocks/loop-event-info/editor.scss | 0
OLD/src/blocks/loop-event-info/index.js | 97 ++
OLD/src/blocks/loop-event-info/index.scss | 0
OLD/src/blocks/past-events-notice/block.json | 27 +
OLD/src/blocks/past-events-notice/index.js | 21 +
OLD/src/blocks/upcoming-events/block.json | 67 ++
OLD/src/blocks/upcoming-events/index.js | 279 +++++
OLD/src/blocks/upcoming-events/index.scss | 71 ++
OLD/src/calendar-functions.php | 292 +++++
OLD/src/classes/class-se-admin.php | 81 ++
OLD/src/classes/class-se-block-variations.php | 170 +++
OLD/src/classes/class-se-blocks.php | 898 ++++++++++++++
OLD/src/classes/class-se-calendar-export.php | 116 ++
OLD/src/classes/class-se-calendar.php | 452 +++++++
OLD/src/classes/class-se-event-post-type.php | 561 +++++++++
.../classes/class-se-event-query-dates.php | 157 +++
.../classes/class-se-rest-ticket-products.php | 245 ++++
OLD/src/classes/class-se-settings.php | 608 ++++++++++
OLD/src/classes/class-se-template-loader.php | 153 +++
OLD/src/event-functions.php | 530 +++++++++
OLD/src/init.php | 11 +
OLD/src/rest-api.php | 30 +
OLD/src/template-functions.php | 488 ++++++++
OLD/src/template-hooks.php | 37 +
OLD/src/templates/archive.php | 59 +
OLD/src/templates/calendar/calendar-body.php | 53 +
.../templates/calendar/calendar-container.php | 37 +
.../templates/calendar/calendar-header.php | 33 +
OLD/src/templates/calendar/calendar-main.php | 119 ++
.../calendar/calendar-mobile-events.php | 48 +
.../templates/calendar/calendar-top-bar.php | 42 +
OLD/src/templates/calendar/day/day.php | 44 +
OLD/src/templates/calendar/day/event.php | 91 ++
OLD/src/templates/calendar/day/events.php | 34 +
.../templates/calendar/mobile-events/day.php | 33 +
.../templates/calendar/mobile-events/nav.php | 46 +
.../calendar/mobile-events/today.php | 25 +
.../calendar/top-bar/current-date.php | 17 +
OLD/src/templates/calendar/top-bar/nav.php | 31 +
OLD/src/templates/calendar/top-bar/today.php | 21 +
OLD/src/templates/content-archive.php | 20 +
OLD/src/templates/content-single.php | 20 +
OLD/src/templates/single.php | 36 +
.../variations/blocks/unregister_blocks.js | 9 +
OLD/src/variations/index.js | 2 +
OLD/src/variations/query-loop-events/block.js | 128 ++
OLD/src/woocommerce-hooks.php | 95 ++
simple-events (1).zip | Bin 0 -> 196822 bytes
src/variations/blocks/unregister_blocks.js | 15 +-
139 files changed, 11819 insertions(+), 3 deletions(-)
create mode 100644 OLD/README.md
create mode 100644 OLD/build/blocks/calendar/block.json
create mode 100644 OLD/build/blocks/calendar/index-rtl.css
create mode 100644 OLD/build/blocks/calendar/index.asset.php
create mode 100644 OLD/build/blocks/calendar/index.css
create mode 100644 OLD/build/blocks/calendar/index.js
create mode 100644 OLD/build/blocks/calendar/style-index-rtl.css
create mode 100644 OLD/build/blocks/calendar/style-index.css
create mode 100644 OLD/build/blocks/calendar/view.asset.php
create mode 100644 OLD/build/blocks/calendar/view.js
create mode 100644 OLD/build/blocks/countdown/block.json
create mode 100644 OLD/build/blocks/countdown/index.asset.php
create mode 100644 OLD/build/blocks/countdown/index.js
create mode 100644 OLD/build/blocks/countdown/style-index-rtl.css
create mode 100644 OLD/build/blocks/countdown/style-index.css
create mode 100644 OLD/build/blocks/countdown/view.asset.php
create mode 100644 OLD/build/blocks/countdown/view.js
create mode 100644 OLD/build/blocks/event-info/block.json
create mode 100644 OLD/build/blocks/event-info/index-rtl.css
create mode 100644 OLD/build/blocks/event-info/index.asset.php
create mode 100644 OLD/build/blocks/event-info/index.css
create mode 100644 OLD/build/blocks/event-info/index.js
create mode 100644 OLD/build/blocks/event-tickets/block.json
create mode 100644 OLD/build/blocks/event-tickets/index-rtl.css
create mode 100644 OLD/build/blocks/event-tickets/index.asset.php
create mode 100644 OLD/build/blocks/event-tickets/index.css
create mode 100644 OLD/build/blocks/event-tickets/index.js
create mode 100644 OLD/build/blocks/event-tickets/style-index-rtl.css
create mode 100644 OLD/build/blocks/event-tickets/style-index.css
create mode 100644 OLD/build/blocks/external-link/block.json
create mode 100644 OLD/build/blocks/external-link/index-rtl.css
create mode 100644 OLD/build/blocks/external-link/index.asset.php
create mode 100644 OLD/build/blocks/external-link/index.css
create mode 100644 OLD/build/blocks/external-link/index.js
create mode 100644 OLD/build/blocks/inner-blocks/block.json
create mode 100644 OLD/build/blocks/inner-blocks/index.asset.php
create mode 100644 OLD/build/blocks/inner-blocks/index.js
create mode 100644 OLD/build/blocks/inner-blocks/style-index-rtl.css
create mode 100644 OLD/build/blocks/inner-blocks/style-index.css
create mode 100644 OLD/build/blocks/loop-event-info/block.json
create mode 100644 OLD/build/blocks/loop-event-info/index-rtl.css
create mode 100644 OLD/build/blocks/loop-event-info/index.asset.php
create mode 100644 OLD/build/blocks/loop-event-info/index.css
create mode 100644 OLD/build/blocks/loop-event-info/index.js
create mode 100644 OLD/build/blocks/past-events-notice/block.json
create mode 100644 OLD/build/blocks/past-events-notice/index.asset.php
create mode 100644 OLD/build/blocks/past-events-notice/index.js
create mode 100644 OLD/build/blocks/upcoming-events/block.json
create mode 100644 OLD/build/blocks/upcoming-events/index-rtl.css
create mode 100644 OLD/build/blocks/upcoming-events/index.asset.php
create mode 100644 OLD/build/blocks/upcoming-events/index.css
create mode 100644 OLD/build/blocks/upcoming-events/index.js
create mode 100644 OLD/build/js/admin.asset.php
create mode 100644 OLD/build/js/admin.js
create mode 100644 OLD/build/variations/index.asset.php
create mode 100644 OLD/build/variations/index.js
create mode 100644 OLD/plugin.php
create mode 100644 OLD/postcss.config.js
create mode 100644 OLD/src/assets/js/admin.js
create mode 100644 OLD/src/back-compat.php
create mode 100644 OLD/src/blocks/calendar/block.json
create mode 100644 OLD/src/blocks/calendar/calendar.js
create mode 100644 OLD/src/blocks/calendar/color-panel/index.js
create mode 100644 OLD/src/blocks/calendar/editor.scss
create mode 100644 OLD/src/blocks/calendar/index.js
create mode 100644 OLD/src/blocks/calendar/style.scss
create mode 100644 OLD/src/blocks/calendar/view.js
create mode 100644 OLD/src/blocks/common.scss
create mode 100644 OLD/src/blocks/countdown/block.json
create mode 100644 OLD/src/blocks/countdown/index.js
create mode 100644 OLD/src/blocks/countdown/style.scss
create mode 100644 OLD/src/blocks/countdown/view.js
create mode 100644 OLD/src/blocks/event-info/block.json
create mode 100644 OLD/src/blocks/event-info/editor-compat.scss
create mode 100644 OLD/src/blocks/event-info/editor.scss
create mode 100644 OLD/src/blocks/event-info/index.js
create mode 100644 OLD/src/blocks/event-tickets/block.json
create mode 100644 OLD/src/blocks/event-tickets/draggable/index.js
create mode 100644 OLD/src/blocks/event-tickets/editor.scss
create mode 100644 OLD/src/blocks/event-tickets/index.js
create mode 100644 OLD/src/blocks/event-tickets/search-list-control/index.js
create mode 100644 OLD/src/blocks/event-tickets/style.scss
create mode 100644 OLD/src/blocks/event-tickets/ticket-data-control/index.js
create mode 100644 OLD/src/blocks/external-link/block.json
create mode 100644 OLD/src/blocks/external-link/editor.scss
create mode 100644 OLD/src/blocks/external-link/index.js
create mode 100644 OLD/src/blocks/inner-blocks/block.json
create mode 100644 OLD/src/blocks/inner-blocks/index.js
create mode 100644 OLD/src/blocks/inner-blocks/style.scss
create mode 100644 OLD/src/blocks/loop-event-info/block.json
create mode 100644 OLD/src/blocks/loop-event-info/editor.scss
create mode 100644 OLD/src/blocks/loop-event-info/index.js
create mode 100644 OLD/src/blocks/loop-event-info/index.scss
create mode 100644 OLD/src/blocks/past-events-notice/block.json
create mode 100644 OLD/src/blocks/past-events-notice/index.js
create mode 100644 OLD/src/blocks/upcoming-events/block.json
create mode 100644 OLD/src/blocks/upcoming-events/index.js
create mode 100644 OLD/src/blocks/upcoming-events/index.scss
create mode 100644 OLD/src/calendar-functions.php
create mode 100644 OLD/src/classes/class-se-admin.php
create mode 100644 OLD/src/classes/class-se-block-variations.php
create mode 100644 OLD/src/classes/class-se-blocks.php
create mode 100644 OLD/src/classes/class-se-calendar-export.php
create mode 100644 OLD/src/classes/class-se-calendar.php
create mode 100644 OLD/src/classes/class-se-event-post-type.php
create mode 100644 OLD/src/classes/class-se-event-query-dates.php
create mode 100644 OLD/src/classes/class-se-rest-ticket-products.php
create mode 100644 OLD/src/classes/class-se-settings.php
create mode 100644 OLD/src/classes/class-se-template-loader.php
create mode 100644 OLD/src/event-functions.php
create mode 100644 OLD/src/init.php
create mode 100644 OLD/src/rest-api.php
create mode 100644 OLD/src/template-functions.php
create mode 100644 OLD/src/template-hooks.php
create mode 100644 OLD/src/templates/archive.php
create mode 100644 OLD/src/templates/calendar/calendar-body.php
create mode 100644 OLD/src/templates/calendar/calendar-container.php
create mode 100644 OLD/src/templates/calendar/calendar-header.php
create mode 100644 OLD/src/templates/calendar/calendar-main.php
create mode 100644 OLD/src/templates/calendar/calendar-mobile-events.php
create mode 100644 OLD/src/templates/calendar/calendar-top-bar.php
create mode 100644 OLD/src/templates/calendar/day/day.php
create mode 100644 OLD/src/templates/calendar/day/event.php
create mode 100644 OLD/src/templates/calendar/day/events.php
create mode 100644 OLD/src/templates/calendar/mobile-events/day.php
create mode 100644 OLD/src/templates/calendar/mobile-events/nav.php
create mode 100644 OLD/src/templates/calendar/mobile-events/today.php
create mode 100644 OLD/src/templates/calendar/top-bar/current-date.php
create mode 100644 OLD/src/templates/calendar/top-bar/nav.php
create mode 100644 OLD/src/templates/calendar/top-bar/today.php
create mode 100644 OLD/src/templates/content-archive.php
create mode 100644 OLD/src/templates/content-single.php
create mode 100644 OLD/src/templates/single.php
create mode 100644 OLD/src/variations/blocks/unregister_blocks.js
create mode 100644 OLD/src/variations/index.js
create mode 100644 OLD/src/variations/query-loop-events/block.js
create mode 100644 OLD/src/woocommerce-hooks.php
create mode 100644 simple-events (1).zip
diff --git a/OLD/README.md b/OLD/README.md
new file mode 100644
index 0000000..156ca3a
--- /dev/null
+++ b/OLD/README.md
@@ -0,0 +1,105 @@
+# Simple Events
+
+A simple Gutenberg-first event management plugin that integrates with WooCommerce Box Office.
+
+**If you want to install this plugin**, DON'T DOWNLOAD THIS REPO. You can download the latest stable version from the [releases page](https://github.com/a8cteam51/simple-events/releases)
+or just click [here](https://github.com/a8cteam51/simple-events/releases/latest/download/simple-events.zip).
+
+## Dependencies
+
+Simple Events uses [Composer](https://getcomposer.org), a dependency manager for PHP. Visit the official Composer [download instructions](https://getcomposer.org/download/) to install Composer.
+
+Then, run:
+
+```
+composer install
+```
+
+## Building
+
+Run `npm install` to install all the Node.js dependencies.
+
+Below you will find some information on how to run scripts.
+
+### `npm start`
+- Use to compile and run the block in development mode.
+- Watches for any changes and reports back any errors in your code.
+
+### `npm run build`
+- Use to build production code for your block inside `build` folder.
+- Runs once and reports back the gzip file sizes of the produced code.
+
+## Hooks
+
+### Next & Previous Links
+
+**This must be enabled in the `settings` before they are shown.**
+
+> Change the previous link text (defaults to `<< {Event Title}`)
+```php
+add_filter('se_event_previous_link_text', function( string $link_text, WP_Post $event ) {
+ return "Previous Event ({$event->post_title})";
+}, 10, 2);
+```
+
+> Change the next link text (defaults to `{Event Title} >>`)
+```php
+add_filter('se_event_next_link_text', function( string $link_text, WP_Post $event ) {
+ return "Next Event ({$event->post_title})";
+}, 10, 2);
+```
+
+> Change the link text to the calendar if page set in settings, if not set in settings will not show. (defaults to `View Full Calendar`)
+```php
+add_filter('se_event_calendar_link_text', function( string $link_text ) {
+ return "View Full Calendar";
+}, 10, 1);
+```
+
+### Cron Tasks for Event Start Date
+
+> When the cron task runs to update the event start date to a future date if its passed and future dates exist.
+
+#### How often to check events.
+```php
+add_filter('se_event_update_query_dates_interval', function( int $interval ) {
+ return 'hourly'; // Please use the WP Cron intervals: https://developer.wordpress.org/reference/functions/wp_get_schedules/
+}, 10, 1);
+```
+
+#### Age of events to check
+```php
+add_filter('se_event_update_dates_search_range', function( int $age ) {
+ return 48 * HOUR_IN_SECONDS; // The number of days to check for events that are older than this.
+}, 10, 1);
+```
+
+#### Skip event
+It is possible to skip and event from being updated by adding a filter to the event.
+```php
+add_filter('se_event_update_query_dates_skip', function( bool $skip, intget $event ) {
+ // Skip the event if it is a specific event.
+ if ( $event === 1234 ) {
+ return true;
+ }
+
+ return $skip;
+}, 10, 2);
+```
+
+#### Post Update
+When an event has been updated, the `se_event_updated_query_dates` is fired.
+```php
+add_action('se_event_updated_query_dates', function( int $event_id ) {
+ // Do something with the event.
+}, 10, 2);
+```
+
+## Extensions
+
+### Featured image with Focal Point
+Simple plugin to add a focal point control to the featured post image.
+
+**If you want to use this plugin extension**, you can find it at https://github.com/a8cteam51/bamberg-ua/tree/trunk/mu-plugins/team51-focal-point
+
+Copy the `team51-focal-point` folder to your `mu-plugins` directory.
diff --git a/OLD/build/blocks/calendar/block.json b/OLD/build/blocks/calendar/block.json
new file mode 100644
index 0000000..26fd372
--- /dev/null
+++ b/OLD/build/blocks/calendar/block.json
@@ -0,0 +1,135 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/calendar",
+ "title": "Simple Events Calendar",
+ "icon": "calendar",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "calendar",
+ "view",
+ "Simple Events"
+ ],
+ "attributes": {
+ "alignment": {
+ "type": "string",
+ "default": "none"
+ },
+ "eventModalAccess": {
+ "type": "boolean",
+ "default": false
+ },
+ "modalBgColor": {
+ "type": "string",
+ "default": ""
+ },
+ "modalTextColor": {
+ "type": "string",
+ "default": ""
+ },
+ "modalIconColor": {
+ "type": "string",
+ "default": ""
+ },
+ "showModalTitle": {
+ "type": "boolean",
+ "default": false
+ },
+ "showModalExcerpt": {
+ "type": "boolean",
+ "default": false
+ },
+ "showModalWhenNoThumbnails": {
+ "type": "boolean",
+ "default": false
+ },
+ "hideNeighbourEvents": {
+ "type": "boolean",
+ "default": false
+ },
+ "presentDayBg": {
+ "type": "string",
+ "default": ""
+ },
+ "presentDayColor": {
+ "type": "string",
+ "default": ""
+ },
+ "presentDayBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "eventDaysBg": {
+ "type": "string",
+ "default": ""
+ },
+ "eventDaysColor": {
+ "type": "string",
+ "default": ""
+ },
+ "eventDaysBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "pastDaysBg": {
+ "type": "string",
+ "default": ""
+ },
+ "pastDaysColor": {
+ "type": "string",
+ "default": ""
+ },
+ "pastDaysBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "upcomingDaysBg": {
+ "type": "string",
+ "default": ""
+ },
+ "upcomingDaysColor": {
+ "type": "string",
+ "default": ""
+ },
+ "upcomingDaysBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "monthYearColor": {
+ "type": "string",
+ "default": ""
+ },
+ "arrowColor": {
+ "type": "string",
+ "default": ""
+ },
+ "arrowPosition": {
+ "type": "string",
+ "default": "top"
+ },
+ "mobileArrowPosition": {
+ "type": "string",
+ "default": "top"
+ },
+ "showDot": {
+ "type": "boolean",
+ "default": true
+ },
+ "eventDotColor": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "supports": {
+ "multiple": false,
+ "reusable": false,
+ "html": false,
+ "align": true,
+ "alignment": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style-index.css",
+ "viewScript": "file:./view.js"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/calendar/index-rtl.css b/OLD/build/blocks/calendar/index-rtl.css
new file mode 100644
index 0000000..72595a2
--- /dev/null
+++ b/OLD/build/blocks/calendar/index-rtl.css
@@ -0,0 +1 @@
+.block-editor-panel-color-gradient-settings.block-editor-panel-color-gradient-settings{padding:0;border-top:0}
diff --git a/OLD/build/blocks/calendar/index.asset.php b/OLD/build/blocks/calendar/index.asset.php
new file mode 100644
index 0000000..fefefa2
--- /dev/null
+++ b/OLD/build/blocks/calendar/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n', 'wp-server-side-render'), 'version' => '75657bbc7dd0c39e0948');
diff --git a/OLD/build/blocks/calendar/index.css b/OLD/build/blocks/calendar/index.css
new file mode 100644
index 0000000..72595a2
--- /dev/null
+++ b/OLD/build/blocks/calendar/index.css
@@ -0,0 +1 @@
+.block-editor-panel-color-gradient-settings.block-editor-panel-color-gradient-settings{padding:0;border-top:0}
diff --git a/OLD/build/blocks/calendar/index.js b/OLD/build/blocks/calendar/index.js
new file mode 100644
index 0000000..9ffd587
--- /dev/null
+++ b/OLD/build/blocks/calendar/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e,o={461:(e,o,t)=>{const l=window.wp.blocks,n=window.wp.serverSideRender;var s=t.n(n);const r=window.wp.components,a=window.wp.blockEditor,i=window.wp.i18n,v=window.ReactJSXRuntime,d=({title:e,bgAttr:o,colorAttr:t,borderAttr:l,attributes:n,setAttributes:s})=>(0,v.jsx)(r.PanelBody,{title:e,initialOpen:!1,children:(0,v.jsx)(a.PanelColorSettings,{colorSettings:[{label:(0,i.__)("Background Color","simple-events"),value:n[o],onChange:e=>s({[o]:e}),clearable:!0},{label:(0,i.__)("Text Color","simple-events"),value:n[t],onChange:e=>s({[t]:e}),clearable:!0},{label:(0,i.__)("Border Color","simple-events"),value:n[l],onChange:e=>s({[l]:e}),clearable:!0}]})});(0,l.registerBlockType)("simple-events/calendar",{edit:({attributes:e,setAttributes:o})=>(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(a.BlockControls,{children:(0,v.jsx)(a.AlignmentToolbar,{value:e.alignment,onChange:e=>{o({alignment:void 0===e?"none":e})},alignmentControls:[]})}),(0,v.jsxs)(a.InspectorControls,{children:[(0,v.jsxs)(r.PanelBody,{title:"Event Configuration",initialOpen:!0,children:[(0,v.jsx)(r.ToggleControl,{label:(0,i.__)("Hide Events on Neighbouring Months","simple-events"),help:(0,i.__)("Used to hide events on the previous and next month for a particular month.","simple-events"),checked:e?.hideNeighbourEvents,onChange:e=>o({hideNeighbourEvents:e})}),(0,v.jsx)(r.ToggleControl,{label:(0,i.__)("Show Dot for Events ( Mobile )","simple-events"),help:(0,i.__)("Toggle to show / hide the dot to indicate events for smaller devices.","simple-events"),checked:e?.showDot,onChange:e=>o({showDot:e})}),(0,v.jsx)(a.PanelColorSettings,{colorSettings:[{label:(0,i.__)("Event Dot Color ( Mobile )","simple-events"),value:e.eventDotColor,onChange:e=>o({eventDotColor:e}),clearable:!0}]})]}),(0,v.jsxs)(r.PanelBody,{title:"Event Modal Configuration",initialOpen:!0,children:[(0,v.jsx)(r.ToggleControl,{label:(0,i.__)("Enable Event Modal","simple-events"),help:(0,i.__)("Enables modal for all events. Modals for specific events can be disabled on the event edit page","simple-events"),checked:e?.eventModalAccess,onChange:e=>o({eventModalAccess:e})}),(0,v.jsx)(r.ToggleControl,{label:(0,i.__)("Show Event Title in Modal","simple-events"),help:(0,i.__)("Toggle to show/hide the title of the event in the modal. Applicable to all events.","simple-events"),checked:e?.showModalTitle,onChange:e=>o({showModalTitle:e})}),(0,v.jsx)(r.ToggleControl,{label:(0,i.__)("Show Event Excerpt in Modal","simple-events"),help:(0,i.__)("Toggle to show/hide the excerpt of the event in the modal. Applicable to all events.","simple-events"),checked:e?.showModalExcerpt,onChange:e=>o({showModalExcerpt:e})}),(0,v.jsx)(r.ToggleControl,{label:(0,i.__)("Show Event Modal when no thumbnail is defined","simple-events"),help:(0,i.__)("Toggle to show/hide the modal even if no thumbnail is defined.","simple-events"),checked:e?.showModalWhenNoThumbnails,onChange:e=>o({showModalWhenNoThumbnails:e})}),(0,v.jsx)("h3",{children:(0,i.__)("Color Configuration","simple-events")}),(0,v.jsx)(a.PanelColorSettings,{colorSettings:[{label:(0,i.__)("Background Color","simple-events"),value:e?.modalBgColor,onChange:e=>o({modalBgColor:e}),clearable:!0},{label:(0,i.__)("Text Color","simple-events"),value:e?.modalTextColor,onChange:e=>o({modalTextColor:e}),clearable:!0},{label:(0,i.__)("Icon Color","simple-events"),value:e?.modalIconColor,onChange:e=>o({modalIconColor:e}),clearable:!0}]})]}),(0,v.jsx)(d,{attributes:e,setAttributes:o,title:(0,i.__)("Today's Date","simple-events"),bgAttr:"presentDayBg",colorAttr:"presentDayColor",borderAttr:"presentDayBorder"}),(0,v.jsx)(d,{attributes:e,setAttributes:o,title:(0,i.__)("Days with Events","simple-events"),bgAttr:"eventDaysBg",colorAttr:"eventDaysColor",borderAttr:"eventDaysBorder"}),(0,v.jsx)(d,{attributes:e,setAttributes:o,title:(0,i.__)("Past Dates","simple-events"),bgAttr:"pastDaysBg",colorAttr:"pastDaysColor",borderAttr:"pastDaysBorder"}),(0,v.jsx)(d,{attributes:e,setAttributes:o,title:(0,i.__)("Upcoming Dates","simple-events"),bgAttr:"upcomingDaysBg",colorAttr:"upcomingDaysColor",borderAttr:"upcomingDaysBorder"}),(0,v.jsx)(r.PanelBody,{title:(0,i.__)("Month and Year","simple-events"),initialOpen:!1,children:(0,v.jsx)(a.PanelColorSettings,{colorSettings:[{label:(0,i.__)("Text Color","simple-events"),value:e.monthYearColor,onChange:e=>o({monthYearColor:e}),clearable:!0}]})}),(0,v.jsxs)(r.PanelBody,{title:(0,i.__)("Arrows","simple-events"),initialOpen:!1,children:[(0,v.jsx)(a.PanelColorSettings,{colorSettings:[{label:(0,i.__)("Arrow Color","simple-events"),value:e.arrowColor,onChange:e=>o({arrowColor:e}),clearable:!0}]}),(0,v.jsx)("br",{}),(0,v.jsx)(r.SelectControl,{label:(0,i.__)("Arrow Position","simple-events"),value:e.arrowPosition,onChange:e=>o({arrowPosition:e}),options:[{label:(0,i.__)("Top","simple-events"),value:"top"},{label:(0,i.__)("Bottom","simple-events"),value:"bottom"}]}),(0,v.jsx)(r.SelectControl,{label:(0,i.__)("Arrow Position ( Mobile )","simple-events"),value:e.mobileArrowPosition,onChange:e=>o({mobileArrowPosition:e}),options:[{label:(0,i.__)("Top","simple-events"),value:"top"},{label:(0,i.__)("Bottom","simple-events"),value:"bottom"}]})]})]}),(0,v.jsx)("div",{...(0,a.useBlockProps)(),children:(0,v.jsx)(s(),{block:"simple-events/calendar",attributes:e})})]}),save:()=>null})}},t={};function l(e){var n=t[e];if(void 0!==n)return n.exports;var s=t[e]={exports:{}};return o[e](s,s.exports,l),s.exports}l.m=o,e=[],l.O=(o,t,n,s)=>{if(!t){var r=1/0;for(d=0;d=s)&&Object.keys(l.O).every((e=>l.O[e](t[i])))?t.splice(i--,1):(a=!1,s0&&e[d-1][2]>s;d--)e[d]=e[d-1];e[d]=[t,n,s]},l.n=e=>{var o=e&&e.__esModule?()=>e.default:()=>e;return l.d(o,{a:o}),o},l.d=(e,o)=>{for(var t in o)l.o(o,t)&&!l.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},l.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),(()=>{var e={76:0,108:0};l.O.j=o=>0===e[o];var o=(o,t)=>{var n,s,[r,a,i]=t,v=0;if(r.some((o=>0!==e[o]))){for(n in a)l.o(a,n)&&(l.m[n]=a[n]);if(i)var d=i(l)}for(o&&o(t);vl(461)));n=l.O(n)})();
\ No newline at end of file
diff --git a/OLD/build/blocks/calendar/style-index-rtl.css b/OLD/build/blocks/calendar/style-index-rtl.css
new file mode 100644
index 0000000..c30a3ac
--- /dev/null
+++ b/OLD/build/blocks/calendar/style-index-rtl.css
@@ -0,0 +1 @@
+.simple-events-calendar{margin-right:auto;margin-left:auto;width:100%;min-height:700px}.simple-events-calendar .simple-events-top-bar{position:relative;display:flex;flex-direction:row;gap:20px;align-items:center;width:100%}.simple-events-calendar .simple-events-top-bar nav{display:block;flex:none}.simple-events-calendar .simple-events-top-bar nav ul{display:flex;list-style:none;margin:10px 0;padding:0}.simple-events-calendar .simple-events-top-bar nav ul li{flex:none}.simple-events-calendar .simple-events-top-bar nav ul li:nth-child(2){position:absolute;left:0}.simple-events-calendar .simple-events-top-bar nav ul li a{display:flex}.simple-events-calendar .simple-events-top-bar nav ul li svg{width:25px}.simple-events-calendar .simple-events-top-bar__today-button{font-size:16px}.simple-events-calendar .simple-events-top-bar__month{font-size:24px}.simple-events-calendar .simple-events-calendar-month__header-row{display:flex}.simple-events-calendar .simple-events-calendar-month__header-column{flex:1;display:flex}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__header-column{justify-content:center}}.simple-events-calendar .simple-events-calendar-month__header-column h3{font-size:18px}.simple-events-calendar .simple-events-calendar-month__week{display:flex}.simple-events-calendar .simple-events-calendar-month__day{display:flex;flex-direction:column;min-height:150px;flex:1;border:1px solid #fff;background-clip:padding-box}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__day{align-items:center;min-height:50px;border:none}}.simple-events-calendar .simple-events-calendar-month__day-date-daynum{line-height:1.4;font-size:24px;padding:8px 12px}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__day-date-daynum{line-height:1.2;font-size:18px;padding:4px 8px}}.simple-events-calendar .simple-events-calendar-month__day--today{background-color:rgba(60,120,200,.3)}.simple-events-calendar .simple-events-calendar-month__day--past>time,.simple-events-calendar .simple-events-calendar-month__day--past .simple-events-calendar-month__calendar-event{opacity:.6}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__day--active{background-color:rgba(15,58,184,.8)}}.simple-events-calendar .simple-events-calendar-month__calendar-event{padding:0 12px 0;margin:0 0 16px 0}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-start-time time:first-child{display:none}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-start-time .simple-events-calendar-month__calendar-event-datetime-separator{display:none}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-end-time time:last-child{display:none}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-end-time .simple-events-calendar-month__calendar-event-datetime-separator{display:none}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__calendar-event{margin:0 0 24px 0}}.simple-events-calendar .simple-events-calendar-month__calendar-event-datetime{font-size:13px}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__calendar-event-datetime{font-size:18px}}.simple-events-calendar .simple-events-calendar-month__calendar-event-datetime>*{vertical-align:middle}.simple-events-calendar .simple-events-calendar-month__calendar-event-title{font-size:14px;margin:0}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__calendar-event-title{font-size:18px}}.simple-events-calendar .simple-events-calendar-month__calendar-event-title-link{text-decoration:none}.simple-events-calendar .simple-events-calendar-month__calendar-event-title-link:hover{text-decoration:underline}.simple-events-calendar .simple-events-calendar-month__mobile-events-icon{background-color:#fff;height:8px;width:8px;border-radius:50%}.simple-events-calendar .simple-events-calendar-month-mobile-events__mobile-day{display:none}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month-mobile-events__mobile-day--active{display:block}}.simple-events-calendar .simple-events-mobile__nav-list{display:flex;list-style:none;margin:10px 0;padding:0;justify-content:center;align-items:center}.simple-events-calendar .simple-events-mobile__nav-list-item{flex:1;display:flex}.simple-events-calendar .simple-events-mobile__nav-list-item--prev{justify-content:flex-start;width:33.33%}.simple-events-calendar .simple-events-mobile__nav-list-item--today{justify-content:center;font-size:18px;width:33.33%}.simple-events-calendar .simple-events-mobile__nav-list-item--next{justify-content:flex-end;width:33.33%}.simple-events-calendar .simple-events-mobile__nav-list-item a{display:flex}.simple-events-calendar .simple-events-mobile__nav-list-item svg{width:35px}.simple-events-calendar .disabled{opacity:.4}@media(max-width: 768px){.simple-events-calendar .simple-events-hidden-mobile{display:none !important}}@media(min-width: 768px){.simple-events-calendar .simple-events-hidden-desktop{display:none !important}}.simple-events-calendar-month-mobile-events__mobile-day .se-event-modal.hidden{display:none}.simple-events-calendar-month__events{position:relative}.simple-events-calendar-month__events .se-event-modal{position:absolute;z-index:1000;border:1px solid gray;padding:10px;border-radius:5px;width:300px;top:0;background-color:var(--wp--preset--color--primary, #fff);color:var(--wp--preset--color--secondary, #000)}.simple-events-calendar-month__events .se-event-modal.hidden{display:none}.simple-events-calendar-month__events .se-event-modal__image img{width:100%;height:fit-content;max-height:250px;object-fit:contain;border-radius:5px}.simple-events-calendar-month__events .se-event-modal__flex{display:flex;align-items:center;gap:8px}.simple-events-calendar-month__events .se-event-modal__flex:last-of-type{margin-bottom:5px}.simple-events-calendar-month__events .se-event-modal__flex .se-event-modal__date{font-size:12px;margin:10px 0;line-height:1.5}.simple-events-calendar-month__events .se-event-modal__flex ul{list-style:none;padding:0;margin:0}.simple-events-calendar-month__events .se-event-modal__flex .se-event-modal__title{font-size:15px;margin:0}.simple-events-calendar-month__events .se-event-modal__excerpt{margin:5px 8px 0 0;font-size:14px}
diff --git a/OLD/build/blocks/calendar/style-index.css b/OLD/build/blocks/calendar/style-index.css
new file mode 100644
index 0000000..624f59b
--- /dev/null
+++ b/OLD/build/blocks/calendar/style-index.css
@@ -0,0 +1 @@
+.simple-events-calendar{margin-left:auto;margin-right:auto;width:100%;min-height:700px}.simple-events-calendar .simple-events-top-bar{position:relative;display:flex;flex-direction:row;gap:20px;align-items:center;width:100%}.simple-events-calendar .simple-events-top-bar nav{display:block;flex:none}.simple-events-calendar .simple-events-top-bar nav ul{display:flex;list-style:none;margin:10px 0;padding:0}.simple-events-calendar .simple-events-top-bar nav ul li{flex:none}.simple-events-calendar .simple-events-top-bar nav ul li:nth-child(2){position:absolute;right:0}.simple-events-calendar .simple-events-top-bar nav ul li a{display:flex}.simple-events-calendar .simple-events-top-bar nav ul li svg{width:25px}.simple-events-calendar .simple-events-top-bar__today-button{font-size:16px}.simple-events-calendar .simple-events-top-bar__month{font-size:24px}.simple-events-calendar .simple-events-calendar-month__header-row{display:flex}.simple-events-calendar .simple-events-calendar-month__header-column{flex:1;display:flex}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__header-column{justify-content:center}}.simple-events-calendar .simple-events-calendar-month__header-column h3{font-size:18px}.simple-events-calendar .simple-events-calendar-month__week{display:flex}.simple-events-calendar .simple-events-calendar-month__day{display:flex;flex-direction:column;min-height:150px;flex:1;border:1px solid #fff;background-clip:padding-box}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__day{align-items:center;min-height:50px;border:none}}.simple-events-calendar .simple-events-calendar-month__day-date-daynum{line-height:1.4;font-size:24px;padding:8px 12px}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__day-date-daynum{line-height:1.2;font-size:18px;padding:4px 8px}}.simple-events-calendar .simple-events-calendar-month__day--today{background-color:rgba(60,120,200,.3)}.simple-events-calendar .simple-events-calendar-month__day--past>time,.simple-events-calendar .simple-events-calendar-month__day--past .simple-events-calendar-month__calendar-event{opacity:.6}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__day--active{background-color:rgba(15,58,184,.8)}}.simple-events-calendar .simple-events-calendar-month__calendar-event{padding:0 12px 0;margin:0 0 16px 0}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-start-time time:first-child{display:none}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-start-time .simple-events-calendar-month__calendar-event-datetime-separator{display:none}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-end-time time:last-child{display:none}.simple-events-calendar .simple-events-calendar-month__calendar-event.se-event-hide-end-time .simple-events-calendar-month__calendar-event-datetime-separator{display:none}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__calendar-event{margin:0 0 24px 0}}.simple-events-calendar .simple-events-calendar-month__calendar-event-datetime{font-size:13px}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__calendar-event-datetime{font-size:18px}}.simple-events-calendar .simple-events-calendar-month__calendar-event-datetime>*{vertical-align:middle}.simple-events-calendar .simple-events-calendar-month__calendar-event-title{font-size:14px;margin:0}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month__calendar-event-title{font-size:18px}}.simple-events-calendar .simple-events-calendar-month__calendar-event-title-link{text-decoration:none}.simple-events-calendar .simple-events-calendar-month__calendar-event-title-link:hover{text-decoration:underline}.simple-events-calendar .simple-events-calendar-month__mobile-events-icon{background-color:#fff;height:8px;width:8px;border-radius:50%}.simple-events-calendar .simple-events-calendar-month-mobile-events__mobile-day{display:none}@media(max-width: 768px){.simple-events-calendar .simple-events-calendar-month-mobile-events__mobile-day--active{display:block}}.simple-events-calendar .simple-events-mobile__nav-list{display:flex;list-style:none;margin:10px 0;padding:0;justify-content:center;align-items:center}.simple-events-calendar .simple-events-mobile__nav-list-item{flex:1;display:flex}.simple-events-calendar .simple-events-mobile__nav-list-item--prev{justify-content:flex-start;width:33.33%}.simple-events-calendar .simple-events-mobile__nav-list-item--today{justify-content:center;font-size:18px;width:33.33%}.simple-events-calendar .simple-events-mobile__nav-list-item--next{justify-content:flex-end;width:33.33%}.simple-events-calendar .simple-events-mobile__nav-list-item a{display:flex}.simple-events-calendar .simple-events-mobile__nav-list-item svg{width:35px}.simple-events-calendar .disabled{opacity:.4}@media(max-width: 768px){.simple-events-calendar .simple-events-hidden-mobile{display:none !important}}@media(min-width: 768px){.simple-events-calendar .simple-events-hidden-desktop{display:none !important}}.simple-events-calendar-month-mobile-events__mobile-day .se-event-modal.hidden{display:none}.simple-events-calendar-month__events{position:relative}.simple-events-calendar-month__events .se-event-modal{position:absolute;z-index:1000;border:1px solid gray;padding:10px;border-radius:5px;width:300px;top:0;background-color:var(--wp--preset--color--primary, #fff);color:var(--wp--preset--color--secondary, #000)}.simple-events-calendar-month__events .se-event-modal.hidden{display:none}.simple-events-calendar-month__events .se-event-modal__image img{width:100%;height:fit-content;max-height:250px;object-fit:contain;border-radius:5px}.simple-events-calendar-month__events .se-event-modal__flex{display:flex;align-items:center;gap:8px}.simple-events-calendar-month__events .se-event-modal__flex:last-of-type{margin-bottom:5px}.simple-events-calendar-month__events .se-event-modal__flex .se-event-modal__date{font-size:12px;margin:10px 0;line-height:1.5}.simple-events-calendar-month__events .se-event-modal__flex ul{list-style:none;padding:0;margin:0}.simple-events-calendar-month__events .se-event-modal__flex .se-event-modal__title{font-size:15px;margin:0}.simple-events-calendar-month__events .se-event-modal__excerpt{margin:5px 0 0 8px;font-size:14px}
diff --git a/OLD/build/blocks/calendar/view.asset.php b/OLD/build/blocks/calendar/view.asset.php
new file mode 100644
index 0000000..627bb64
--- /dev/null
+++ b/OLD/build/blocks/calendar/view.asset.php
@@ -0,0 +1 @@
+ array('wp-api-fetch', 'wp-dom-ready'), 'version' => 'ae1fb5d2b42761475884');
diff --git a/OLD/build/blocks/calendar/view.js b/OLD/build/blocks/calendar/view.js
new file mode 100644
index 0000000..6601351
--- /dev/null
+++ b/OLD/build/blocks/calendar/view.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var s in a)e.o(a,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:a[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.domReady;var a=e.n(t);const s=window.wp.apiFetch;var n=e.n(s);class i{constructor(){this.DOM={calendars:".simple-events-calendar",desktopElements:".simple-events-hidden-mobile",navigationItems:"simple-events-navigation-item",calendarDay:"simple-events-calendar-day",mobileEventContainer:"simple-events-calendar-month-mobile-events",status:{dayActive:"simple-events-calendar-month__day--active",mobileDayActive:"simple-events-calendar-month-mobile-events__mobile-day--active"},calendarModal:".se-event-modal",calendarModalContainer:".simple-events-calendar-month__day"},this.calendars=document.querySelectorAll(this.DOM.calendars)}init(){this.calendars.length&&this.calendars.forEach((e=>{this.initListeners(e)}))}initListeners(e){this.addNavigationItemsListeners(e),this.addCalendarDayListeners(e),this.handleModalFunctionality()}isMobile(e){const t=e.querySelectorAll(this.DOM.desktopElements);return!(!t.length||"none"!==window.getComputedStyle(t[0],null).display)}addCalendarDayListeners(e){const t=e.querySelectorAll(`[data-js="${this.DOM.calendarDay}"]`);t.length&&t.forEach((t=>{t.addEventListener("click",(t=>{if(!this.isMobile(e))return;t.preventDefault();let a=!1;const s=e.querySelector(`[data-js="${this.DOM.mobileEventContainer}"]`);if(t.currentTarget.classList.contains(this.DOM.status.dayActive)&&(a=!0),s){const n=s.querySelector("#"+t.currentTarget.dataset.mobileControl),i=s.querySelectorAll("."+this.DOM.status.mobileDayActive),l=e.querySelectorAll("."+this.DOM.status.dayActive);n&&(i.length&&i.forEach((e=>{e.classList.remove(this.DOM.status.mobileDayActive)})),l.length&&l.forEach((e=>{e.classList.remove(this.DOM.status.dayActive)})),a||(t.currentTarget.classList.add(this.DOM.status.dayActive),n.classList.add(this.DOM.status.mobileDayActive)))}}))}))}addNavigationItemsListeners(e){const t=e.querySelectorAll(`[data-js="${this.DOM.navigationItems}"]`);t.length&&t.forEach((t=>{t.addEventListener("click",(t=>{if(t.preventDefault(),t.currentTarget.classList.contains("disabled"))return;const a=t.currentTarget.closest(`[data-js="${this.DOM.navigationItems}"]`).dataset.date;this.sendCalendarRequest(a,e)}))}))}sendCalendarRequest(e,t){n()({path:"/simple-events/calendar",method:"POST",data:{date:e,attributes}}).then((e=>{e.html?(t.innerHTML=e.html,this.initListeners(t)):console.log(e)}))}handleHideTimeout(e){return setTimeout((()=>{e.classList.add("hidden")}),150)}handleModalFunctionality(){document.querySelectorAll(this.DOM.calendarModalContainer).forEach(((e,t)=>{let a=null;const s=e.querySelectorAll(".simple-events-calendar-month__calendar-event-title");if(!s||!s.length)return;let n=null;s.forEach((e=>{e.addEventListener("mouseenter",(e=>{const s=e.currentTarget.closest("article");if(a=s.nextElementSibling,!a)return;a.addEventListener("mouseenter",(()=>{clearTimeout(n)})),a.addEventListener("mouseleave",(()=>{n=this.handleHideTimeout(a)})),a.classList.remove("hidden");const i=(t+1)%7;0!==i&&i<4?a.style.left="80px":a.style.right="80px",t+1>22&&(a.style.top="-220px")})),e.addEventListener("mouseleave",(()=>{a&&(n=this.handleHideTimeout(a))}))}))}))}}a()((()=>{(new i).init()}))})();
\ No newline at end of file
diff --git a/OLD/build/blocks/countdown/block.json b/OLD/build/blocks/countdown/block.json
new file mode 100644
index 0000000..cd1ab2e
--- /dev/null
+++ b/OLD/build/blocks/countdown/block.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/countdown",
+ "title": "Countdown",
+ "description": "Display a countdown to your next upcoming event.",
+ "icon": "clock",
+ "category": "simple-events",
+ "keywords": [
+ "countdown",
+ "event",
+ "upcoming",
+ "Simple Events"
+ ],
+ "attributes": {
+ "className": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "supports": {
+ "html": false,
+ "multiple": false
+ },
+ "editorScript": "file:./index.js",
+ "style": "file:./style-index.css",
+ "viewScript": "file:./view.js"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/countdown/index.asset.php b/OLD/build/blocks/countdown/index.asset.php
new file mode 100644
index 0000000..41079ea
--- /dev/null
+++ b/OLD/build/blocks/countdown/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-i18n', 'wp-server-side-render'), 'version' => '43e0661f0b0ac2bb2abe');
diff --git a/OLD/build/blocks/countdown/index.js b/OLD/build/blocks/countdown/index.js
new file mode 100644
index 0000000..f1efb4c
--- /dev/null
+++ b/OLD/build/blocks/countdown/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e,r={385:(e,r,t)=>{window.wp.i18n;const n=window.wp.blocks,o=window.wp.serverSideRender;var i=t.n(o);const s=window.wp.blockEditor,l=window.ReactJSXRuntime;(0,n.registerBlockType)("simple-events/countdown",{edit:({attributes:e})=>{const r=()=>{document.getElementById("event-timer")?"function"==typeof simpleEventsCountdownTimer&&simpleEventsCountdownTimer():setTimeout((()=>r()),100)};return r(),(0,l.jsx)("div",{...(0,s.useBlockProps)(),children:(0,l.jsx)(i(),{block:"simple-events/countdown",attributes:e})})},save:()=>null})}},t={};function n(e){var o=t[e];if(void 0!==o)return o.exports;var i=t[e]={exports:{}};return r[e](i,i.exports,n),i.exports}n.m=r,e=[],n.O=(r,t,o,i)=>{if(!t){var s=1/0;for(u=0;u=i)&&Object.keys(n.O).every((e=>n.O[e](t[p])))?t.splice(p--,1):(l=!1,i0&&e[u-1][2]>i;u--)e[u]=e[u-1];e[u]=[t,o,i]},n.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),(()=>{var e={449:0,797:0};n.O.j=r=>0===e[r];var r=(r,t)=>{var o,i,[s,l,p]=t,a=0;if(s.some((r=>0!==e[r]))){for(o in l)n.o(l,o)&&(n.m[o]=l[o]);if(p)var u=p(n)}for(r&&r(t);an(385)));o=n.O(o)})();
\ No newline at end of file
diff --git a/OLD/build/blocks/countdown/style-index-rtl.css b/OLD/build/blocks/countdown/style-index-rtl.css
new file mode 100644
index 0000000..9f3076a
--- /dev/null
+++ b/OLD/build/blocks/countdown/style-index-rtl.css
@@ -0,0 +1 @@
+#event-timer{display:flex;justify-content:center;margin:2rem auto;max-width:350px;padding:2rem 0;text-align:center}@media(min-width: 768px){#event-timer{margin:3rem auto;max-width:550px}}@media(min-width: 1024px){#event-timer{max-width:50vw}}#event-timer .event-timer__col{flex:1 1 0}
diff --git a/OLD/build/blocks/countdown/style-index.css b/OLD/build/blocks/countdown/style-index.css
new file mode 100644
index 0000000..9f3076a
--- /dev/null
+++ b/OLD/build/blocks/countdown/style-index.css
@@ -0,0 +1 @@
+#event-timer{display:flex;justify-content:center;margin:2rem auto;max-width:350px;padding:2rem 0;text-align:center}@media(min-width: 768px){#event-timer{margin:3rem auto;max-width:550px}}@media(min-width: 1024px){#event-timer{max-width:50vw}}#event-timer .event-timer__col{flex:1 1 0}
diff --git a/OLD/build/blocks/countdown/view.asset.php b/OLD/build/blocks/countdown/view.asset.php
new file mode 100644
index 0000000..623eb64
--- /dev/null
+++ b/OLD/build/blocks/countdown/view.asset.php
@@ -0,0 +1 @@
+ array(), 'version' => '9dfa9d5a72621fe8fdfb');
diff --git a/OLD/build/blocks/countdown/view.js b/OLD/build/blocks/countdown/view.js
new file mode 100644
index 0000000..9001a4f
--- /dev/null
+++ b/OLD/build/blocks/countdown/view.js
@@ -0,0 +1 @@
+!function(){const e=document.getElementById("event-timer");if(!e||!e.dataset.eventStartDate)return void(e&&e.remove());const t=+e.dataset.eventStartDate;let n,r,o,i,a;const m=setInterval(l,1e3),c=e.querySelector(".event-timer__time-days"),s=e.querySelector(".event-timer__time-hours"),v=e.querySelector(".event-timer__time-minutes"),u=e.querySelector(".event-timer__time-seconds");function l(){n=(t-Date.now())/1e3|0,n<=0&&(clearInterval(m),n=0),r=n/86400|0,n-=3600*r*24,o=n/3600|0,n-=3600*o,i=n/60|0,a=n%60|0,r=r<10?"0"+r:r,o=o<10?"0"+o:o,i=i<10?"0"+i:i,a=a<10?"0"+a:a,c.textContent=c&&r,s.textContent=s&&o,v.textContent=v&&i,u.textContent=u&&a}l()}();
\ No newline at end of file
diff --git a/OLD/build/blocks/event-info/block.json b/OLD/build/blocks/event-info/block.json
new file mode 100644
index 0000000..57f644b
--- /dev/null
+++ b/OLD/build/blocks/event-info/block.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/event-info",
+ "title": "Event Information",
+ "icon": "calendar",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "information",
+ "Simple Events"
+ ],
+ "attributes": {
+ "eventVenue": {
+ "type": "string"
+ },
+ "eventLocation": {
+ "type": "string"
+ },
+ "eventDates": {
+ "type": "array"
+ },
+ "eventDateStart": {
+ "type": "string"
+ },
+ "eventDateEnd": {
+ "type": "string"
+ },
+ "showOnFrontEnd": {
+ "type": "boolean",
+ "default": true
+ },
+ "externalLinkLabel": {
+ "type": "string"
+ },
+ "externalLink": {
+ "type": "string"
+ }
+ },
+ "supports": {
+ "inserter": false,
+ "multiple": false,
+ "reusable": false,
+ "html": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/event-info/index-rtl.css b/OLD/build/blocks/event-info/index-rtl.css
new file mode 100644
index 0000000..9b45b31
--- /dev/null
+++ b/OLD/build/blocks/event-info/index-rtl.css
@@ -0,0 +1 @@
+.wp-block-simple-events-event-info .components-placeholder__fieldset{flex-direction:column}.wp-block-simple-events-event-info .components-base-control{margin:0 0 8px;text-align:right}.wp-block-simple-events-event-info .components-base-control:last-of-type{margin-bottom:0}.wp-block-simple-events-event-info .components-base-control:last-of-type .components-base-control__field{white-space:nowrap}.wp-block-simple-events-event-info .components-base-control:last-of-type .components-checkbox-control__input-container{align-content:center;align-items:center;display:inline-flex;height:33px;position:relative}.wp-block-simple-events-event-info .components-base-control:last-of-type .components-checkbox-control__input-container .components-checkbox-control__input{height:20px;min-width:20px}.wp-block-simple-events-event-info .components-base-control__label{display:block}.se__button-done{align-self:flex-start;margin-top:10px}.se-datetime-popover .components-popover__content{justify-content:center;width:max-content;padding:1em;position:relative}.se-datetime-popover .components-popover__content .components-datetime__date{margin-top:50px}.se-datetime-popover .components-datetime__time{padding-bottom:0}.se-datetime-popover .components-datetime__time fieldset{margin-bottom:0}.se-datetime-popover .components-datetime__time fieldset legend{display:none}.se-datetime-popover .components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field-time{align-items:center;display:flex}.se-datetime-popover .components-datetime__time .components-datetime__time-field-am-pm{white-space:nowrap}.se-datetime-popover__date fieldset:first-child{display:none}.se-datetime-popover__date .components-datetime__time-field-month-select{height:100%}.se-datetime-popover__time .components-datetime__time fieldset+fieldset{margin-top:10px}.se-datetime-popover__time .components-datetime__timezone{display:none}.se-datetime-popover__set-datetime{margin-top:15px;position:absolute;top:100px}.se-datetimegroup-controls-label{display:flex;font-weight:700}.se-datetimegroup-controls-label+div{margin-top:-4px}.se-datetimegroup-container{border-bottom:1px solid #e2e4e7}.se-datetimegroup-controls{display:grid;grid-template-columns:auto auto 70px;grid-column-gap:10px;margin-top:8px;max-width:450px}.se-datetimegroup-controls .components-dropdown{width:100%}.se-datetimegroup-controls .components-base-control:nth-of-type(3){flex:none;align-self:flex-end;margin-bottom:10px;margin-right:auto}.se-datetimegroup-controls .components-base-control:nth-of-type(3) .components-checkbox-control__input-container{margin-left:8px}.se-datetimegroup-controls .components-base-control:nth-of-type(3) label{font-weight:normal;font-size:14px}.se-datetimegroup-controls .se-datetime-control__delete{grid-column-start:4;margin-top:-68px;margin-left:-50px;margin-right:auto;align-self:center}.se-datetimegroup-controls .se-datetime-popover__button{width:100%;justify-content:center;margin-bottom:0;margin-top:0}.se-datetimegroup-controls .is-button.is-default:not(:disabled){border-color:#7e8993;background-color:#fff;color:#32373c}.se-datetime-addmore{display:flex;margin-top:10px;margin-bottom:20px}.se-location-label{max-width:450px}.se-location-label label{font-weight:700}.se-site-timezone-label{flex-direction:column;font-size:12px;color:#757575}.se-event-calendar-export{margin-top:20px}.se-all-day-checkbox .components-flex{align-items:center}
diff --git a/OLD/build/blocks/event-info/index.asset.php b/OLD/build/blocks/event-info/index.asset.php
new file mode 100644
index 0000000..19e07cb
--- /dev/null
+++ b/OLD/build/blocks/event-info/index.asset.php
@@ -0,0 +1 @@
+ array('lodash', 'moment', 'react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '29c2c2c30de5bc53ac53');
diff --git a/OLD/build/blocks/event-info/index.css b/OLD/build/blocks/event-info/index.css
new file mode 100644
index 0000000..1d83654
--- /dev/null
+++ b/OLD/build/blocks/event-info/index.css
@@ -0,0 +1 @@
+.wp-block-simple-events-event-info .components-placeholder__fieldset{flex-direction:column}.wp-block-simple-events-event-info .components-base-control{margin:0 0 8px;text-align:left}.wp-block-simple-events-event-info .components-base-control:last-of-type{margin-bottom:0}.wp-block-simple-events-event-info .components-base-control:last-of-type .components-base-control__field{white-space:nowrap}.wp-block-simple-events-event-info .components-base-control:last-of-type .components-checkbox-control__input-container{align-content:center;align-items:center;display:inline-flex;height:33px;position:relative}.wp-block-simple-events-event-info .components-base-control:last-of-type .components-checkbox-control__input-container .components-checkbox-control__input{height:20px;min-width:20px}.wp-block-simple-events-event-info .components-base-control__label{display:block}.se__button-done{align-self:flex-start;margin-top:10px}.se-datetime-popover .components-popover__content{justify-content:center;width:max-content;padding:1em;position:relative}.se-datetime-popover .components-popover__content .components-datetime__date{margin-top:50px}.se-datetime-popover .components-datetime__time{padding-bottom:0}.se-datetime-popover .components-datetime__time fieldset{margin-bottom:0}.se-datetime-popover .components-datetime__time fieldset legend{display:none}.se-datetime-popover .components-datetime__time .components-datetime__time-wrapper .components-datetime__time-field-time{align-items:center;display:flex}.se-datetime-popover .components-datetime__time .components-datetime__time-field-am-pm{white-space:nowrap}.se-datetime-popover__date fieldset:first-child{display:none}.se-datetime-popover__date .components-datetime__time-field-month-select{height:100%}.se-datetime-popover__time .components-datetime__time fieldset+fieldset{margin-top:10px}.se-datetime-popover__time .components-datetime__timezone{display:none}.se-datetime-popover__set-datetime{margin-top:15px;position:absolute;top:100px}.se-datetimegroup-controls-label{display:flex;font-weight:700}.se-datetimegroup-controls-label+div{margin-top:-4px}.se-datetimegroup-container{border-bottom:1px solid #e2e4e7}.se-datetimegroup-controls{display:grid;grid-template-columns:auto auto 70px;grid-column-gap:10px;margin-top:8px;max-width:450px}.se-datetimegroup-controls .components-dropdown{width:100%}.se-datetimegroup-controls .components-base-control:nth-of-type(3){flex:none;align-self:flex-end;margin-bottom:10px;margin-left:auto}.se-datetimegroup-controls .components-base-control:nth-of-type(3) .components-checkbox-control__input-container{margin-right:8px}.se-datetimegroup-controls .components-base-control:nth-of-type(3) label{font-weight:normal;font-size:14px}.se-datetimegroup-controls .se-datetime-control__delete{grid-column-start:4;margin-top:-68px;margin-right:-50px;margin-left:auto;align-self:center}.se-datetimegroup-controls .se-datetime-popover__button{width:100%;justify-content:center;margin-bottom:0;margin-top:0}.se-datetimegroup-controls .is-button.is-default:not(:disabled){border-color:#7e8993;background-color:#fff;color:#32373c}.se-datetime-addmore{display:flex;margin-top:10px;margin-bottom:20px}.se-location-label{max-width:450px}.se-location-label label{font-weight:700}.se-site-timezone-label{flex-direction:column;font-size:12px;color:#757575}.se-event-calendar-export{margin-top:20px}.se-all-day-checkbox .components-flex{align-items:center}
diff --git a/OLD/build/blocks/event-info/index.js b/OLD/build/blocks/event-info/index.js
new file mode 100644
index 0000000..e437968
--- /dev/null
+++ b/OLD/build/blocks/event-info/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.moment;var n=e.n(t);const s=window.lodash,a=window.wp.i18n,l=window.wp.blocks,i=window.wp.element,o=window.wp.components,_=window.wp.serverSideRender;var d=e.n(_);const r=window.wp.date,m=window.wp.blockEditor,c=window.wp.compose,v=window.wp.coreData,p=window.ReactJSXRuntime,u=(0,r.getSettings)(),h=Number(u.timezone.offset),x=u.timezone.string;let g=x;""===x&&(g="UTC"+(h>=0?"+":"")+h);const f=n().tz.names().map((e=>({label:e,value:e})));f.unshift({label:(0,a.__)("Same as site","simple-events"),value:""});const w=e=>{const t=n()().utcOffset(h),a=e.filter((e=>n().unix(e.datetime_end).utcOffset(h).isAfter(t)));if(0===a.length&&e.length>0){const t=e.map((e=>n().unix(e.datetime_start).utcOffset(h))),s=e.map((e=>n().unix(e.datetime_end).utcOffset(h)));return{datetime_start:n().max(t).unix().toString(),datetime_end:n().max(s).unix().toString()}}let l=null,i=null;return a.forEach((e=>{const s=n().unix(e.datetime_start).utcOffset(h),a=n().unix(e.datetime_end).utcOffset(h);if(a.isBefore(t))return;(s.isAfter(t)&&a.isAfter(t)||s.isBefore(t)&&a.isAfter(t))&&((e,n)=>{(!l||e.isBefore(l)||l.isAfter(e)&&l.isBefore(t))&&(l=e),i&&!n.isAfter(i)||(i=n)})(s,a)})),l||(l=n().unix((0,s.head)(a).datetime_start).utcOffset(h)),i||(i=n().unix((0,s.last)(a).datetime_end).utcOffset(h)),{datetime_start:l.unix().toString(),datetime_end:i.unix().toString()}};(0,l.registerBlockType)("simple-events/event-info",{edit:e=>{var t,l,_,r,C,b,j,k,y,T,E;const{attributes:D,setAttributes:B}=e,{editMode:S,showOnFrontEnd:O}=D,[N,z]=(0,v.useEntityProp)("postType","se-event","meta");let M=N?.se_event_timezone;""===M&&(M=x);const P=(e,t=null)=>{if(null===t&&(t=M),""===t)return h;const s=n().tz.zone(t),a=s.untils.findIndex((function(t){return t/1e3>e}));return-1*s.offsets[a]},A=(e,t=!1)=>{const s=n().unix(e).utcOffset(P(e));return t?s.format("YYYY-MM-DD HH:mm"):s},Y=e=>String(n()(e).utcOffset(P(n()(e).unix()),!0).utc().unix()),F=()=>(0,p.jsx)(m.BlockControls,{children:(0,p.jsx)(o.Toolbar,{controls:[{icon:"edit",title:(0,a.__)("Edit","simple-events"),onClick:()=>B({editMode:!S}),isActive:S},{icon:O?"visibility":"hidden",title:(0,a.__)("Show on Front-End?","simple-events"),onClick:()=>{B({showOnFrontEnd:!O}),z({se_event_show_on_frontend:!O})},isActive:O}]})}),L=(0,c.withState)({tempEventDate:null,tempEventTime:null})((({eventDateTime:e,removeDate:t,multiDay:l,tempEventDate:_,tempEventTime:d,setState:r})=>{const m=A(e.datetime_start,!0),c=A(e.datetime_end,!0),v=u.formats.datetime,h=/a(?!\\)/i.test(v.toLowerCase().replace(/\\\\/g,"").split("").reverse().join("")),x=t=>{if(!_&&!d)return;const a=Y(((e,t)=>{const s=n()(t);return n()(e).set({hour:s.get("hour"),minute:s.get("minute")})})(_||(t?m:c),d||(t?m:c))),l=(0,s.clone)(e);t?(l.datetime_start=a,parseInt(l.datetime_start)>=parseInt(l.datetime_end)&&(l.datetime_end=String(parseInt(l.datetime_start)+3600))):(l.datetime_end=a,parseInt(l.datetime_start)>=parseInt(l.datetime_end)&&(l.datetime_start=String(parseInt(l.datetime_end)-3600))),r({tempEventDate:null,tempEventTime:null}),((e,t)=>{if(!(0,s.isEqual)(e,t)){const n=(0,s.sortBy)(N?.se_event_dates.map((n=>n===e?t:n)),"datetime_start");z({...N,se_event_dates:n,se_event_date_start:w(n).datetime_start,se_event_date_end:w(n).datetime_end})}})(e,l)},g=(e,t)=>{const s=n()(e).format("YYYY-MM-DD")===n()(t).format("YYYY-MM-DD");r(s?{tempEventTime:t}:{tempEventDate:t})};return(0,p.jsx)("div",{className:"se-datetimegroup-container",children:(0,p.jsxs)("div",{className:"se-datetimegroup-controls",children:[(0,p.jsx)(o.BaseControl,{label:(0,a.__)("Start Date/Time","simple-events"),children:(0,p.jsx)(o.Dropdown,{contentClassName:"se-datetime-popover se-datetime-popover__time",popoverProps:{placement:"bottom"},renderToggle:({isOpen:t,onToggle:n})=>(0,p.jsx)(o.Button,{className:"se-datetime-popover__button",variant:"secondary",onClick:()=>{n()},"aria-expanded":t,children:e.all_day?wp.date.format("F j, Y",m):wp.date.format(v,m)}),renderContent:()=>(0,p.jsxs)(i.Fragment,{children:[(0,p.jsx)(o.DateTimePicker,{currentDate:m,is12Hour:h,onChange:e=>g(m,e),__nextRemoveHelpButton:!0,__nextRemoveResetButton:!0}),(0,p.jsx)(o.Button,{className:"se-datetime-popover__set-datetime",onClick:()=>x(!0),variant:"secondary",children:(0,a.__)("Set time","simple-events")})]})})}),(0,p.jsx)(o.BaseControl,{label:(0,a.__)("End Date/Time","simple-events"),children:(0,p.jsx)(o.Dropdown,{contentClassName:"se-datetime-popover se-datetime-popover__time",popoverProps:{placement:"bottom"},renderToggle:({isOpen:t,onToggle:n})=>(0,p.jsx)(o.Button,{className:"se-datetime-popover__button",variant:"secondary",onClick:()=>{n()},"aria-expanded":t,disabled:e.all_day,children:e.all_day?"--:--":wp.date.format(v,c)}),renderContent:()=>(0,p.jsxs)(i.Fragment,{children:[(0,p.jsx)(o.DateTimePicker,{currentDate:c,is12Hour:h,onChange:e=>g(c,e),__nextRemoveHelpButton:!0,__nextRemoveResetButton:!0}),(0,p.jsx)(o.Button,{className:"se-datetime-popover__set-datetime",onClick:()=>x(!1),variant:"secondary",text:(0,a.__)("Set time","simple-events")})]})})}),(0,p.jsx)(o.BaseControl,{children:(0,p.jsx)(o.CheckboxControl,{label:(0,a.__)("All day","simple-events"),className:"se-all-day-checkbox",checked:e.all_day,onChange:()=>{const t=(0,s.clone)(e);t.all_day=!e.all_day,t.datetime_start=A(t.datetime_start),t.datetime_end=A(t.datetime_end),t.all_day?(t.datetime_start.startOf("date"),t.datetime_end.endOf("date")):(t.datetime_start.hour(9).minute(0),t.datetime_end.hour(10).minute(0)),t.datetime_start=Y(t.datetime_start),t.datetime_end=Y(t.datetime_end);const n=(0,s.sortBy)(N?.se_event_dates.map((n=>n===e?t:n)),"datetime_start");z({...N,se_event_dates:n,se_event_date_start:w(n).datetime_start,se_event_date_end:w(n).datetime_end})}})}),l&&(0,p.jsx)("div",{className:"se-datetime-control__delete",children:(0,p.jsx)(o.Button,{isDestructive:!0,icon:"no-alt",label:(0,a.__)("Remove date","simple-events"),onClick:()=>t(e)})})]})})})),R=({dates:e})=>{const t=()=>{const t=e&&0!==e.length?e:[];let a=n()().utcOffset(h);a.hour(9),a.minute(0),a.second(0);let l=a.clone();l.hour(10),t.length&&(a=A((0,s.last)(t).datetime_start),l=A((0,s.last)(t).datetime_end)),a.add(1,"days"),l.add(1,"days");const i=(0,s.sortBy)([...t,{datetime_start:wp.date.date("U",a),datetime_end:wp.date.date("U",l),all_day:!1}],"datetime_start");z({...N,se_event_dates:i,se_event_date_start:w(i).datetime_start,se_event_date_end:w(i).datetime_end})},l=t=>{if(!e.length)return;const n=(0,s.pull)(e,t);z({...N,se_event_dates:n,se_event_date_start:w(n).datetime_start,se_event_date_end:w(n).datetime_end})};e&&0!==e.length||t();const _=[];return(0,s.sortBy)(e,"datetime_start").forEach(((t,n)=>{_.push((0,p.jsx)(L,{eventDateTime:t,removeDate:l,multiDay:e.length>1},n))})),(0,p.jsxs)(i.Fragment,{children:[(0,p.jsx)("span",{className:"se-datetimegroup-controls-label",children:(0,a.__)("Date & Time","simple-events")}),_,(0,p.jsx)("div",{className:"se-datetime-addmore",children:(0,p.jsx)(o.Button,{isLink:!0,onClick:()=>t(),children:(0,a.__)("+ Add another date","simple-events")})})]})};return 0!==N?.se_event_location.length||N?.se_event_dates&&N?.se_event_dates?.length||B({editMode:!0}),S?(0,p.jsxs)("div",{...(0,m.useBlockProps)(),children:[F(),(0,p.jsxs)(o.Placeholder,{label:(0,a.__)("Event Information","simple-events"),icon:"calendar",isColumnLayout:!0,className:e.className,children:[(0,p.jsx)(R,{dates:N?.se_event_dates}),(0,p.jsx)(o.TextControl,{className:"se-location-label",label:(0,a.__)("Venue","simple-events"),value:N?.se_event_venue,onChange:e=>z({...N,se_event_venue:e}),type:"text"}),(0,p.jsx)(o.TextControl,{className:"se-location-label",label:(0,a.__)("Location","simple-events"),value:N?.se_event_location,onChange:e=>{z({...N,se_event_location:e})},type:"text"}),(0,p.jsx)(o.TextControl,{className:"se-location-label",label:(0,a.__)("External Link","simple-events"),value:N?.se_event_external_link,onChange:e=>z({...N,se_event_external_link:e}),type:"url"}),N?.se_event_external_link&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(o.TextControl,{className:"se-location-label",label:(0,a.__)("External Link Label","simple-events"),value:N?.se_event_external_link_label,onChange:e=>z({...N,se_event_external_link_label:e}),type:"text"}),(0,p.jsx)(o.CheckboxControl,{label:(0,a.__)("Open external link from calendar","simple-events"),checked:null!==(t=N?.se_open_external_link)&&void 0!==t&&t,onChange:e=>{z({...N,se_open_external_link:e})}})]}),(0,p.jsx)(o.Button,{className:"se__button-done",variant:"primary",onClick:()=>{B({editMode:!1})},text:(0,a.__)("Done","simple-events")})]}),(0,p.jsxs)(m.InspectorControls,{children:[(0,p.jsxs)(o.PanelBody,{title:(0,a.__)("Settings","simple-events"),children:[(0,p.jsxs)(o.PanelRow,{className:"se-site-timezone-label",children:[(0,a.__)("Site TimeZone","simple-events"),":"," ",(0,p.jsx)("strong",{children:g})," ",(0,p.jsxs)("a",{target:"_blank",href:ajaxurl.replace("admin-ajax.php","options-general.php#timezone_string"),rel:"noreferrer",children:["(",(0,a.__)("Change","simple-events"),")"]})]}),(0,p.jsx)(o.ComboboxControl,{className:"se-timezone-label",label:(0,a.__)("Time Zone","simple-events"),help:(0,a.__)("Events default to the site's time zone as configured in the WordPress settings. If this event is happening in a different region, manually set the time zone here.","simple-events"),value:null!==(l=N?.se_event_timezone)&&void 0!==l?l:x,options:f,onChange:e=>{var t;const a=(0,s.clone)(null!==(t=N?.se_event_dates)&&void 0!==t?t:[]);e=Boolean(e)?e:"",M=e,""===e&&(M=x),a.forEach((e=>{["datetime_start","datetime_end"].forEach((t=>{const s=n().unix(e[t]).utcOffset(P(e[t],N?.se_event_timezone)),a=""!==M?P(e[t],M):h;e[t]=String(s.utcOffset(a,!0).utc().unix())}))})),z({...N,se_event_dates:a,se_event_date_start:w(a).datetime_start,se_event_date_end:w(a).datetime_end,se_event_timezone:e})}}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Display Time Zone in Front-end","simple-events"),checked:null!==(_=N?.se_event_display_timezone)&&void 0!==_&&_,onChange:e=>z({...N,se_event_display_timezone:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Group event dates with matching times","simple-events"),checked:null!==(r=N?.se_event_display_grouped)&&void 0!==r&&r,onChange:e=>z({...N,se_event_display_grouped:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Hide Start Time","simple-events"),help:(0,a.__)("Hides the Start Time on the Front-end"),checked:null!==(C=N?.se_event_hide_start_time)&&void 0!==C&&C,onChange:e=>z({...N,se_event_hide_start_time:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Hide End Timer","simple-events"),help:(0,a.__)("Hides the End Time on the Front-end"),checked:null!==(b=N?.se_event_hide_end_time)&&void 0!==b&&b,onChange:e=>z({...N,se_event_hide_end_time:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)('Show "Add to calendar" links',"simple-events"),help:(0,a.__)('Shows the "Add to calendar" links on the Front-end',"simple-events"),checked:null!==(j=N?.se_event_add_calendar_links)&&void 0!==j&&j,onChange:e=>z({...N,se_event_add_calendar_links:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Open event in new window","simple-events"),help:(0,a.__)("Open the event in new window from calender","simple-events"),checked:null!==(k=N?.se_event_open_in_new_window)&&void 0!==k&&k,onChange:e=>z({...N,se_event_open_in_new_window:e})})]}),(0,p.jsxs)(o.PanelBody,{title:(0,a.__)("Calendar Modal Configuration","simple-events"),children:[(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Enable Event Modal","simple-events"),help:(0,a.__)("Enable modal for this event.","simple-events"),checked:null===(y=N?.se_event_modal_access)||void 0===y||y,onChange:e=>z({...N,se_event_modal_access:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Show Event Title in Modal","simple-events"),help:(0,a.__)("Toggle to show/hide the title of the event in the modal.","simple-events"),checked:null===(T=N?.se_show_modal_title)||void 0===T||T,onChange:e=>z({...N,se_show_modal_title:e})}),(0,p.jsx)(o.ToggleControl,{label:(0,a.__)("Show Event Excerpt in Modal","simple-events"),help:(0,a.__)("Toggle to show/hide the excerpt of the event in the modal.","simple-events"),checked:null===(E=N?.se_show_modal_excerpt)||void 0===E||E,onChange:e=>z({...N,se_show_modal_excerpt:e})})]})]})]}):(0,p.jsxs)("div",{...(0,m.useBlockProps)(),children:[F(),(0,p.jsx)(o.Disabled,{children:(0,p.jsx)(d(),{block:"simple-events/event-info",attributes:{eventVenue:N?.se_event_venue,eventLocation:N?.se_event_location,eventDates:N?.se_event_dates,eventTimezone:N?.se_event_timezone,externalLink:N?.se_event_external_link,externalLinkLabel:N?.se_event_external_link_label,addCalendarLinks:N?.se_event_add_calendar_links}})})]})},save:()=>null})})();
\ No newline at end of file
diff --git a/OLD/build/blocks/event-tickets/block.json b/OLD/build/blocks/event-tickets/block.json
new file mode 100644
index 0000000..24edcce
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/block.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/event-tickets",
+ "title": "Event Tickets",
+ "icon": "tickets-alt",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "tickets",
+ "Simple Events"
+ ],
+ "attributes": {
+ "selected": {
+ "type": "array"
+ }
+ },
+ "supports": {
+ "inserter": true,
+ "multiple": false,
+ "reusable": false,
+ "html": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style-index.css"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/event-tickets/index-rtl.css b/OLD/build/blocks/event-tickets/index-rtl.css
new file mode 100644
index 0000000..949a68e
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/index-rtl.css
@@ -0,0 +1 @@
+.wp-block-simple-events-event-tickets .components-placeholder{min-height:0}.wp-block-simple-events-event-tickets .simple-events-tickets+.components-button,.wp-block-simple-events-event-tickets .se-selected-tickets+.components-button,.wp-block-simple-events-event-tickets .se-new-ticket+.components-button{margin:16px auto 0}.wp-block-simple-events-event-tickets .se-selected-tickets_list{padding-right:20px}.wp-block-simple-events-event-tickets .woocommerce-search-list{padding-bottom:0}.wp-block-simple-events-event-tickets .woocommerce-tag .woocommerce-tag__remove.components-icon-button,.wp-block-simple-events-event-tickets .woocommerce-search-list__list .woocommerce-search-list__item{height:auto}.wp-block-simple-events-event-tickets .se-mode-button-container{display:flex;width:100%}.wp-block-simple-events-event-tickets .se-mode-button-container .components-button.is-pressed,.wp-block-simple-events-event-tickets .se-mode-button-container .components-button.is-pressed:hover{background:#ddd;color:var(--wp-admin-theme-color)}.wp-block-simple-events-event-tickets .woocommerce-search-list__search{border-top:none;margin-top:0;padding:0}.wp-block-simple-events-event-tickets .woocommerce-search-list__list.is-not-found{align-items:center;display:flex;justify-content:center}.wp-block-simple-events-event-tickets .components-button .woocommerce-tag__remove{height:40px;padding:6px 12px;position:absolute;left:-48px;top:0}.wp-block-simple-events-event-tickets .components-panel__body-title span[aria-hidden=true] svg{display:none}.wp-block-simple-events-event-tickets .dashicons-edit,.wp-block-simple-events-event-tickets svg.edit{color:var(--wp-admin-theme-color);fill:currentColor;position:absolute;left:16px;top:50%;transform:translateY(-50%);transition:color .1s ease-in-out}.wp-block-simple-events-event-tickets .dashicons-edit{left:0}.wp-block-simple-events-event-tickets .components-panel__body.is-opened{border-bottom:none;padding-top:0}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title{height:0;margin:0}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title button:focus{box-shadow:none}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title span,.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title .edit{display:none}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .se-ticket-data{margin-top:9px}.wp-block-simple-events-event-tickets .components-disabled{opacity:.5}.wp-block-simple-events-event-tickets .components-disabled svg{color:#000}.wp-block-simple-events-event-tickets .se-selected-tickets+.se-mode-button-container,.wp-block-simple-events-event-tickets .se-selected-tickets+.simple-events-tickets{margin-top:50px}.wp-block-simple-events-event-tickets .se-ticket-data-container{width:calc(100% - 32px);padding-left:0}.wp-block-simple-events-event-tickets .se-new-ticket{margin:-1px 20px 0 0;width:calc(100% - 52px)}.wp-block-simple-events-event-tickets .woocommerce-search-list__list .woocommerce-search-list__item:hover{background:var(--wp-admin-theme-color);color:#fff}.wp-block-simple-events-event-tickets .woocommerce-search-list__list .woocommerce-search-list__item .woocommerce-search-list__item-state{display:none}.se-ticket-data{position:relative}.se-ticket-data .components-spinner{float:none;right:0;margin:0 auto;position:absolute;left:0;top:50%;transform:translateY(-50%)}.se-ticket-data_inner{align-items:center;display:grid;grid-column-gap:10px;grid-row-gap:8px;grid-template-columns:155px 155px 1fr;margin-bottom:20px;width:100%}.se-ticket-data_name{grid-column:1/-1}.se-ticket-data .se-ticket-data_stock-help{align-items:center;align-self:flex-end;display:flex;margin-bottom:4px}.se-ticket-data .se-ticket-data_stock-help .components-button{margin:0;padding:12px 6px}.se-ticket-data .se-help{cursor:help;height:40px}.se-ticket-data .se-help svg{margin:10px 0}.se-ticket-data_sale-price{align-self:start}.se-ticket-data_sale-price .components-base-control__label{width:100%}.se-ticket-data .se-ticket-data_sale-schedule p,.se-ticket-data .se-ticket-data_field-labels p{margin:0 0 8px}.se-ticket-data .se-ticket-data_sale-schedule .components-dropdown{width:100%}.se-ticket-data .se-ticket-data_sale-schedule .components-button{border:1px solid #757575;border-radius:2px;height:auto;margin-bottom:8px;padding:6px 8px;width:100%}.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder{color:rgba(0,0,0,.5)}.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder:active,.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder:focus,.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder:hover{color:rgba(0,0,0,.5)}.se-ticket-data .se-ticket-data_sale-help{align-items:center;display:flex;margin-bottom:17px}.se-ticket-data .se-ticket-data_sale-help .components-button{margin:0;padding:12px 6px}.se-ticket-data_additional-fields{grid-column:1/-1}.editor-styles-wrapper .se-ticket-data_additional-fields>p{margin:8px 0}.se-ticket-data_field-labels{display:grid;grid-column-gap:10px;grid-template-columns:155px 155px}.se-ticket-data_additional-field{align-items:center;display:grid;grid-column-gap:10px;grid-template-columns:22px 155px 155px 1fr 44px;margin:0 -32px 8px 0}.se-ticket-data_additional-field .components-base-control .components-base-control__field{margin-bottom:0}.se-ticket-data .disabled *{cursor:default}.se-ticket-data .se-ticket-data_additional-field-remove{margin:0}.se-ticket-data_additional-field-options{grid-column:2/2;margin-top:8px}.se-ticket-data .components-base-control .components-base-control__help{margin-bottom:0}.se-ticket-data .components-disabled .components-base-control__help{opacity:.5}.se-draggable-item_dragging{opacity:.5}#editor .se-draggable-item_dragging-clone{background:#fff;pointer-events:none;position:fixed;z-index:1000000000}body:not(.is-dragging-components-draggable) .se-draggable-item_handle{cursor:move;display:flex}.se-selected-tickets{width:100%}.se-selected-tickets_header{align-items:center;display:flex;justify-content:space-between}.se-selected-tickets_header strong{line-height:1;padding:14px 0}.se-selected-tickets .components-button{padding:12px 12px}.se-selected-tickets .components-button span:last-of-type{overflow:hidden;padding-left:34px;text-overflow:ellipsis;white-space:nowrap}.editor-styles-wrapper .se-selected-tickets_list>p{margin:9px 0}.se-selected-tickets_list>.components-spinner{display:block;float:none;margin:5px auto}.se-selected-tickets_list,.se-selected-tickets .se-selected-ticket{position:relative}.se-selected-tickets .se-selected-ticket{margin-top:-1px}.se-selected-tickets .se-selected-ticket>.se-draggable-item_handle{display:flex;right:-32px;padding:12px 6px;position:absolute;top:0}.se-selected-tickets .se-selected-ticket.se-is-open>.se-draggable-item_handle{display:none;pointer-events:none}
diff --git a/OLD/build/blocks/event-tickets/index.asset.php b/OLD/build/blocks/event-tickets/index.asset.php
new file mode 100644
index 0000000..e76c3f2
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/index.asset.php
@@ -0,0 +1 @@
+ array('lodash', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-date', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => 'c1d197a7de185a506cca');
diff --git a/OLD/build/blocks/event-tickets/index.css b/OLD/build/blocks/event-tickets/index.css
new file mode 100644
index 0000000..93a452e
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/index.css
@@ -0,0 +1 @@
+.wp-block-simple-events-event-tickets .components-placeholder{min-height:0}.wp-block-simple-events-event-tickets .simple-events-tickets+.components-button,.wp-block-simple-events-event-tickets .se-selected-tickets+.components-button,.wp-block-simple-events-event-tickets .se-new-ticket+.components-button{margin:16px auto 0}.wp-block-simple-events-event-tickets .se-selected-tickets_list{padding-left:20px}.wp-block-simple-events-event-tickets .woocommerce-search-list{padding-bottom:0}.wp-block-simple-events-event-tickets .woocommerce-tag .woocommerce-tag__remove.components-icon-button,.wp-block-simple-events-event-tickets .woocommerce-search-list__list .woocommerce-search-list__item{height:auto}.wp-block-simple-events-event-tickets .se-mode-button-container{display:flex;width:100%}.wp-block-simple-events-event-tickets .se-mode-button-container .components-button.is-pressed,.wp-block-simple-events-event-tickets .se-mode-button-container .components-button.is-pressed:hover{background:#ddd;color:var(--wp-admin-theme-color)}.wp-block-simple-events-event-tickets .woocommerce-search-list__search{border-top:none;margin-top:0;padding:0}.wp-block-simple-events-event-tickets .woocommerce-search-list__list.is-not-found{align-items:center;display:flex;justify-content:center}.wp-block-simple-events-event-tickets .components-button .woocommerce-tag__remove{height:40px;padding:6px 12px;position:absolute;right:-48px;top:0}.wp-block-simple-events-event-tickets .components-panel__body-title span[aria-hidden=true] svg{display:none}.wp-block-simple-events-event-tickets .dashicons-edit,.wp-block-simple-events-event-tickets svg.edit{color:var(--wp-admin-theme-color);fill:currentColor;position:absolute;right:16px;top:50%;transform:translateY(-50%);transition:color .1s ease-in-out}.wp-block-simple-events-event-tickets .dashicons-edit{right:0}.wp-block-simple-events-event-tickets .components-panel__body.is-opened{border-bottom:none;padding-top:0}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title{height:0;margin:0}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title button:focus{box-shadow:none}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title span,.wp-block-simple-events-event-tickets .components-panel__body.is-opened .components-panel__body-title .edit{display:none}.wp-block-simple-events-event-tickets .components-panel__body.is-opened .se-ticket-data{margin-top:9px}.wp-block-simple-events-event-tickets .components-disabled{opacity:.5}.wp-block-simple-events-event-tickets .components-disabled svg{color:#000}.wp-block-simple-events-event-tickets .se-selected-tickets+.se-mode-button-container,.wp-block-simple-events-event-tickets .se-selected-tickets+.simple-events-tickets{margin-top:50px}.wp-block-simple-events-event-tickets .se-ticket-data-container{width:calc(100% - 32px);padding-right:0}.wp-block-simple-events-event-tickets .se-new-ticket{margin:-1px 0 0 20px;width:calc(100% - 52px)}.wp-block-simple-events-event-tickets .woocommerce-search-list__list .woocommerce-search-list__item:hover{background:var(--wp-admin-theme-color);color:#fff}.wp-block-simple-events-event-tickets .woocommerce-search-list__list .woocommerce-search-list__item .woocommerce-search-list__item-state{display:none}.se-ticket-data{position:relative}.se-ticket-data .components-spinner{float:none;left:0;margin:0 auto;position:absolute;right:0;top:50%;transform:translateY(-50%)}.se-ticket-data_inner{align-items:center;display:grid;grid-column-gap:10px;grid-row-gap:8px;grid-template-columns:155px 155px 1fr;margin-bottom:20px;width:100%}.se-ticket-data_name{grid-column:1/-1}.se-ticket-data .se-ticket-data_stock-help{align-items:center;align-self:flex-end;display:flex;margin-bottom:4px}.se-ticket-data .se-ticket-data_stock-help .components-button{margin:0;padding:12px 6px}.se-ticket-data .se-help{cursor:help;height:40px}.se-ticket-data .se-help svg{margin:10px 0}.se-ticket-data_sale-price{align-self:start}.se-ticket-data_sale-price .components-base-control__label{width:100%}.se-ticket-data .se-ticket-data_sale-schedule p,.se-ticket-data .se-ticket-data_field-labels p{margin:0 0 8px}.se-ticket-data .se-ticket-data_sale-schedule .components-dropdown{width:100%}.se-ticket-data .se-ticket-data_sale-schedule .components-button{border:1px solid #757575;border-radius:2px;height:auto;margin-bottom:8px;padding:6px 8px;width:100%}.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder{color:rgba(0,0,0,.5)}.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder:active,.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder:focus,.se-ticket-data .se-ticket-data_sale-schedule .se-ticket-data_datetime-placeholder:hover{color:rgba(0,0,0,.5)}.se-ticket-data .se-ticket-data_sale-help{align-items:center;display:flex;margin-bottom:17px}.se-ticket-data .se-ticket-data_sale-help .components-button{margin:0;padding:12px 6px}.se-ticket-data_additional-fields{grid-column:1/-1}.editor-styles-wrapper .se-ticket-data_additional-fields>p{margin:8px 0}.se-ticket-data_field-labels{display:grid;grid-column-gap:10px;grid-template-columns:155px 155px}.se-ticket-data_additional-field{align-items:center;display:grid;grid-column-gap:10px;grid-template-columns:22px 155px 155px 1fr 44px;margin:0 0 8px -32px}.se-ticket-data_additional-field .components-base-control .components-base-control__field{margin-bottom:0}.se-ticket-data .disabled *{cursor:default}.se-ticket-data .se-ticket-data_additional-field-remove{margin:0}.se-ticket-data_additional-field-options{grid-column:2/2;margin-top:8px}.se-ticket-data .components-base-control .components-base-control__help{margin-bottom:0}.se-ticket-data .components-disabled .components-base-control__help{opacity:.5}.se-draggable-item_dragging{opacity:.5}#editor .se-draggable-item_dragging-clone{background:#fff;pointer-events:none;position:fixed;z-index:1000000000}body:not(.is-dragging-components-draggable) .se-draggable-item_handle{cursor:move;display:flex}.se-selected-tickets{width:100%}.se-selected-tickets_header{align-items:center;display:flex;justify-content:space-between}.se-selected-tickets_header strong{line-height:1;padding:14px 0}.se-selected-tickets .components-button{padding:12px 12px}.se-selected-tickets .components-button span:last-of-type{overflow:hidden;padding-right:34px;text-overflow:ellipsis;white-space:nowrap}.editor-styles-wrapper .se-selected-tickets_list>p{margin:9px 0}.se-selected-tickets_list>.components-spinner{display:block;float:none;margin:5px auto}.se-selected-tickets_list,.se-selected-tickets .se-selected-ticket{position:relative}.se-selected-tickets .se-selected-ticket{margin-top:-1px}.se-selected-tickets .se-selected-ticket>.se-draggable-item_handle{display:flex;left:-32px;padding:12px 6px;position:absolute;top:0}.se-selected-tickets .se-selected-ticket.se-is-open>.se-draggable-item_handle{display:none;pointer-events:none}
diff --git a/OLD/build/blocks/event-tickets/index.js b/OLD/build/blocks/event-tickets/index.js
new file mode 100644
index 0000000..97f44ef
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/index.js
@@ -0,0 +1 @@
+(()=>{var e,t={131:(e,t,s)=>{"use strict";const n=window.wp.element,i=window.lodash;var a=s(556),o=s.n(a);const r=({countLabel:e,className:t,depth:s=0,controlId:a="",item:o,isSelected:r,isSingle:l,onSelect:c,search:d="",...p})=>{const m=!(0,i.isNil)(e)||!(0,i.isNil)(o.count),u=[t,"woocommerce-search-list__item"];u.push(`depth-${s}`),l&&u.push("is-radio-button"),m&&u.push("has-count");const h=o.breadcrumbs&&o.breadcrumbs.length,g=p.name||`search-list-item-${a}`,_=`${g}-${o.id}`;return(0,n.createElement)("label",{htmlFor:_,className:u.join(" ")},l?(0,n.createElement)("input",{type:"radio",id:_,name:g,value:o.value,onChange:c(o),checked:r,className:"woocommerce-search-list__item-input",...p}):(0,n.createElement)("input",{type:"checkbox",id:_,name:g,value:o.value,onChange:c(o),checked:r,className:"woocommerce-search-list__item-input",...p}),(0,n.createElement)("span",{className:"woocommerce-search-list__item-label"},h?(0,n.createElement)("span",{className:"woocommerce-search-list__item-prefix"},1===(v=o.breadcrumbs).length?(0,i.first)(v):2===v.length?(0,i.first)(v)+" › "+(0,i.last)(v):(0,i.first)(v)+" … "+(0,i.last)(v)):null,(0,n.createElement)("span",{className:"woocommerce-search-list__item-name"},function(e,t){if(!t)return e;const s=new RegExp((0,i.escapeRegExp)(t),"ig");return e.split(s).map(((e,s)=>0===s?e:(0,n.createElement)(n.Fragment,{key:s},(0,n.createElement)("strong",null,t),e)))}(o.name,d))),!!m&&(0,n.createElement)("span",{className:"woocommerce-search-list__item-count"},e||o.count));var v};r.propTypes={className:o().string,countLabel:o().node,controlId:o().node,depth:o().number,item:o().object,name:o().string,isSelected:o().bool,isSingle:o().bool,onSelect:o().func,search:o().string};const l=r,c=window.wp.i18n,d=window.wp.components,p=window.wp.compose,m=window.ReactJSXRuntime,u={clear:(0,c.__)("Clear all selected tickets","simple-events"),list:(0,c.__)("Ticket Products","simple-events"),resultsList:(0,c.__)("Results","simple-events"),noItems:(0,c.__)("Your store doesn't have any ticket products.","simple-events"),noResults:(0,c.__)("No results for %s","simple-events"),search:(0,c.__)("Search for a ticket product:","simple-events"),updated:(0,c.__)("Ticket products search results updated.","simple-events")};class h extends n.Component{constructor(){super(...arguments),this.onSelect=this.onSelect.bind(this),this.renderList=this.renderList.bind(this)}componentDidUpdate(e){const{onSearch:t,search:s}=this.props;s!==e.search&&"function"==typeof t&&t(s)}onSelect(e){const{onChange:t,selected:s}=this.props;return()=>{t([...s,e])}}getFilteredList(e,t){if(!t)return e;const s=new RegExp((0,i.escapeRegExp)(t),"i");return this.props.debouncedSpeak(u.updated),e.map((e=>!!s.test(e.name)&&e)).filter(Boolean)}renderList(e,t=0){const{search:s}=this.props;return e?e.map((e=>(0,m.jsx)(l,{depth:t,isSingle:!1,item:e,onSelect:this.onSelect,search:s}))):null}renderListSection(){const{isLoading:e,search:t}=this.props;if(e)return(0,m.jsx)("div",{className:"woocommerce-search-list__list is-loading",children:(0,m.jsx)(d.Spinner,{})});const s=this.getFilteredList(this.props.list,t);return s.length?(0,m.jsx)(d.MenuGroup,{label:t?u.resultsList:u.list,className:"woocommerce-search-list__list",children:this.renderList(s)}):(0,m.jsxs)("div",{className:"woocommerce-search-list__list is-not-found",children:[(0,m.jsx)(d.Icon,{className:"woocommerce-search-list__not-found-icon",role:"img","aria-hidden":"true",focusable:"false",icon:"warning"}),(0,m.jsx)("span",{className:"woocommerce-search-list__not-found-text",children:t?(0,c.sprintf)(u.noResults,t):u.noItems})]})}render(){const{className:e="",search:t,setState:s}=this.props;return(0,m.jsxs)("div",{className:`woocommerce-search-list ${e}`,children:[(0,m.jsx)("div",{className:"woocommerce-search-list__search",children:(0,m.jsx)(d.TextControl,{label:u.search,type:"search",value:t,onChange:e=>s({search:e})})}),this.renderListSection()]})}}const g=(0,p.compose)([(0,p.withState)({search:""}),d.withSpokenMessages])(h),_=e=>{const t=(0,n.useRef)(),{startDrag:s}=(0,p.__experimentalUseDragging)({onDragStart:e=>{const s=t.current,n=e.target.closest(".se-draggable-item").cloneNode(!0);n.classList.add("se-draggable-item_dragging-clone"),n.style.top=`${s.getBoundingClientRect().top}px`,n.style.width=`${s.offsetWidth}px`,s.classList.add("se-draggable-item_dragging"),s.parentElement.appendChild(n),document.body.classList.add("is-dragging-components-draggable")},onDragMove:e=>{e.preventDefault();const s=e.target.closest(".se-draggable-item"),n=document.querySelector(".se-draggable-item_dragging-clone"),i=e.clientY,a=n.parentElement.getBoundingClientRect();!s||ia.bottom||(s.after(t.current),n.style.top=i-n.clientHeight/2+"px")},onDragEnd:()=>{t.current.classList.remove("se-draggable-item_dragging"),document.querySelector(".se-draggable-item_dragging-clone").remove(),document.body.classList.remove("is-dragging-components-draggable");const s=[...t.current.parentElement.children].map((e=>Number(e.dataset.index)));e.onChange(s)}}),{className:i=""}=e;return(0,m.jsxs)("div",{className:`se-draggable-item ${i}`,"data-index":e.index,ref:t,children:[(0,m.jsx)("div",{className:"se-draggable-item_handle",onMouseDown:s,children:(0,m.jsx)(d.Icon,{icon:(0,m.jsx)("svg",{width:"18",height:"18",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 18 18",role:"img","aria-hidden":"true",focusable:"false",children:(0,m.jsx)("path",{d:"M13,8c0.6,0,1-0.4,1-1s-0.4-1-1-1s-1,0.4-1,1S12.4,8,13,8z M5,6C4.4,6,4,6.4,4,7s0.4,1,1,1s1-0.4,1-1S5.6,6,5,6z M5,10 c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S5.6,10,5,10z M13,10c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S13.6,10,13,10z M9,6 C8.4,6,8,6.4,8,7s0.4,1,1,1s1-0.4,1-1S9.6,6,9,6z M9,10c-0.6,0-1,0.4-1,1s0.4,1,1,1s1-0.4,1-1S9.6,10,9,10z"})})})}),e.children]})},v=window.wp.apiFetch;var f=s.n(v);const x=window.wp.htmlEntities,b=window.wp.date,j=s(503),k=window.seSettings.BOTicketFieldTypes||[],w=()=>{const e=Object.entries(k).map((([e,t])=>({value:e,label:t})));return e.unshift({value:"",label:(0,c.__)("Type","simple-events")}),e},y=(0,m.jsx)(d.SVG,{width:"20",height:"20",viewBox:"0 0 20 20",focusable:"false",role:"img",xmlns:"http://www.w3.org/2000/svg",children:(0,m.jsx)(d.Path,{d:"M10 2c4.42 0 8 3.58 8 8s-3.58 8-8 8-8-3.58-8-8 3.58-8 8-8zm5.657 6.586H4.343v2.828h11.314V8.586z"})}),C=(0,p.withState)({dataLoaded:!1,loading:!1,name:"",price:"",saleData:!1,saleDateFrom:"",saleDateTo:"",salePrice:"",stock:"",additionalFields:[{label:(0,c.__)("First Name","simple-events"),type:"first_name",required:!0},{label:(0,c.__)("Last Name","simple-events"),type:"last_name",required:!0},{label:(0,c.__)("Email Address","simple-events"),type:"email",required:!0}]})((({dataLoaded:e,editingProduct:t,index:s,loading:a,name:o,price:r,saleData:l,saleDateFrom:u,saleDateTo:h,salePrice:g,stock:v,additionalFields:k,setState:C,attributes:S,setAttributes:T,title:N,onRemove:F,onReorder:B,onSave:M})=>{t&&!e&&(C({loading:!0,dataLoaded:!0}),f()({path:`/wc/v2/products/${t}`}).then((e=>{const t=Object.values(e.meta_data.find((e=>"_ticket_fields"===e.key)).value);C({name:e.name,price:e.regular_price,saleData:!!e.date_on_sale_from,saleDateFrom:e.date_on_sale_from,saleDateTo:e.date_on_sale_to,salePrice:e.sale_price,stock:e.stock_quantity,additionalFields:t,loading:!1})})));const L=(e,t,s)=>{(0,i.isEqual)(t,s)||("From"===e&&C({saleDateFrom:s}),"To"===e&&C({saleDateTo:s}))},O=(0,p.withState)({tempDate:""})((({label:e,value:t,tempDate:s,setState:n})=>{const i=(0,b.getSettings)(),a=s||t,o=/a(?!\\)/i.test(i.formats.time.toLowerCase().replace(/\\\\/g,"").split("").reverse().join(""));return(0,m.jsx)(d.Dropdown,{contentClassName:"se-datetime-popover se-datetime-popover__date",onToggle:()=>{s&&(L(e,t,s),n({tempDate:void 0}))},position:"bottom center",renderContent:()=>(0,m.jsx)(d.TimePicker,{currentTime:a,is12Hour:o,onChange:e=>n({tempDate:e})}),renderToggle:({isOpen:s,onToggle:n})=>(0,m.jsx)(d.Button,{className:t?"":"se-ticket-data_datetime-placeholder",onClick:n,"aria-expanded":s,children:t?(0,b.date)("Y-m-d",a):`${e}…`})})})),D=e=>{const t=k;t.splice(e,1),C({additionalFields:t})},E=(0,p.withState)({fields:[]})((({fields:e,setState:t,fieldset:s,index:n,onChange:i})=>{const a=["first_name","last_name","email"].includes(s.type);return(0,m.jsxs)(_,{className:"se-ticket-data_additional-field",index:n,onChange:e=>i(e.map((e=>k[e]))),children:[(0,m.jsx)(d.TextControl,{autoComplete:"off",disabled:a,label:(0,c.__)("Field label","simple-events"),hideLabelFromVision:!0,placeholder:(0,c.__)("Field label","simple-events"),value:s.label,onChange:i=>{const a=e;a[n]=Object.assign(s,{label:i}),t({fields:a})},onBlur:()=>t({additionalFields:e})}),(0,m.jsx)(d.SelectControl,{disabled:a,label:(0,c.__)("Field type","simple-events"),hideLabelFromVision:!0,value:s.type,options:w(),onChange:e=>{const i=k;i[n]=Object.assign(s,{type:e}),t({additionalFields:i})}}),(0,m.jsx)(d.CheckboxControl,{className:a?"disabled":"",checked:s.required,disabled:a,label:(0,c.__)("Required","simple-events"),onChange:e=>{const i=k;i[n]=Object.assign(s,{required:e}),t({additionalFields:i})}}),!a&&(0,m.jsx)(d.Button,{className:"se-ticket-data_additional-field-remove",label:sprintf((0,c.__)("Remove field","simple-events")),onClick:()=>D(n),children:y}),["select","radio","checkbox"].includes(s.type)&&(0,m.jsx)(d.TextareaControl,{className:"se-ticket-data_additional-field-options",label:(0,c.__)("Options","simple-events"),hideLabelFromVision:!0,help:(0,c.__)("Comma-separated list of available options","simple-events"),value:s.options,onChange:i=>{const a=e;a[n]=Object.assign(s,{options:i}),t({fields:a})},onBlur:()=>t({additionalFields:e})})]})})),P=(0,m.jsxs)(n.Fragment,{children:[(0,m.jsxs)("div",{className:"se-ticket-data_inner",children:[(0,m.jsx)(d.TextControl,{autoComplete:"off",className:"se-ticket-data_name",label:(0,c.__)("Name","simple-events"),value:(0,x.decodeEntities)(o),onChange:e=>C({name:e})}),(0,m.jsx)(d.TextControl,{autoComplete:"off",className:"se-ticket-data_price",label:(0,c.__)("Price","simple-events"),type:"number",min:"0",value:r,onChange:e=>C({price:e})}),(0,m.jsx)(d.TextControl,{autoComplete:"off",className:"se-ticket-data_stock",label:(0,c.__)("Stock","simple-events"),type:"number",min:"0",value:v,onChange:e=>C({stock:e})}),(0,m.jsxs)("div",{className:"se-ticket-data_stock-help",children:[(0,m.jsx)(d.Tooltip,{text:(0,c.__)('If less than 1, ticket will appear as "Sold Out".',"simple-events"),children:(0,m.jsx)("div",{className:"se-help",children:(0,m.jsx)(d.Dashicon,{icon:"editor-help",size:20})})}),!l&&(0,m.jsx)(d.Button,{isLink:!0,onClick:()=>C({saleData:!0}),children:(0,c.__)("+ Add sale price","simple-events")})]}),l&&(0,m.jsxs)(n.Fragment,{children:[(0,m.jsx)(d.TextControl,{autoComplete:"off",className:"se-ticket-data_sale-price",label:(0,c.__)("Sale price","simple-events"),type:"number",min:"0",value:g,onChange:e=>C({salePrice:e})}),(0,m.jsxs)("div",{className:"se-ticket-data_sale-schedule",children:[(0,m.jsx)("p",{children:(0,c.__)("Sale dates")}),(0,m.jsx)(O,{label:(0,c.__)("From","simple-events"),value:u}),(0,m.jsx)(O,{label:(0,c.__)("To","simple-events"),value:h})]}),(0,m.jsxs)("div",{className:"se-ticket-data_sale-help",children:[(0,m.jsx)(d.Tooltip,{text:(0,c.__)('The sale will start at 00:00:00 of "From" date and end at 23:59:59 of "To" date.',"simple-events"),children:(0,m.jsx)("div",{className:"se-help",children:(0,m.jsx)(d.Dashicon,{icon:"editor-help",size:20})})}),(0,m.jsx)(d.Button,{isLink:!0,onClick:()=>C({saleData:!1,saleDateFrom:"",saleDateTo:""}),children:(0,c.__)("Cancel","simple-events")})]})]}),(0,m.jsxs)("div",{className:"se-ticket-data_additional-fields",children:[(0,m.jsx)("p",{children:(0,m.jsx)("strong",{children:(0,c.__)("Ticket Fields","simple-events")})}),(0,m.jsxs)("div",{className:"se-ticket-data_fields",children:[(0,m.jsxs)("div",{className:"se-ticket-data_field-labels",children:[(0,m.jsx)("p",{children:(0,c.__)("Label","simple-events")}),(0,m.jsx)("p",{children:(0,c.__)("Type","simple-events")})]}),k.map(((e,t)=>(0,m.jsx)(E,{fieldset:e,index:t,onChange:e=>C({additionalFields:e}),additionalFields:k})))]}),(0,m.jsx)(d.Button,{isLink:!0,onClick:()=>(()=>{const e=k,t=(0,i.concat)(...e,{label:"",type:"",required:!1});C({additionalFields:t})})(),children:(0,c.__)("+ Add Field","simple-events")})]})]}),(0,m.jsx)(d.Button,{isPrimary:!0,onClick:()=>{C({loading:!0}),(async()=>{let e="/wc/v2/products";const s={name:o,regular_price:r,sale_price:g,stock_quantity:v,meta_data:[]};g&&l&&u&&h&&(s.date_on_sale_from=`${(0,b.date)("Y-m-d",u)}T00:00:00`,s.date_on_sale_to=`${(0,b.date)("Y-m-d",h)}T23:59:59`);const n={};return k.forEach((({label:e,type:t,required:s,options:i=""})=>{if(""!==t){const a=j(e+t);n[a]={label:e,type:t,required:s,options:i,autofill:"none",email_contact:"yes",email_gravatar:"yes"}}})),0!==Object.keys(n).length&&s.meta_data.push({key:"_ticket_fields",value:n}),t||(s.virtual=!0,s.meta_data.push({key:"_ticket",value:"yes"}),v&&(s.manage_stock=!0)),t&&(e+=`/${t}`),await f()({path:e,method:t?"PUT":"POST",data:s})})().then((e=>{if(!t){const t=S.selected||[];t.push(e.id),M(t)}C({loading:!1}),S.editMode&&(document.querySelector(`.se-selected-ticket[data-index="${S.editMode}"]`).querySelector("h2 button").click(),T({editMode:null}))}))},children:(0,c.__)(t?"Update Ticket":"Create ticket","simple-events")}),(0,m.jsx)(d.Button,{isLink:!0,onClick:e=>{S.editMode?e.target.closest(".se-ticket-data-container").querySelector("h2 button").click():T({addMode:!1})},children:(0,c.__)("Cancel","simple-events")})]}),R=(0,m.jsx)("div",{className:"se-ticket-data",children:a?(0,m.jsxs)(n.Fragment,{children:[(0,m.jsx)(d.Disabled,{children:P}),(0,m.jsx)(d.Spinner,{})]}):P}),A=(0,m.jsxs)(n.Fragment,{children:[t?(0,m.jsxs)(n.Fragment,{children:[(0,m.jsx)("span",{className:"screen-reader-text",children:(0,c.__)("Edit ","simple-events")}),(0,m.jsx)("span",{"aria-hidden":"true",children:(0,x.decodeEntities)(N)})]}):(0,m.jsx)("span",{className:"screen-reader-text",children:(0,c.__)("Create new ticket","simple-events")}),(0,m.jsxs)(d.SVG,{className:"edit",width:"20",height:"20",viewBox:"0 0 20 20",focusable:"false",role:"img",xmlns:"http://www.w3.org/2000/svg",children:[(0,m.jsx)(d.Path,{d:"M3.012 15.92l1.212-4.36L14.834.92l3.178 3.134-10.56 10.634z"}),(0,m.jsx)(d.Path,{d:"M1.988 18.276v1.453h8v-1.453z"})]}),(0,m.jsx)(d.Button,{className:"woocommerce-tag__remove",onClick:e=>{S.editMode&&s===S.editMode?T({editMode:null}):e.stopPropagation(),F()},label:sprintf((0,c.__)("Remove %s","simple-events"),o),children:y})]}),q=(0,m.jsx)(n.Fragment,{children:t?(0,m.jsx)(_,{className:S.editMode&&s===S.editMode?"se-selected-ticket se-is-open":"se-selected-ticket",index:s,onChange:B,children:(0,m.jsx)(d.PanelBody,{className:"se-ticket-data-container",initialOpen:!1,onToggle:()=>{if(S.editMode)T({editMode:null});else{C({dataLoaded:!1}),T({editMode:t});const e=document.activeElement.closest(".se-ticket-data-container"),s=()=>{const t=e.querySelector(".se-ticket-data_name input");t?t.focus():setTimeout(s,10)};s()}T({searchMode:!1})},title:A,children:R})}):(0,m.jsx)(d.PanelBody,{className:"se-ticket-data-container se-new-ticket",initialOpen:!0,title:A,children:R})});return(0,m.jsx)(n.Fragment,{children:S.editMode&&t!==S.editMode||S.addMode&&t?(0,m.jsx)(d.Disabled,{children:q}):q})})),S=window.wp.blocks,T=window.wp.url,N=window.wp.blockEditor,F=window.seSettings.productCount||50,B=window.seSettings.isLargeCatalog||!1,M=window.seSettings.isWCActive||!1,L=window.seSettings.isBOActive||!1,O=()=>{const e=[];return M||e.push("WooCommerce"),L||e.push("WooCommerce Box Office"),e.length?(0,m.jsx)("p",{children:(0,c.sprintf)((0,c.__)("%s must be installed and active to use this block.","simple-events"),e.join((0,c.__)(" and ","simple-events")))}):null},D=(0,p.withState)({loading:!0,selected:[],products:[],search:""})((e=>{var t;const{setState:s,setAttributes:a,attributes:o,loading:r,selected:l,products:p,search:u}=e,h=null!==(t=o.selected)&&void 0!==t?t:[],_=h.length;_&&!l.length&&s({selected:h}),o.newTicketAdded&&(s({loading:!0}),a({newTicketAdded:!1})),r&&(async({selected:e=[],search:t=""})=>{const s=["product","product_variation"];let n=0,a=[],o=null;for(;nf()({path:e}))));return(0,i.uniqBy)((0,i.flatten)(r),"id").map((e=>({id:parseInt(e.id,10),name:e.name})))})({savedSelected:h,search:u}).then((e=>{s({products:e,loading:!1})})).catch((()=>{s({products:[],loading:!1})}));const v=(0,i.debounce)((e=>{s({loading:!0,search:e})}),400),x=e=>{s({selected:e}),a({selected:e})},b=(e=p)=>l.map((t=>e.find((e=>e.id===t)))),j=(0,m.jsxs)(n.Fragment,{children:[(0,m.jsx)(g,{className:"simple-events-tickets",isLoading:r,list:l?p.filter((({id:e})=>!l.includes(e)&&!isNaN(e))):p,selected:l?p.filter((({id:e})=>l.includes(e))):[],onChange:e=>{let t=l;e.length>l.length?t.push(e.pop().id):t=b(e).filter(Boolean).map((({id:e})=>e)),x(t)},onSearch:B?v:null}),(0,m.jsx)(d.Button,{isPrimary:!0,onClick:()=>a({searchMode:!1}),children:(0,c.__)("Done adding existing tickets","simple-events")})]}),k=(0,m.jsxs)("div",{className:"se-selected-tickets",children:[(0,m.jsx)("div",{className:"se-selected-tickets_header",children:!r&&1<_?(0,m.jsx)(d.Button,{isLink:!0,isDestructive:!0,onClick:()=>x([]),"aria-label":(0,c.__)("Clear all selected tickets","simple-events"),children:(0,c.__)("Clear all","simple-events")}):null}),(0,m.jsx)("div",{className:"se-selected-tickets_list",children:_&&(0,m.jsx)(n.Fragment,{children:r||o.newTicketAdded?(0,m.jsx)(d.Spinner,{}):b().map(((t,s)=>(0,m.jsx)(C,{...e,editingProduct:t.id,index:t.id,onRemove:()=>{const e=l;e.splice(s,1),x(e)},onReorder:e=>{a({selected:e})},title:t.name})))})})]});return(0,m.jsxs)(n.Fragment,{children:[_?k:(0,m.jsx)("p",{children:(0,c.__)("No tickets have been added to this event.","simple-events")}),o.searchMode&&j]})}));(0,S.registerBlockType)("simple-events/event-tickets",{edit:e=>{const{attributes:t,setAttributes:s}=e,{addMode:i,editMode:a,searchMode:o,selected:r}=t;return(0,m.jsx)("div",{...(0,N.useBlockProps)(),children:(0,m.jsx)(d.Placeholder,{icon:"tickets-alt",label:(0,c.__)("Event Tickets","simple-events"),children:M&&L?(0,m.jsxs)(n.Fragment,{children:[(0,m.jsx)(D,{...e}),i&&(0,m.jsx)(C,{...e,onRemove:()=>s({addMode:!1}),onSave:e=>s({selected:e,newTicketAdded:!0,addMode:!1})}),!i&&!a&&!o&&(0,m.jsxs)("div",{className:"se-mode-button-container",children:[(0,m.jsx)(d.Button,{isSecondary:!0,onClick:()=>s({addMode:!0}),children:(0,c.__)("Create new ticket","simple-events")}),(0,m.jsx)(d.Button,{isSecondary:!0,onClick:()=>s({searchMode:!0}),children:(0,c.__)("Add existing tickets","simple-events")})]})]}):O()})})},save:()=>null})},151:e=>{var t={utf8:{stringToBytes:function(e){return t.bin.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(t.bin.bytesToString(e)))}},bin:{stringToBytes:function(e){for(var t=[],s=0;s{function t(e){return!!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}e.exports=function(e){return null!=e&&(t(e)||function(e){return"function"==typeof e.readFloatLE&&"function"==typeof e.slice&&t(e.slice(0,0))}(e)||!!e._isBuffer)}},503:(e,t,s)=>{var n,i,a,o,r;n=s(939),i=s(151).utf8,a=s(206),o=s(151).bin,(r=function(e,t){e.constructor==String?e=t&&"binary"===t.encoding?o.stringToBytes(e):i.stringToBytes(e):a(e)?e=Array.prototype.slice.call(e,0):Array.isArray(e)||e.constructor===Uint8Array||(e=e.toString());for(var s=n.bytesToWords(e),l=8*e.length,c=1732584193,d=-271733879,p=-1732584194,m=271733878,u=0;u>>24)|4278255360&(s[u]<<24|s[u]>>>8);s[l>>>5]|=128<>>9<<4)]=l;var h=r._ff,g=r._gg,_=r._hh,v=r._ii;for(u=0;u>>0,d=d+x>>>0,p=p+b>>>0,m=m+j>>>0}return n.endian([c,d,p,m])})._ff=function(e,t,s,n,i,a,o){var r=e+(t&s|~t&n)+(i>>>0)+o;return(r<>>32-a)+t},r._gg=function(e,t,s,n,i,a,o){var r=e+(t&n|s&~n)+(i>>>0)+o;return(r< >>32-a)+t},r._hh=function(e,t,s,n,i,a,o){var r=e+(t^s^n)+(i>>>0)+o;return(r< >>32-a)+t},r._ii=function(e,t,s,n,i,a,o){var r=e+(s^(t|~n))+(i>>>0)+o;return(r< >>32-a)+t},r._blocksize=16,r._digestsize=16,e.exports=function(e,t){if(null==e)throw new Error("Illegal argument "+e);var s=n.wordsToBytes(r(e,t));return t&&t.asBytes?s:t&&t.asString?o.bytesToString(s):n.bytesToHex(s)}},556:(e,t,s)=>{e.exports=s(694)()},694:(e,t,s)=>{"use strict";var n=s(925);function i(){}function a(){}a.resetWarningCache=i,e.exports=function(){function e(e,t,s,i,a,o){if(o!==n){var r=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw r.name="Invariant Violation",r}}function t(){return e}e.isRequired=e;var s={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:a,resetWarningCache:i};return s.PropTypes=s,s}},925:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},939:e=>{var t,s;t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s={rotl:function(e,t){return e<>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return 16711935&s.rotl(e,8)|4278255360&s.rotl(e,24);for(var t=0;t0;e--)t.push(Math.floor(256*Math.random()));return t},bytesToWords:function(e){for(var t=[],s=0,n=0;s>>5]|=e[s]<<24-n%32;return t},wordsToBytes:function(e){for(var t=[],s=0;s<32*e.length;s+=8)t.push(e[s>>>5]>>>24-s%32&255);return t},bytesToHex:function(e){for(var t=[],s=0;s>>4).toString(16)),t.push((15&e[s]).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],s=0;s>>6*(3-a)&63)):s.push("=");return s.join("")},base64ToBytes:function(e){e=e.replace(/[^A-Z0-9+\/]/gi,"");for(var s=[],n=0,i=0;n>>6-2*i);return s}},e.exports=s}},s={};function n(e){var i=s[e];if(void 0!==i)return i.exports;var a=s[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.m=t,e=[],n.O=(t,s,i,a)=>{if(!s){var o=1/0;for(d=0;d=a)&&Object.keys(n.O).every((e=>n.O[e](s[l])))?s.splice(l--,1):(r=!1,a0&&e[d-1][2]>a;d--)e[d]=e[d-1];e[d]=[s,i,a]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var s in t)n.o(t,s)&&!n.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={88:0,640:0};n.O.j=t=>0===e[t];var t=(t,s)=>{var i,a,[o,r,l]=s,c=0;if(o.some((t=>0!==e[t]))){for(i in r)n.o(r,i)&&(n.m[i]=r[i]);if(l)var d=l(n)}for(t&&t(s);cn(131)));i=n.O(i)})();
\ No newline at end of file
diff --git a/OLD/build/blocks/event-tickets/style-index-rtl.css b/OLD/build/blocks/event-tickets/style-index-rtl.css
new file mode 100644
index 0000000..ac36e95
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/style-index-rtl.css
@@ -0,0 +1 @@
+.wp-block-se-event-tickets__heading{margin:0 0 32px}.wp-block-se-event-tickets__ticket-row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;width:100%;margin:0 0 16px}.wp-block-se-event-tickets__ticket-column{display:flex;flex-direction:column;flex-basis:100%}.wp-block-se-event-tickets__ticket-column--title{flex:3}.wp-block-se-event-tickets__ticket-column--price{flex:2}.wp-block-se-event-tickets__ticket-column--buy{flex:1}.wp-block-se-event-tickets__ticket-stock{display:block;opacity:.6;font-size:.75em}.wp-block-se-event-tickets__button{text-align:center}
diff --git a/OLD/build/blocks/event-tickets/style-index.css b/OLD/build/blocks/event-tickets/style-index.css
new file mode 100644
index 0000000..ac36e95
--- /dev/null
+++ b/OLD/build/blocks/event-tickets/style-index.css
@@ -0,0 +1 @@
+.wp-block-se-event-tickets__heading{margin:0 0 32px}.wp-block-se-event-tickets__ticket-row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;width:100%;margin:0 0 16px}.wp-block-se-event-tickets__ticket-column{display:flex;flex-direction:column;flex-basis:100%}.wp-block-se-event-tickets__ticket-column--title{flex:3}.wp-block-se-event-tickets__ticket-column--price{flex:2}.wp-block-se-event-tickets__ticket-column--buy{flex:1}.wp-block-se-event-tickets__ticket-stock{display:block;opacity:.6;font-size:.75em}.wp-block-se-event-tickets__button{text-align:center}
diff --git a/OLD/build/blocks/external-link/block.json b/OLD/build/blocks/external-link/block.json
new file mode 100644
index 0000000..9d22a62
--- /dev/null
+++ b/OLD/build/blocks/external-link/block.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/external-link",
+ "title": "External Tickets",
+ "description": "Add a link to an external ticket website.",
+ "icon": "admin-links",
+ "category": "simple-events",
+ "keywords": [
+ "event info",
+ "tickets",
+ "link",
+ "Simple Events",
+ "meta"
+ ],
+ "usesContext": [
+ "postId"
+ ],
+ "attributes": {
+ "thePostId": {
+ "type": "integer",
+ "default": 0
+ }
+ },
+ "supports": {
+ "html": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/external-link/index-rtl.css b/OLD/build/blocks/external-link/index-rtl.css
new file mode 100644
index 0000000..9104436
--- /dev/null
+++ b/OLD/build/blocks/external-link/index-rtl.css
@@ -0,0 +1 @@
+.wp-block-se-event-link{pointer-events:none}
diff --git a/OLD/build/blocks/external-link/index.asset.php b/OLD/build/blocks/external-link/index.asset.php
new file mode 100644
index 0000000..6de9625
--- /dev/null
+++ b/OLD/build/blocks/external-link/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-server-side-render'), 'version' => 'e180eaf0a2b263deac10');
diff --git a/OLD/build/blocks/external-link/index.css b/OLD/build/blocks/external-link/index.css
new file mode 100644
index 0000000..9104436
--- /dev/null
+++ b/OLD/build/blocks/external-link/index.css
@@ -0,0 +1 @@
+.wp-block-se-event-link{pointer-events:none}
diff --git a/OLD/build/blocks/external-link/index.js b/OLD/build/blocks/external-link/index.js
new file mode 100644
index 0000000..3ae1840
--- /dev/null
+++ b/OLD/build/blocks/external-link/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var n in s)e.o(s,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:s[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"simple-events/external-link","title":"External Tickets","description":"Add a link to an external ticket website.","icon":"admin-links","category":"simple-events","keywords":["event info","tickets","link","Simple Events","meta"],"usesContext":["postId"],"attributes":{"thePostId":{"type":"integer","default":0}},"supports":{"html":false},"editorScript":"file:./index.js","editorStyle":"file:./index.css"}'),s=window.wp.blockEditor,n=window.wp.blocks,i=window.wp.serverSideRender;var o=e.n(i);const r=window.ReactJSXRuntime;(0,n.registerBlockType)(t,{edit:({attributes:{thePostId:e},context:{postId:n}})=>{const i=(0,s.useBlockProps)();return(0,r.jsx)("div",{...i,children:(0,r.jsx)(o(),{block:t.name,attributes:{thePostId:n}})})},save:e=>null})})();
\ No newline at end of file
diff --git a/OLD/build/blocks/inner-blocks/block.json b/OLD/build/blocks/inner-blocks/block.json
new file mode 100644
index 0000000..607cfe3
--- /dev/null
+++ b/OLD/build/blocks/inner-blocks/block.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/inner-blocks",
+ "title": "Event Blocks",
+ "category": "simple-events",
+ "supports": {
+ "inserter": false,
+ "multiple": true,
+ "reusable": false,
+ "align": [
+ "wide",
+ "full"
+ ],
+ "layout": true
+ },
+ "editorScript": "file:./index.js"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/inner-blocks/index.asset.php b/OLD/build/blocks/inner-blocks/index.asset.php
new file mode 100644
index 0000000..f6817f6
--- /dev/null
+++ b/OLD/build/blocks/inner-blocks/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-i18n'), 'version' => 'f4ca020f4e96839bd58f');
diff --git a/OLD/build/blocks/inner-blocks/index.js b/OLD/build/blocks/inner-blocks/index.js
new file mode 100644
index 0000000..1188f22
--- /dev/null
+++ b/OLD/build/blocks/inner-blocks/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e,r={149:()=>{window.wp.i18n;const e=window.wp.blocks,r=window.wp.blockEditor,o=window.ReactJSXRuntime;(0,e.registerBlockType)("simple-events/inner-blocks",{edit:()=>(0,o.jsx)("div",{...(0,r.useBlockProps)(),children:(0,o.jsx)(r.InnerBlocks,{templateLock:!1})}),save:()=>(0,o.jsx)(r.InnerBlocks.Content,{})})}},o={};function n(e){var s=o[e];if(void 0!==s)return s.exports;var i=o[e]={exports:{}};return r[e](i,i.exports,n),i.exports}n.m=r,e=[],n.O=(r,o,s,i)=>{if(!o){var t=1/0;for(a=0;a=i)&&Object.keys(n.O).every((e=>n.O[e](o[p])))?o.splice(p--,1):(l=!1,i0&&e[a-1][2]>i;a--)e[a]=e[a-1];e[a]=[o,s,i]},n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),(()=>{var e={817:0,749:0};n.O.j=r=>0===e[r];var r=(r,o)=>{var s,i,[t,l,p]=o,c=0;if(t.some((r=>0!==e[r]))){for(s in l)n.o(l,s)&&(n.m[s]=l[s]);if(p)var a=p(n)}for(r&&r(o);cn(149)));s=n.O(s)})();
\ No newline at end of file
diff --git a/OLD/build/blocks/inner-blocks/style-index-rtl.css b/OLD/build/blocks/inner-blocks/style-index-rtl.css
new file mode 100644
index 0000000..6de3e3c
--- /dev/null
+++ b/OLD/build/blocks/inner-blocks/style-index-rtl.css
@@ -0,0 +1 @@
+.block-editor-block-list__block[data-type="simple-events/inner-blocks"]:not(.is-selected):not(.has-child-selected) .block-editor-default-block-appender{display:block}
diff --git a/OLD/build/blocks/inner-blocks/style-index.css b/OLD/build/blocks/inner-blocks/style-index.css
new file mode 100644
index 0000000..6de3e3c
--- /dev/null
+++ b/OLD/build/blocks/inner-blocks/style-index.css
@@ -0,0 +1 @@
+.block-editor-block-list__block[data-type="simple-events/inner-blocks"]:not(.is-selected):not(.has-child-selected) .block-editor-default-block-appender{display:block}
diff --git a/OLD/build/blocks/loop-event-info/block.json b/OLD/build/blocks/loop-event-info/block.json
new file mode 100644
index 0000000..63232d5
--- /dev/null
+++ b/OLD/build/blocks/loop-event-info/block.json
@@ -0,0 +1,75 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/loop-event-info",
+ "title": "Event Metadata",
+ "description": "Display event meta in a custom query loop.",
+ "icon": "tag",
+ "category": "simple-events",
+ "keywords": [
+ "event info",
+ "date",
+ "location",
+ "Simple Events",
+ "meta"
+ ],
+ "usesContext": [
+ "postId"
+ ],
+ "attributes": {
+ "textAlign": {
+ "type": "string",
+ "default": "left"
+ },
+ "thePostId": {
+ "type": "integer",
+ "default": 0
+ },
+ "metaName": {
+ "enum": [
+ "location",
+ "venue",
+ "dates",
+ "date",
+ "time"
+ ],
+ "type": "string",
+ "default": "dates"
+ },
+ "metaPrefix": {
+ "type": "string",
+ "default": ""
+ },
+ "addCalendarLinks": {
+ "type": "boolean"
+ }
+ },
+ "supports": {
+ "html": false,
+ "align": true,
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontFamily": true,
+ "__experimentalFontWeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalTextTransform": true,
+ "__experimentalTextDecoration": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ },
+ "spacing": {
+ "margin": true,
+ "padding": true,
+ "blockGap": true
+ },
+ "color": {
+ "text": true
+ }
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./editor.css",
+ "style": "file:./style.css"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/loop-event-info/index-rtl.css b/OLD/build/blocks/loop-event-info/index-rtl.css
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/OLD/build/blocks/loop-event-info/index-rtl.css
@@ -0,0 +1,2 @@
+
+
diff --git a/OLD/build/blocks/loop-event-info/index.asset.php b/OLD/build/blocks/loop-event-info/index.asset.php
new file mode 100644
index 0000000..a140fec
--- /dev/null
+++ b/OLD/build/blocks/loop-event-info/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n', 'wp-server-side-render'), 'version' => '942591a32ff4f3a153b6');
diff --git a/OLD/build/blocks/loop-event-info/index.css b/OLD/build/blocks/loop-event-info/index.css
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/OLD/build/blocks/loop-event-info/index.css
@@ -0,0 +1,2 @@
+
+
diff --git a/OLD/build/blocks/loop-event-info/index.js b/OLD/build/blocks/loop-event-info/index.js
new file mode 100644
index 0000000..f4593f3
--- /dev/null
+++ b/OLD/build/blocks/loop-event-info/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"simple-events/loop-event-info","title":"Event Metadata","description":"Display event meta in a custom query loop.","icon":"tag","category":"simple-events","keywords":["event info","date","location","Simple Events","meta"],"usesContext":["postId"],"attributes":{"textAlign":{"type":"string","default":"left"},"thePostId":{"type":"integer","default":0},"metaName":{"enum":["location","venue","dates","date","time"],"type":"string","default":"dates"},"metaPrefix":{"type":"string","default":""},"addCalendarLinks":{"type":"boolean"}},"supports":{"html":false,"align":true,"typography":{"fontSize":true,"lineHeight":true,"__experimentalFontFamily":true,"__experimentalFontWeight":true,"__experimentalFontStyle":true,"__experimentalTextTransform":true,"__experimentalTextDecoration":true,"__experimentalLetterSpacing":true,"__experimentalDefaultControls":{"fontSize":true}},"spacing":{"margin":true,"padding":true,"blockGap":true},"color":{"text":true}},"editorScript":"file:./index.js","editorStyle":"file:./editor.css","style":"file:./style.css"}'),n=window.wp.i18n,a=window.wp.blocks,l=window.wp.components,o=window.wp.serverSideRender;var i=e.n(o);const s=window.wp.blockEditor,r=window.ReactJSXRuntime;(0,a.registerBlockType)(t,{edit:({attributes:{metaName:e,metaPrefix:a,thePostId:o,textAlign:d,addCalendarLinks:p},setAttributes:m,context:{postId:u}})=>(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.InspectorControls,{children:(0,r.jsxs)(l.PanelBody,{title:(0,n.__)("Display Options","simple-events"),children:[(0,r.jsx)(l.SelectControl,{label:(0,n.__)("Show what event info?","simple-events"),value:e,options:[{label:(0,n.__)("Date & Time","simple-events"),value:"dates"},{label:(0,n.__)("Location","simple-events"),value:"location"},{label:(0,n.__)("Venue","simple-events"),value:"venue"},{label:(0,n.__)("Date Only","simple-events"),value:"date"},{label:(0,n.__)("Time Only","simple-events"),value:"time"}],onChange:e=>m({metaName:e}),__nextHasNoMarginBottom:!0}),(0,r.jsx)(l.TextControl,{label:(0,n.__)("Prefix","simple-events"),value:a,onChange:e=>m({metaPrefix:e}),__nextHasNoMarginBottom:!0}),(0,r.jsx)(l.ToggleControl,{label:(0,n.__)('Show "Add to calendar" links',"simple-events"),checked:p,onChange:e=>m({addCalendarLinks:e})})]})}),(0,r.jsx)(s.BlockControls,{group:"block",children:(0,r.jsx)(s.AlignmentControl,{value:d,onChange:e=>{m({textAlign:e})}})}),(0,r.jsx)("div",{...(0,s.useBlockProps)(),children:(0,r.jsx)(i(),{block:t.name,attributes:{metaName:e,metaPrefix:a,textAlign:d,thePostId:u,addCalendarLinks:p}})})]}),save:e=>null})})();
\ No newline at end of file
diff --git a/OLD/build/blocks/past-events-notice/block.json b/OLD/build/blocks/past-events-notice/block.json
new file mode 100644
index 0000000..90311e8
--- /dev/null
+++ b/OLD/build/blocks/past-events-notice/block.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/past-events-notice",
+ "title": "Past Events Notice",
+ "description": "Display the notice message set in the Simple Events settings.",
+ "icon": "megaphone",
+ "category": "simple-events",
+ "keywords": [
+ "past-events",
+ "notice",
+ "Simple Events"
+ ],
+ "supports": {
+ "html": false,
+ "align": true,
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true
+ },
+ "spacing": {
+ "margin": true,
+ "padding": true,
+ "blockGap": true
+ },
+ "color": {
+ "text": true
+ }
+ },
+ "editorScript": "file:./index.js"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/past-events-notice/index.asset.php b/OLD/build/blocks/past-events-notice/index.asset.php
new file mode 100644
index 0000000..af393d1
--- /dev/null
+++ b/OLD/build/blocks/past-events-notice/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks'), 'version' => '62ffcf7acbdcff27842a');
diff --git a/OLD/build/blocks/past-events-notice/index.js b/OLD/build/blocks/past-events-notice/index.js
new file mode 100644
index 0000000..88237da
--- /dev/null
+++ b/OLD/build/blocks/past-events-notice/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";const e=window.wp.blocks,t=window.wp.blockEditor,s=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"simple-events/past-events-notice","title":"Past Events Notice","description":"Display the notice message set in the Simple Events settings.","icon":"megaphone","category":"simple-events","keywords":["past-events","notice","Simple Events"],"supports":{"html":false,"align":true,"typography":{"fontSize":true,"lineHeight":true},"spacing":{"margin":true,"padding":true,"blockGap":true},"color":{"text":true}},"editorScript":"file:./index.js"}'),n=window.ReactJSXRuntime;(0,e.registerBlockType)(s,{edit:()=>{var e;return(0,n.jsx)(t.InnerBlocks,{template:[["core/paragraph",{content:null!==(e=seSettings?.pastEventsNotice)&&void 0!==e?e:"Event has passed"}]]})},save:()=>(0,n.jsx)(t.InnerBlocks.Content,{})})})();
\ No newline at end of file
diff --git a/OLD/build/blocks/upcoming-events/block.json b/OLD/build/blocks/upcoming-events/block.json
new file mode 100644
index 0000000..efca385
--- /dev/null
+++ b/OLD/build/blocks/upcoming-events/block.json
@@ -0,0 +1,78 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/upcoming-events",
+ "title": "Events Feed",
+ "description": "Display upcoming events, past events or events between a date range.",
+ "icon": "layout",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "upcoming",
+ "Simple Events"
+ ],
+ "attributes": {
+ "className": {
+ "type": "string",
+ "default": ""
+ },
+ "count": {
+ "type": "number",
+ "default": 10
+ },
+ "columns": {
+ "type": "number",
+ "default": 3
+ },
+ "layout": {
+ "type": "string",
+ "default": "list"
+ },
+ "align": {
+ "type": "string",
+ "default": ""
+ },
+ "feedType": {
+ "type": "string",
+ "default": "upcoming",
+ "enum": [
+ "upcoming",
+ "past",
+ "mixed",
+ "range"
+ ]
+ },
+ "showYearDividers": {
+ "type": "boolean",
+ "default": false
+ },
+ "overrideFeedOrder": {
+ "type": "boolean",
+ "default": false
+ },
+ "feedOrder": {
+ "type": "string",
+ "default": "ASC",
+ "enum": [
+ "ASC",
+ "DESC"
+ ]
+ },
+ "dateRange": {
+ "type": "object",
+ "default": {
+ "from": "",
+ "to": ""
+ }
+ }
+ },
+ "supports": {
+ "html": false,
+ "align": [
+ "wide",
+ "full"
+ ]
+ },
+ "editorScript": "file:./index.js",
+ "style": "file:./index.css"
+}
\ No newline at end of file
diff --git a/OLD/build/blocks/upcoming-events/index-rtl.css b/OLD/build/blocks/upcoming-events/index-rtl.css
new file mode 100644
index 0000000..525a724
--- /dev/null
+++ b/OLD/build/blocks/upcoming-events/index-rtl.css
@@ -0,0 +1 @@
+.wp-block-se-upcoming-events ul{list-style:none;margin:0;padding:0}.wp-block-se-upcoming-events ul .se-event-date{font-weight:bold}.wp-block-se-upcoming-events ul .se-event-location{margin-bottom:1rem}.wp-block-se-upcoming-events ul li h2.entry-title{font-size:max(2.5rem,28px)}.wp-block-se-upcoming-events ul li h2.entry-title a{color:var(--wp--preset--color--primary, #28303d)}.wp-block-se-upcoming-events ul li h2.entry-title a:hover{text-decoration:underline}.wp-block-se-upcoming-events ul li>a{background:var(--wp--preset--color--primary, #28303d);color:var(--wp--preset--color--white, #fff);padding:1rem 1.5rem;display:inline-block;font-size:max(1rem,16px);text-transform:uppercase;text-decoration:none;letter-spacing:.05em;font-weight:bold}.wp-block-se-upcoming-events ul li>a:hover{background:var(--wp--preset--color--black, #000)}.wp-block-se-upcoming-events-view-grid ul{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width: 768px){.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-2 ul,.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-3 ul,.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-4 ul{grid-template-columns:1fr 1fr}}@media(min-width: 1024px){.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-3 ul{grid-template-columns:1fr 1fr 1fr}.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-4 ul{grid-template-columns:1fr 1fr 1fr 1fr}}
diff --git a/OLD/build/blocks/upcoming-events/index.asset.php b/OLD/build/blocks/upcoming-events/index.asset.php
new file mode 100644
index 0000000..6be6b7a
--- /dev/null
+++ b/OLD/build/blocks/upcoming-events/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => 'e7063f90b7238e53eda6');
diff --git a/OLD/build/blocks/upcoming-events/index.css b/OLD/build/blocks/upcoming-events/index.css
new file mode 100644
index 0000000..525a724
--- /dev/null
+++ b/OLD/build/blocks/upcoming-events/index.css
@@ -0,0 +1 @@
+.wp-block-se-upcoming-events ul{list-style:none;margin:0;padding:0}.wp-block-se-upcoming-events ul .se-event-date{font-weight:bold}.wp-block-se-upcoming-events ul .se-event-location{margin-bottom:1rem}.wp-block-se-upcoming-events ul li h2.entry-title{font-size:max(2.5rem,28px)}.wp-block-se-upcoming-events ul li h2.entry-title a{color:var(--wp--preset--color--primary, #28303d)}.wp-block-se-upcoming-events ul li h2.entry-title a:hover{text-decoration:underline}.wp-block-se-upcoming-events ul li>a{background:var(--wp--preset--color--primary, #28303d);color:var(--wp--preset--color--white, #fff);padding:1rem 1.5rem;display:inline-block;font-size:max(1rem,16px);text-transform:uppercase;text-decoration:none;letter-spacing:.05em;font-weight:bold}.wp-block-se-upcoming-events ul li>a:hover{background:var(--wp--preset--color--black, #000)}.wp-block-se-upcoming-events-view-grid ul{display:grid;grid-template-columns:1fr;gap:20px}@media(min-width: 768px){.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-2 ul,.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-3 ul,.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-4 ul{grid-template-columns:1fr 1fr}}@media(min-width: 1024px){.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-3 ul{grid-template-columns:1fr 1fr 1fr}.wp-block-se-upcoming-events-view-grid.wp-block-se-upcoming-events-columns-4 ul{grid-template-columns:1fr 1fr 1fr 1fr}}
diff --git a/OLD/build/blocks/upcoming-events/index.js b/OLD/build/blocks/upcoming-events/index.js
new file mode 100644
index 0000000..d3329e0
--- /dev/null
+++ b/OLD/build/blocks/upcoming-events/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.i18n,n=window.wp.blocks,o=window.wp.components,l=window.wp.serverSideRender;var s=e.n(l);const i=window.wp.blockEditor,r=window.wp.element,a=window.wp.primitives,d=(0,r.createElement)(a.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},(0,r.createElement)(a.Path,{d:"M4 4v1.5h16V4H4zm8 8.5h8V11h-8v1.5zM4 20h16v-1.5H4V20zm4-8c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2z"})),c=(0,r.createElement)(a.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,r.createElement)(a.Path,{d:"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7.8 16.5H5c-.3 0-.5-.2-.5-.5v-6.2h6.8v6.7zm0-8.3H4.5V5c0-.3.2-.5.5-.5h6.2v6.7zm8.3 7.8c0 .3-.2.5-.5.5h-6.2v-6.8h6.8V19zm0-7.8h-6.8V4.5H19c.3 0 .5.2.5.5v6.2z",fillRule:"evenodd",clipRule:"evenodd"})),v=(0,r.createElement)(a.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,r.createElement)(a.Path,{d:"M20.1 5.1L16.9 2 6.2 12.7l-1.3 4.4 4.5-1.3L20.1 5.1zM4 20.8h8v-1.5H4v1.5z"})),m=window.ReactJSXRuntime;(0,n.registerBlockType)("simple-events/upcoming-events",{edit:({attributes:e,setAttributes:n})=>{const{count:l,layout:a,columns:p,feedType:u,feedOrder:w,overrideFeedOrder:g,showYearDividers:h,dateRange:x}=e,[C,b]=(0,r.useState)(!1);return(0,m.jsxs)("div",{...(0,i.useBlockProps)(),children:[(0,m.jsxs)(i.InspectorControls,{children:[(0,m.jsxs)(o.PanelBody,{title:(0,t.__)("Display Options","simple-events"),className:"panelbody-custom-latest-posts",children:[(0,m.jsx)(o.SelectControl,{label:(0,t.__)("Feed layout","simple-events"),value:a,options:[{label:"List",value:"list"},{label:"Grid",value:"grid"}],onChange:e=>n({layout:e}),__nextHasNoMarginBottom:!0}),"grid"===a&&(0,m.jsx)(o.RangeControl,{label:(0,t.__)("Grid columns","simple-events"),value:p,onChange:e=>n({columns:e}),min:1,max:4}),(0,m.jsx)(o.Button,{variant:"primary",onClick:()=>b(!C),children:C?(0,t.__)('Hide "no results" view',"simple-events"):(0,t.__)('Edit "no results" view',"simple-events")})]}),(0,m.jsxs)(o.PanelBody,{title:(0,t.__)("Event Options","simple-events"),className:"panelbody-custom-latest-posts",children:[(0,m.jsx)(o.SelectControl,{label:(0,t.__)("Feed type","simple-events"),value:u,options:[{label:"Future Events",value:"upcoming"},{label:"Past Events",value:"past"},{label:"Past & Future Events",value:"mixed"},{label:"Events in a Date Range",value:"range"}],onChange:e=>n({feedType:e})}),"range"===u&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(o.BaseControl,{label:"From Date: "+new Date(x.from).toLocaleString("en-US"),children:(0,m.jsx)(o.Dropdown,{position:"bottom left",renderToggle:({isOpen:e,onToggle:t})=>(0,m.jsx)(o.Button,{isSecondary:!0,onClick:t,"aria-expanded":e,children:e?"Close Calendar":"Pick 'From' Date"}),renderContent:()=>(0,m.jsx)(o.TimePicker,{currentDate:x.from,onChange:e=>n({dateRange:{...x,from:e}})})})}),(0,m.jsx)(o.BaseControl,{label:"To Date: "+new Date(x.to).toLocaleString("en-US"),children:(0,m.jsx)(o.Dropdown,{position:"bottom left",renderToggle:({isOpen:e,onToggle:t})=>(0,m.jsx)(o.Button,{isSecondary:!0,onClick:t,"aria-expanded":e,children:e?"Close Calendar":"Pick 'To' Date"}),renderContent:()=>(0,m.jsx)(o.TimePicker,{currentDate:x.to,onChange:e=>n({dateRange:{...x,to:e}})})})})]}),(0,m.jsx)(o.RangeControl,{label:(0,t.__)("Number of Events","simple-events"),value:l,onChange:e=>n({count:e}),min:1,max:50}),(0,m.jsx)(o.ToggleControl,{label:"Show Year Dividers",checked:h,onChange:e=>n({showYearDividers:e})}),(0,m.jsx)(o.ToggleControl,{label:"Override Default Order",checked:g,onChange:e=>n({overrideFeedOrder:e})}),g&&(0,m.jsx)(o.SelectControl,{label:"Feed Order",value:w,options:[{label:"Oldest First",value:"ASC"},{label:"Newest First",value:"DESC"}],onChange:e=>n({feedOrder:e})})]})]}),(0,m.jsx)(i.BlockControls,{children:(0,m.jsx)(o.ToolbarGroup,{controls:[{icon:d,title:(0,t.__)("List view","simple-events"),onClick:()=>n({layout:"list"}),isActive:"list"===a},{icon:c,title:(0,t.__)("Grid view","simple-events"),onClick:()=>n({layout:"grid"}),isActive:"grid"===a},{icon:v,title:(0,t.__)('Edit "no results" view',"simple-events"),onClick:()=>b(!C),isActive:C}]})}),C?(0,m.jsx)(i.InnerBlocks,{}):(0,m.jsx)(o.Disabled,{children:(0,m.jsx)(s(),{block:"simple-events/upcoming-events",attributes:e})})]})},save:e=>(0,m.jsx)(i.InnerBlocks.Content,{})})})();
\ No newline at end of file
diff --git a/OLD/build/js/admin.asset.php b/OLD/build/js/admin.asset.php
new file mode 100644
index 0000000..4c784e0
--- /dev/null
+++ b/OLD/build/js/admin.asset.php
@@ -0,0 +1 @@
+ array(), 'version' => 'f7b2e838744dc7050779');
diff --git a/OLD/build/js/admin.js b/OLD/build/js/admin.js
new file mode 100644
index 0000000..ac6e7c6
--- /dev/null
+++ b/OLD/build/js/admin.js
@@ -0,0 +1 @@
+jQuery(document).ready((function(e){e("#se_ajax_btn").on("click",(function(){e(this).prop("disabled",!0);const t=e(this).data("action");e.ajax({url:ajaxurl,type:"POST",data:{action:t},success:function(t){e("#se_ajax_response").html(`${t?.data}
`)},error:function(){e("#se_ajax_response").html("Something went wrong!
")},complete:function(){e("#se_ajax_btn").prop("disabled",!1),setTimeout((()=>{e("#se_ajax_response").html("")}),2e3)}})}));const t=e('input[name="se_options[skip_cart]"]'),n=e('input[name="se_options[empty_cart_before_adding_tickets]"]');e(window).on("load",(function(){t&&!t.is(":checked")&&(n.prop("checked",!1),n.closest("tr").hide())})),t.on("input",(function(){e(this).is(":checked")?n.closest("tr").show():(n.prop("checked",!1),n.closest("tr").hide())}))}));
\ No newline at end of file
diff --git a/OLD/build/variations/index.asset.php b/OLD/build/variations/index.asset.php
new file mode 100644
index 0000000..6c379cd
--- /dev/null
+++ b/OLD/build/variations/index.asset.php
@@ -0,0 +1 @@
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-dom-ready', 'wp-hooks', 'wp-i18n'), 'version' => 'fc68ac1a366af59ac8bc');
diff --git a/OLD/build/variations/index.js b/OLD/build/variations/index.js
new file mode 100644
index 0000000..0c9d37a
--- /dev/null
+++ b/OLD/build/variations/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={n:t=>{var o=t&&t.__esModule?()=>t.default:()=>t;return e.d(o,{a:o}),o},d:(t,o)=>{for(var s in o)e.o(o,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:o[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.blockEditor,o=window.wp.blocks,s=window.wp.hooks,n=window.wp.components,r=window.wp.i18n,a=window.ReactJSXRuntime,i="se-events/query-loop-events";(0,o.registerBlockVariation)("core/query",{name:i,title:"Query Loop Events",description:"Displays query loop events with upcoming, past settings",category:"simple-events",isActive:({namespace:e,query:t})=>e===i&&"se-event"===t.postType,icon:"layout",attributes:{namespace:i,query:{perPage:6,pages:0,offset:0,postType:"se-event",order:"asc",orderBy:"date",author:"",search:"",exclude:[],sticky:"",inherit:!1,inheritTaxQuery:!0,feedType:"default"}},innerBlocks:[["core/post-template",{},[["core/post-title"],["core/post-date"]]],["core/query-pagination"],["core/query-no-results"]],scope:["inserter"],allowedControls:["taxQuery","search","feedType"]});const l=({attributes:e,setAttributes:t})=>{const{query:o}=e,s=o.feedType||"default",r=o.order||"asc";return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.SelectControl,{label:"Feed Type",value:s,options:[{label:"Default",value:"default"},{label:"Upcoming",value:"upcoming"},{label:"Past",value:"past"}],onChange:e=>{t({query:{...o,feedType:e}})},__nextHasNoMarginBottom:!0}),(0,a.jsx)(n.SelectControl,{label:"Feed Order",value:r,options:[{label:"Oldest First",value:"asc"},{label:"Newest First",value:"desc"}],onChange:e=>{t({query:{...o,order:e}})},__nextHasNoMarginBottom:!0})]})};(0,s.addFilter)("editor.BlockEdit","se-events/my-awesome-pattern",(e=>o=>function(e){return e.attributes.namespace===i}(o)?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(e,{...o}),(0,a.jsx)(t.InspectorControls,{children:(0,a.jsx)(n.PanelBody,{title:(0,r.__)("Events Query Loop","simple-events"),className:"query-loop-events-panel",children:(0,a.jsx)(l,{...o})})})]}):(0,a.jsx)(e,{...o})));const p=window.wp.domReady;e.n(p)()((function(){window?.seSettings?.postType&&"se-event"!==window.seSettings.postType&&(0,o.unregisterBlockType)("simple-events/event-tickets")}))})();
\ No newline at end of file
diff --git a/OLD/plugin.php b/OLD/plugin.php
new file mode 100644
index 0000000..7fdb5ee
--- /dev/null
+++ b/OLD/plugin.php
@@ -0,0 +1,98 @@
+Simple Events is corrupted. Please reinstall!', 'simple-events' );
+ $html_message = wp_sprintf( '%s
', wpautop( $message ) );
+ echo wp_kses_post( $html_message );
+ }
+ );
+ return;
+}
+require_once SE_PLUGIN_DIR . '/vendor/autoload.php';
+
+// Initialize the plugin if system requirements check out.
+$se_requirements = validate_plugin_requirements( SE_BASENAME );
+define( 'SE_REQUIREMENTS', $se_requirements );
+
+if ( $se_requirements instanceof WP_Error ) {
+ add_action(
+ 'admin_notices',
+ static function () use ( $se_requirements ) {
+ $html_message = wp_sprintf( '%s
', $se_requirements->get_error_message() );
+ echo wp_kses_post( $html_message );
+ }
+ );
+ return;
+}
+
+require_once SE_SRC_PATH . '/classes/class-se-event-post-type.php';
+require_once SE_SRC_PATH . '/classes/class-se-blocks.php';
+require_once SE_SRC_PATH . '/classes/class-se-block-variations.php';
+require_once SE_SRC_PATH . '/classes/class-se-template-loader.php';
+require_once SE_SRC_PATH . '/classes/class-se-settings.php';
+require_once SE_SRC_PATH . '/classes/class-se-admin.php';
+require_once SE_SRC_PATH . '/classes/class-se-calendar-export.php';
+require_once SE_SRC_PATH . '/classes/class-se-calendar.php';
+require_once SE_SRC_PATH . '/classes/class-se-event-query-dates.php';
+
+require_once SE_SRC_PATH . '/calendar-functions.php';
+require_once SE_SRC_PATH . '/event-functions.php';
+require_once SE_SRC_PATH . '/template-functions.php';
+require_once SE_SRC_PATH . '/template-hooks.php';
+require_once SE_SRC_PATH . '/woocommerce-hooks.php';
+require_once SE_SRC_PATH . '/rest-api.php';
+require_once SE_SRC_PATH . '/back-compat.php';
+
+/**
+ * Add a flag to leverage for flushing rewrite rules.
+ *
+ * @return void
+ */
+function simple_events_activate() {
+ if ( ! get_option( 'simple_events_flush_rewrite_rules_flag' ) ) {
+ add_option( 'simple_events_flush_rewrite_rules_flag', true );
+ }
+}
+register_activation_hook( __FILE__, 'simple_events_activate' );
diff --git a/OLD/postcss.config.js b/OLD/postcss.config.js
new file mode 100644
index 0000000..ad99dd2
--- /dev/null
+++ b/OLD/postcss.config.js
@@ -0,0 +1,19 @@
+const postcssPlugins = require( '@wordpress/postcss-plugins-preset' );
+
+module.exports = ( ctx ) => {
+ const isDevelopment = ( 'development' === ctx.env );
+ const isSass = ( '.scss' === ctx.file.extname );
+
+ return {
+ map: {
+ inline: isDevelopment,
+ annotation: true
+ },
+ parser: isSass ? 'postcss-scss' : false,
+ plugins: [
+ ...( isSass ? [ require( '@csstools/postcss-sass' ) ] : [] ),
+ ...postcssPlugins,
+ // Additional plugins here.
+ ]
+ };
+};
\ No newline at end of file
diff --git a/OLD/src/assets/js/admin.js b/OLD/src/assets/js/admin.js
new file mode 100644
index 0000000..1aa6c57
--- /dev/null
+++ b/OLD/src/assets/js/admin.js
@@ -0,0 +1,58 @@
+/**
+ * Simple EventsAdmin functionalities.
+ *
+ * @package simple-events
+ */
+
+jQuery( document ).ready( function( $ ) {
+
+ // Handle Ticket Only Order Completion..
+ $( '#se_ajax_btn' ).on( 'click', function() {
+ // Disable button and extract action.
+ $( this ).prop( 'disabled', true );
+ const action = $( this ).data( 'action' );
+
+ // Perform AJAX request.
+ $.ajax( {
+ url: ajaxurl,
+ type: 'POST',
+ data: {
+ action: action,
+ },
+ success: function( response ) {
+ $( '#se_ajax_response' ).html( `${response?.data}
` );
+ },
+ error: function() {
+ $( '#se_ajax_response' ).html( 'Something went wrong!
' );
+ },
+ complete: function() {
+ $( '#se_ajax_btn' ).prop( 'disabled', false );
+ setTimeout( () => {
+ $( '#se_ajax_response' ).html( '' );
+ }, 2000 );
+ },
+ } );
+ } );
+
+ // Handle Skip Cart and Empty Cart options.
+ const skipCart = $( 'input[name="se_options[skip_cart]"]' );
+ const emptyCartBeforeAddingTickets = $( 'input[name="se_options[empty_cart_before_adding_tickets]"]' );
+
+ // If Skip Cart is not enabled, disable Empty Cart Before Adding Tickets.
+ $( window ).on( 'load', function () {
+ if( skipCart && ! skipCart.is( ':checked' ) ) {
+ emptyCartBeforeAddingTickets.prop( 'checked', false );
+ emptyCartBeforeAddingTickets.closest( 'tr' ).hide();
+ }
+ } );
+
+ // Handle Skip Cart option change.
+ skipCart.on( 'input', function() {
+ if( $( this ).is( ':checked' ) ) {
+ emptyCartBeforeAddingTickets.closest( 'tr' ).show();
+ } else {
+ emptyCartBeforeAddingTickets.prop( 'checked', false );
+ emptyCartBeforeAddingTickets.closest( 'tr' ).hide();
+ }
+ } );
+} );
diff --git a/OLD/src/back-compat.php b/OLD/src/back-compat.php
new file mode 100644
index 0000000..6f50ea2
--- /dev/null
+++ b/OLD/src/back-compat.php
@@ -0,0 +1,28 @@
+=' ) )
+ ) {
+ $classes .= ' se-datepicker-compat';
+ }
+
+ return $classes;
+}
+
+add_filter( 'admin_body_class', 'se_pre_gutenberg_14_3_0_compat' );
diff --git a/OLD/src/blocks/calendar/block.json b/OLD/src/blocks/calendar/block.json
new file mode 100644
index 0000000..06337f1
--- /dev/null
+++ b/OLD/src/blocks/calendar/block.json
@@ -0,0 +1,130 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/calendar",
+ "title": "Simple Events Calendar",
+ "icon": "calendar",
+ "category": "simple-events",
+ "keywords": ["event", "calendar", "view", "Simple Events"],
+ "attributes": {
+ "alignment": {
+ "type": "string",
+ "default": "none"
+ },
+ "eventModalAccess": {
+ "type": "boolean",
+ "default": false
+ },
+ "modalBgColor": {
+ "type": "string",
+ "default": ""
+ },
+ "modalTextColor": {
+ "type": "string",
+ "default": ""
+ },
+ "modalIconColor": {
+ "type": "string",
+ "default": ""
+ },
+ "showModalTitle": {
+ "type": "boolean",
+ "default": false
+ },
+ "showModalExcerpt": {
+ "type": "boolean",
+ "default": false
+ },
+ "showModalWhenNoThumbnails": {
+ "type": "boolean",
+ "default": false
+ },
+ "hideNeighbourEvents": {
+ "type": "boolean",
+ "default": false
+ },
+ "presentDayBg": {
+ "type": "string",
+ "default": ""
+ },
+ "presentDayColor": {
+ "type": "string",
+ "default": ""
+ },
+ "presentDayBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "eventDaysBg": {
+ "type": "string",
+ "default": ""
+ },
+ "eventDaysColor": {
+ "type": "string",
+ "default": ""
+ },
+ "eventDaysBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "pastDaysBg": {
+ "type": "string",
+ "default": ""
+ },
+ "pastDaysColor": {
+ "type": "string",
+ "default": ""
+ },
+ "pastDaysBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "upcomingDaysBg": {
+ "type": "string",
+ "default": ""
+ },
+ "upcomingDaysColor": {
+ "type": "string",
+ "default": ""
+ },
+ "upcomingDaysBorder": {
+ "type": "string",
+ "default": ""
+ },
+ "monthYearColor": {
+ "type": "string",
+ "default": ""
+ },
+ "arrowColor": {
+ "type": "string",
+ "default": ""
+ },
+ "arrowPosition": {
+ "type": "string",
+ "default": "top"
+ },
+ "mobileArrowPosition": {
+ "type": "string",
+ "default": "top"
+ },
+ "showDot": {
+ "type": "boolean",
+ "default": true
+ },
+ "eventDotColor": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "supports": {
+ "multiple": false,
+ "reusable": false,
+ "html": false,
+ "align": true,
+ "alignment": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style-index.css",
+ "viewScript": "file:./view.js"
+}
diff --git a/OLD/src/blocks/calendar/calendar.js b/OLD/src/blocks/calendar/calendar.js
new file mode 100644
index 0000000..9c13d3d
--- /dev/null
+++ b/OLD/src/blocks/calendar/calendar.js
@@ -0,0 +1,245 @@
+/* global attributes */
+import apiFetch from '@wordpress/api-fetch';
+
+export default class Calendar {
+ constructor() {
+ this.DOM = {
+ calendars: '.simple-events-calendar',
+ desktopElements: '.simple-events-hidden-mobile',
+ navigationItems: 'simple-events-navigation-item',
+ calendarDay: 'simple-events-calendar-day',
+ mobileEventContainer: 'simple-events-calendar-month-mobile-events',
+ status: {
+ dayActive: 'simple-events-calendar-month__day--active',
+ mobileDayActive: 'simple-events-calendar-month-mobile-events__mobile-day--active',
+ },
+ calendarModal: '.se-event-modal',
+ calendarModalContainer: '.simple-events-calendar-month__day'
+ };
+
+ this.calendars = document.querySelectorAll( this.DOM.calendars );
+ }
+
+ /**
+ * Init Class
+ */
+ init() {
+ if ( this.calendars.length ) {
+ this.calendars.forEach( ( calendarItem ) => {
+ this.initListeners( calendarItem );
+ } );
+ }
+ }
+
+ /**
+ * Init listeners
+ *
+ * @param calendarItem
+ */
+ initListeners( calendarItem ) {
+ this.addNavigationItemsListeners( calendarItem );
+ this.addCalendarDayListeners( calendarItem );
+ this.handleModalFunctionality();
+ }
+
+ /**
+ * Check if mobile view
+ *
+ * @param calendarItem
+ * @return {boolean}
+ */
+ isMobile( calendarItem ) {
+ const mobileElements = calendarItem.querySelectorAll( this.DOM.desktopElements );
+ if ( mobileElements.length ) {
+ if ( 'none' === window.getComputedStyle( mobileElements[ 0 ], null ).display ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add Calendar day listeners
+ *
+ * @param calendarItem
+ */
+ addCalendarDayListeners( calendarItem ) {
+ const calendarDays = calendarItem.querySelectorAll( `[data-js="${ this.DOM.calendarDay }"]` );
+
+ if ( calendarDays.length ) {
+ calendarDays.forEach( ( item ) => {
+ item.addEventListener( 'click', ( event ) => {
+ if ( ! this.isMobile( calendarItem ) ) {
+ return;
+ }
+
+ event.preventDefault();
+ let isActive = false;
+ const mobileDaysContainer = calendarItem.querySelector( `[data-js="${ this.DOM.mobileEventContainer }"]` );
+
+ if ( event.currentTarget.classList.contains( this.DOM.status.dayActive ) ) {
+ isActive = true;
+ }
+
+ if ( mobileDaysContainer ) {
+ const mobileDay = mobileDaysContainer.querySelector( '#' + event.currentTarget.dataset.mobileControl );
+ const activeMobileDays = mobileDaysContainer.querySelectorAll( '.' + this.DOM.status.mobileDayActive );
+ const activeDays = calendarItem.querySelectorAll( '.' + this.DOM.status.dayActive );
+
+ if ( mobileDay ) {
+ if ( activeMobileDays.length ) {
+ activeMobileDays.forEach( ( item ) => {
+ item.classList.remove( this.DOM.status.mobileDayActive );
+ } );
+ }
+ if ( activeDays.length ) {
+ activeDays.forEach( ( item ) => {
+ item.classList.remove( this.DOM.status.dayActive );
+ } );
+ }
+
+ if ( ! isActive ) {
+ event.currentTarget.classList.add( this.DOM.status.dayActive );
+ mobileDay.classList.add( this.DOM.status.mobileDayActive );
+ }
+ }
+ }
+ } );
+ } );
+ }
+ }
+
+ /**
+ * Add navigation items listeners
+ *
+ * @param calendarItem
+ */
+ addNavigationItemsListeners( calendarItem ) {
+ const navigation = calendarItem.querySelectorAll( `[data-js="${ this.DOM.navigationItems }"]` );
+
+ if ( navigation.length ) {
+ navigation.forEach( ( item ) => {
+ item.addEventListener( 'click', ( event ) => {
+ event.preventDefault();
+
+ if ( event.currentTarget.classList.contains( 'disabled' ) ) {
+ return;
+ }
+
+ const date = event.currentTarget.closest( `[data-js="${ this.DOM.navigationItems }"]` ).dataset.date;
+ this.sendCalendarRequest( date, calendarItem );
+ } );
+ } );
+ }
+ }
+
+ /**
+ * Send calendar API request
+ *
+ * @param date
+ * @param calendarItem
+ */
+ sendCalendarRequest( date, calendarItem ) {
+ /**
+ * Convert GET request to POST
+ * Implemented to send block attributes in body instead of URL.
+ */
+ apiFetch( {
+ path: '/simple-events/calendar',
+ method: 'POST',
+ data: {
+ date,
+ attributes,
+ },
+ } ).then( ( result ) => {
+ if ( result.html ) {
+ calendarItem.innerHTML = result.html;
+ this.initListeners( calendarItem );
+ } else {
+ console.log( result );
+ }
+ });
+ }
+
+ /**
+ * Sets a timeout to hide the modal after 500 milliseconds.
+ *
+ * @param {Element} modal - The modal element to hide.
+ * @return {number} The ID of the timeout that can be used to clear it.
+ */
+ handleHideTimeout( modal ) {
+ return setTimeout( () => {
+ modal.classList.add( 'hidden' );
+ }, 150 );
+ }
+
+ /**
+ * Handles the modal functionality for the calendar.
+ *
+ * @return {void}
+ */
+ handleModalFunctionality() {
+ // Target all modal containers.
+ const modalContainer = document.querySelectorAll( this.DOM.calendarModalContainer );
+ modalContainer.forEach( ( element, idx ) => {
+ let modal = null;
+ // Target event titles.
+ const titles = element.querySelectorAll( '.simple-events-calendar-month__calendar-event-title' );
+
+ if ( ! titles || ! titles.length ) {
+ return;
+ }
+
+ let hideTimeout = null;
+
+ titles.forEach( ( title ) => {
+ // On hovering an event's title, show its corresponding modal.
+ title.addEventListener( 'mouseenter', ( e ) => {
+ const article = e.currentTarget.closest( 'article' );
+ modal = article.nextElementSibling;
+
+ if ( ! modal ) {
+ return;
+ }
+
+ // Keep the modal open when hovered, remove timeout.
+ modal.addEventListener( 'mouseenter', () => {
+ clearTimeout( hideTimeout );
+ } );
+
+ // Hide the modal after cursor leaves the modal.
+ modal.addEventListener( 'mouseleave', () => {
+ hideTimeout = this.handleHideTimeout( modal );
+ } );
+
+ modal.classList.remove( 'hidden' );
+
+ // Position of the event in the calendar.
+ const position = ( idx + 1 ) % 7;
+
+ // Set the modal's position based on its position in the calendar.
+ if ( position !== 0 && position < 4 ) {
+ modal.style.left = '80px';
+ } else {
+ modal.style.right = '80px';
+ }
+ if ( ( idx + 1 ) > 22 ) {
+ modal.style.top = '-220px';
+ }
+ } );
+
+ title.addEventListener( 'mouseleave', () => {
+
+ if ( ! modal ) {
+ return;
+ }
+
+ // Hide the modal on leaving the title.
+ hideTimeout = this.handleHideTimeout( modal );
+ } );
+ } );
+
+ } );
+ }
+}
diff --git a/OLD/src/blocks/calendar/color-panel/index.js b/OLD/src/blocks/calendar/color-panel/index.js
new file mode 100644
index 0000000..c78d755
--- /dev/null
+++ b/OLD/src/blocks/calendar/color-panel/index.js
@@ -0,0 +1,58 @@
+/**
+ * Dependencies
+ */
+import { PanelColorSettings } from '@wordpress/block-editor';
+import { PanelBody } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Renders a color panel component with customizable attributes.
+ *
+ * @param {Object} props - The properties passed to the component.
+ * @param {string} props.title - The title of the color panel.
+ * @param {string} props.bgAttr - The attribute for the background color.
+ * @param {string} props.colorAttr - The attribute for the text color.
+ * @param {string} props.borderAttr - The attribute for the border color.
+ * @param {Object} props.attributes - The attributes object.
+ * @param {Function} props.setAttributes - The function to update the attributes.
+ */
+const ColorPanel = ( {
+ title,
+ bgAttr,
+ colorAttr,
+ borderAttr,
+ attributes,
+ setAttributes,
+} ) => {
+ return (
+
+
+ setAttributes( { [ bgAttr ]: value } ),
+ clearable: true,
+ },
+ {
+ label: __( 'Text Color', 'simple-events' ),
+ value: attributes[ colorAttr ],
+ onChange: ( value ) =>
+ setAttributes( { [ colorAttr ]: value } ),
+ clearable: true,
+ },
+ {
+ label: __( 'Border Color', 'simple-events' ),
+ value: attributes[ borderAttr ],
+ onChange: ( value ) =>
+ setAttributes( { [ borderAttr ]: value } ),
+ clearable: true,
+ },
+ ] }
+ />
+
+ );
+};
+
+export default ColorPanel;
diff --git a/OLD/src/blocks/calendar/editor.scss b/OLD/src/blocks/calendar/editor.scss
new file mode 100644
index 0000000..6113e77
--- /dev/null
+++ b/OLD/src/blocks/calendar/editor.scss
@@ -0,0 +1,14 @@
+/**
+ * #.# Editor Styles
+ *
+ * CSS for just Backend enqueued after style.scss
+ * which makes it higher in priority.
+ */
+
+.wp-block-simple-events-calendar {
+}
+
+.block-editor-panel-color-gradient-settings.block-editor-panel-color-gradient-settings {
+ padding: 0;
+ border-top: 0;
+}
diff --git a/OLD/src/blocks/calendar/index.js b/OLD/src/blocks/calendar/index.js
new file mode 100644
index 0000000..3726d2d
--- /dev/null
+++ b/OLD/src/blocks/calendar/index.js
@@ -0,0 +1,266 @@
+/**
+ * BLOCK: Calendar
+ *
+ * Calendar view.
+ */
+
+import './style.scss';
+import './editor.scss';
+
+import { registerBlockType } from '@wordpress/blocks';
+import ServerSideRender from '@wordpress/server-side-render';
+import { PanelBody, ToggleControl, SelectControl } from '@wordpress/components';
+import {
+ BlockControls,
+ useBlockProps,
+ AlignmentToolbar,
+ InspectorControls,
+ PanelColorSettings,
+} from '@wordpress/block-editor';
+import ColorPanel from './color-panel';
+import { __ } from '@wordpress/i18n';
+
+registerBlockType( 'simple-events/calendar', {
+ edit: ( { attributes, setAttributes } ) => {
+ const onChangeAlignment = ( newAlignment ) => {
+ setAttributes( {
+ alignment: newAlignment === undefined ? 'none' : newAlignment,
+ } );
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ setAttributes( { hideNeighbourEvents: value } )
+ }
+ />
+
+ setAttributes( { showDot: value } )
+ }
+ />
+
+ setAttributes( {
+ eventDotColor: value,
+ } ),
+ clearable: true,
+ },
+ ] }
+ />
+
+
+ setAttributes( { eventModalAccess: value } ) }
+ />
+ setAttributes( { showModalTitle: value } ) }
+ />
+ setAttributes( { showModalExcerpt: value } ) }
+ />
+ setAttributes( { showModalWhenNoThumbnails: value } ) }
+ />
+ { __( 'Color Configuration', 'simple-events' ) }
+ setAttributes( { modalBgColor: value } ),
+ clearable: true,
+ },
+ {
+ label: __( 'Text Color', 'simple-events' ),
+ value: attributes?.modalTextColor,
+ onChange: ( value ) => setAttributes( { modalTextColor: value } ),
+ clearable: true,
+ },
+ {
+ label: __( 'Icon Color', 'simple-events' ),
+ value: attributes?.modalIconColor,
+ onChange: ( value ) => setAttributes( { modalIconColor: value } ),
+ clearable: true,
+ }
+ ] }
+ />
+
+
+
+
+
+
+
+ setAttributes( {
+ monthYearColor: value,
+ } ),
+ clearable: true,
+ },
+ ] }
+ />
+
+
+
+ setAttributes( { arrowColor: value } ),
+ clearable: true,
+ },
+ ] }
+ />
+
+
+ setAttributes( { arrowPosition: value } )
+ }
+ options={ [
+ {
+ label: __( 'Top', 'simple-events' ),
+ value: 'top',
+ },
+ {
+ label: __( 'Bottom', 'simple-events' ),
+ value: 'bottom',
+ },
+ ] }
+ />
+
+ setAttributes( { mobileArrowPosition: value } )
+ }
+ options={ [
+ {
+ label: __( 'Top', 'simple-events' ),
+ value: 'top',
+ },
+ {
+ label: __( 'Bottom', 'simple-events' ),
+ value: 'bottom',
+ },
+ ] }
+ />
+
+
+
+
+
+ >
+ );
+ },
+
+ save: () => {
+ return null;
+ },
+} );
diff --git a/OLD/src/blocks/calendar/style.scss b/OLD/src/blocks/calendar/style.scss
new file mode 100644
index 0000000..15d344b
--- /dev/null
+++ b/OLD/src/blocks/calendar/style.scss
@@ -0,0 +1,275 @@
+/**
+ * #.# Styles
+ *
+ * CSS for both Frontend.
+ */
+
+@import '../common.scss';
+.simple-events-calendar {
+ margin-left: auto;
+ margin-right: auto;
+ width: 100%;
+ min-height: 700px;
+ .simple-events-top-bar {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ gap: 20px;
+ align-items: center;
+ width: 100%;
+ nav {
+ display: block;
+ flex: none;
+ ul {
+ display: flex;
+ list-style: none;
+ margin: 10px 0;
+ padding: 0;
+ li {
+ flex: none;
+ &:nth-child(2) {
+ position: absolute;
+ right: 0;
+ }
+ a {
+ display: flex;
+ }
+ svg {
+ width: 25px;
+ }
+ }
+ }
+ }
+ &__today-button {
+ font-size: $font-size-sm;
+ }
+ &__month {
+ font-size: $font-size-xl;
+ }
+ }
+ .simple-events-calendar-month {
+ &__body {}
+ &__header {
+ &-row {
+ display: flex;
+ }
+ &-column {
+ flex: 1;
+ display: flex;
+ @include max-mobile {
+ justify-content: center;
+ }
+ h3 {
+ font-size: $font-size-md;
+ }
+ }
+ }
+ &__body {}
+ &__week {
+ display: flex;
+ }
+ &__day {
+ display: flex;
+ flex-direction: column;
+ min-height: 150px;
+ flex: 1;
+ border: 1px solid rgba(255, 255, 255, 1);
+ -webkit-background-clip: padding-box;
+ /* for Safari */
+ background-clip: padding-box;
+ /* for IE9+, Firefox 4+, Opera, Chrome */
+ @include max-mobile {
+ align-items: center;
+ min-height: 50px;
+ border: none;
+ }
+ &-date-daynum {
+ line-height: 1.4;
+ font-size: $font-size-xl;
+ padding: $spacer-xs $spacer-sm;
+ @include max-mobile {
+ line-height: 1.2;
+ font-size: $font-size-md;
+ padding: $spacer-xxs $spacer-xs;
+ }
+ }
+ &--today {
+ background-color: rgba(60, 120, 200, 0.3);
+ }
+ &--past>time,
+ &--past .simple-events-calendar-month__calendar-event {
+ opacity: 0.6;
+ }
+ &--active {
+ @include max-mobile {
+ background-color: rgba(15, 58, 184, 0.8);
+ }
+ }
+ }
+ &__calendar-event {
+ padding: 0 $spacer-sm 0;
+ margin: 0 0 $spacer-md 0;
+ &.se-event-hide-start-time {
+ // Hide first time.
+ time:first-child {
+ display: none;
+ }
+ .simple-events-calendar-month__calendar-event-datetime-separator {
+ display: none;
+ }
+ }
+ &.se-event-hide-end-time {
+ // Hide last time.
+ time:last-child {
+ display: none;
+ }
+ .simple-events-calendar-month__calendar-event-datetime-separator {
+ display: none;
+ }
+ }
+ @include max-mobile {
+ margin: 0 0 $spacer-xl 0;
+ }
+ &-datetime {
+ font-size: 13px;
+ @include max-mobile {
+ font-size: $font-size-md;
+ }
+ >* {
+ vertical-align: middle;
+ }
+ }
+ &-title {
+ font-size: $font-size-xs;
+ margin: 0;
+ @include max-mobile {
+ font-size: $font-size-md;
+ }
+ &-link {
+ text-decoration: none;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+ &__mobile-events-icon {
+ background-color: white;
+ height: 8px;
+ width: 8px;
+ border-radius: 50%;
+ }
+ }
+ .simple-events-calendar-month-mobile {
+ &-events__mobile-day {
+ display: none;
+ &--active {
+ @include max-mobile {
+ display: block;
+ }
+ }
+ }
+ }
+ .simple-events-mobile__nav {
+ &-list {
+ display: flex;
+ list-style: none;
+ margin: 10px 0;
+ padding: 0;
+ justify-content: center;
+ align-items: center;
+ &-item {
+ flex: 1;
+ display: flex;
+ &--prev {
+ justify-content: flex-start;
+ width: 33.33%;
+ }
+ &--today {
+ justify-content: center;
+ font-size: $font-size-md;
+ width: 33.33%;
+ }
+ &--next {
+ justify-content: flex-end;
+ width: 33.33%;
+ }
+ a {
+ display: flex;
+ }
+ svg {
+ width: 35px;
+ }
+ }
+ }
+ }
+ .disabled {
+ opacity: 0.4;
+ }
+ .simple-events-hidden-mobile {
+ @include max-mobile {
+ display: none !important;
+ }
+ }
+ .simple-events-hidden-desktop {
+ @include tablet {
+ display: none !important;
+ }
+ }
+}
+
+.simple-events-calendar-month-mobile-events__mobile-day .se-event-modal.hidden {
+ display: none;
+}
+
+.simple-events-calendar-month__events {
+ position: relative;
+ .se-event-modal {
+ position: absolute;
+ z-index: 1000;
+ border: 1px solid #808080;
+ padding: 10px;
+ border-radius: 5px;
+ width: 300px;
+ top: 0;
+ background-color: var(--wp--preset--color--primary, #fff);
+ color: var(--wp--preset--color--secondary, #000);
+ $parent: &;
+ &.hidden {
+ display: none;
+ }
+ &__image img {
+ width: 100%;
+ height: fit-content;
+ max-height: 250px;
+ object-fit: contain;
+ border-radius: 5px;
+ }
+ &__flex {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ &:last-of-type {
+ margin-bottom: 5px;
+ }
+ .se-event-modal__date {
+ font-size: 12px;
+ margin: 10px 0;
+ line-height: 1.5;
+ }
+ ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+ .se-event-modal__title {
+ font-size: 15px;
+ margin: 0;
+ }
+ }
+ &__excerpt {
+ margin: 5px 0 0 8px;
+ font-size: 14px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/calendar/view.js b/OLD/src/blocks/calendar/view.js
new file mode 100644
index 0000000..e3b410d
--- /dev/null
+++ b/OLD/src/blocks/calendar/view.js
@@ -0,0 +1,15 @@
+import domReady from '@wordpress/dom-ready';
+import Calendar from './calendar';
+
+/**
+ * DOM Ready
+ */
+domReady( () => {
+ /**
+ * Init Calendar
+ *
+ * @type {Calendar}
+ */
+ const calendar = new Calendar();
+ calendar.init();
+} );
diff --git a/OLD/src/blocks/common.scss b/OLD/src/blocks/common.scss
new file mode 100644
index 0000000..5dc3cfa
--- /dev/null
+++ b/OLD/src/blocks/common.scss
@@ -0,0 +1,60 @@
+/**
+ * #.# Common SCSS
+ *
+ * Can include things like variables and mixins
+ * that are used across the project.
+*/
+
+// Colors.
+$black: rgb(41, 41, 41);
+$white: #f4f4f4;
+$gray: #dedede;
+$green: #bada55;
+$red: orangered;
+
+// Breakpoints
+$breakpoint-mobile: 480px;
+$breakpoint-tablet: 768px;
+$breakpoint-desktop: 1024px;
+
+// Fonts Sizes
+$font-size-xxs: 12px;
+$font-size-xs: 14px;
+$font-size-sm: 16px;
+$font-size-md: 18px;
+$font-size-lg: 20px;
+$font-size-xl: 24px;
+$font-size-xxl: 32px;
+
+// Spacer
+$spacer-xxs: 4px;
+$spacer-xs: 8px;
+$spacer-sm: 12px;
+$spacer-md: 16px;
+$spacer-lg: 20px;
+$spacer-xl: 24px;
+$spacer-xxl: 32px;
+
+@mixin tablet {
+ @media (min-width: $breakpoint-tablet) {
+ @content;
+ }
+}
+
+@mixin desktop-medium {
+ @media (min-width: $breakpoint-desktop) {
+ @content;
+ }
+}
+
+@mixin max-tablet {
+ @media (max-width: $breakpoint-desktop) {
+ @content;
+ }
+}
+
+@mixin max-mobile {
+ @media (max-width: $breakpoint-tablet) {
+ @content;
+ }
+}
diff --git a/OLD/src/blocks/countdown/block.json b/OLD/src/blocks/countdown/block.json
new file mode 100644
index 0000000..c0711dc
--- /dev/null
+++ b/OLD/src/blocks/countdown/block.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/countdown",
+ "title": "Countdown",
+ "description": "Display a countdown to your next upcoming event.",
+ "icon": "clock",
+ "category": "simple-events",
+ "keywords": [
+ "countdown",
+ "event",
+ "upcoming",
+ "Simple Events"
+ ],
+ "attributes": {
+ "className": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "supports": {
+ "html": false,
+ "multiple": false
+ },
+ "editorScript": "file:./index.js",
+ "style": "file:./style-index.css",
+ "viewScript": "file:./view.js"
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/countdown/index.js b/OLD/src/blocks/countdown/index.js
new file mode 100644
index 0000000..16dcb7d
--- /dev/null
+++ b/OLD/src/blocks/countdown/index.js
@@ -0,0 +1,44 @@
+/**
+ * BLOCK: Countdown
+ *
+ * Displays a countdown to the next upcoming post.
+ */
+
+import './style.scss';
+
+// Import JS dependencies.
+import { __ } from '@wordpress/i18n';
+import { registerBlockType } from '@wordpress/blocks';
+import ServerSideRender from '@wordpress/server-side-render';
+import { useBlockProps } from '@wordpress/block-editor';
+
+
+registerBlockType( 'simple-events/countdown', {
+ edit: ( { attributes } ) => {
+ // Fire `simpleEventsCountdownTimer()` once the `#event-timer` element loads.
+ const loadTimerScript = () => {
+ if ( document.getElementById( 'event-timer' ) ) {
+ if ( typeof simpleEventsCountdownTimer === 'function' ) {
+ simpleEventsCountdownTimer(); // eslint-disable-line no-undef
+ }
+ } else {
+ setTimeout( () => loadTimerScript(), 100 );
+ }
+ };
+
+ loadTimerScript();
+
+ return (
+
+
+
+ );
+ },
+
+ save: () => {
+ return null;
+ },
+} );
diff --git a/OLD/src/blocks/countdown/style.scss b/OLD/src/blocks/countdown/style.scss
new file mode 100644
index 0000000..1d910d3
--- /dev/null
+++ b/OLD/src/blocks/countdown/style.scss
@@ -0,0 +1,28 @@
+/**
+ * #.# Styles
+ *
+ * CSS for both Frontend+Backend.
+ */
+@import '../common.scss';
+
+#event-timer {
+ display: flex;
+ justify-content: center;
+ margin: 2rem auto;
+ max-width: 350px;
+ padding: 2rem 0;
+ text-align: center;
+
+ @include tablet {
+ margin: 3rem auto;
+ max-width: 550px;
+ }
+
+ @include desktop-medium {
+ max-width: 50vw;
+ }
+
+ .event-timer__col {
+ flex: 1 1 0;
+ }
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/countdown/view.js b/OLD/src/blocks/countdown/view.js
new file mode 100644
index 0000000..a812450
--- /dev/null
+++ b/OLD/src/blocks/countdown/view.js
@@ -0,0 +1,62 @@
+/* eslint-disable no-bitwise */
+function simpleEventsCountdownTimer() {
+ const eventTimerElement = document.getElementById( 'event-timer' );
+
+ if ( ! eventTimerElement || ! eventTimerElement.dataset.eventStartDate ) {
+ if ( eventTimerElement ) {
+ eventTimerElement.remove();
+ }
+
+ return;
+ }
+
+ const startDate = +eventTimerElement.dataset.eventStartDate;
+ let diff, days, hours, minutes, seconds;
+ const interval = setInterval( eventsTimer, 1000 );
+ const daysElement = eventTimerElement.querySelector(
+ '.event-timer__time-days'
+ );
+ const hoursElement = eventTimerElement.querySelector(
+ '.event-timer__time-hours'
+ );
+ const minutesElement = eventTimerElement.querySelector(
+ '.event-timer__time-minutes'
+ );
+ const secondsElement = eventTimerElement.querySelector(
+ '.event-timer__time-seconds'
+ );
+
+ function eventsTimer() {
+ // get the number of seconds that have elapsed since
+ diff = ( ( startDate - Date.now() ) / 1000 ) | 0;
+
+ if ( diff <= 0 ) {
+ clearInterval( interval );
+ diff = 0;
+ }
+
+ days = ( diff / ( 3600 * 24 ) ) | 0;
+ diff -= days * 3600 * 24;
+
+ hours = ( diff / 3600 ) | 0;
+ diff -= hours * 3600;
+
+ minutes = ( diff / 60 ) | 0;
+ seconds = diff % 60 | 0;
+
+ days = days < 10 ? '0' + days : days;
+ hours = hours < 10 ? '0' + hours : hours;
+ minutes = minutes < 10 ? '0' + minutes : minutes;
+ seconds = seconds < 10 ? '0' + seconds : seconds;
+
+ daysElement.textContent = daysElement && days;
+ hoursElement.textContent = hoursElement && hours;
+ minutesElement.textContent = minutesElement && minutes;
+ secondsElement.textContent = secondsElement && seconds;
+ }
+
+ // we don't want to wait a full second before the timer starts
+ eventsTimer();
+}
+
+simpleEventsCountdownTimer();
diff --git a/OLD/src/blocks/event-info/block.json b/OLD/src/blocks/event-info/block.json
new file mode 100644
index 0000000..21872a9
--- /dev/null
+++ b/OLD/src/blocks/event-info/block.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/event-info",
+ "title": "Event Information",
+ "icon": "calendar",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "information",
+ "Simple Events"
+ ],
+ "attributes": {
+ "eventVenue": {
+ "type": "string"
+ },
+ "eventLocation": {
+ "type": "string"
+ },
+ "eventDates": {
+ "type": "array"
+ },
+ "eventDateStart": {
+ "type": "string"
+ },
+ "eventDateEnd": {
+ "type": "string"
+ },
+ "showOnFrontEnd": {
+ "type": "boolean",
+ "default": true
+ },
+ "externalLinkLabel": {
+ "type": "string"
+ },
+ "externalLink": {
+ "type": "string"
+ }
+ },
+ "supports": {
+ "inserter": false,
+ "multiple": false,
+ "reusable": false,
+ "html": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css"
+}
diff --git a/OLD/src/blocks/event-info/editor-compat.scss b/OLD/src/blocks/event-info/editor-compat.scss
new file mode 100644
index 0000000..74a9fce
--- /dev/null
+++ b/OLD/src/blocks/event-info/editor-compat.scss
@@ -0,0 +1,30 @@
+/**
+ * #.# Back compat for Editor Styles
+ *
+ * CSS for just Backend enqueued after style.scss
+ * which makes it higher in priority.
+ */
+
+.se-datepicker-compat {
+ .se-datetime-popover__date {
+ fieldset:first-child {
+ display: block;
+ }
+
+ fieldset+fieldset {
+ display: none;
+ }
+ }
+
+ .se-datetime-popover__time {
+ .components-datetime__time {
+ fieldset:first-child {
+ display: none;
+ }
+
+ fieldset+fieldset {
+ display: block;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/event-info/editor.scss b/OLD/src/blocks/event-info/editor.scss
new file mode 100644
index 0000000..222e043
--- /dev/null
+++ b/OLD/src/blocks/event-info/editor.scss
@@ -0,0 +1,202 @@
+/**
+ * #.# Editor Styles
+ *
+ * CSS for just Backend enqueued after style.scss
+ * which makes it higher in priority.
+ */
+
+.wp-block-simple-events-event-info {
+ .components-placeholder__fieldset {
+ flex-direction: column;
+ }
+
+ .components-base-control {
+ margin: 0 0 8px;
+ text-align: left;
+
+ &:last-of-type {
+ margin-bottom: 0;
+
+ .components-base-control__field {
+ white-space: nowrap;
+ }
+
+ .components-checkbox-control__input-container {
+ align-content: center;
+ align-items: center;
+ display: inline-flex;
+ height: 33px;
+ position: relative;
+
+ .components-checkbox-control__input {
+ height: 20px;
+ min-width: 20px;
+ }
+ }
+ }
+ }
+
+ .components-base-control__label {
+ display: block;
+ }
+}
+
+.se__button-done {
+ align-self: flex-start;
+ margin-top: 10px;
+}
+
+.se-datetime-popover {
+ .components-popover__content {
+ justify-content: center;
+ width: max-content;
+ padding: 1em;
+ position: relative;
+
+ .components-datetime__date {
+ margin-top: 50px;
+ }
+ }
+
+ .components-datetime__time {
+ padding-bottom: 0;
+
+ fieldset {
+ margin-bottom: 0;
+
+ legend {
+ display: none;
+ }
+ }
+
+ .components-datetime__time-wrapper .components-datetime__time-field-time {
+ align-items: center;
+ display: flex;
+ }
+
+ .components-datetime__time-field-am-pm {
+ white-space: nowrap;
+ }
+ }
+}
+
+/* Date: remove time */
+.se-datetime-popover__date {
+ fieldset:first-child {
+ display: none;
+ }
+
+ .components-datetime__time-field-month-select {
+ height: 100%;
+ }
+}
+
+/* Time: remove date */
+.se-datetime-popover__time {
+ .components-datetime__time {
+ fieldset + fieldset {
+ margin-top: 10px;
+ }
+ }
+
+ .components-datetime__timezone {
+ display: none;
+ }
+}
+
+/* Set date/time button */
+.se-datetime-popover__set-datetime {
+ margin-top: 15px;
+ position: absolute;
+ top: 100px;
+}
+
+.se-datetimegroup-controls-label {
+ display: flex;
+ font-weight: 700;
+
+ + div {
+ margin-top: -4px;
+ }
+}
+
+.se-datetimegroup-container {
+ border-bottom: 1px solid #e2e4e7;
+}
+
+.se-datetimegroup-controls {
+ display: grid;
+ grid-template-columns: auto auto 70px;
+ grid-column-gap: 10px;
+ margin-top: 8px;
+ max-width: 450px;
+
+ .components-dropdown {
+ width: 100%;
+ }
+
+ .components-base-control:nth-of-type(3) {
+ flex: none;
+ align-self: flex-end;
+ margin-bottom: 10px;
+ margin-left: auto;
+
+ .components-checkbox-control__input-container {
+ margin-right: 8px;
+ }
+
+ label {
+ font-weight: normal;
+ font-size: 14px;
+ }
+ }
+
+ .se-datetime-control__delete {
+ grid-column-start: 4;
+ margin-top: -68px;
+ margin-right: -50px;
+ margin-left: auto;
+ align-self: center;
+ }
+
+ .se-datetime-popover__button {
+ width: 100%;
+ justify-content: center;
+ margin-bottom: 0;
+ margin-top: 0;
+ }
+
+ .is-button.is-default:not(:disabled) {
+ border-color: #7e8993;
+ background-color: #fff;
+ color: #32373c;
+ }
+}
+
+.se-datetime-addmore {
+ display: flex;
+ margin-top: 10px;
+ margin-bottom: 20px;
+}
+
+.se-location-label {
+ max-width: 450px;
+
+ label {
+ font-weight: 700;
+ }
+}
+
+.se-site-timezone-label {
+ flex-direction: column;
+ font-size: 12px;
+ color: rgb(117, 117, 117);
+}
+
+.se-event-calendar-export {
+ margin-top: 20px;
+}
+
+.se-all-day-checkbox .components-flex {
+ align-items: center;
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/event-info/index.js b/OLD/src/blocks/event-info/index.js
new file mode 100644
index 0000000..391407f
--- /dev/null
+++ b/OLD/src/blocks/event-info/index.js
@@ -0,0 +1,1055 @@
+/* global lodash, ajaxurl */
+/**
+ * BLOCK: Events Info
+ *
+ * Event date and location management.
+ */
+
+import './editor.scss';
+
+import moment from 'moment';
+import { clone, isEqual, sortBy, head, last, pull } from 'lodash';
+import { __ } from '@wordpress/i18n';
+import { registerBlockType } from '@wordpress/blocks';
+import { Fragment } from '@wordpress/element';
+import {
+ PanelRow,
+ Placeholder,
+ BaseControl,
+ Dropdown,
+ Button,
+ TextControl,
+ Toolbar,
+ Disabled,
+ CheckboxControl,
+ ComboboxControl,
+ DateTimePicker,
+ PanelBody,
+ ToggleControl,
+} from '@wordpress/components';
+import ServerSideRender from '@wordpress/server-side-render';
+import { getSettings } from '@wordpress/date';
+import {
+ BlockControls,
+ InspectorControls,
+ useBlockProps,
+} from '@wordpress/block-editor';
+import { withState } from '@wordpress/compose';
+import { useEntityProp } from '@wordpress/core-data';
+
+/**
+ * Constants
+ */
+const DEFAULT_START_HOUR = 9;
+const DEFAULT_END_HOUR = 10;
+const DATE_SETTINGS = getSettings(); // eslint-disable-line no-restricted-syntax
+
+const OFFSET = Number(DATE_SETTINGS.timezone.offset);
+const TIMEZONE = DATE_SETTINGS.timezone.string;
+let TIMEZONE_NAME = TIMEZONE;
+if ('' === TIMEZONE) {
+ TIMEZONE_NAME = 'UTC' + (OFFSET >= 0 ? '+' : '') + OFFSET;
+}
+const FORMAT = 'YYYY-MM-DD HH:mm';
+const TIMEZONES = moment.tz
+ .names()
+ .map((tz) => ({ label: tz, value: tz }));
+
+// Add an option to use the site settings.
+// (This label is as helpful as we can be since manual offsets have no string.)
+TIMEZONES.unshift({
+ label: __('Same as site', 'simple-events'),
+ value: '',
+});
+
+/**
+ * Get the start and end date from a collection of dates.
+ * Will remove any event that has passed.
+ *
+ * @param {{all_day: boolean, datetime_start: string, datetime_end: string}[]} dates The dates to check.
+ *
+ * returns {{ datetime_start: string, datetime_end: string }}
+ */
+const getStartAndEndDate = (dates) => {
+ // iterate over and remove any that has passed.
+ const now = moment().utcOffset(OFFSET);
+ const filteredDates = dates.filter((date) => {
+
+ const endDate = moment.unix(date.datetime_end).utcOffset(OFFSET);
+ return endDate.isAfter(now);
+ });
+
+ // If we have no filtered dates, but we had dates, before.
+ if (filteredDates.length === 0 && dates.length > 0) {
+ // Extract all start dates with the offset.
+ const allStartDates = dates.map((date) =>
+ moment.unix(date.datetime_start).utcOffset(OFFSET)
+ );
+ const allEndDates = dates.map((date) =>
+ moment.unix(date.datetime_end).utcOffset(OFFSET)
+ );
+
+ // Return the latest start date and earliest end date.
+ return {
+ datetime_start: moment.max(allStartDates).unix().toString(),
+ datetime_end: moment.max(allEndDates).unix().toString(),
+ }
+ }
+
+ let startDate = null;
+ let endDate = null;
+
+ // Loop over the dates and set the start date as the earliest and the end as the latest.
+ filteredDates.forEach((date) => {
+ const startDateMoment = moment.unix(date.datetime_start).utcOffset(OFFSET);
+ const endDateMoment = moment.unix(date.datetime_end).utcOffset(OFFSET);
+
+ // If the end date has passed, skip it.
+ if (endDateMoment.isBefore(now)) {
+ return;
+ }
+
+ /**
+ * Closure for setting the start or end date.
+ * @param {moment.Moment} startDateMoment
+ * @param {moment.Moment} endDateMoment
+ */
+ const setDate = (startDateMoment, endDateMoment) => {
+ // If the start date is before the current start date, set it.
+ if (!startDate || startDateMoment.isBefore(startDate) || (startDate.isAfter(startDateMoment) && startDate.isBefore(now))) {
+ startDate = startDateMoment;
+ }
+
+ // If the end date is after the current end date, set it.
+ if (!endDate || endDateMoment.isAfter(endDate)) {
+ endDate = endDateMoment;
+ }
+ };
+
+ // If the start date if after now
+ if (startDateMoment.isAfter(now) && endDateMoment.isAfter(now)) {
+ setDate(startDateMoment, endDateMoment);
+ } else if (startDateMoment.isBefore(now) && endDateMoment.isAfter(now)) {
+ setDate(startDateMoment, endDateMoment);
+ }
+ });
+
+ // If we have no startDate or endDate, just get the first from dates.
+ if (!startDate) {
+ startDate = moment.unix(head(filteredDates).datetime_start).utcOffset(OFFSET);
+ }
+ if (!endDate) {
+ endDate = moment.unix(last(filteredDates).datetime_end).utcOffset(OFFSET);
+ }
+
+ return {
+ datetime_start: startDate.unix().toString(),
+ datetime_end: endDate.unix().toString(),
+ };
+}
+
+
+/**
+ * Register: a Gutenberg Block.
+ *
+ * Registers a new block provided a unique name and an object defining its
+ * behavior. Once registered, the block is made editor as an option to any
+ * editor interface where blocks are implemented.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/
+ * @param {string} name Block name.
+ * @param {Object} settings Block settings.
+ * @return {?WPBlock} The block, if it has been successfully
+ * registered; otherwise `undefined`.
+ */
+registerBlockType('simple-events/event-info', {
+ /**
+ * The edit function describes the structure of your block in the context of the editor.
+ * This represents what the editor will render when the block is used.
+ *
+ * The "edit" property must be a valid function.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
+ *
+ * @param {Object} props Props.
+ * @return {JSX.Element} JSX Component.
+ */
+ edit: (props) => {
+ const { attributes, setAttributes } = props;
+ const { editMode, showOnFrontEnd } = attributes;
+
+ const [meta, setMeta] = useEntityProp(
+ 'postType',
+ 'se-event',
+ 'meta'
+ );
+
+
+ // Sets the default timezone for calculations.
+
+ let currentTimezone = meta?.se_event_timezone;
+ if ('' === currentTimezone) {
+ currentTimezone = TIMEZONE;
+ }
+
+ const getDstOffset = (timestamp, timezone = null) => {
+ // Return no offset if the event timezone is the same as the site.
+ if (null === timezone) {
+ timezone = currentTimezone;
+ }
+
+ if ('' === timezone) {
+ return OFFSET;
+ }
+
+ // Get the timezone details.
+ const timezoneDetails = moment.tz.zone(timezone);
+
+ // Get the index of the current timezone offset i.e DST or non-DST. -1 at the end to account for search algorithm.
+ const untilIndex = timezoneDetails.untils.findIndex(function (
+ number
+ ) {
+ return number / 1000 > timestamp;
+ });
+
+ return timezoneDetails.offsets[untilIndex] * -1;
+ };
+
+ /**
+ * Creates a moment in the site timezone from the provided unix timestamp.
+ *
+ * @param {string} timestamp Timestamp to convert to a moment.
+ * @param {boolean} formatted Whether to return a human-readable formatted string.
+ * @return {Mixed} Human readable formatted string if `formatted` is true,
+ * moment object otherwise.
+ */
+ const getMoment = (timestamp, formatted = false) => {
+ const dateTime = moment
+ .unix(timestamp)
+ .utcOffset(getDstOffset(timestamp));
+
+ if (!formatted) {
+ return dateTime;
+ }
+
+ return dateTime.format(FORMAT);
+ };
+
+ /**
+ * Creates a timestamp from the provided date string.
+ *
+ * @param {string} dateTime Date string to convert to a timestamp.
+ * @return {string} The timestamp, cast as a string.
+ */
+ const getTimestamp = (dateTime) => {
+ return String(
+ moment(dateTime)
+ .utcOffset(
+ getDstOffset(moment(dateTime).unix()),
+ true
+ )
+ .utc()
+ .unix()
+ );
+ };
+
+ const onDone = () => {
+ setAttributes({ editMode: false });
+ };
+
+ const onChangeEventLocation = (value) => {
+ setMeta({
+ ...meta,
+ se_event_location: value,
+ });
+ };
+
+ const maybeUpdateEventDateTime = (oldDate, newDate) => {
+ if (!isEqual(oldDate, newDate)) {
+ const updatedDates = sortBy(
+ meta?.se_event_dates.map((item) =>
+ item === oldDate ? newDate : item
+ ),
+ 'datetime_start'
+ );
+
+ setMeta({
+ ...meta,
+ se_event_dates: updatedDates,
+ se_event_date_start: getStartAndEndDate(updatedDates).datetime_start,
+ se_event_date_end: getStartAndEndDate(updatedDates).datetime_end,
+ });
+ }
+ };
+
+ const getBlockControls = () => (
+
+
+ setAttributes({ editMode: !editMode }),
+ isActive: editMode,
+ },
+ {
+ icon: showOnFrontEnd ? 'visibility' : 'hidden',
+ title: __('Show on Front-End?', 'simple-events'),
+ onClick: () => {
+ setAttributes({
+ showOnFrontEnd: !showOnFrontEnd,
+ });
+ setMeta({
+ se_event_show_on_frontend: !showOnFrontEnd,
+ });
+ },
+ isActive: showOnFrontEnd,
+ },
+ ]}
+ />
+
+ );
+
+ const DateTimeGroup = withState({
+ tempEventDate: null,
+ tempEventTime: null,
+ })(
+ ({
+ eventDateTime,
+ removeDate,
+ multiDay,
+ tempEventDate,
+ tempEventTime,
+ setState,
+ }) => {
+ const eventStart = getMoment(
+ eventDateTime.datetime_start,
+ true
+ );
+ const eventEnd = getMoment(eventDateTime.datetime_end, true);
+ const timeFormat = DATE_SETTINGS.formats.datetime;
+
+ // To know if the current timezone is a 12 hour time with look for an "a" in the time format.
+ // We also make sure this a is not escaped by a "/".
+ const is12HourTime = /a(?!\\)/i.test(
+ timeFormat
+ .toLowerCase() // Test only the lower case a
+ .replace(/\\\\/g, '') // Replace "//" with empty strings
+ .split('')
+ .reverse()
+ .join('') // Reverse the string and test for "a" not followed by a slash
+ );
+
+ /**
+ * Combines a given date and time into a moment object.
+ *
+ * @param {string} date The date to combine.
+ * @param {string} time The time to combine.
+ *
+ * @return {moment} The combined date and time.
+ */
+ const combineDateAndTime = (date, time) => {
+ const timeMoment = moment(time);
+ const dateMoment = moment(date);
+
+ // Set the timeMoment's time to the dateMoment.
+ return dateMoment.set({
+ hour: timeMoment.get('hour'),
+ minute: timeMoment.get('minute'),
+ });
+ };
+
+ /**
+ * Handle the set date and time button click.
+ *
+ * @param {boolean} isStartChange Whether this is start or end date change.
+ *
+ * @return {void}
+ */
+ const setDateTimeHandler = (isStartChange) => {
+ // Ensure we have either new date or time in state.
+ if (!tempEventDate && !tempEventTime) {
+ return;
+ }
+
+ const newDate =
+ tempEventDate ||
+ (isStartChange ? eventStart : eventEnd);
+ const newTime =
+ tempEventTime ||
+ (isStartChange ? eventStart : eventEnd);
+
+ // Combine the new date and time and convert to a timestamp.
+ const newDateTime = getTimestamp(
+ combineDateAndTime(newDate, newTime)
+ );
+
+ const newEventDateTime = clone(eventDateTime);
+
+ if (isStartChange) {
+ newEventDateTime.datetime_start = newDateTime;
+
+ // Check if the new start time is after the cuurent end time.
+ if (
+ parseInt(newEventDateTime.datetime_start) >=
+ parseInt(newEventDateTime.datetime_end)
+ ) {
+ // Set the new end time to be 1 hour after the start dateTime.
+ newEventDateTime.datetime_end = String(
+ parseInt(newEventDateTime.datetime_start) +
+ 3600
+ );
+ }
+ } else {
+ newEventDateTime.datetime_end = newDateTime;
+
+ // Check if the new end time is before the current start time.
+ if (
+ parseInt(newEventDateTime.datetime_start) >=
+ parseInt(newEventDateTime.datetime_end)
+ ) {
+ // Set the new start time to be 1 hour before the end dateTime.
+ newEventDateTime.datetime_start = String(
+ parseInt(newEventDateTime.datetime_end) - 3600
+ );
+ }
+ }
+
+ // Reset the temp date and time.
+ setState({
+ tempEventDate: null,
+ tempEventTime: null,
+ });
+
+
+ maybeUpdateEventDateTime(eventDateTime, newEventDateTime);
+ };
+
+ /**
+ * Handles DateTimePicker changes.
+ *
+ * @param {string} currentDateTime The current dateTime.
+ * @param {string} newDateTime The new selected dateTime.
+ *
+ * @return {void}
+ */
+ const datePickerHandler = (currentDateTime, newDateTime) => {
+ // Compare the date without time to see if the time or date was changed.
+ const isDateChange =
+ moment(currentDateTime).format('YYYY-MM-DD') ===
+ moment(newDateTime).format('YYYY-MM-DD');
+ const stateUpdate = isDateChange
+ ? { tempEventTime: newDateTime }
+ : { tempEventDate: newDateTime };
+ setState(stateUpdate);
+ };
+
+ return (
+
+
+
+ (
+ {
+ onToggle();
+ }}
+ aria-expanded={isOpen}
+ >
+ {eventDateTime.all_day
+ ? wp.date.format(
+ 'F j, Y',
+ eventStart
+ )
+ : wp.date.format(
+ timeFormat,
+ eventStart
+ )}
+
+ )}
+ renderContent={() => (
+
+
+ datePickerHandler(
+ eventStart,
+ newDateTime
+ )
+ }
+ __nextRemoveHelpButton
+ __nextRemoveResetButton
+ />
+
+ setDateTimeHandler(true)
+ }
+ variant="secondary"
+ >
+ {__(
+ 'Set time',
+ 'simple-events'
+ )}
+
+
+ )}
+ />
+
+
+ (
+ {
+ onToggle();
+ }}
+ aria-expanded={isOpen}
+ disabled={eventDateTime.all_day}
+ >
+ {eventDateTime.all_day
+ ? '--:--'
+ : wp.date.format(
+ timeFormat,
+ eventEnd
+ )}
+
+ )}
+ renderContent={() => (
+
+
+ datePickerHandler(
+ eventEnd,
+ newDateTime
+ )
+ }
+ __nextRemoveHelpButton
+ __nextRemoveResetButton
+ />
+
+ setDateTimeHandler(false)
+ }
+ variant="secondary"
+ text={__(
+ 'Set time',
+ 'simple-events'
+ )}
+ />
+
+ )}
+ />
+
+
+ {
+ const newEventDateTime =
+ clone(eventDateTime);
+
+ newEventDateTime.all_day =
+ !eventDateTime.all_day;
+
+ newEventDateTime.datetime_start =
+ getMoment(
+ newEventDateTime.datetime_start
+ );
+ newEventDateTime.datetime_end =
+ getMoment(
+ newEventDateTime.datetime_end
+ );
+
+ // If all day event, set time between 00:00 and 23:59.
+ if (newEventDateTime.all_day) {
+ newEventDateTime.datetime_start.startOf(
+ 'date'
+ );
+ newEventDateTime.datetime_end.endOf(
+ 'date'
+ );
+ } else {
+ newEventDateTime.datetime_start
+ .hour(DEFAULT_START_HOUR)
+ .minute(0);
+ newEventDateTime.datetime_end
+ .hour(DEFAULT_END_HOUR)
+ .minute(0);
+ }
+
+ newEventDateTime.datetime_start =
+ getTimestamp(
+ newEventDateTime.datetime_start
+ );
+ newEventDateTime.datetime_end =
+ getTimestamp(
+ newEventDateTime.datetime_end
+ );
+
+ const updatedDates = sortBy(
+ meta?.se_event_dates.map((item) =>
+ item === eventDateTime
+ ? newEventDateTime
+ : item
+ ),
+ 'datetime_start'
+ );
+
+ setMeta({
+ ...meta,
+ se_event_dates: updatedDates,
+ se_event_date_start: getStartAndEndDate(updatedDates).datetime_start,
+ se_event_date_end: getStartAndEndDate(updatedDates).datetime_end,
+ });
+ }}
+ />
+
+ {multiDay && (
+
+
+ removeDate(eventDateTime)
+ }
+ />
+
+ )}
+
+
+ );
+ }
+ );
+
+ const EventDateTime = ({ dates }) => {
+ const addNewDate = () => {
+ const existingDates =
+ !dates || 0 === dates.length ? [] : dates;
+
+ // Set default date and time.
+ let eventStart = moment().utcOffset(OFFSET);
+
+ eventStart.hour(DEFAULT_START_HOUR);
+ eventStart.minute(0);
+ eventStart.second(0);
+
+ let eventEnd = eventStart.clone();
+
+ eventEnd.hour(DEFAULT_END_HOUR);
+
+ // Override with existing date if there is one.
+ if (existingDates.length) {
+ eventStart = getMoment(
+ last(existingDates).datetime_start
+ );
+ eventEnd = getMoment(last(existingDates).datetime_end);
+ }
+
+ // Set default date to be +1 day from the last date.
+ eventStart.add(1, 'days');
+ eventEnd.add(1, 'days');
+
+ const updatedDates = sortBy(
+ [
+ ...existingDates,
+ {
+ datetime_start: wp.date.date('U', eventStart),
+ datetime_end: wp.date.date('U', eventEnd),
+ all_day: false,
+ },
+ ],
+ 'datetime_start'
+ );
+
+ setMeta({
+ ...meta,
+ se_event_dates: updatedDates,
+ se_event_date_start: getStartAndEndDate(updatedDates).datetime_start,
+ se_event_date_end: getStartAndEndDate(updatedDates).datetime_end,
+ });
+ };
+
+ const removeDate = (date) => {
+ if (!dates.length) {
+ return;
+ }
+
+ const updatedDates = pull(dates, date);
+
+ setMeta({
+ ...meta,
+ se_event_dates: updatedDates,
+ se_event_date_start: getStartAndEndDate(updatedDates).datetime_start,
+ se_event_date_end: getStartAndEndDate(updatedDates).datetime_end,
+ });
+ };
+
+ // If no dates, add a date.
+ if (!dates || 0 === dates.length) {
+ addNewDate();
+ }
+
+ const datesOutput = [];
+
+ sortBy(dates, 'datetime_start').forEach((date, index) => {
+ datesOutput.push(
+ 1}
+ />
+ );
+ });
+
+ return (
+
+
+ {__('Date & Time', 'simple-events')}
+
+ {datesOutput}
+
+ addNewDate()}>
+ {__('+ Add another date', 'simple-events')}
+
+
+
+ );
+ };
+
+ const renderPreview = () => (
+
+ {getBlockControls()}
+
+
+
+
+ );
+
+ // Show editMode if no location or date set.
+ if (
+ meta?.se_event_location.length === 0 &&
+ (!meta?.se_event_dates || !meta?.se_event_dates?.length)
+ ) {
+ setAttributes({ editMode: true });
+ }
+
+ if (!editMode) {
+ return renderPreview();
+ }
+
+ return (
+
+ {getBlockControls()}
+
+
+ setMeta({ ...meta, se_event_venue: value })}
+ type="text"
+ />
+
+
+ setMeta({ ...meta, se_event_external_link: url })
+ }
+ type="url"
+ />
+ {meta?.se_event_external_link && (
+ <>
+
+ setMeta({
+ ...meta,
+ se_event_external_link_label: value,
+ })
+ }
+ type="text"
+ />
+ {
+ setMeta({
+ ...meta,
+ se_open_external_link: value,
+ });
+ }}
+ />
+ >
+
+ )}
+
+
+
+
+
+
+ {__('Site TimeZone', 'simple-events')}:{' '}
+ {TIMEZONE_NAME} {' '}
+
+ ({__('Change', 'simple-events')})
+
+
+ {
+ const updatedDates = clone(
+ meta?.se_event_dates ?? []
+ );
+
+ // Ensure that the value is a string.
+ value = !Boolean(value) ? '' : value;
+ currentTimezone = value;
+
+ if ('' === value) {
+ currentTimezone = TIMEZONE;
+ }
+
+ updatedDates.forEach((eventDateTime) => {
+ [
+ 'datetime_start',
+ 'datetime_end',
+ ].forEach((key) => {
+ const dateTime = moment
+ .unix(eventDateTime[key])
+ .utcOffset(
+ getDstOffset(
+ eventDateTime[key],
+ meta?.se_event_timezone
+ )
+ );
+
+ const newOffset =
+ '' !== currentTimezone
+ ? getDstOffset(
+ eventDateTime[key],
+ currentTimezone
+ )
+ : OFFSET;
+
+ eventDateTime[key] = String(
+ dateTime
+ .utcOffset(newOffset, true)
+ .utc()
+ .unix()
+ );
+ });
+ });
+
+ setMeta({
+ ...meta,
+ se_event_dates: updatedDates,
+ se_event_date_start: getStartAndEndDate(updatedDates).datetime_start,
+ se_event_date_end: getStartAndEndDate(updatedDates).datetime_end,
+ se_event_timezone: value,
+ });
+ }}
+ />
+
+ setMeta({
+ ...meta,
+ se_event_display_timezone: value,
+ })
+ }
+ />
+
+ setMeta({
+ ...meta,
+ se_event_display_grouped: value,
+ })
+ }
+ />
+
+ setMeta({
+ ...meta,
+ se_event_hide_start_time: value,
+ })
+ }
+ />
+
+ setMeta({
+ ...meta,
+ se_event_hide_end_time: value,
+ })
+ }
+ />
+
+ setMeta({
+ ...meta,
+ se_event_add_calendar_links: value,
+ })
+ }
+ />
+
+ setMeta({
+ ...meta,
+ se_event_open_in_new_window: value,
+ })
+ }
+ />
+
+
+ setMeta({ ...meta, se_event_modal_access: value })}
+ />
+ setMeta({ ...meta, se_show_modal_title: value })}
+ />
+ setMeta({ ...meta, se_show_modal_excerpt: value })}
+ />
+
+
+
+ );
+ },
+
+ /**
+ * The save function defines the way in which the different attributes should be combined
+ * into the final markup, which is then serialized by Gutenberg into post_content.
+ *
+ * The "save" property must be specified and must be a valid function.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
+ *
+ * @param {Object} props Props.
+ * @return {Mixed} JSX Frontend HTML.
+ */
+ save: () => {
+ return null;
+ },
+});
diff --git a/OLD/src/blocks/event-tickets/block.json b/OLD/src/blocks/event-tickets/block.json
new file mode 100644
index 0000000..62d9268
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/block.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/event-tickets",
+ "title": "Event Tickets",
+ "icon": "tickets-alt",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "tickets",
+ "Simple Events"
+ ],
+ "attributes": {
+ "selected": {
+ "type": "array"
+ }
+ },
+ "supports": {
+ "inserter": true,
+ "multiple": false,
+ "reusable": false,
+ "html": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css",
+ "style": "file:./style-index.css"
+}
diff --git a/OLD/src/blocks/event-tickets/draggable/index.js b/OLD/src/blocks/event-tickets/draggable/index.js
new file mode 100644
index 0000000..dbeed77
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/draggable/index.js
@@ -0,0 +1,94 @@
+import { Icon } from '@wordpress/components';
+import { useRef } from '@wordpress/element';
+import { __experimentalUseDragging as useDragging } from '@wordpress/compose';
+
+const DraggableItem = ( props ) => {
+ const sortableItemRef = useRef();
+
+ const onDragStart = ( event ) => {
+ const element = sortableItemRef.current;
+ const clone = event.target
+ .closest( '.se-draggable-item' )
+ .cloneNode( true );
+
+ clone.classList.add( 'se-draggable-item_dragging-clone' );
+ clone.style.top = `${ element.getBoundingClientRect().top }px`;
+ clone.style.width = `${ element.offsetWidth }px`;
+ element.classList.add( 'se-draggable-item_dragging' );
+ element.parentElement.appendChild( clone );
+
+ document.body.classList.add( 'is-dragging-components-draggable' );
+ };
+
+ const onDragMove = ( event ) => {
+ event.preventDefault();
+
+ const target = event.target.closest( '.se-draggable-item' );
+ const clone = document.querySelector(
+ '.se-draggable-item_dragging-clone'
+ );
+ const cursor = event.clientY;
+ const bounds = clone.parentElement.getBoundingClientRect();
+
+ if ( ! target || cursor < bounds.top || cursor > bounds.bottom ) {
+ return;
+ }
+
+ target.after( sortableItemRef.current );
+
+ clone.style.top = `${ cursor - clone.clientHeight / 2 }px`;
+ };
+
+ const onDragEnd = () => {
+ sortableItemRef.current.classList.remove(
+ 'se-draggable-item_dragging'
+ );
+
+ document.querySelector( '.se-draggable-item_dragging-clone' ).remove();
+ document.body.classList.remove( 'is-dragging-components-draggable' );
+
+ const sortableItems = sortableItemRef.current.parentElement.children;
+ const updatedOrder = [ ...sortableItems ].map( ( item ) =>
+ Number( item.dataset.index )
+ );
+
+ props.onChange( updatedOrder );
+ };
+
+ const { startDrag } = useDragging( {
+ onDragStart,
+ onDragMove,
+ onDragEnd,
+ } );
+
+ const { className = '' } = props;
+
+ return (
+
+ );
+};
+
+export default DraggableItem;
diff --git a/OLD/src/blocks/event-tickets/editor.scss b/OLD/src/blocks/event-tickets/editor.scss
new file mode 100644
index 0000000..a92fb3f
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/editor.scss
@@ -0,0 +1,371 @@
+/**
+ * #.# Editor Styles
+ *
+ * CSS for just Backend enqueued after style.scss
+ * which makes it higher in priority.
+ */
+.wp-block-simple-events-event-tickets {
+ .components-placeholder {
+ min-height: 0;
+ }
+
+ .simple-events-tickets,
+ .se-selected-tickets,
+ .se-new-ticket {
+ +.components-button {
+ margin: 16px auto 0;
+ }
+ }
+
+ .se-selected-tickets_list {
+ padding-left: 20px;
+ }
+
+ .woocommerce-search-list {
+ padding-bottom: 0;
+ }
+
+ .woocommerce-tag .woocommerce-tag__remove.components-icon-button,
+ .woocommerce-search-list__list .woocommerce-search-list__item {
+ height: auto;
+ }
+
+ .se-mode-button-container {
+ display: flex;
+ width: 100%;
+
+ .components-button.is-pressed,
+ .components-button.is-pressed:hover {
+ background: #ddd;
+ color: var(--wp-admin-theme-color);
+ }
+ }
+
+ .woocommerce-search-list__search {
+ border-top: none;
+ margin-top: 0;
+ padding: 0;
+ }
+
+ .woocommerce-search-list__list.is-not-found {
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ }
+
+ .components-button .woocommerce-tag__remove {
+ height: 40px;
+ padding: 6px 12px;
+ position: absolute;
+ right: -48px;
+ top: 0;
+ }
+
+ .components-panel__body-title {
+
+ span[aria-hidden='true'] svg {
+ display: none;
+ }
+ }
+
+ .dashicons-edit,
+ svg.edit {
+ color: var(--wp-admin-theme-color);
+ fill: currentColor;
+ position: absolute;
+ right: 16px;
+ top: 50%;
+ transform: translateY(-50%);
+ transition: color .1s ease-in-out;
+ }
+
+ .dashicons-edit {
+ right: 0;
+ }
+
+ .components-panel__body.is-opened {
+ border-bottom: none;
+ padding-top: 0;
+
+ .components-panel__body-title {
+ height: 0;
+ margin: 0;
+
+ button:focus {
+ box-shadow: none;
+ }
+
+ span,
+ .edit {
+ display: none;
+ }
+ }
+
+ .se-ticket-data {
+ margin-top: 9px;
+ }
+ }
+
+ .components-disabled {
+ opacity: .5;
+
+ svg {
+ color: #000;
+ }
+ }
+
+ .se-selected-tickets+.se-mode-button-container,
+ .se-selected-tickets+.simple-events-tickets {
+ margin-top: 50px;
+ }
+
+ .se-ticket-data-container {
+ width: calc(100% - 32px);
+ padding-right: 0;
+ }
+
+ .se-new-ticket {
+ margin: -1px 0 0 20px;
+ width: calc(100% - 52px);
+ }
+
+ // Search list
+ .woocommerce-search-list__list .woocommerce-search-list__item {
+ &:hover {
+ background: var(--wp-admin-theme-color);
+ color: #fff;
+ }
+
+ .woocommerce-search-list__item-state {
+ display: none;
+ }
+ }
+}
+
+.se-ticket-data {
+ position: relative;
+
+ .components-spinner {
+ float: none;
+ left: 0;
+ margin: 0 auto;
+ position: absolute;
+ right: 0;
+ top: 50%;
+ transform: translateY(-50%);
+
+ }
+
+ &_inner {
+ align-items: center;
+ display: grid;
+ grid-column-gap: 10px;
+ grid-row-gap: 8px;
+ grid-template-columns: 155px 155px 1fr;
+ margin-bottom: 20px;
+ width: 100%;
+ }
+
+ &_name {
+ grid-column: 1 / -1;
+ }
+
+ & &_stock-help {
+ align-items: center;
+ align-self: flex-end;
+ display: flex;
+ margin-bottom: 4px;
+
+ .components-button {
+ margin: 0;
+ padding: 12px 6px;
+ }
+ }
+
+ .se-help {
+ cursor: help;
+ height: 40px;
+
+ svg {
+ margin: 10px 0;
+ }
+ }
+
+ &_sale-price {
+ align-self: start;
+
+ .components-base-control__label {
+ width: 100%;
+ }
+ }
+
+ & &_sale-schedule,
+ & &_field-labels {
+ p {
+ margin: 0 0 8px;
+ }
+ }
+
+ & &_sale-schedule {
+ .components-dropdown {
+ width: 100%;
+ }
+
+ .components-button {
+ border: 1px solid #757575;
+ border-radius: 2px;
+ height: auto;
+ margin-bottom: 8px;
+ padding: 6px 8px;
+ width: 100%;
+ }
+
+ .se-ticket-data_datetime-placeholder {
+ color: rgba(0, 0, 0, 0.5);
+
+ &:active,
+ &:focus,
+ &:hover {
+ color: rgba(0, 0, 0, 0.5);
+ }
+ }
+ }
+
+ & &_sale-help {
+ align-items: center;
+ display: flex;
+ margin-bottom: 17px;
+
+ .components-button {
+ margin: 0;
+ padding: 12px 6px;
+ }
+ }
+
+ &_additional-fields {
+ grid-column: 1 / -1;
+
+ .editor-styles-wrapper &>p {
+ margin: 8px 0;
+ }
+ }
+
+ &_field-labels {
+ display: grid;
+ grid-column-gap: 10px;
+ grid-template-columns: 155px 155px;
+ }
+
+ &_additional-field {
+ align-items: center;
+ display: grid;
+ grid-column-gap: 10px;
+ grid-template-columns: 22px 155px 155px 1fr 44px;
+ margin: 0 0 8px -32px;
+
+ .components-base-control .components-base-control__field {
+ margin-bottom: 0;
+ }
+ }
+
+ .disabled * {
+ cursor: default;
+ }
+
+ & &_additional-field-remove {
+ margin: 0;
+ }
+
+ &_additional-field-options {
+ grid-column: 2 / 2;
+ margin-top: 8px;
+ }
+
+ .components-base-control .components-base-control__help {
+ margin-bottom: 0;
+ }
+
+ .components-disabled .components-base-control__help {
+ opacity: .5;
+ }
+}
+
+.se-draggable-item {
+ &_dragging {
+ opacity: .5;
+ }
+
+ #editor &_dragging-clone {
+ background: #fff;
+ pointer-events: none;
+ position: fixed;
+ z-index: 1000000000;
+ }
+
+ body:not(.is-dragging-components-draggable) &_handle {
+ cursor: move;
+ display: flex;
+ }
+}
+
+.se-selected-tickets {
+ width: 100%;
+
+ &_header {
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+
+ strong {
+ line-height: 1;
+ padding: 14px 0;
+ }
+ }
+
+ .components-button {
+ padding: 12px 12px;
+
+ span:last-of-type {
+ overflow: hidden;
+ padding-right: 34px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
+
+ &_list {
+ .editor-styles-wrapper &>p {
+ margin: 9px 0;
+ }
+
+ >.components-spinner {
+ display: block;
+ float: none;
+ margin: 5px auto;
+ }
+ }
+
+ &_list,
+ .se-selected-ticket {
+ position: relative;
+ }
+
+ .se-selected-ticket {
+ margin-top: -1px;
+
+ >.se-draggable-item_handle {
+ display: flex;
+ left: -32px;
+ padding: 12px 6px;
+ position: absolute;
+ top: 0;
+ }
+ }
+
+ .se-selected-ticket.se-is-open {
+ >.se-draggable-item_handle {
+ display: none;
+ pointer-events: none;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/event-tickets/index.js b/OLD/src/blocks/event-tickets/index.js
new file mode 100644
index 0000000..6b616a8
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/index.js
@@ -0,0 +1,371 @@
+/**
+ * BLOCK: Event Tickets
+ *
+ * Fetches tickets from WooCommerce.
+ */
+import './style.scss';
+import './editor.scss';
+
+import SearchListControl from './search-list-control';
+import TicketDataControl from './ticket-data-control';
+
+import { __, sprintf } from '@wordpress/i18n';
+import apiFetch from "@wordpress/api-fetch";
+import { registerBlockType } from '@wordpress/blocks';
+import { Placeholder, Button, Spinner } from '@wordpress/components';
+import { Fragment } from '@wordpress/element';
+import { addQueryArgs } from '@wordpress/url';
+import { withState } from '@wordpress/compose';
+import { useBlockProps } from '@wordpress/block-editor';
+import { flatten, uniqBy, debounce } from 'lodash';
+
+const productCount = window.seSettings.productCount || 50;
+const isLargeCatalog = window.seSettings.isLargeCatalog || false;
+const isWCActive = window.seSettings.isWCActive || false;
+const isBOActive = window.seSettings.isBOActive || false;
+
+const renderMissingDependencies = () => {
+ const dependencies = [];
+
+ if ( ! isWCActive ) {
+ dependencies.push( 'WooCommerce' );
+ }
+
+ if ( ! isBOActive ) {
+ dependencies.push( 'WooCommerce Box Office' );
+ }
+
+ return dependencies.length ? (
+
+ { sprintf(
+ __(
+ '%s must be installed and active to use this block.',
+ 'simple-events'
+ ),
+ dependencies.join( __( ' and ', 'simple-events' ) )
+ ) }
+
+ ) : null;
+};
+
+/**
+ * Get a promise that resolves to a list of products from the API.
+ *
+ * @param {string} - A query string with the search term.
+ * @return {Array} - An array of products.
+ */
+const getProducts = async ( { selected = [], search = '' } ) => {
+ const postTypes = [ 'product', 'product_variation' ];
+ const pageSize = 25;
+ let offset = 0;
+ let requests = [];
+ let queryArgs = null;
+
+ while ( offset < productCount ) {
+ queryArgs = {
+ post_type: postTypes,
+ offset: offset,
+ per_page: pageSize,
+ status: 'publish',
+ search,
+ };
+
+ requests.push( addQueryArgs( 'simple-events/tickets', queryArgs ) );
+ offset += pageSize;
+ }
+
+ if ( selected.length ) {
+ requests.push(
+ addQueryArgs( 'simple-events/tickets', {
+ status: 'publish',
+ include: selected,
+ } )
+ );
+ }
+
+ const data = await Promise.all(
+ requests.map( ( path ) => apiFetch( { path } ) )
+ );
+
+ return uniqBy( flatten( data ), 'id' ).map( ( item ) => {
+ return {
+ id: parseInt( item.id, 10 ),
+ name: item.name,
+ };
+ } );
+};
+
+const TicketSelection = withState( {
+ loading: true,
+ selected: [],
+ products: [],
+ search: '',
+} )( ( props ) => {
+ const {
+ setState,
+ setAttributes,
+ attributes,
+ loading,
+ selected,
+ products,
+ search,
+ } = props;
+
+
+
+ // Read selected from attributes.
+ const savedSelected = attributes.selected ?? [];
+ const selectedCount = savedSelected.length;
+
+ if ( selectedCount && ! selected.length ) {
+ setState( { selected: savedSelected } );
+ }
+
+ // Reload products if a new ticket has been added.
+ if ( attributes.newTicketAdded ) {
+ setState( { loading: true } );
+ setAttributes( { newTicketAdded: false } );
+ }
+
+ // Load products.
+ if ( loading ) {
+ getProducts( { savedSelected, search } )
+ .then( ( data ) => {
+ setState( { products: data, loading: false } );
+ } )
+ .catch( () => {
+ setState( { products: [], loading: false } );
+ } );
+ }
+
+ const debounceSearch = debounce( ( searchValue ) => {
+ setState( { loading: true, search: searchValue } );
+ }, 400 );
+
+ const onChange = ( ids ) => {
+ setState( { selected: ids } );
+ setAttributes( { selected: ids } );
+ };
+
+ const getSelectedProducts = ( items = products ) => {
+ return selected.map( ( id ) =>
+ items.find( ( item ) => item.id === id )
+ );
+ };
+
+ const searchList = (
+
+ ! selected.includes( id ) && ! isNaN( id )
+ )
+ : products
+ }
+ selected={
+ selected
+ ? products.filter( ( { id } ) =>
+ selected.includes( id )
+ )
+ : []
+ }
+ onChange={ ( items ) => {
+ let updatedSelected = selected;
+
+ if ( items.length > selected.length ) {
+ updatedSelected.push( items.pop().id );
+ } else {
+ updatedSelected = getSelectedProducts( items )
+ .filter( Boolean )
+ .map( ( { id } ) => id );
+ }
+
+ onChange( updatedSelected );
+ } }
+ onSearch={ isLargeCatalog ? debounceSearch : null }
+ />
+ setAttributes( { searchMode: false } ) }
+ >
+ { __( 'Done adding existing tickets', 'simple-events' ) }
+
+
+ );
+
+ const selectedList = (
+
+
+ { ! loading && 1 < selectedCount ? (
+ onChange( [] ) }
+ aria-label={ __(
+ 'Clear all selected tickets',
+ 'simple-events'
+ ) }
+ >
+ { __( 'Clear all', 'simple-events' ) }
+
+ ) : null }
+
+
+
+ { selectedCount && (
+
+ { loading || attributes.newTicketAdded ? (
+
+ ) : (
+ getSelectedProducts().map( ( item, i ) => (
+ {
+ const updatedSelected = selected;
+
+ updatedSelected.splice( i, 1 );
+
+ onChange( updatedSelected );
+ } }
+ onReorder={ ( reorderedSelected ) => {
+ setAttributes( {
+ selected: reorderedSelected,
+ } );
+ } }
+ title={ item.name }
+ />
+ ) )
+ ) }
+
+ ) }
+
+
+ );
+
+ return (
+
+ { ! selectedCount ? (
+
+ { __(
+ 'No tickets have been added to this event.',
+ 'simple-events'
+ ) }
+
+ ) : (
+ selectedList
+ ) }
+ { attributes.searchMode && searchList }
+
+ );
+} );
+
+/**
+ * Register: a Gutenberg Block.
+ *
+ * Registers a new block provided a unique name and an object defining its
+ * behavior. Once registered, the block is made editor as an option to any
+ * editor interface where blocks are implemented.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/
+ * @param {string} name Block name.
+ * @param {Object} settings Block settings.
+ * @return {?WPBlock} The block, if it has been successfully
+ * registered; otherwise `undefined`.
+ */
+registerBlockType( 'simple-events/event-tickets', {
+ /**
+ * The edit function describes the structure of your block in the context of the editor.
+ * This represents what the editor will render when the block is used.
+ *
+ * The "edit" property must be a valid function.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
+ *
+ * @param {Object} props Props.
+ * @return {Mixed} JSX Component.
+ */
+ edit: ( props ) => {
+ const { attributes, setAttributes } = props;
+ const { addMode, editMode, searchMode, selected } = attributes;
+
+ return (
+
+
+ { isWCActive && isBOActive ? (
+
+
+ { addMode && (
+
+ setAttributes( { addMode: false } )
+ }
+ onSave={ ( updatedSelected ) =>
+ setAttributes( {
+ selected: updatedSelected,
+ newTicketAdded: true,
+ addMode: false,
+ } )
+ }
+ />
+ ) }
+ { ! addMode && ! editMode && ! searchMode && (
+
+
+ setAttributes( { addMode: true } )
+ }
+ >
+ { __(
+ 'Create new ticket',
+ 'simple-events'
+ ) }
+
+
+ setAttributes( {
+ searchMode: true,
+ } )
+ }
+ >
+ { __(
+ 'Add existing tickets',
+ 'simple-events'
+ ) }
+
+
+ ) }
+
+ ) : (
+ renderMissingDependencies()
+ ) }
+
+
+ );
+ },
+
+ /**
+ * The save function defines the way in which the different attributes should be combined
+ * into the final markup, which is then serialized by Gutenberg into post_content.
+ *
+ * The "save" property must be specified and must be a valid function.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
+ *
+ * @param {Object} props Props.
+ * @return {Mixed} JSX Frontend HTML.
+ */
+ save: () => {
+ return null;
+ },
+} );
diff --git a/OLD/src/blocks/event-tickets/search-list-control/index.js b/OLD/src/blocks/event-tickets/search-list-control/index.js
new file mode 100644
index 0000000..9a36413
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/search-list-control/index.js
@@ -0,0 +1,163 @@
+/**
+ * Internal dependencies
+ */
+import SearchListItem from '@woocommerce/components/build-module/search-list-control/item';
+
+/**
+ * External dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+import {
+ MenuGroup,
+ Spinner,
+ TextControl,
+ Icon,
+ withSpokenMessages,
+} from '@wordpress/components';
+import { Component } from '@wordpress/element';
+import { compose, withState } from '@wordpress/compose';
+import { escapeRegExp } from 'lodash';
+
+const Messages = {
+ clear: __( 'Clear all selected tickets', 'simple-events' ),
+ list: __( 'Ticket Products', 'simple-events' ),
+ resultsList: __( 'Results', 'simple-events' ),
+ noItems: __(
+ "Your store doesn't have any ticket products.",
+ 'simple-events'
+ ),
+ noResults: __( 'No results for %s', 'simple-events' ),
+ search: __( 'Search for a ticket product:', 'simple-events' ),
+ updated: __( 'Ticket products search results updated.', 'simple-events' ),
+};
+
+/**
+ * Component to display a searchable, selectable list of items.
+ */
+export class SearchListControl extends Component {
+ constructor() {
+ super( ...arguments );
+
+ this.onSelect = this.onSelect.bind( this );
+ this.renderList = this.renderList.bind( this );
+ }
+
+ componentDidUpdate( prevProps ) {
+ const { onSearch, search } = this.props;
+
+ if ( search !== prevProps.search && typeof onSearch === 'function' ) {
+ onSearch( search );
+ }
+ }
+
+ onSelect( item ) {
+ const { onChange, selected } = this.props;
+
+ return () => {
+ onChange( [ ...selected, item ] );
+ };
+ }
+
+ getFilteredList( list, search ) {
+ if ( ! search ) {
+ return list;
+ }
+
+ const re = new RegExp( escapeRegExp( search ), 'i' );
+
+ this.props.debouncedSpeak( Messages.updated );
+
+ const filteredList = list
+ .map( ( item ) => ( re.test( item.name ) ? item : false ) )
+ .filter( Boolean );
+
+ return filteredList;
+ }
+
+ renderList( list, depth = 0 ) {
+ const { search } = this.props;
+
+ if ( ! list ) {
+ return null;
+ }
+
+ return list.map( ( item ) => (
+
+ ) );
+ }
+
+ renderListSection() {
+ const { isLoading, search } = this.props;
+
+ if ( isLoading ) {
+ return (
+
+
+
+ );
+ }
+
+ const list = this.getFilteredList( this.props.list, search );
+
+ if ( ! list.length ) {
+ return (
+
+
+
+ { search
+ ? // eslint-disable-next-line @wordpress/valid-sprintf
+ sprintf( Messages.noResults, search )
+ : Messages.noItems }
+
+
+ );
+ }
+
+ return (
+
+ { this.renderList( list ) }
+
+ );
+ }
+
+ render() {
+ const { className = '', search, setState } = this.props;
+
+ return (
+
+
+ setState( { search: value } ) }
+ />
+
+
+ { this.renderListSection() }
+
+ );
+ }
+}
+
+export default compose( [
+ withState( {
+ search: '',
+ } ),
+ withSpokenMessages,
+] )( SearchListControl );
diff --git a/OLD/src/blocks/event-tickets/style.scss b/OLD/src/blocks/event-tickets/style.scss
new file mode 100644
index 0000000..c84ff66
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/style.scss
@@ -0,0 +1,42 @@
+.wp-block-se-event-tickets {
+ &__heading {
+ margin: 0 0 32px;
+ }
+
+ &__ticket-row {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: center;
+ width: 100%;
+ margin: 0 0 16px;
+ }
+
+ &__ticket-column {
+ display: flex;
+ flex-direction: column;
+ flex-basis: 100%;
+
+ &--title {
+ flex: 3;
+ }
+
+ &--price {
+ flex: 2;
+ }
+
+ &--buy {
+ flex: 1;
+ }
+ }
+
+ &__ticket-stock {
+ display: block;
+ opacity: 0.6;
+ font-size: .75em;
+ }
+
+ &__button {
+ text-align: center;
+ }
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/event-tickets/ticket-data-control/index.js b/OLD/src/blocks/event-tickets/ticket-data-control/index.js
new file mode 100644
index 0000000..3e0ba75
--- /dev/null
+++ b/OLD/src/blocks/event-tickets/ticket-data-control/index.js
@@ -0,0 +1,766 @@
+/**
+ * Internal dependencies
+ */
+import DraggableItem from '../draggable';
+
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import apiFetch from "@wordpress/api-fetch";
+import {
+ Button,
+ Disabled,
+ TextControl,
+ SelectControl,
+ CheckboxControl,
+ Spinner,
+ Dashicon,
+ PanelBody,
+ TimePicker,
+ Dropdown,
+ Tooltip,
+ TextareaControl,
+ Path,
+ SVG,
+} from '@wordpress/components';
+import { Fragment } from '@wordpress/element';
+import { withState } from '@wordpress/compose';
+import { decodeEntities } from '@wordpress/html-entities';
+import { getSettings, date } from '@wordpress/date';
+import { concat, isEqual } from 'lodash';
+const md5 = require( 'md5' );
+
+const BOTicketFieldTypes = window.seSettings.BOTicketFieldTypes || [];
+
+const fieldTypeOptions = () => {
+ const options = Object.entries( BOTicketFieldTypes ).map(
+ ( [ key, value ] ) => {
+ return { value: key, label: value };
+ }
+ );
+
+ options.unshift( { value: '', label: __( 'Type', 'simple-events' ) } );
+
+ return options;
+};
+
+const removeIcon = (
+
+
+
+);
+
+/**
+ * The block details interface.
+ *
+ * @param {*} param0
+ * @return {Object}
+ */
+const TicketDataControl = ( {
+ dataLoaded,
+ editingProduct,
+ index,
+ loading,
+ name,
+ price,
+ saleData,
+ saleDateFrom,
+ saleDateTo,
+ salePrice,
+ stock,
+ additionalFields,
+ setState,
+ attributes,
+ setAttributes,
+ title,
+ onRemove,
+ onReorder,
+ onSave,
+} ) => {
+ // Load data if this is an existing product being edited.
+ if ( editingProduct && ! dataLoaded ) {
+ setState( {
+ loading: true,
+ dataLoaded: true,
+ } );
+
+ apiFetch( { path: `/wc/v2/products/${ editingProduct }` } ).then(
+ ( response ) => {
+ const ticketFields = Object.values(
+ response.meta_data.find(
+ ( item ) => item.key === '_ticket_fields'
+ ).value
+ );
+
+ setState( {
+ name: response.name,
+ price: response.regular_price,
+ saleData: response.date_on_sale_from ? true : false,
+ saleDateFrom: response.date_on_sale_from,
+ saleDateTo: response.date_on_sale_to,
+ salePrice: response.sale_price,
+ stock: response.stock_quantity,
+ additionalFields: ticketFields,
+ loading: false,
+ } );
+ }
+ );
+ }
+
+ /**
+ * Saves a new product post or updates an existing one.
+ */
+ const saveProduct = async () => {
+ let path = '/wc/v2/products';
+
+ const productData = {
+ name,
+ regular_price: price,
+ sale_price: salePrice,
+ stock_quantity: stock,
+ meta_data: [],
+ };
+
+ if ( salePrice && saleData && saleDateFrom && saleDateTo ) {
+ productData.date_on_sale_from = `${ date(
+ 'Y-m-d',
+ saleDateFrom
+ ) }T00:00:00`;
+ productData.date_on_sale_to = `${ date(
+ 'Y-m-d',
+ saleDateTo
+ ) }T23:59:59`;
+ }
+
+ const ticketFields = {};
+
+ additionalFields.forEach(
+ ( { label, type, required, options = '' } ) => {
+ if ( '' !== type ) {
+ const key = md5( label + type );
+ ticketFields[ key ] = {
+ label,
+ type,
+ required,
+ options,
+ autofill: 'none',
+ email_contact: 'yes',
+ email_gravatar: 'yes',
+ };
+ }
+ }
+ );
+
+ if ( Object.keys( ticketFields ).length !== 0 ) {
+ productData.meta_data.push( {
+ key: '_ticket_fields',
+ value: ticketFields,
+ } );
+ }
+
+ if ( ! editingProduct ) {
+ productData.virtual = true;
+ productData.meta_data.push( { key: '_ticket', value: 'yes' } );
+
+ if ( stock ) {
+ productData.manage_stock = true;
+ }
+ }
+
+ if ( editingProduct ) {
+ path += `/${ editingProduct }`;
+ }
+
+ return await apiFetch( {
+ path,
+ method: editingProduct ? 'PUT' : 'POST',
+ data: productData,
+ } );
+ };
+
+ /**
+ * Update the given date with the new selection from TimePicker.
+ *
+ * @param {string} whichDate Which date to update.
+ * @param {string} oldDate The previous date value.
+ * @param {string} newDate The new date value.
+ */
+ const maybeUpdateDateTime = ( whichDate, oldDate, newDate ) => {
+ if ( isEqual( oldDate, newDate ) ) {
+ return;
+ }
+
+ if ( whichDate === 'From' ) {
+ setState( { saleDateFrom: newDate } );
+ }
+
+ if ( whichDate === 'To' ) {
+ setState( { saleDateTo: newDate } );
+ }
+ };
+
+ /**
+ * The sale scheduling interface.
+ */
+ const DateField = withState( {
+ tempDate: '',
+ } )( ( { label, value, tempDate, setState } ) => {
+ const settings = getSettings();
+ const displayDate = tempDate ? tempDate : value;
+
+ // To know if the current timezone is a 12 hour time with look for an "a" in the time format.
+ // We also make sure this a is not escaped by a "/".
+ const is12HourTime = /a(?!\\)/i.test(
+ settings.formats.time
+ .toLowerCase() // Test only the lower case a
+ .replace( /\\\\/g, '' ) // Replace "//" with empty strings
+ .split( '' )
+ .reverse()
+ .join( '' ) // Reverse the string and test for "a" not followed by a slash
+ );
+
+ return (
+ {
+ if ( tempDate ) {
+ // Send to the parent scope to update state there.
+ maybeUpdateDateTime( label, value, tempDate );
+ setState( { tempDate: undefined } );
+ }
+ } }
+ position="bottom center"
+ renderContent={ () => (
+
+ setState( { tempDate: newDate } )
+ }
+ />
+ ) }
+ renderToggle={ ( { isOpen, onToggle } ) => (
+
+ { ! value
+ ? `${ label }…`
+ : date( 'Y-m-d', displayDate ) }
+
+ ) }
+ />
+ );
+ } );
+
+ /**
+ * Remove the field at the given index.
+ * (Done in this scope to ensure that the component updates.)
+ *
+ * @param {number} i The index of the field to remove.
+ */
+ const removeFieldset = ( i ) => {
+ const updatedFields = additionalFields;
+
+ updatedFields.splice( i, 1 );
+
+ setState( { additionalFields: updatedFields } );
+ };
+
+ /**
+ * Displays the interface for adding or editing additional fields.
+ */
+ const TicketFieldSet = withState( {
+ fields: [],
+ } )( ( { fields, setState, fieldset, index, onChange } ) => {
+ const defaultRequired = [ 'first_name', 'last_name', 'email' ].includes(
+ fieldset.type
+ );
+
+ return (
+
+ onChange( order.map( ( i ) => additionalFields[ i ] ) )
+ }
+ >
+ {
+ const updatedFields = fields;
+
+ updatedFields[ index ] = Object.assign( fieldset, {
+ label: value,
+ } );
+
+ setState( { fields: updatedFields } );
+ } }
+ onBlur={ () => setState( { additionalFields: fields } ) }
+ />
+ {
+ const updatedFields = additionalFields;
+
+ updatedFields[ index ] = Object.assign( fieldset, {
+ type,
+ } );
+
+ setState( { additionalFields: updatedFields } );
+ } }
+ />
+ {
+ const updatedFields = additionalFields;
+
+ updatedFields[ index ] = Object.assign( fieldset, {
+ required,
+ } );
+
+ setState( { additionalFields: updatedFields } );
+ } }
+ />
+ { ! defaultRequired && (
+ removeFieldset( index ) }
+ >
+ { removeIcon }
+
+ ) }
+ { [ 'select', 'radio', 'checkbox' ].includes(
+ fieldset.type
+ ) && (
+ {
+ const updatedFields = fields;
+
+ updatedFields[ index ] = Object.assign( fieldset, {
+ options: value,
+ } );
+
+ setState( { fields: updatedFields } );
+ } }
+ onBlur={ () =>
+ setState( { additionalFields: fields } )
+ }
+ />
+ ) }
+
+ );
+ } );
+
+ /**
+ * Adds a new additional field to the product.
+ */
+ const addFieldset = () => {
+ const existingFields = additionalFields;
+ const updatedFields = concat( ...existingFields, {
+ label: '',
+ type: '',
+ required: false,
+ } );
+
+ setState( { additionalFields: updatedFields } );
+ };
+
+ /**
+ * The product information form.
+ * @param name
+ * @param price
+ * @param stock
+ * @param salePrice
+ * @param e
+ */
+ const ticketDataForm = (
+
+
+
setState( { name } ) }
+ />
+
+ setState( { price } ) }
+ />
+
+ setState( { stock } ) }
+ />
+
+
+
+
+
+
+
+
+ { ! saleData && (
+
setState( { saleData: true } ) }
+ >
+ { __( '+ Add sale price', 'simple-events' ) }
+
+ ) }
+
+
+ { saleData && (
+
+
+ setState( { salePrice } )
+ }
+ />
+
+
+
{ __( 'Sale dates' ) }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setState( {
+ saleData: false,
+ saleDateFrom: '',
+ saleDateTo: '',
+ } )
+ }
+ >
+ { __( 'Cancel', 'simple-events' ) }
+
+
+
+ ) }
+
+
+
+
+ { __( 'Ticket Fields', 'simple-events' ) }
+
+
+
+
+
{ __( 'Label', 'simple-events' ) }
+
{ __( 'Type', 'simple-events' ) }
+
+ { additionalFields.map( ( field, index ) => (
+
+ setState( { additionalFields } )
+ }
+ additionalFields={ additionalFields }
+ />
+ ) ) }
+
+
addFieldset() }>
+ { __( '+ Add Field', 'simple-events' ) }
+
+
+
+ {
+ setState( { loading: true } );
+
+ saveProduct().then( ( data ) => {
+ if ( ! editingProduct ) {
+ const updatedSelected = attributes.selected || [];
+
+ updatedSelected.push( data.id );
+
+ onSave( updatedSelected );
+ }
+
+ setState( { loading: false } );
+
+ if ( attributes.editMode ) {
+ const ticketContainer = document.querySelector(
+ `.se-selected-ticket[data-index="${ attributes.editMode }"]`
+ );
+
+ ticketContainer
+ .querySelector( 'h2 button' )
+ .click();
+
+ setAttributes( { editMode: null } );
+ }
+ } );
+ } }
+ >
+ { __(
+ editingProduct ? 'Update Ticket' : 'Create ticket',
+ 'simple-events'
+ ) }
+
+ {
+ if ( attributes.editMode ) {
+ const ticketContainer = e.target.closest(
+ '.se-ticket-data-container'
+ );
+
+ ticketContainer.querySelector( 'h2 button' ).click();
+ } else {
+ setAttributes( { addMode: false } );
+ }
+ } }
+ >
+ { __( 'Cancel', 'simple-events' ) }
+
+
+ );
+
+ const ticketDataFormContainer = (
+
+ { loading ? (
+
+ { ticketDataForm }
+
+
+ ) : (
+ ticketDataForm
+ ) }
+
+ );
+
+ const labelTextNode = (
+
+ { editingProduct ? (
+
+
+ { __( 'Edit ', 'simple-events' ) }
+
+ { decodeEntities( title ) }
+
+ ) : (
+
+ { __( 'Create new ticket', 'simple-events' ) }
+
+ ) }
+
+
+
+
+ {
+ if (
+ attributes.editMode &&
+ index === attributes.editMode
+ ) {
+ setAttributes( { editMode: null } );
+ } else {
+ e.stopPropagation();
+ }
+
+ onRemove();
+ } }
+ label={ sprintf( __( 'Remove %s', 'simple-events' ), name ) }
+ >
+ { removeIcon }
+
+
+ );
+
+ const item = (
+
+ { editingProduct ? (
+
+ {
+ if ( attributes.editMode ) {
+ setAttributes( { editMode: null } );
+ } else {
+ // Ensure that the most current data is loaded in.
+ setState( { dataLoaded: false } );
+
+ setAttributes( { editMode: editingProduct } );
+
+ // Set focus on the name field.
+ const ticketContainer =
+ document.activeElement.closest(
+ '.se-ticket-data-container'
+ );
+
+ const focusNameInput = () => {
+ const nameInput =
+ ticketContainer.querySelector(
+ '.se-ticket-data_name input'
+ );
+
+ if ( nameInput ) {
+ nameInput.focus();
+ } else {
+ setTimeout( focusNameInput, 10 );
+ }
+ };
+
+ focusNameInput();
+ }
+
+ setAttributes( { searchMode: false } );
+ } }
+ title={ labelTextNode }
+ >
+ { ticketDataFormContainer }
+
+
+ ) : (
+
+ { ticketDataFormContainer }
+
+ ) }
+
+ );
+
+ return (
+
+ { ( attributes.editMode &&
+ editingProduct !== attributes.editMode ) ||
+ ( attributes.addMode && editingProduct ) ? (
+ { item }
+ ) : (
+ item
+ ) }
+
+ );
+};
+
+export default withState( {
+ dataLoaded: false,
+ loading: false,
+ name: '',
+ price: '',
+ saleData: false,
+ saleDateFrom: '',
+ saleDateTo: '',
+ salePrice: '',
+ stock: '',
+ additionalFields: [
+ {
+ label: __( 'First Name', 'simple-events' ),
+ type: 'first_name',
+ required: true,
+ },
+ {
+ label: __( 'Last Name', 'simple-events' ),
+ type: 'last_name',
+ required: true,
+ },
+ {
+ label: __( 'Email Address', 'simple-events' ),
+ type: 'email',
+ required: true,
+ },
+ ],
+} )( TicketDataControl );
diff --git a/OLD/src/blocks/external-link/block.json b/OLD/src/blocks/external-link/block.json
new file mode 100644
index 0000000..809ed90
--- /dev/null
+++ b/OLD/src/blocks/external-link/block.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/external-link",
+ "title": "External Tickets",
+ "description": "Add a link to an external ticket website.",
+ "icon": "admin-links",
+ "category": "simple-events",
+ "keywords": ["event info", "tickets", "link", "Simple Events", "meta"],
+ "usesContext": ["postId"],
+ "attributes": {
+ "thePostId": {
+ "type": "integer",
+ "default": 0
+ }
+ },
+ "supports": {
+ "html": false
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./index.css"
+}
diff --git a/OLD/src/blocks/external-link/editor.scss b/OLD/src/blocks/external-link/editor.scss
new file mode 100644
index 0000000..6003dd2
--- /dev/null
+++ b/OLD/src/blocks/external-link/editor.scss
@@ -0,0 +1,3 @@
+.wp-block-se-event-link {
+ pointer-events: none;
+}
diff --git a/OLD/src/blocks/external-link/index.js b/OLD/src/blocks/external-link/index.js
new file mode 100644
index 0000000..ee10a67
--- /dev/null
+++ b/OLD/src/blocks/external-link/index.js
@@ -0,0 +1,24 @@
+import './editor.scss';
+import metadata from './block.json';
+
+import { useBlockProps } from '@wordpress/block-editor';
+import { registerBlockType } from '@wordpress/blocks';
+import ServerSideRender from '@wordpress/server-side-render';
+
+registerBlockType( metadata, {
+ edit: ({ attributes: { thePostId }, context: { postId } }) => {
+ const blockProps = useBlockProps();
+
+ return (
+
+
+
+ );
+ },
+ save: (props) => null,
+});
diff --git a/OLD/src/blocks/inner-blocks/block.json b/OLD/src/blocks/inner-blocks/block.json
new file mode 100644
index 0000000..3a213c3
--- /dev/null
+++ b/OLD/src/blocks/inner-blocks/block.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/inner-blocks",
+ "title": "Event Blocks",
+ "category": "simple-events",
+ "supports": {
+ "inserter": false,
+ "multiple": true,
+ "reusable": false,
+ "align": ["wide", "full"],
+ "layout": true
+ },
+ "editorScript": "file:./index.js"
+}
diff --git a/OLD/src/blocks/inner-blocks/index.js b/OLD/src/blocks/inner-blocks/index.js
new file mode 100644
index 0000000..4a6898e
--- /dev/null
+++ b/OLD/src/blocks/inner-blocks/index.js
@@ -0,0 +1,25 @@
+/**
+ * BLOCK: Inner Blocks
+ *
+ * Single block that nests other blocks using the InnerBlocks component.
+ */
+
+import './style.scss';
+
+import { __ } from '@wordpress/i18n';
+import { registerBlockType } from '@wordpress/blocks';
+import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
+
+registerBlockType( 'simple-events/inner-blocks', {
+ edit: () => {
+ return (
+
+
+
+ );
+ },
+
+ save: () => {
+ return ;
+ },
+} );
diff --git a/OLD/src/blocks/inner-blocks/style.scss b/OLD/src/blocks/inner-blocks/style.scss
new file mode 100644
index 0000000..20119f9
--- /dev/null
+++ b/OLD/src/blocks/inner-blocks/style.scss
@@ -0,0 +1,3 @@
+.block-editor-block-list__block[data-type='simple-events/inner-blocks']:not(.is-selected):not(.has-child-selected) .block-editor-default-block-appender {
+ display: block;
+}
\ No newline at end of file
diff --git a/OLD/src/blocks/loop-event-info/block.json b/OLD/src/blocks/loop-event-info/block.json
new file mode 100644
index 0000000..7a6d35f
--- /dev/null
+++ b/OLD/src/blocks/loop-event-info/block.json
@@ -0,0 +1,61 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/loop-event-info",
+ "title": "Event Metadata",
+ "description": "Display event meta in a custom query loop.",
+ "icon": "tag",
+ "category": "simple-events",
+ "keywords": ["event info", "date", "location", "Simple Events", "meta"],
+ "usesContext": ["postId"],
+ "attributes": {
+ "textAlign": {
+ "type": "string",
+ "default": "left"
+ },
+ "thePostId": {
+ "type": "integer",
+ "default": 0
+ },
+ "metaName": {
+ "enum": ["location", "venue", "dates", "date", "time"],
+ "type": "string",
+ "default": "dates"
+ },
+ "metaPrefix": {
+ "type": "string",
+ "default": ""
+ },
+ "addCalendarLinks": {
+ "type": "boolean"
+ }
+ },
+ "supports": {
+ "html": false,
+ "align": true,
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontFamily": true,
+ "__experimentalFontWeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalTextTransform": true,
+ "__experimentalTextDecoration": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ },
+ "spacing": {
+ "margin": true,
+ "padding": true,
+ "blockGap": true
+ },
+ "color": {
+ "text": true
+ }
+ },
+ "editorScript": "file:./index.js",
+ "editorStyle": "file:./editor.css",
+ "style": "file:./style.css"
+}
diff --git a/OLD/src/blocks/loop-event-info/editor.scss b/OLD/src/blocks/loop-event-info/editor.scss
new file mode 100644
index 0000000..e69de29
diff --git a/OLD/src/blocks/loop-event-info/index.js b/OLD/src/blocks/loop-event-info/index.js
new file mode 100644
index 0000000..e0bdfe6
--- /dev/null
+++ b/OLD/src/blocks/loop-event-info/index.js
@@ -0,0 +1,97 @@
+import './index.scss';
+import './editor.scss';
+import metadata from './block.json';
+
+import { __ } from '@wordpress/i18n';
+import { registerBlockType } from '@wordpress/blocks';
+import {
+ PanelBody,
+ SelectControl,
+ TextControl,
+ ToggleControl,
+} from '@wordpress/components'
+import ServerSideRender from '@wordpress/server-side-render';
+import {
+ AlignmentControl,
+ useBlockProps,
+ InspectorControls,
+ BlockControls,
+} from '@wordpress/block-editor';
+
+registerBlockType(metadata, {
+ edit: ({ attributes: { metaName, metaPrefix, thePostId, textAlign, addCalendarLinks }, setAttributes, context: { postId } }) => {
+ return (
+ <>
+
+
+
+ setAttributes({ metaName: value })
+ }
+ __nextHasNoMarginBottom
+ />
+
+ setAttributes({ metaPrefix: value })
+ }
+ __nextHasNoMarginBottom
+ />
+
+ setAttributes({ addCalendarLinks: value } )
+ }
+ />
+
+
+
+ {
+ setAttributes({ textAlign: nextAlign });
+ }}
+ />
+
+
+
+
+ >
+ );
+ },
+
+ /**
+ * The save function defines the way in which the different attributes should be combined
+ * into the final markup, which is then serialized by Gutenberg into post_content.
+ *
+ * The "save" property must be specified and must be a valid function.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
+ *
+ * @param {Object} props Props.
+ * @return {Mixed} JSX Frontend HTML.
+ */
+ save: (props) => null,
+});
diff --git a/OLD/src/blocks/loop-event-info/index.scss b/OLD/src/blocks/loop-event-info/index.scss
new file mode 100644
index 0000000..e69de29
diff --git a/OLD/src/blocks/past-events-notice/block.json b/OLD/src/blocks/past-events-notice/block.json
new file mode 100644
index 0000000..e7e12c2
--- /dev/null
+++ b/OLD/src/blocks/past-events-notice/block.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/past-events-notice",
+ "title": "Past Events Notice",
+ "description": "Display the notice message set in the Simple Events settings.",
+ "icon": "megaphone",
+ "category": "simple-events",
+ "keywords": ["past-events", "notice", "Simple Events"],
+ "supports": {
+ "html": false,
+ "align": true,
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true
+ },
+ "spacing": {
+ "margin": true,
+ "padding": true,
+ "blockGap": true
+ },
+ "color": {
+ "text": true
+ }
+ },
+ "editorScript": "file:./index.js"
+}
diff --git a/OLD/src/blocks/past-events-notice/index.js b/OLD/src/blocks/past-events-notice/index.js
new file mode 100644
index 0000000..60db2b2
--- /dev/null
+++ b/OLD/src/blocks/past-events-notice/index.js
@@ -0,0 +1,21 @@
+/* global seSettings */
+import { registerBlockType } from "@wordpress/blocks";
+import { InnerBlocks } from "@wordpress/block-editor";
+
+import metadata from "./block.json";
+
+registerBlockType( metadata, {
+ edit: () => (
+
+ ),
+ save: () => (
+
+ )
+} );
diff --git a/OLD/src/blocks/upcoming-events/block.json b/OLD/src/blocks/upcoming-events/block.json
new file mode 100644
index 0000000..e2018ee
--- /dev/null
+++ b/OLD/src/blocks/upcoming-events/block.json
@@ -0,0 +1,67 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "simple-events/upcoming-events",
+ "title": "Events Feed",
+ "description": "Display upcoming events, past events or events between a date range.",
+ "icon": "layout",
+ "category": "simple-events",
+ "keywords": [
+ "event",
+ "upcoming",
+ "Simple Events"
+ ],
+ "attributes": {
+ "className": {
+ "type": "string",
+ "default": ""
+ },
+ "count": {
+ "type": "number",
+ "default": 10
+ },
+ "columns": {
+ "type": "number",
+ "default": 3
+ },
+ "layout": {
+ "type": "string",
+ "default": "list"
+ },
+ "align": {
+ "type": "string",
+ "default": ""
+ },
+ "feedType": {
+ "type": "string",
+ "default": "upcoming",
+ "enum": [ "upcoming", "past", "mixed", "range" ]
+ },
+ "showYearDividers": {
+ "type": "boolean",
+ "default": false
+ },
+ "overrideFeedOrder": {
+ "type": "boolean",
+ "default": false
+ },
+ "feedOrder": {
+ "type": "string",
+ "default": "ASC",
+ "enum": [ "ASC", "DESC" ]
+ },
+ "dateRange": {
+ "type": "object",
+ "default": {
+ "from": "",
+ "to": ""
+ }
+ }
+ },
+ "supports": {
+ "html": false,
+ "align": [ "wide", "full" ]
+ },
+ "editorScript": "file:./index.js",
+ "style": "file:./index.css"
+}
diff --git a/OLD/src/blocks/upcoming-events/index.js b/OLD/src/blocks/upcoming-events/index.js
new file mode 100644
index 0000000..76482c3
--- /dev/null
+++ b/OLD/src/blocks/upcoming-events/index.js
@@ -0,0 +1,279 @@
+/**
+ * BLOCK: Upcoming Events
+ *
+ * Displays upcoming event posts.
+ */
+import './index.scss';
+
+import { __ } from '@wordpress/i18n';
+import { registerBlockType } from '@wordpress/blocks';
+import {
+ Disabled,
+ PanelBody,
+ RangeControl,
+ ToolbarGroup,
+ SelectControl,
+ TimePicker,
+ BaseControl,
+ Button,
+ Dropdown,
+ ToggleControl,
+} from '@wordpress/components';
+import ServerSideRender from '@wordpress/server-side-render';
+import {
+ InspectorControls,
+ BlockControls,
+ useBlockProps,
+ InnerBlocks,
+} from '@wordpress/block-editor';
+import { list, grid, edit } from '@wordpress/icons';
+import { useState } from '@wordpress/element';
+
+registerBlockType( 'simple-events/upcoming-events', {
+ edit: ( { attributes, setAttributes } ) => {
+ const { count, layout, columns, feedType, feedOrder, overrideFeedOrder, showYearDividers, dateRange } = attributes;
+
+ const [ noEvents, setNoEvents ] = useState( false );
+
+ return (
+
+
+
+
+ setAttributes( { layout: value } )
+ }
+ __nextHasNoMarginBottom
+ />
+ { 'grid' === layout && (
+
+ setAttributes( { columns: value } )
+ }
+ min={ 1 }
+ max={ 4 }
+ />
+ ) }
+ setNoEvents( ! noEvents ) }
+ >
+ { noEvents
+ ? __(
+ 'Hide "no results" view',
+ 'simple-events'
+ )
+ : __(
+ 'Edit "no results" view',
+ 'simple-events'
+ ) }
+
+
+
+
+ setAttributes( { feedType: value } )
+ }
+ />
+ { 'range' === feedType && (
+ <>
+
+ (
+
+ { isOpen
+ ? 'Close Calendar'
+ : "Pick 'From' Date" }
+
+ ) }
+ renderContent={ () => {
+ return (
+
+ setAttributes( {
+ dateRange: {
+ ...dateRange,
+ from: value,
+ },
+ } )
+ }
+ />
+ );
+ } }
+ />
+
+
+ (
+
+ { isOpen
+ ? 'Close Calendar'
+ : "Pick 'To' Date" }
+
+ ) }
+ renderContent={ () => {
+ return (
+
+ setAttributes( {
+ dateRange: {
+ ...dateRange,
+ to: value,
+ },
+ } )
+ }
+ />
+ );
+ } }
+ />
+
+ >
+ ) }
+
+ setAttributes( { count: value } )
+ }
+ min={ 1 }
+ max={ 50 }
+ />
+ setAttributes( { showYearDividers: value } ) }
+ />
+ setAttributes( { overrideFeedOrder: value } ) }
+ />
+ { overrideFeedOrder && setAttributes( { feedOrder: value } ) }
+ /> }
+
+
+
+
+ setAttributes( { layout: 'list' } ),
+ isActive: layout === 'list',
+ },
+ {
+ icon: grid,
+ title: __( 'Grid view', 'simple-events' ),
+ onClick: () =>
+ setAttributes( { layout: 'grid' } ),
+ isActive: layout === 'grid',
+ },
+ {
+ icon: edit,
+ title: __(
+ 'Edit "no results" view',
+ 'simple-events'
+ ),
+ onClick: () => setNoEvents( ! noEvents ),
+ isActive: noEvents,
+ },
+ ] }
+ />
+
+ { noEvents ? (
+
+ ) : (
+
+
+
+ ) }
+
+ );
+ },
+
+ /**
+ * The save function defines the way in which the different attributes should be combined
+ * into the final markup, which is then serialized by Gutenberg into post_content.
+ *
+ * The "save" property must be specified and must be a valid function.
+ *
+ * @link https://wordpress.org/gutenberg/handbook/block-api/block-edit-save/
+ *
+ * @param {Object} props Props.
+ * @return {Mixed} JSX Frontend HTML.
+ */
+ save: ( props ) => ,
+} );
diff --git a/OLD/src/blocks/upcoming-events/index.scss b/OLD/src/blocks/upcoming-events/index.scss
new file mode 100644
index 0000000..b2fb45e
--- /dev/null
+++ b/OLD/src/blocks/upcoming-events/index.scss
@@ -0,0 +1,71 @@
+@import '../common.scss';
+
+.wp-block-se-upcoming-events {
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ .se-event-date {
+ font-weight: bold;
+ }
+
+ .se-event-location {
+ margin-bottom: 1rem;
+ }
+
+ li {
+ h2.entry-title {
+ font-size: max( 2.5rem, 28px );
+
+ a {
+ color: var(--wp--preset--color--primary, #28303d);
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ > a {
+ background: var(--wp--preset--color--primary, #28303d);
+ color: var(--wp--preset--color--white, #fff);
+ padding: 1rem 1.5rem;
+ display: inline-block;
+ font-size: max( 1rem, 16px );
+ text-transform: uppercase;
+ text-decoration: none;
+ letter-spacing: 0.05em;
+ font-weight: bold;
+
+ &:hover {
+ background: var(--wp--preset--color--black, #000);
+ }
+ }
+ }
+ }
+
+ &-view-grid {
+ ul {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 20px;
+ }
+ @include tablet {
+ &.wp-block-se-upcoming-events-columns-2 ul,
+ &.wp-block-se-upcoming-events-columns-3 ul,
+ &.wp-block-se-upcoming-events-columns-4 ul {
+ grid-template-columns: 1fr 1fr;
+ }
+ }
+ @include desktop-medium {
+ &.wp-block-se-upcoming-events-columns-3 ul {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+
+ &.wp-block-se-upcoming-events-columns-4 ul {
+ grid-template-columns: 1fr 1fr 1fr 1fr;
+ }
+ }
+ }
+}
diff --git a/OLD/src/calendar-functions.php b/OLD/src/calendar-functions.php
new file mode 100644
index 0000000..90c7ec2
--- /dev/null
+++ b/OLD/src/calendar-functions.php
@@ -0,0 +1,292 @@
+format( 'F Y' );
+ } catch ( Exception $e ) {
+ return '';
+ }
+}
+
+/**
+ * Returns element classes based on particular day.
+ *
+ * @param array $day_data Day's data.
+ *
+ * @return string
+ */
+function se_get_day_element_classes( $day_data ): string {
+ $classes = array( 'simple-events-calendar-month__day' );
+
+ if ( $day_data['is_other_month'] ) {
+ $classes[] = 'simple-events-calendar-month__day--other-month';
+ }
+
+ if ( $day_data['is_previous_month'] ) {
+ $classes[] = 'simple-events-calendar-month__day--previous-month';
+ }
+
+ if ( $day_data['is_next_month'] ) {
+ $classes[] = 'simple-events-calendar-month__day--next-month';
+ }
+
+ if ( $day_data['is_past'] ) {
+ $classes[] = 'simple-events-calendar-month__day--past';
+ }
+
+ if ( $day_data['is_today'] ) {
+ $classes[] = 'simple-events-calendar-month__day--today simple-events-calendar-month__day--active';
+ }
+
+ if ( ! $day_data['is_past'] ) {
+ $classes[] = 'simple-events-calendar-month__day--upcoming';
+ }
+
+ if ( ! empty( $day_data['events'] ) ) {
+ $classes[] = 'simple-events-calendar-month__day--has-events';
+ }
+
+ return implode( ' ', $classes );
+}
+
+/**
+ * Returns element classes based on particular day.
+ *
+ * @param array $day_data Day's data.
+ *
+ * @return string
+ */
+function se_get_day_mobile_classes( $day_data ): string {
+
+ $classes = array( 'simple-events-calendar-month-mobile-events__mobile-day' );
+
+ if ( $day_data['is_today'] ) {
+ $classes[] = 'simple-events-calendar-month-mobile-events__mobile-day--active';
+ }
+
+ return implode( ' ', $classes );
+}
+
+/**
+ * Returns the mobile day id.
+ *
+ * @param array $day_data Day's data.
+ *
+ * @return string
+ */
+function se_get_mobile_day_id( $day_data ): string {
+ return sprintf( 'simple-events-calendar-mobile__day-%s', $day_data['date_formatted'] );
+}
+
+
+/**
+ * Determines if the event should be hidden based on the given block and day attributes.
+ *
+ * @param array $attributes The attributes of the event.
+ * @param array $day The day of the event.
+ *
+ * @return boolean Returns true if the event should be hidden, false otherwise.
+ */
+function se_hide_event( $attributes, $day ): bool {
+ if ( ! $attributes['hideNeighbourEvents'] ) {
+ return false;
+ }
+
+ return $day['is_previous_month'] || $day['is_next_month'];
+}
+
+/**
+ * Verify if any of the attributes are valid colors.
+ *
+ * @param array $attributes The attributes to be checked.
+ * @param string $prefix The prefix for the attribute keys.
+ *
+ * @return boolean Returns true if any of the attributes are valid colors, false otherwise.
+ */
+function se_verify_attributes( $attributes, $prefix ): bool {
+ $text_color_bool = isset( $attributes[ $prefix . 'Color' ] ) && sanitize_hex_color( $attributes[ $prefix . 'Color' ] );
+ $bg_color_bool = isset( $attributes[ $prefix . 'Bg' ] ) && sanitize_hex_color( $attributes[ $prefix . 'Bg' ] );
+ $border_color_bool = isset( $attributes[ $prefix . 'Border' ] ) && sanitize_hex_color( $attributes[ $prefix . 'Border' ] );
+
+ if ( $text_color_bool || $bg_color_bool || $border_color_bool ) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Generates the customized CSS for repeated elements based on the given attributes.
+ *
+ * @param array $attributes The attributes used to generate the CSS.
+ * @param string $prefix The prefix to use for attribute keys.
+ * @param string &$customized_css The CSS string to append the generated CSS to.
+ *
+ * @return string The updated customized CSS string.
+ */
+function se_generate_repeated_css( $attributes, $prefix, &$customized_css ): string {
+
+ if ( isset( $attributes[ $prefix . 'Color' ] ) && sanitize_hex_color( $attributes[ $prefix . 'Color' ] ) ) {
+ $customized_css .= sprintf(
+ '.simple-events-calendar-month__calendar-event-title-link {
+ color: %1$s;
+ }
+ color: %1$s;',
+ sanitize_hex_color( $attributes[ $prefix . 'Color' ] )
+ );
+ }
+
+ if ( isset( $attributes[ $prefix . 'Bg' ] ) && sanitize_hex_color( $attributes[ $prefix . 'Bg' ] ) ) {
+ $customized_css .= sprintf( 'background-color: %s;', sanitize_hex_color( $attributes[ $prefix . 'Bg' ] ) );
+ }
+
+ if ( isset( $attributes[ $prefix . 'Border' ] ) && sanitize_hex_color( $attributes[ $prefix . 'Border' ] ) ) {
+ $customized_css .= sprintf( 'border-color: %s;', sanitize_hex_color( $attributes[ $prefix . 'Border' ] ) );
+ }
+
+ $customized_css .= "}}\n";
+
+ return $customized_css;
+}
+
+/**
+ * Applies customizations to the simple events calendar based on the provided attributes.
+ *
+ * @param array $attributes The attributes used to customize the calendar.
+ *
+ * @return string The customized CSS string.
+ */
+function se_apply_customization( $attributes ): string {
+
+ $customized_css = '';
+
+ if ( se_verify_attributes( $attributes, 'upcomingDays' ) ) {
+ // Customization for Upcoming Days
+ $customized_css .= '.simple-events-calendar {
+ .simple-events-calendar-month__day--upcoming {';
+
+ se_generate_repeated_css( $attributes, 'upcomingDays', $customized_css );
+ }
+
+ if ( se_verify_attributes( $attributes, 'eventDays' ) ) {
+ // Customization for Event Days
+ $customized_css .= '.simple-events-calendar {
+ .simple-events-calendar-month__day--has-events {';
+
+ se_generate_repeated_css( $attributes, 'eventDays', $customized_css );
+ }
+
+ if ( se_verify_attributes( $attributes, 'presentDay' ) ) {
+ // Customization for Present Day
+ $customized_css .= '.simple-events-calendar {
+ .simple-events-calendar-month__day--active {';
+
+ se_generate_repeated_css( $attributes, 'presentDay', $customized_css );
+ }
+
+ if ( se_verify_attributes( $attributes, 'pastDays' ) ) {
+ // Customization for Past Days
+ $customized_css .= '.simple-events-calendar {
+ .simple-events-calendar-month__day--past {';
+
+ se_generate_repeated_css( $attributes, 'pastDays', $customized_css );
+ }
+
+ if ( isset( $attributes['monthYearColor'] ) && sanitize_hex_color( $attributes['monthYearColor'] ) ) {
+ // Customization for Month Year
+ $customized_css .= sprintf(
+ '.simple-events-top-bar,
+ .simple-events-calendar .simple-events-top-bar__today-button {
+ color: %s !important;
+ }',
+ esc_attr( $attributes['monthYearColor'] )
+ );
+ }
+
+ if ( isset( $attributes['arrowColor'] ) && sanitize_hex_color( $attributes['arrowColor'] ) ) {
+ // Customization for Arrow
+ $customized_css .= sprintf(
+ '.simple-events-calendar .simple-events-top-bar nav ul li a,
+ .simple-events-calendar .simple-events-top-bar nav ul li svg,
+ .simple-events-calendar .simple-events-mobile__nav-list-item a {
+ color: %s;
+ }',
+ esc_attr( $attributes['arrowColor'] )
+ );
+ }
+
+ if ( isset( $attributes['eventDotColor'] ) && sanitize_hex_color( $attributes['eventDotColor'] ) ) {
+ // Customization for Arrow Hover
+ $customized_css .= sprintf(
+ '.simple-events-calendar .simple-events-calendar-month__mobile-events-icon {
+ background-color: %s;
+ }',
+ esc_attr( $attributes['eventDotColor'] )
+ );
+ }
+
+ if ( isset( $attributes['modalBgColor'] ) && sanitize_hex_color( $attributes['modalBgColor'] ) ) {
+ // Customization for Modal
+ $customized_css .= sprintf(
+ '.simple-events-calendar .simple-events-calendar-month__events .se-event-modal {
+ background-color: %s;
+ }',
+ esc_attr( $attributes['modalBgColor'] )
+ );
+ }
+
+ if ( isset( $attributes['modalTextColor'] ) && sanitize_hex_color( $attributes['modalTextColor'] ) ) {
+ // Customization for Modal
+ $customized_css .= sprintf(
+ '.simple-events-calendar .simple-events-calendar-month__events .se-event-modal,
+ .simple-events-calendar .simple-events-calendar-month__events .se-event-modal h6 {
+ color: %s;
+ }',
+ esc_attr( $attributes['modalTextColor'] )
+ );
+ }
+
+ if ( isset( $attributes['modalIconColor'] ) && sanitize_hex_color( $attributes['modalIconColor'] ) ) {
+ // Customization for Modal
+ $customized_css .= sprintf(
+ '.simple-events-calendar .simple-events-calendar-month__events .se-event-modal .dashicons:before {
+ color: %s;
+ }',
+ esc_attr( $attributes['modalIconColor'] )
+ );
+ }
+
+ return $customized_css;
+}
diff --git a/OLD/src/classes/class-se-admin.php b/OLD/src/classes/class-se-admin.php
new file mode 100644
index 0000000..ad438f5
--- /dev/null
+++ b/OLD/src/classes/class-se-admin.php
@@ -0,0 +1,81 @@
+ false,
+ 'strategy' => 'async',
+ )
+ );
+ }
+
+ /**
+ * Add a new column to the se-event admin page.
+ *
+ * @param array $columns The array of existing columns in the event list table.
+ *
+ * @return array The modified array of columns.
+ */
+ public static function customize_event_columns( $columns ) {
+ $columns['event-date-time'] = esc_html__( 'Event Date/Time', 'simple-events' );
+ $columns['event-location'] = esc_html__( 'Event Location', 'simple-events' );
+ return $columns;
+ }
+
+ /**
+ * Add custom data to the se-event admin page for custom columns.
+ *
+ * @param string $column Column key of Admin Panel.
+ * @param integer $post_id Post ID of the row in the Admin Panel.
+ *
+ * @return void
+ */
+ public static function customize_event_columns_data( $column, $post_id ) {
+ switch ( $column ) {
+ case 'event-date-time':
+ echo wp_kses_post( se_event_get_formatted_dates( $post_id ) );
+ break;
+ case 'event-location':
+ $location = se_event_get_location( $post_id );
+ echo esc_html( $location ? $location : '-' );
+ break;
+ }
+ }
+}
+
+SE_Admin::init();
diff --git a/OLD/src/classes/class-se-block-variations.php b/OLD/src/classes/class-se-block-variations.php
new file mode 100644
index 0000000..3a4ed18
--- /dev/null
+++ b/OLD/src/classes/class-se-block-variations.php
@@ -0,0 +1,170 @@
+parsed_block = $parsed_block;
+
+ if ( $this->is_events_variation( $parsed_block ) ) {
+ add_filter( 'query_loop_block_query_vars', array( $this, 'build_query' ), 10, 1 );
+ }
+ }
+
+ /**
+ * Return a custom query based on attributes, filters and global WP_Query.
+ *
+ * @param WP_Query $query The WordPress Query.
+ *
+ * @return WP_Query
+ */
+ public function build_query( $query ) {
+ $parsed_block = $this->parsed_block;
+ if ( ! $this->is_events_variation( $parsed_block ) ) {
+ return $query;
+ }
+
+ $query['sub-type'] = self::QUERY_LOOP_EVENTS;
+
+ if ( ! isset( $parsed_block['attrs']['query']['feedType'] ) ) {
+ $parsed_block['attrs']['query']['feedType'] = 'default';
+ }
+
+ $feed_type = $parsed_block['attrs']['query']['feedType'];
+ $feed_order = $parsed_block['attrs']['query']['order'];
+
+ // Inherit taxonomy query from global WP_Query if in taxonomy archive context
+ if ( ! empty( $parsed_block['attrs']['query']['inheritTaxQuery'] ) ) {
+ global $wp_query;
+ if ( is_tax() && ! empty( $wp_query->tax_query ) ) {
+ $query['tax_query'] = $wp_query->tax_query->queries;
+ }
+ }
+
+ return $this->set_event_query_args( $query, $feed_type, $feed_order );
+ }
+
+ /**
+ * Set the query args for the event loop query admin.
+ *
+ * @param mixed $args The arguments for the query.
+ * @param mixed $request The request object.
+ *
+ * @return mixed The result of the set event query args.
+ */
+ public function set_admin_query( $args, $request ) {
+
+ $feed_type = $request->get_param( 'feedType' );
+ $feed_order = $request->get_param( 'order' );
+
+ return $this->set_event_query_args( $args, $feed_type, $feed_order );
+ }
+
+ /**
+ * Set the Event Query Loop Args.
+ *
+ * @param mixed $args The arguments for the query.
+ * @param mixed $feed_type The feed type.
+ * @param mixed $feed_order The feed order.
+ *
+ * @return mixed The result of the set event query args.
+ */
+ private function set_event_query_args( $args, $feed_type, $feed_order = 'ASC' ) {
+
+ if ( 'upcoming' === $feed_type ) {
+ $args['meta_query'] = array(
+ array(
+ 'key' => 'se_event_date_end',
+ 'value' => wp_date( 'U' ),
+ 'compare' => '>=',
+ ),
+ );
+
+ $args['orderby'] = 'meta_value';
+ $args['meta_key'] = 'se_event_date_start';
+ $args['order'] = $feed_order;
+ }
+
+ if ( 'past' === $feed_type ) {
+ $args['meta_query'] = array(
+ array(
+ 'key' => 'se_event_date_end',
+ 'value' => wp_date( 'U' ),
+ 'compare' => '<',
+ ),
+ );
+
+ $args['orderby'] = 'meta_value';
+ $args['meta_key'] = 'se_event_date_start';
+ $args['order'] = $feed_order;
+ }
+
+ /**
+ * A filter to customize the args of the event query loop.
+ *
+ * @param array $args The built args passed in to the query.
+ * @param string|null $feed_type The feed type.
+ * @param string|null $feed_order The feed order.
+ */
+ return apply_filters( 'se_pre_set_event_query_loop_args', $args, $feed_type, $feed_order );
+ }
+}
+
+( new SE_Block_Variations() )->init();
diff --git a/OLD/src/classes/class-se-blocks.php b/OLD/src/classes/class-se-blocks.php
new file mode 100644
index 0000000..bdf2f6f
--- /dev/null
+++ b/OLD/src/classes/class-se-blocks.php
@@ -0,0 +1,898 @@
+
+
+
+ npm install',
+ 'npm run build',
+ '' . esc_html( str_replace( ABSPATH, '', SE_PLUGIN_DIR ) ) . ''
+ );
+ ?>
+
+
+ 'simple-events',
+ 'title' => esc_html__( 'Simple Events', 'simple-events' ),
+ ),
+ )
+ );
+ }
+
+ /**
+ * Enqueue block assets.
+ *
+ * @return void
+ */
+ public static function block_assets() {
+
+ // JS globals.
+ $block_settings = array();
+
+ if ( class_exists( 'WooCommerce' ) ) {
+ $product_counts = wp_count_posts( 'product' );
+
+ $block_settings['isWCActive'] = true;
+ $block_settings['isLargeCatalog'] = $product_counts->publish > 200;
+ $block_settings['productCount'] = $product_counts->publish;
+
+ if ( class_exists( 'WC_Box_Office' ) ) {
+ $block_settings['isBOActive'] = true;
+ }
+
+ if ( function_exists( 'wc_box_office_ticket_field_types' ) ) {
+ $block_settings['BOTicketFieldTypes'] = wc_box_office_ticket_field_types();
+ }
+ }
+
+ $options = get_option( 'se_options' );
+ $value = isset( $options['past_event_notice'] ) ? $options['past_event_notice'] : esc_html__( 'Event has passed', 'simple-events' );
+
+ // For Past events notice block.
+ $block_settings['pastEventsNotice'] = $value;
+ $block_settings['postType'] = get_post_type();
+
+ wp_localize_script(
+ 'wp-blocks',
+ 'seSettings',
+ $block_settings
+ );
+
+ if ( file_exists( SE_PLUGIN_DIR . '/build/variations/index.asset.php' ) ) {
+ $variations = include_once SE_PLUGIN_DIR . '/build/variations/index.asset.php';
+
+ wp_enqueue_script(
+ 'se-block-variations',
+ SE_PLUGIN_URL . '/build/variations/index.js',
+ $variations['dependencies'],
+ $variations['version'],
+ true
+ );
+ }
+ }
+
+ /**
+ * Register blocks.
+ *
+ * @return void
+ */
+ public static function register_block_type() {
+ // Event Info.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/event-info',
+ array(
+ 'render_callback' => array( __CLASS__, 'event_info_render' ),
+ )
+ );
+
+ // Event Tickets.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/event-tickets',
+ array(
+ 'render_callback' => array( __CLASS__, 'event_tickets_render' ),
+ )
+ );
+
+ // Inner Blocks.
+ register_block_type( SE_PLUGIN_DIR . '/build/blocks/inner-blocks' );
+
+ // Upcoming Events.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/upcoming-events',
+ array(
+ 'render_callback' => array( __CLASS__, 'upcoming_events_render' ),
+ )
+ );
+
+ // Next Event Countdown.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/countdown',
+ array(
+ 'render_callback' => array( __CLASS__, 'countdown_render' ),
+ )
+ );
+
+ // Calendar View.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/calendar',
+ array(
+ 'render_callback' => array( __CLASS__, 'calendar_render' ),
+ )
+ );
+
+ // Event meta in query loop.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/loop-event-info',
+ array(
+ 'render_callback' => array( __CLASS__, 'loop_event_info_render' ),
+ )
+ );
+
+ // Event external links.
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/external-link',
+ array(
+ 'render_callback' => array( __CLASS__, 'loop_event_external_link_render' ),
+ )
+ );
+
+ // Past Events Notice
+ register_block_type(
+ SE_PLUGIN_DIR . '/build/blocks/past-events-notice',
+ array(
+ 'render_callback' => array( __CLASS__, 'past_events_notice_render' ),
+ )
+ );
+ }
+
+
+ /**
+ * Render event info block.
+ *
+ * @param array $attributes The attributes for the event.
+ * @param string $content The content of the event.
+ * @param object $block The block object.
+ *
+ * @return HTML The rendered event information.
+ */
+ public static function event_info_render( $attributes, $content, $block ) {
+
+ // Check if we're looking at the block on the front-end.
+ if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
+ // If yes, check if we're supposed to show the block on the front-end.
+ if ( false === $attributes['showOnFrontEnd'] ) {
+ return '';
+ }
+ }
+
+ $post_ID = isset( $block->context['postId'] ) ? $block->context['postId'] : get_the_ID();
+
+ $output = '';
+
+ // Event time / date.
+ $event_dates = get_post_meta( $post_ID, 'se_event_dates', true );
+
+ // Previewing?
+ if ( ! empty( $attributes['eventDates'] ) ) {
+ $event_dates = $attributes['eventDates'];
+ }
+
+ // Set up timezone. Defaults to site settings if the post has no timezone meta.
+ $event_timezone = get_post_meta( $post_ID, 'se_event_timezone', true );
+
+ // Previewing?
+ if ( isset( $attributes['eventTimezone'] ) ) {
+ $event_timezone = $attributes['eventTimezone'];
+ }
+
+ $dates_output = '';
+
+ if ( ! empty( $event_dates ) ) {
+ $dates_count = count( $event_dates );
+ $date_heading = '' . _n( 'Date', 'Dates', $dates_count, 'simple-events' ) . ' ';
+
+ /**
+ * Filter the markup used for the date heading.
+ *
+ * @param string $date_heading The HTML used to display the date heading.
+ * @param int $dates_count The number of event dates.
+ */
+ $dates_output .= apply_filters( 'se_event_info_date_heading', $date_heading, $dates_count );
+ $dates_output .= se_event_get_formatted_dates( $post_ID, false, false, $event_dates );
+ }
+
+ // Event location.
+ $event_location = get_post_meta( $post_ID, 'se_event_location', true );
+ $event_venue = get_post_meta( $post_ID, 'se_event_venue', true );
+
+ $event_link = get_post_meta( $post_ID, 'se_event_external_link', true );
+ $event_link_label = get_post_meta( $post_ID, 'se_event_external_link_label', true );
+
+ // Previewing?
+ if ( isset( $attributes['eventLocation'] ) ) {
+ $event_location = $attributes['eventLocation'];
+ }
+
+ if ( isset( $attributes['eventVenue'] ) ) {
+ $event_venue = $attributes['eventVenue'];
+ }
+
+ // Previewing?
+ if ( isset( $attributes['externalLink'] ) ) {
+ $event_link = $attributes['externalLink'];
+ }
+
+ if ( isset( $attributes['externalLinkLabel'] ) ) {
+ $event_link_label = $attributes['externalLinkLabel'];
+ }
+
+ if ( ! empty( $dates_output ) ) {
+ $output .= '';
+
+ if ( ! empty( $dates_output ) ) {
+ $output .= $dates_output;
+ }
+
+ if ( ! empty( $event_venue ) ) {
+ $output .= '
' . __( 'Venue', 'simple-events' ) . ' ';
+ $output .= '
' . wp_kses_post( $event_venue ) . '
';
+ }
+
+ if ( ! empty( $event_location ) ) {
+ $output .= '
' . __( 'Location', 'simple-events' ) . ' ';
+ $output .= '
' . wp_kses_post( $event_location ) . '
';
+ }
+
+ if ( ! empty( $event_link ) ) {
+ $cta = apply_filters( 'se_event_external_link_text', $event_link_label, $event_link );
+
+ $output .= '
' . $cta . '
';
+ }
+
+ $output .= '
';
+ }
+
+ // Add "Add to calendar links" if the attribute is set to true.
+ $calendar_links = get_post_meta( $post_ID, 'se_event_add_calendar_links', true );
+ if ( $calendar_links ) {
+ $output .= se_template_calendar_links( false );
+ }
+
+ return apply_filters( 'simple_events_event_info_render', $output, $event_dates, $event_timezone, $event_location, $attributes );
+ }
+
+ /**
+ * Render event tickets block.
+ *
+ * @param array $attributes The attributes for the event.
+ *
+ * @return HTML The rendered event information.
+ */
+ public static function event_tickets_render( $attributes = array() ) {
+ $output = '';
+
+ if ( ! empty( $attributes['selected'] ) ) {
+ $output .= '';
+ $output .= '
';
+ $output .= '
' . __( 'Tickets', 'simple-events' ) . ' ';
+
+ $available_tickets = '';
+ $unavailable_tickets = '';
+
+ foreach ( $attributes['selected'] as $product ) {
+ $product = wc_get_product( (int) $product );
+
+ if ( ! $product ) {
+ continue;
+ }
+
+ if ( $product->is_type( 'variable' ) ) {
+ $variations = $product->get_available_variations();
+
+ foreach ( $variations as $variation ) {
+ $variation = wc_get_product( $variation['variation_id'] );
+
+ if ( ! $variation ) {
+ continue;
+ }
+
+ $ticket_render = self::render_ticket( $variation );
+
+ if ( $ticket_render['available'] ) {
+ $available_tickets .= $ticket_render['output'];
+ } else {
+ $unavailable_tickets .= $ticket_render['output'];
+ }
+ }
+ } else {
+ $ticket_render = self::render_ticket( $product );
+
+ if ( $ticket_render['available'] ) {
+ $available_tickets .= $ticket_render['output'];
+ } else {
+ $unavailable_tickets .= $ticket_render['output'];
+ }
+ }
+ }
+
+ $output .= $available_tickets;
+ $output .= $unavailable_tickets;
+ $output .= '';
+ $output .= '
';
+ }
+
+ return apply_filters( 'simple_events_event_tickets_render', $output, $attributes );
+ }
+
+ /**
+ * Render single ticket.
+ *
+ * @param WC_Product $product WooCommerce product.
+ *
+ * @return array
+ */
+ private static function render_ticket( $product ) {
+
+ $name = $product->get_name();
+ $price = wc_price( $product->get_price() );
+ $stock = ( $product->managing_stock() ? $product->get_stock_quantity() : false );
+
+ $available = 'outofstock' !== $product->get_stock_status() && ! empty( $stock ) && 0 < $stock;
+
+ $row_class = 'wp-block-se-event-tickets__ticket-row';
+ $row_class .= ( $available ) ? '' : ' wp-block-se-event-tickets__ticket-row--unavailable';
+
+ $product_output = '';
+ $product_output .= '
' . $name . '
';
+
+ $product_output .= '
';
+ $product_output .= $price;
+
+ /* translators: %s: number tickets in stock */
+ $product_output .= '' . sprintf( __( '%s available', 'simple-events' ), 0 < $stock ? $stock : 0 ) . ' ';
+ $product_output .= '
';
+
+ $product_output .= '
';
+
+ if ( $available ) {
+ $attributes = array(
+ /* translators: %s: event title */
+ 'aria-label' => sprintf( __( 'Buy ticket "%s"', 'simple-events' ), $product->get_name() ),
+ 'rel' => 'nofollow',
+ 'class' => 'wp-block-se-event-tickets__button button add_to_cart_button',
+ );
+
+ $button = sprintf(
+ '
%s ',
+ esc_url( $product->get_permalink() ),
+ wc_implode_html_attributes( $attributes ),
+ esc_html( __( 'Buy Ticket', 'simple-events' ) )
+ );
+
+ $product_output .= $button;
+ } else {
+ $product_output .= __( 'No longer available for sale', 'simple-events' );
+ }
+
+ $product_output .= '
';
+ $product_output .= '
';
+
+ return array(
+ 'available' => $available,
+ 'output' => $product_output,
+ );
+ }
+
+ /**
+ * Render upcoming events block.
+ *
+ * @param array $attributes Block attributes.
+ * @param string $content Block content.
+ *
+ * @return HTML Upcoming events render.
+ */
+ public static function upcoming_events_render( $attributes = array(), $content = '' ) {
+ $events_query_args = array();
+ $events_query = null;
+ $output = '';
+
+ if ( ! empty( $attributes['count'] ) ) {
+
+ // By default shows the "mixed" feed type (no meta_query).
+ $events_query_args = array(
+ 'post_type' => SE_Event_Post_Type::$post_type,
+ 'post_status' => 'publish',
+ 'posts_per_page' => absint( $attributes['count'] ),
+ );
+
+ // Handle "future events" feed type
+ if ( 'upcoming' === $attributes['feedType'] ) {
+ $events_query_args['meta_query'] = array(
+ array(
+ 'key' => 'se_event_date_end',
+ 'value' => wp_date( 'U' ),
+ 'compare' => '>=',
+ ),
+ );
+ }
+
+ // Handle "past events" feed type
+ if ( 'past' === $attributes['feedType'] ) {
+ $events_query_args['meta_query'] = array(
+ array(
+ 'key' => 'se_event_date_end',
+ 'value' => wp_date( 'U' ),
+ 'compare' => '<=',
+ ),
+ );
+ }
+
+ // Handle "range" feed type
+ if (
+ 'range' === $attributes['feedType']
+ && ! empty( $attributes['dateRange']['from'] )
+ && ! empty( $attributes['dateRange']['to'] )
+ ) {
+ $events_query_args['meta_query'] = array(
+ array(
+ 'key' => 'se_event_date_start',
+ 'value' => strtotime( $attributes['dateRange']['from'] ),
+ 'compare' => '>=',
+ ),
+ array(
+ 'key' => 'se_event_date_end',
+ 'value' => strtotime( $attributes['dateRange']['to'] ),
+ 'compare' => '<=',
+ ),
+ );
+ }
+
+ // If feed order is overridden, set the order to custom query var.
+ if ( ! empty( $attributes['overrideFeedOrder'] ) ) {
+ $events_query_args['se_event_order'] = $attributes['feedOrder'];
+ }
+
+ $show_year_dividers = ! empty( $attributes['showYearDividers'] );
+
+ $events_query = new \WP_Query( $events_query_args );
+
+ if ( $events_query->have_posts() ) {
+ $container_class[] = 'wp-block-se-upcoming-events';
+ $container_class[] = 'wp-block-se-upcoming-events-view-' . $attributes['layout'];
+ $container_class[] = 'wp-block-se-upcoming-events-columns-' . $attributes['columns'];
+ $container_class[] = 'align' . $attributes['align'];
+ $container_class[] = ( ! empty( $attributes['className'] ) ) ? $attributes['className'] : '';
+ $current_year = '';
+
+ $output .= '';
+ $output .= '
';
+
+ while ( $events_query->have_posts() ) {
+ $events_query->the_post();
+
+ if ( $show_year_dividers ) {
+ $year_output = self::get_year_divider( get_the_ID(), $current_year );
+ $current_year = $year_output['current_year'];
+ $output .= $year_output['output'];
+ }
+
+ $output .= SE_TEMPLATE_LOADER::get_template_part( 'content', 'archive', true, array(), true );
+ }
+ $output .= ' ';
+ $output .= '
';
+ } else {
+ // If nothing was found, output the inner blocks.
+ $output = $content;
+ }
+
+ wp_reset_postdata();
+ }
+
+ /**
+ * A filter to customize the render of the upcoming-events block.
+ *
+ * @param string $output The output html. May be an empty string.
+ * @param WP_Query $events_query The query object used to generate the output.
+ * @param array $events_query_args The built args passed in to the query.
+ * @param array $attributes The attributes passed to the block renderer.
+ */
+ return apply_filters( 'se_upcoming_events_render', $output, $events_query, $events_query_args, $attributes );
+ }
+
+ /**
+ * Get year divider markup for events list.
+ *
+ * @param integer $post_id The post ID.
+ * @param string $current_loop_year The current year in the loop.
+ *
+ * @return array
+ */
+ public static function get_year_divider( $post_id, $current_loop_year ): array {
+ $event_year = get_post_meta( $post_id, 'se_event_date_start', true );
+ $post_year = gmdate( 'Y', $event_year );
+ $output = '';
+
+ if ( $current_loop_year !== $post_year ) {
+ $output .= '' . esc_html( $post_year ) . ' ';
+ }
+
+ return array(
+ 'output' => $output,
+ 'current_year' => $post_year,
+ );
+ }
+
+ /**
+ * Render next event countdown block.
+ *
+ * @param array $attributes The attributes passed to the block renderer.
+ *
+ * @return HTML Countdown render.
+ */
+ public static function countdown_render( $attributes = array() ) {
+ $output = '';
+
+ $events_query_args = array(
+ 'se_countdown' => true,
+ 'post_type' => SE_Event_Post_Type::$post_type,
+ 'post_status' => 'publish',
+ 'posts_per_page' => 1,
+ 'orderby' => 'meta_value',
+ 'meta_key' => 'se_event_date_start',
+ 'order' => 'ASC',
+ 'meta_query' => array(
+ array(
+ 'key' => 'se_event_date_start',
+ 'value' => wp_date( 'U' ),
+ 'compare' => '>=',
+ ),
+ ),
+ );
+
+ $events_query = new \WP_Query( $events_query_args );
+
+ $event_id = ( 'se-event' === get_post_type( get_the_ID() ) ) ? get_the_ID() : false;
+
+ if ( $events_query->have_posts() ) {
+ ob_start();
+
+ while ( $events_query->have_posts() ) {
+ $events_query->the_post();
+
+ $container_class = 'wp-block-se-next-event-countdown';
+ $container_class .= ( ! empty( $attributes['className'] ) ) ? ' ' . $attributes['className'] : '';
+
+ $start_date = get_post_meta( get_the_ID(), 'se_event_date_start', true );
+
+ /**
+ * Adding filter to manage the use of countdown block in events single post (CPT).
+ */
+ $start_date = apply_filters( 'se_countdown_start_date', $start_date, $event_id );
+
+ $time_until_start = $start_date * 1000;
+ ?>
+
+ create_date_time( 'now' );
+ $previous_date_time = SE_Calendar::get_instance()->get_previous_month_with_events( $current_date_time );
+ $next_date_time = SE_Calendar::get_instance()->get_next_month_with_events( $current_date_time );
+
+ $current_date = $current_date_time->format( 'Y-m-01' );
+ $month_data = SE_Calendar::get_instance()->get_month_days( $current_date );
+
+ if ( ! $month_data['month_has_events'] ) {
+ if ( $next_date_time ) {
+ $current_date = $next_date_time->format( 'Y-m-01' );
+ $month_data = SE_Calendar::get_instance()->get_month_days( $current_date );
+ $next_date_time = SE_Calendar::get_instance()->get_next_month_with_events( $next_date_time );
+ } elseif ( $previous_date_time ) {
+ $current_date = $previous_date_time->format( 'Y-m-01' );
+ $month_data = SE_Calendar::get_instance()->get_month_days( $current_date );
+ $previous_date_time = SE_Calendar::get_instance()->get_previous_month_with_events( $previous_date_time );
+ }
+ }
+
+ // Passing Attributes to the calendar block. Required as the API request replaces the block attribute with default array
+ wp_add_inline_script( 'simple-events-calendar-view-script', 'const attributes = ' . wp_json_encode( $attributes ) . ';', 'before' );
+
+ $output = SE_Template_Loader::get_template_part(
+ 'calendar/calendar',
+ 'container',
+ true,
+ array(
+ 'attributes' => $attributes,
+ 'current_date' => $current_date,
+ 'days' => $month_data['days'],
+ 'month_has_events' => $month_data['month_has_events'],
+ 'previous_date' => $previous_date_time?->format( 'Y-m-01' ),
+ 'next_date' => $next_date_time?->format( 'Y-m-01' ),
+ ),
+ true
+ );
+
+ return apply_filters( 'simple_events_calendar_render', $output );
+ }
+
+ /**
+ * Renders the loop event info block.
+ *
+ * @param array $attributes Block attributes.
+ * @param string $content Block default content.
+ * @param WP_Block $block Block instance.
+ *
+ * @return string Returns the filtered post date for the current post wrapped inside "time" tags.
+ */
+ public static function loop_event_info_render( $attributes, $content, $block ): string {
+
+ $output = '';
+ $prefix = '';
+ $post_ID = ( isset( $attributes['thePostId'] ) && $attributes['thePostId'] > 0 ) ? $attributes['thePostId'] : $block->context['postId'];
+
+ if ( isset( $attributes['metaPrefix'] ) ) {
+ $prefix = '' . esc_html( $attributes['metaPrefix'] ) . ' ';
+ }
+
+ // Generate output based on meta name.
+ if ( ! empty( $post_ID ) ) {
+ switch ( $attributes['metaName'] ) {
+ case 'location':
+ $output = se_event_get_location( $post_ID );
+ break;
+ case 'venue':
+ $output = se_event_get_venue( $post_ID );
+ break;
+ case 'dates':
+ $output = se_event_get_future_dates( $post_ID );
+ break;
+ case 'date':
+ $output = se_event_get_future_dates( $post_ID, true, false );
+ break;
+ case 'time':
+ $output = se_event_get_future_dates( $post_ID, false, true );
+ break;
+ }
+ }
+
+ // If post ID is empty at this point, we're in the FSE editor.
+ if ( empty( $post_ID ) ) {
+ // Generate placeholder output based on meta name.
+ switch ( $attributes['metaName'] ) {
+ case 'location':
+ $output = esc_html__( 'Example Location Name', 'simple-events' );
+ break;
+ case 'venue':
+ $output = esc_html__( 'Example Venue Name', 'simple-events' );
+ break;
+ case 'dates':
+ $output = esc_html__( 'June 20, 2023 9:00 am - 10:00 am', 'simple-events' );
+ break;
+ case 'date':
+ $output = esc_html__( 'June 28, 2023', 'simple-events' );
+ break;
+ case 'time':
+ $output = esc_html__( '9:00 am - 10:00 am', 'simple-events' );
+ break;
+ }
+ }
+
+ // Add calendar links if the attribute is set to true.
+ if ( isset( $attributes['addCalendarLinks'] ) && $attributes['addCalendarLinks'] ) {
+ $output .= se_template_calendar_links( false );
+ }
+
+ // Add gutenberg generated wrapper atts.
+ $output = sprintf(
+ '%s%s
',
+ get_block_wrapper_attributes(
+ array(
+ 'class' => 'has-text-align-' . esc_attr( $attributes['textAlign'] ),
+ )
+ ),
+ $prefix,
+ $output
+ );
+
+ return apply_filters( 'simple_events_loop_info_render', $output, $attributes, $content, $block );
+ }
+
+ /**
+ * Renders the loop external link block.
+ *
+ * @param array $attributes Block attributes.
+ * @param string $content Block default content.
+ * @param WP_Block $block Block instance.
+ *
+ * @return string Returns the html for the link block.
+ */
+ public static function loop_event_external_link_render( $attributes, $content, $block ): string {
+ $post_ID = ( isset( $attributes['thePostId'] ) && $attributes['thePostId'] > 0 ) ? $attributes['thePostId'] : $block->context['postId'];
+
+ if ( ! $post_ID ) {
+ return sprintf(
+ '%s ',
+ esc_html__( 'Tickets', 'simple-events' )
+ );
+ }
+
+ $has_meta = get_post_meta( $post_ID, 'se_event_external_link', true );
+ $event_link = $has_meta ? $has_meta : get_the_permalink( $post_ID );
+ $link_text = $has_meta ? __( 'Tickets', 'simple-events' ) : __( 'Details', 'simple-events' );
+ $cta = apply_filters( 'se_event_loop_external_link_text', $link_text, $has_meta );
+ $link_target = $has_meta && ! strstr( wp_parse_url( $has_meta, PHP_URL_HOST ), wp_parse_url( get_site_url(), PHP_URL_HOST ) ) ? 'target="_blank" rel="nofollow"' : '';
+
+ if ( ! $event_link ) {
+ return '';
+ }
+
+ $output = sprintf(
+ '%s ',
+ esc_url( $event_link ),
+ $link_target,
+ esc_html( $cta )
+ );
+
+ return apply_filters( 'simple_events_loop_link_render', $output, $attributes, $content, $block );
+ }
+
+ /**
+ * Renders the notice for past events.
+ *
+ * @param array $attributes The attributes passed to the function.
+ * @param string $content The content passed to the function.
+ *
+ * @return void
+ */
+ public static function past_events_notice_render( $attributes, $content ) {
+ if (
+ 'se-event' !== get_post_type() || // If not an event.
+ ! se_event_is_expired( get_the_ID() ) || // If event has expired.
+ defined( 'REST_REQUEST' ) // If event is being edited.
+ ) {
+ return;
+ }
+
+ return wp_kses_post( $content );
+ }
+
+ /**
+ * Loads the asset file for the given script or style.
+ * Returns a default if the asset file is not found.
+ *
+ * @param string $file_path The name of the file without the extension.
+ *
+ * @return array The asset file contents.
+ */
+ public static function get_asset_file( $file_path ) {
+ $asset_path = SE_PLUGIN_DIR . $file_path . '.asset.php';
+
+ return file_exists( $asset_path )
+ ? include $asset_path
+ : array(
+ 'dependencies' => array(),
+ 'version' => SE_VERSION,
+ );
+ }
+
+ /**
+ * Add custom query vars.
+ *
+ * @param array $vars The current query vars.
+ *
+ * @return array $vars The updated query vars.
+ */
+ public static function add_event_query_vars( $vars ) {
+ $vars[] = 'se_event_order';
+
+ return $vars;
+ }
+}
+
+SE_Blocks::init();
diff --git a/OLD/src/classes/class-se-calendar-export.php b/OLD/src/classes/class-se-calendar-export.php
new file mode 100644
index 0000000..6fc8db7
--- /dev/null
+++ b/OLD/src/classes/class-se-calendar-export.php
@@ -0,0 +1,116 @@
+ SE_Event_Post_Type::$post_type,
+ 'post_status' => 'publish',
+ 'posts_per_page' => 10,
+ 'fields' => 'ids',
+ );
+
+ $events = get_posts( apply_filters( 'se_calendar_export_query_args', $events_query_args ) );
+ }
+
+ // Get dates.
+ if ( ! empty( $events ) ) {
+ foreach ( $events as $event_id ) {
+ $event_dates = se_event_get_dates( $event_id );
+
+ foreach ( $event_dates as $event_date ) {
+ $v_event = new \Eluceo\iCal\Component\Event();
+
+ if ( empty( $event_date['datetime_start'] ) || empty( $event_date['datetime_end'] ) ) {
+ continue;
+ }
+
+ $date_start = new \DateTime();
+ $date_start->setTimestamp( $event_date['datetime_start'] );
+
+ $date_end = new \DateTime();
+ $date_end->setTimestamp( $event_date['datetime_end'] );
+
+ $v_event
+ ->setDtStart( $date_start )
+ ->setDtEnd( $date_end )
+ ->setSummary( se_event_get_title( $event_id ) );
+
+ // Ensure we're working with a boolean.
+ $event_date['all_day'] = filter_var( $event_date['all_day'], FILTER_VALIDATE_BOOLEAN );
+
+ if ( $event_date['all_day'] ) {
+ $v_event->setNoTime( true );
+ }
+
+ $v_calendar->addComponent( $v_event );
+ }
+ }
+ }
+
+ header( 'Content-Type: text/calendar; charset=utf-8' );
+ header( 'Content-Disposition: attachment; filename="cal.ics"' );
+
+ echo $v_calendar->render(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+
+ exit;
+ }
+}
+
+SE_Calendar_Export::init();
diff --git a/OLD/src/classes/class-se-calendar.php b/OLD/src/classes/class-se-calendar.php
new file mode 100644
index 0000000..b13db20
--- /dev/null
+++ b/OLD/src/classes/class-se-calendar.php
@@ -0,0 +1,452 @@
+ array( 'POST' ),
+ 'callback' => array( $this, 'api_get_month_days' ),
+ 'permission_callback' => function () {
+ return true;
+ },
+ )
+ );
+ }
+
+ /**
+ * Return an array with the days of the week, numbered with respect to the start_of_week WP option
+ *
+ * @param string $format The display format for the days of the week.
+ *
+ * @return array Days of the week.
+ **@category Events
+ */
+ public function simple_events_get_days_of_week( $format = null ) {
+ global $wp_locale;
+
+ for ( $i = 0; $i <= 6; $i++ ) {
+ $day = $wp_locale->get_weekday( $i );
+ $weekdays['full'][ $i ] = $day;
+ $weekdays['short'][ $i ] = $wp_locale->get_weekday_abbrev( $day );
+ $weekdays['initial'][ $i ] = $wp_locale->get_weekday_initial( $day );
+ }
+
+ switch ( $format ) {
+ case 'min':
+ $days_of_week = $weekdays['initial'];
+ break;
+
+ case 'short':
+ $days_of_week = $weekdays['short'];
+ break;
+
+ default:
+ $days_of_week = $weekdays['full'];
+ break;
+ }
+
+ $start_of_week = get_option( 'start_of_week', 0 );
+ for ( $i = 0; $i < $start_of_week; $i++ ) {
+ $day = $days_of_week[ $i ];
+ unset( $days_of_week[ $i ] );
+ $days_of_week[ $i ] = $day;
+ }
+
+ return apply_filters( 'simple_events_get_days_of_week', $days_of_week );
+ }
+
+
+ /**
+ * A function to create a DateTime object with an optional timezone.
+ *
+ * @param mixed $date_time The date and time to create the object from.
+ * @param string|null $timezone The optional timezone to use for the object. If null, the site timezone is used.
+ *
+ * @return DateTime The created DateTime object.
+ */
+ public function create_date_time( $date_time, $timezone = null ): DateTime {
+ /**
+ * If no timezone is passed, use the site timezone
+ */
+ if ( null === $timezone ) {
+ $timezone = wp_timezone_string();
+ }
+
+ try {
+ $date_time_object = new DateTime( $date_time, new DateTimeZone( $timezone ) );
+ } catch ( Exception $e ) {
+ $date_time_object = new DateTime();
+ // todo handle exception
+ }
+
+ return $date_time_object;
+ }
+
+
+ /**
+ * Create a DateTime object from a timestamp, with an optional timezone.
+ *
+ * @param mixed $timestamp The Unix timestamp to create the DateTime object from.
+ * @param string|null $timezone The timezone to be used, or null to use the site timezone.
+ *
+ * @return DateTime The created DateTime object.
+ */
+ public function create_date_time_from_timestamp( $timestamp, $timezone = null ): DateTime {
+ /**
+ * If no timezone is passed, use the site timezone
+ */
+ if ( null === $timezone ) {
+ $timezone = wp_timezone_string();
+ }
+
+ try {
+ $date_time_object = new DateTime( 'now', new DateTimeZone( $timezone ) );
+ $date_time_object->setTimestamp( $timestamp );
+ } catch ( Exception $e ) {
+ $date_time_object = new DateTime();
+ // todo handle exception
+ }
+
+ return $date_time_object->setTimestamp( $timestamp );
+ }
+
+
+ /**
+ * Retrieves the days of the month and related event information.
+ *
+ * @param mixed $date The date to retrieve month information for.
+ *
+ * @return array The array containing month and event information.
+ */
+ public function get_month_days( $date ): array {
+ $start_of_week = get_option( 'start_of_week', 0 );
+ $current_date = $this->create_date_time( $date );
+ $current_day = $this->create_date_time( 'now' )->setTime( 0, 0, 0 );
+ $current_day_formatted = $current_day->format( 'Y-m-d' );
+ $current_month = $current_date->format( 'n' );
+ $data = array();
+ $data['month_has_events'] = false;
+
+ $start_day = $this->get_start_day( $current_date, $start_of_week );
+ $end_day = $this->get_end_day( $current_date, $start_of_week );
+
+ $period = new DatePeriod( $start_day, new DateInterval( 'P1D' ), $end_day );
+
+ foreach ( $period as $day ) {
+ $day->setTime( 0, 0, 0 );
+ $day_formatted = $day->format( 'Y-m-d' );
+ $day_month = $day->format( 'n' );
+ $events = $this->get_events_by_date( $day );
+
+ if ( ! $data['month_has_events'] && ! empty( $events ) && $day_month === $current_month ) {
+ $data['month_has_events'] = true;
+ }
+
+ $data['days'][] = array(
+ 'date' => $day,
+ 'date_formatted' => $day_formatted,
+ 'events' => $events,
+ 'is_other_month' => $day_month !== $current_month,
+ 'is_previous_month' => $day_month < $current_month,
+ 'is_next_month' => $day_month > $current_month,
+ 'is_past' => $day < $current_day,
+ 'is_today' => $day_formatted === $current_day_formatted,
+ );
+ }
+ return $data;
+ }
+
+
+
+ /**
+ * Get month days from the API request.
+ *
+ * @param WP_REST_Request $request The REST request object.
+ *
+ * @return WP_REST_Response
+ */
+ public function api_get_month_days( WP_REST_Request $request ) {
+ // Get the body from the request.
+ $request_body = $request->get_json_params();
+
+ // Check if the request has a date.
+ $request_date = $request_body['date'];
+
+ if ( ! $request_date ) {
+ $request_date = 'now';
+ }
+
+ $request_date_time = $this->create_date_time( $request_date );
+ $previous_date_time = self::get_instance()->get_previous_month_with_events( $request_date_time );
+ $next_date_time = self::get_instance()->get_next_month_with_events( $request_date_time );
+
+ // Retrieve attributes for template part.
+ $request_attributes = $request_body['attributes'];
+
+ if ( ! $request_attributes ) {
+ $request_attributes = array( 'align' => 'wide' );
+ }
+
+ $month_data = $this->get_month_days( $request_date );
+
+ $output = SE_Template_Loader::get_template_part(
+ 'calendar/calendar',
+ 'main',
+ true,
+ array(
+ 'attributes' => $request_attributes,
+ 'current_date' => $request_date_time->format( 'Y-m-01' ),
+ 'days' => $month_data['days'],
+ 'month_has_events' => $month_data['month_has_events'],
+ 'previous_date' => $previous_date_time?->format( 'Y-m-01' ),
+ 'next_date' => $next_date_time?->format( 'Y-m-01' ),
+ ),
+ true
+ );
+
+ $output = apply_filters( 'simple_events_api_calendar_render', $output );
+
+ $response_data = array(
+ 'html' => $output,
+ );
+
+ $response = new WP_REST_Response( $response_data );
+
+ $response->set_status( 200 );
+
+ return $response;
+ }
+
+ /**
+ * Get first previous event with events.
+ *
+ * @param DateTime $current_date Current date.
+ *
+ * @return DateTime|null
+ */
+ public function get_previous_month_with_events( $current_date ) {
+ global $wpdb;
+
+ $previous_date_time = clone $current_date;
+ $previous_date_time->modify( 'first day of this month' );
+ $previous_date_time->settime( 0, 0, 0 );
+
+ $sql_query = $wpdb->prepare(
+ "SELECT start_meta.meta_value from {$wpdb->prefix}postmeta as start_meta WHERE start_meta.meta_key = 'se_event_date_start' AND start_meta.meta_value < %s ORDER BY start_meta.meta_value DESC LIMIT 1;",
+ $previous_date_time->getTimestamp()
+ );
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $previous_event = $wpdb->get_row( $sql_query ); // the query is prepared above
+
+ if ( empty( $previous_event ) ) {
+ return null;
+ }
+
+ return $this->create_date_time_from_timestamp( $previous_event->meta_value );
+ }
+
+ /**
+ * Get first next event with events.
+ *
+ * @param DateTime $current_date Current date.
+ *
+ * @return DateTime|null
+ */
+ public function get_next_month_with_events( $current_date ) {
+ global $wpdb;
+
+ $next_date_time = clone $current_date;
+ $next_date_time->modify( 'last day of this month' );
+ $next_date_time->settime( 23, 23, 59 );
+
+ $sql_query = $wpdb->prepare(
+ "SELECT start_meta.meta_value from {$wpdb->prefix}postmeta as start_meta WHERE start_meta.meta_key = 'se_event_date_start' AND start_meta.meta_value > %s ORDER BY start_meta.meta_value ASC LIMIT 1;",
+ $next_date_time->getTimestamp()
+ );
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $next_event = $wpdb->get_row( $sql_query ); // the query is prepared above
+
+ // If we dont have a next date, check if we have any end dates.
+ if ( empty( $next_event ) ) {
+ $sql_query = $wpdb->prepare(
+ "SELECT end_meta.meta_value from {$wpdb->prefix}postmeta as end_meta WHERE end_meta.meta_key = 'se_event_date_end' AND end_meta.meta_value > %s ORDER BY end_meta.meta_value ASC LIMIT 1;",
+ $next_date_time->getTimestamp()
+ );
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $next_event = $wpdb->get_row( $sql_query ); // the query is prepared above
+ }
+
+ if ( empty( $next_event ) ) {
+ return null;
+ }
+
+ return $this->create_date_time_from_timestamp( $next_event->meta_value );
+ }
+
+
+ /**
+ * Retrieves events from the database for a given date.
+ *
+ * @param mixed $date The date to retrieve events for.
+ *
+ * @return array The array of events for the given date.
+ */
+ private function get_events_by_date( $date ): array {
+ global $wpdb;
+
+ $day_events = array();
+
+ $start_timestamp = $date->setTime( 0, 0, 0 )->getTimeStamp();
+ $end_timestamp = $date->setTime( 23, 59, 59 )->getTimestamp();
+
+ $sql_query = $wpdb->prepare(
+ "
+SELECT * from {$wpdb->prefix}posts
+INNER JOIN {$wpdb->prefix}postmeta AS start_meta ON {$wpdb->prefix}posts.ID = start_meta.post_id AND start_meta.meta_key = 'se_event_date_start'
+INNER JOIN {$wpdb->prefix}postmeta AS end_meta ON {$wpdb->prefix}posts.ID = end_meta.post_id AND end_meta.meta_key = 'se_event_date_end'
+WHERE wp_posts.post_type = %s AND (wp_posts.post_status = 'publish') AND
+((start_meta.meta_value >= %s AND start_meta.meta_value < %s)
+OR
+(start_meta.meta_value < %s AND end_meta.meta_value > %s)
+OR
+(end_meta.meta_value <= %s AND end_meta.meta_value > %s))
+GROUP BY {$wpdb->prefix}posts.ID
+ORDER BY start_meta.meta_value ASC;",
+ 'se-event',
+ $start_timestamp,
+ $end_timestamp,
+ $start_timestamp,
+ $end_timestamp,
+ $end_timestamp,
+ $start_timestamp
+ );
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $all_events = $wpdb->get_results( $sql_query ); // the query is prepared above
+ if ( $all_events ) {
+ foreach ( $all_events as $event ) {
+ $event_dates = se_event_get_dates( $event->ID );
+
+ if ( ! $event_dates ) {
+ continue;
+ }
+
+ foreach ( $event_dates as $event_date ) {
+ $event->event_start_date = $this->create_date_time_from_timestamp( $event_date['datetime_start'] );
+ $event->event_end_date = $this->create_date_time_from_timestamp( $event_date['datetime_end'] );
+ $event->hide_start_time = '1' === get_post_meta( $event->ID, 'se_event_hide_start_time', true );
+ $event->hide_end_time = '1' === get_post_meta( $event->ID, 'se_event_hide_end_time', true );
+ if ( $event_date['datetime_start'] >= $start_timestamp && $event_date['datetime_start'] <= $end_timestamp ) {
+ $new_event = clone $event;
+ $new_event->event_start_date = $this->create_date_time_from_timestamp( $event_date['datetime_start'] );
+ $new_event->event_end_date = $this->create_date_time_from_timestamp( $event_date['datetime_end'] );
+ $new_event->open_in_new_window = (bool) get_post_meta( $event->ID, 'se_event_open_in_new_window', true );
+
+ $day_events[] = $new_event;
+ }
+ }
+ }
+ }
+ return $day_events;
+ }
+
+
+
+ /**
+ * Get the start day based on the given date and start of the week.
+ *
+ * @param mixed $date The date for which to calculate the start day.
+ * @param integer $start_of_week The start of the week (0-6, where 0 is Sunday).
+ *
+ * @return DateTime The start day based on the given date and start of the week.
+ */
+ private function get_start_day( $date, $start_of_week ) {
+ $start_date = clone $date;
+ $start_day_interval = 0;
+
+ $start_day_week_position = $date->format( 'w' );
+
+ if ( $start_of_week > 0 && 0 === intval( $start_day_week_position ) ) {
+ $start_day_week_position = 7;
+ }
+
+ if ( 0 !== intval( $start_day_week_position ) ) {
+ $start_day_interval = abs( 1 - $start_day_week_position );
+ }
+
+ return $start_date->sub( new DateInterval( 'P' . $start_day_interval . 'D' ) );
+ }
+
+
+ /**
+ * Get the end day based on the given date and start of the week.
+ *
+ * @param Date $date The date to calculate the end day from.
+ * @param integer $start_of_week The start of the `week (0-6, where 0 is Sunday).
+ *
+ * @return DateTime The end day based on the given date and start of the week.
+ */
+ private function get_end_day( $date, $start_of_week ) {
+ $end_date = clone $date;
+ $last_day_interval = 0;
+
+ $last_day_of_month = $end_date->modify( 'last day of this month' );
+ $last_day_week_position = $last_day_of_month->format( 'w' );
+
+ if ( $start_of_week > 0 && 0 === intval( $last_day_week_position ) ) {
+ $last_day_week_position = 7;
+ }
+
+ if ( 0 !== intval( $last_day_week_position ) ) {
+ $last_day_interval = 7 - $last_day_week_position;
+ }
+
+ $end_date->setTime( 23, 59, 59 );
+
+ return $end_date->add( new DateInterval( 'P' . $last_day_interval . 'D' ) );
+ }
+}
diff --git a/OLD/src/classes/class-se-event-post-type.php b/OLD/src/classes/class-se-event-post-type.php
new file mode 100644
index 0000000..c7b91b3
--- /dev/null
+++ b/OLD/src/classes/class-se-event-post-type.php
@@ -0,0 +1,561 @@
+ array(
+ 'name' => __( 'Events', 'simple-events' ),
+ 'singular_name' => __( 'Event', 'simple-events' ),
+ 'all_items' => __( 'All Events', 'simple-events' ),
+ 'archives' => __( 'Event Archives', 'simple-events' ),
+ 'attributes' => __( 'Event Attributes', 'simple-events' ),
+ 'insert_into_item' => __( 'Insert into Event', 'simple-events' ),
+ 'uploaded_to_this_item' => __( 'Uploaded to this Event', 'simple-events' ),
+ 'featured_image' => _x( 'Featured Image', 'se-event', 'simple-events' ),
+ 'set_featured_image' => _x( 'Set featured image', 'se-event', 'simple-events' ),
+ 'remove_featured_image' => _x( 'Remove featured image', 'se-event', 'simple-events' ),
+ 'use_featured_image' => _x( 'Use as featured image', 'se-event', 'simple-events' ),
+ 'filter_items_list' => __( 'Filter Events list', 'simple-events' ),
+ 'items_list_navigation' => __( 'Events list navigation', 'simple-events' ),
+ 'items_list' => __( 'Events list', 'simple-events' ),
+ 'new_item' => __( 'New Event', 'simple-events' ),
+ 'add_new' => __( 'Add New', 'simple-events' ),
+ 'add_new_item' => __( 'Add New Event', 'simple-events' ),
+ 'edit_item' => __( 'Edit Event', 'simple-events' ),
+ 'view_item' => __( 'View Event', 'simple-events' ),
+ 'view_items' => __( 'View Events', 'simple-events' ),
+ 'search_items' => __( 'Search Events', 'simple-events' ),
+ 'not_found' => __( 'No Events found', 'simple-events' ),
+ 'not_found_in_trash' => __( 'No Events found in trash', 'simple-events' ),
+ 'parent_item_colon' => __( 'Parent Event:', 'simple-events' ),
+ 'menu_name' => __( 'Events', 'simple-events' ),
+ ),
+ 'public' => true,
+ 'hierarchical' => false,
+ 'show_ui' => true,
+ 'show_in_nav_menus' => true,
+ 'supports' => array(
+ 'title',
+ 'editor',
+ 'thumbnail',
+ 'custom-fields',
+ 'excerpt',
+ 'author',
+ ),
+ 'rewrite' => array(
+ 'slug' => 'event',
+ 'with_front' => false,
+ ),
+ 'has_archive' => self::$slug,
+ 'query_var' => true,
+ 'menu_position' => null,
+ 'menu_icon' => 'dashicons-calendar-alt',
+ 'show_in_rest' => true,
+ 'rest_base' => self::$post_type,
+ 'rest_controller_class' => 'WP_REST_Posts_Controller',
+ 'template' => $template,
+ 'template_lock' => 'insert',
+ 'taxonomies' => array(
+ 'post_tag',
+ ),
+ )
+ );
+ }
+
+ /**
+ * Taxonomy registration.
+ *
+ * @return void
+ */
+ public static function register_taxonomy() {
+ register_taxonomy(
+ self::$post_type . '-category',
+ self::$post_type,
+ array(
+ 'description' => __( 'Categories for simple event posts', 'simple-events' ),
+ 'public' => true,
+ 'hierarchical' => true,
+ 'show_in_rest' => true,
+ 'show_admin_column' => true,
+ 'rewrite' => array(
+ 'slug' => 'events/category',
+ 'with_front' => false,
+ ),
+ )
+ );
+ }
+
+ /**
+ * Defines protected meta keys for the event post type.
+ *
+ * This method registers meta keys that are used to store event-related data.
+ *
+ * @param boolean $is_protected Whether the meta keys should be protected.
+ * @param string $meta_key The meta key to register.
+ * @param string $meta_type The type of the meta key.
+ *
+ * @return boolean
+ */
+ public static function is_protected_meta( bool $is_protected, string $meta_key, string $meta_type = 'string' ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ $protected_keys = array( 'se_event_date_end', 'se_event_date_start' );
+
+ if ( in_array( $meta_key, $protected_keys, true ) ) {
+ return true;
+ }
+ return $is_protected;
+ }
+
+ /**
+ * Register meta keys.
+ *
+ * @return void
+ */
+ public static function register_meta() {
+ register_meta(
+ 'post',
+ 'se_event_location',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_venue',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_dates',
+ array(
+ 'single' => true,
+ 'type' => 'array',
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'datetime_start' => array(
+ 'type' => 'string',
+ ),
+ 'datetime_end' => array(
+ 'type' => 'string',
+ ),
+ 'all_day' => array(
+ 'type' => 'boolean',
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_date_start',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ 'auth_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_date_end',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ 'auth_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_timezone',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_display_timezone',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_display_grouped',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => true,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_hide_end_time',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => false,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_hide_start_time',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => false,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_add_calendar_links',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => false,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_open_in_new_window',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => false,
+ )
+ );
+ register_meta(
+ 'post',
+ 'se_event_external_link',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_external_link_label',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'string',
+ 'object_subtype' => self::$post_type,
+ 'default' => esc_html__( 'Tickets', 'simple-events' ),
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_open_external_link',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => false,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_modal_access',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => true,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_show_modal_title',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => true,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_show_modal_excerpt',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => true,
+ )
+ );
+
+ register_meta(
+ 'post',
+ 'se_event_show_on_frontend',
+ array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'boolean',
+ 'object_subtype' => self::$post_type,
+ 'default' => true,
+ )
+ );
+ }
+
+ /**
+ * Flush rewrite rules if the flag added during activation exists.
+ *
+ * @return void
+ */
+ public static function maybe_flush_rewrite_rules() {
+ if ( get_option( 'simple_events_flush_rewrite_rules_flag' ) ) {
+ delete_option( 'simple_events_flush_rewrite_rules_flag' );
+ flush_rewrite_rules();
+ }
+ }
+
+ /**
+ * Order events by date.
+ *
+ * @param WP_Query $query WP_Query instance.
+ *
+ * @return void
+ */
+ public static function pre_get_posts( $query ) {
+ if ( is_admin() || ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) || ( isset( $query->query['type'] ) && 'calendar' === $query->query['type'] ) ) {
+ return;
+ }
+
+ $options = get_option( 'se_options' );
+ $sort_order = ( isset( $options['reverse_events_order'] ) ) ? 'DESC' : 'ASC';
+
+ // Check if there's an override for the sort order through the 'se_pre_get_posts_order_override' filter.
+ $order_override = apply_filters( 'se_pre_get_posts_order_override', $query->get( 'se_event_order' ), $query );
+
+ // If a custom order override exists, use it. Otherwise, stick with the existing sort order.
+ $sort_order = ! empty( $order_override ) ? $order_override : $sort_order;
+
+ if (
+ ( $query->is_main_query() && ( is_post_type_archive( self::$post_type )
+ || is_tax( self::$post_type . '-category' ) ) )
+ || ( ! $query->is_main_query() && self::$post_type === $query->get( 'post_type' ) && ! $query->get( 'se_countdown' ) && $query->get( 'sub-type' ) !== SE_Block_Variations::QUERY_LOOP_EVENTS )
+ ) {
+ $query->set( 'orderby', 'meta_value' );
+ $query->set( 'meta_key', 'se_event_date_start' );
+ $query->set( 'order', apply_filters( 'se_pre_get_posts_order', $sort_order, $query ) );
+
+ // Values for which passed events should be hidden on Feed.
+ $event_options = array( 'hide_events_on_both', 'hide_events_on_feed', 'on' );
+
+ if ( isset( $options['hide_past_events'] ) && in_array( $options['hide_past_events'], $event_options, true ) ) {
+ $query->set(
+ 'meta_query',
+ array(
+ array(
+ 'key' => 'se_event_date_end',
+ 'value' => wp_date( 'U' ),
+ 'compare' => '>=',
+ ),
+ )
+ );
+ }
+ }
+ }
+
+ /**
+ * Restrict access to event single page.
+ *
+ * @return void
+ */
+ public static function handle_expired_events() {
+ global $wp_query;
+
+ if ( $wp_query->is_singular( self::$post_type ) ) {
+ $options = get_option( 'se_options' );
+
+ // Values for which passed events should be shown on Single View.
+ $event_options = array( '', 'hide_events_on_feed' );
+
+ if ( ! isset( $options['hide_past_events'] ) || in_array( $options['hide_past_events'], $event_options, true ) ) {
+ return;
+ }
+
+ $event = $wp_query->get_queried_object();
+
+ if ( ! empty( $event ) && se_event_is_expired( $event->ID ) ) {
+ $wp_query->set_404();
+ status_header( 404 );
+ }
+ }
+ }
+
+ /**
+ * Remove "Archive:" from archive title.
+ *
+ * @param string $title Archive title.
+ *
+ * @return string
+ */
+ public static function the_archive_title( $title ) {
+ if ( is_post_type_archive( self::$post_type ) ) {
+ return post_type_archive_title( '', false );
+ }
+
+ return $title;
+ }
+
+ /**
+ * Remove rewrite rules and then recreate rewrite rules.
+ *
+ * @return void
+ */
+ public static function flush_rewrite_rules() {
+ self::register_post_type();
+ flush_rewrite_rules();
+ }
+
+ /**
+ * Deletes event dates if no event info block is present.
+ *
+ * @param integer $event_id Event ID.
+ *
+ * @return void
+ */
+ public static function delete_event_dates_if_no_event_info_block( $event_id ) {
+ if ( wp_is_post_revision( $event_id ) || get_post_type( $event_id ) !== 'se-event' ) {
+ return;
+ }
+
+ $event = get_post( $event_id );
+ $blocks = parse_blocks( $event->post_content );
+
+ $is_event_info_block_present = false;
+
+ foreach ( $blocks as $block ) {
+ if ( 'simple-events/event-info' === $block['blockName'] ) {
+ $is_event_info_block_present = true;
+ break;
+ }
+ }
+
+ if ( ! $is_event_info_block_present ) {
+ delete_post_meta( $event_id, 'se_event_dates' );
+ }
+ }
+}
+
+SE_Event_Post_Type::init();
diff --git a/OLD/src/classes/class-se-event-query-dates.php b/OLD/src/classes/class-se-event-query-dates.php
new file mode 100644
index 0000000..f87c16f
--- /dev/null
+++ b/OLD/src/classes/class-se-event-query-dates.php
@@ -0,0 +1,157 @@
+ SE_Event_Post_Type::$post_type,
+ 'posts_per_page' => -1,
+ 'meta_query' => array(
+ array(
+ 'key' => 'se_event_date_start',
+ 'value' => array(
+ SE_Calendar::get_instance()->create_date_time( 'now' )->modify( '-' . $range . ' seconds' )->format( 'U' ),
+ SE_Calendar::get_instance()->create_date_time( 'now' )->format( 'U' ),
+ ),
+ 'compare' => 'BETWEEN',
+ ),
+ ),
+ )
+ );
+
+ // if we have dates, process them.
+ if ( $events->have_posts() ) {
+ // Loop through the events and update the dates.
+ while ( $events->have_posts() ) {
+ $events->the_post();
+ // Check if event should be updated.
+ if ( (bool) apply_filters( 'se_event_update_query_dates_skip', false, get_the_ID() ) ) {
+ continue;
+ }
+ se_event_update_event_query_dates( get_the_ID() );
+
+ do_action( 'se_event_updated_query_dates', get_the_ID() );
+ }
+ }
+
+ // Reset the post data.
+ wp_reset_postdata();
+
+ // Add the hooks back.
+ add_action( 'pre_get_posts', array( SE_Event_Post_Type::class, 'pre_get_posts' ) );
+ }
+}
+
+// Self initialization.
+SE_Event_Dates::init();
diff --git a/OLD/src/classes/class-se-rest-ticket-products.php b/OLD/src/classes/class-se-rest-ticket-products.php
new file mode 100644
index 0000000..6c497c2
--- /dev/null
+++ b/OLD/src/classes/class-se-rest-ticket-products.php
@@ -0,0 +1,245 @@
+namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => 'GET',
+ 'callback' => array( $this, 'get_items' ),
+ 'permission_callback' => array( $this, 'get_items_permissions_check' ),
+ 'args' => $this->get_collection_params(),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+ }
+
+ /**
+ * Check if a given request has access to read items.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ *
+ * @return WP_Error|boolean
+ */
+ public function get_items_permissions_check( $request ) {
+ if ( ! current_user_can( 'edit_posts' ) ) {
+ return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'simple-events' ), array( 'status' => \rest_authorization_required_code() ) );
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if a given request has access to read an item.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ *
+ * @return WP_Error|boolean
+ */
+ public function get_item_permissions_check( $request ) {
+ if ( ! current_user_can( 'edit_posts' ) ) {
+ return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'simple-events' ), array( 'status' => \rest_authorization_required_code() ) );
+ }
+
+ return true;
+ }
+
+ /**
+ * Change REST API permissions so that authors have access to this API.
+ *
+ * This code only runs for methods of this class. @see Products::get_items below.
+ *
+ * @param boolean $permission Does the current user have access to the API.
+ *
+ * @return boolean
+ */
+ public function force_edit_posts_permission( $permission ) {
+ // If user has access already, we can bypass additonal checks.
+ if ( $permission ) {
+ return $permission;
+ }
+
+ return current_user_can( 'edit_posts' );
+ }
+
+ /**
+ * Get a collection of posts.
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ *
+ * @return WP_Error|WP_REST_Response
+ */
+ public function get_items( $request ) {
+ add_filter( 'woocommerce_rest_check_permissions', array( $this, 'force_edit_posts_permission' ) );
+ $response = parent::get_items( $request );
+ remove_filter( 'woocommerce_rest_check_permissions', array( $this, 'force_edit_posts_permission' ) );
+
+ return $response;
+ }
+
+ /**
+ * Make extra product orderby features supported by WooCommerce available to the WC API.
+ * This includes 'price', 'popularity', and 'rating'.
+ *
+ * @param WP_REST_Request $request Request data.
+ *
+ * @return array
+ */
+ protected function prepare_objects_query( $request ) {
+ $args = parent::prepare_objects_query( $request );
+
+ $args['meta_query'][] = array(
+ 'key' => '_ticket',
+ 'value' => 'yes',
+ 'compare' => '=',
+ );
+
+ return $args;
+ }
+
+ /**
+ * Get product data.
+ *
+ * @param \WC_Product|\WC_Product_Variation $product Product instance.
+ * @param string $context Request context. Options: 'view' and 'edit'.
+ *
+ * @return array
+ */
+ protected function get_product_data( $product, $context = 'view' ) {
+ return array(
+ 'id' => $product->get_id(),
+ 'name' => $product->get_title(),
+ 'variation' => $product->is_type( 'variation' ) ? wc_get_formatted_variation( $product, true, true, false ) : '',
+ 'permalink' => $product->get_permalink(),
+ 'sku' => $product->get_sku(),
+ 'description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ? $product->get_short_description() : wc_trim_string( $product->get_description(), 400 ) ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
+ 'onsale' => $product->is_on_sale(),
+ 'price' => $product->get_price(),
+ 'price_html' => $product->get_price_html(),
+ 'has_options' => $product->has_options(),
+ 'is_purchasable' => $product->is_purchasable(),
+ 'is_in_stock' => $product->is_in_stock(),
+ );
+ }
+
+ /**
+ * Get the Product's schema, conforming to JSON Schema.
+ *
+ * @return array
+ */
+ public function get_item_schema() {
+ $schema = array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => 'simple_events_ticket_products',
+ 'type' => 'object',
+ 'properties' => array(
+ 'id' => array(
+ 'description' => __( 'Unique identifier for the resource.', 'simple-events' ),
+ 'type' => 'integer',
+ 'context' => array( 'view', 'edit', 'embed' ),
+ 'readonly' => true,
+ ),
+ 'name' => array(
+ 'description' => __( 'Product name.', 'simple-events' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit', 'embed' ),
+ ),
+ 'variation' => array(
+ 'description' => __( 'Product variation attributes, if applicable.', 'simple-events' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit', 'embed' ),
+ ),
+ 'permalink' => array(
+ 'description' => __( 'Product URL.', 'simple-events' ),
+ 'type' => 'string',
+ 'format' => 'uri',
+ 'context' => array( 'view', 'edit', 'embed' ),
+ 'readonly' => true,
+ ),
+ 'sku' => array(
+ 'description' => __( 'Unique identifier.', 'simple-events' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit' ),
+ ),
+ 'onsale' => array(
+ 'description' => __( 'Is the product on sale?', 'simple-events' ),
+ 'type' => 'boolean',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'description' => array(
+ 'description' => __( 'Short description or excerpt from description.', 'simple-events' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit', 'embed' ),
+ ),
+ 'price' => array(
+ 'description' => __( 'Current product price.', 'simple-events' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit' ),
+ ),
+ 'price_html' => array(
+ 'description' => __( 'Price formatted in HTML.', 'simple-events' ),
+ 'type' => 'string',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'has_options' => array(
+ 'description' => __( 'Does the product have options?', 'simple-events' ),
+ 'type' => 'boolean',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'is_purchasable' => array(
+ 'description' => __( 'Is the product purchasable?', 'simple-events' ),
+ 'type' => 'boolean',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'is_in_stock' => array(
+ 'description' => __( 'Is the product in stock?', 'simple-events' ),
+ 'type' => 'boolean',
+ 'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ ),
+ );
+
+ return $this->add_additional_fields_schema( $schema );
+ }
+}
diff --git a/OLD/src/classes/class-se-settings.php b/OLD/src/classes/class-se-settings.php
new file mode 100644
index 0000000..f142c7d
--- /dev/null
+++ b/OLD/src/classes/class-se-settings.php
@@ -0,0 +1,608 @@
+Enable this option if you are creating free events only. ' ),
+ ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_editor',
+ array(
+ 'label_for' => 'remove_event_tickets_block',
+ 'description' => esc_html__( 'Select this setting if you are creating free events only.', 'simple-events' ),
+ )
+ );
+
+ // Register an "Archives" section in the `simple_events` option group.
+ add_settings_section(
+ 'se_section_archives',
+ esc_html__( 'Archives', 'simple-events' ),
+ array( __CLASS__, 'section_cb_archives' ),
+ 'simple_events'
+ );
+
+ // Register a "Past Events" field in the `se_section_archives` section.
+ add_settings_field(
+ 'hide_past_events',
+ esc_html__( 'Hide Past Events ( Feed & Single )', 'simple-events' ),
+ array( __CLASS__, 'radio_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'hide_past_events',
+ )
+ );
+
+ // Register a "Past Event Notice" text field in the `se_section_archives` section.
+ add_settings_field(
+ 'past_event_notice',
+ esc_html__( 'Past Event Notice', 'simple-events' ),
+ array( __CLASS__, 'text_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'past_event_notice',
+ )
+ );
+
+ // Register a "Update Start and End Dates" field in the `se_section_archives` section.
+ add_settings_field(
+ 'update_start_end_dates',
+ sprintf(
+ // translators: %s is a HTML break tag.
+ __( 'Update Query Dates%s', 'simple-events' ),
+ wp_kses_post( 'Will update the start date used for queries to the next available starting date. ' ),
+ ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'update_start_end_dates',
+ )
+ );
+
+ // Register a "reverse order" field in the `se_section_archives` section.
+ add_settings_field(
+ 'reverse_events_order',
+ esc_html__( 'Reverse events order in post feed.', 'simple-events' ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'reverse_events_order',
+ )
+ );
+
+ // Show next/previous links on event singles.
+ add_settings_field(
+ 'show_next_prev_links',
+ __( 'Show next/previous links on event singles.', 'simple-events' ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'show_next_prev_links',
+ )
+ );
+
+ // Select link for the caledar page.
+ add_settings_field(
+ 'calendar_page',
+ __( 'Select the page for the calendar.', 'simple-events' ),
+ array( __CLASS__, 'calendar_page_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'calendar_page',
+ )
+ );
+
+ // Show next and previous link above content.
+ add_settings_field(
+ 'show_links_above_content',
+ __( 'Next/Previous link placement.', 'simple-events' ),
+ array( __CLASS__, 'link_position_cb' ),
+ 'simple_events',
+ 'se_section_archives',
+ array(
+ 'label_for' => 'show_links_above_content',
+ )
+ );
+
+ add_settings_section(
+ 'se_section_woocommerce',
+ esc_html__( 'WooCommerce', 'simple-events' ),
+ array( __CLASS__, 'section_cb_woocommerce' ),
+ 'simple_events'
+ );
+
+ // Skip cart for tickets and redirect to checkout.
+ add_settings_field(
+ 'skip_cart_for_ticket',
+ __( 'Skip cart for tickets and redirect to checkout', 'simple-events' ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_woocommerce',
+ array(
+ 'label_for' => 'skip_cart',
+ )
+ );
+
+ // Empty cart before adding tickets.
+ add_settings_field(
+ 'empty_cart_before_adding_tickets',
+ sprintf(
+ // translators: %s is a HTML break tag.
+ __( 'Empty cart before a ticket is added%s', 'simple-events' ),
+ wp_kses_post( 'Clears the cart before a ticket is added to the cart. ' ),
+ ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_woocommerce',
+ array(
+ 'label_for' => 'empty_cart_before_adding_tickets',
+ )
+ );
+
+ add_settings_field(
+ 'autocomplete_ticket_order',
+ sprintf(
+ // translators: %s is a HTML break tag.
+ __( 'Set Ticket Order Status to Completed.%s', 'simple-events' ),
+ wp_kses_post( 'Orders that exclusively contain tickets will be automatically marked as completed. This applys to orders placed after this feature is enabled. ' ),
+ ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_woocommerce',
+ array(
+ 'label_for' => 'autocomplete_ticket_order',
+ )
+ );
+
+ add_settings_field(
+ 'mark_existing_orders_as_completed',
+ sprintf(
+ // translators: %s is a HTML break tag.
+ __( 'Set existing ticket orders as completed.%s', 'simple-events' ),
+ wp_kses_post( 'Customer emails are disabled during the bulk update. ' ),
+ ),
+ array( __CLASS__, 'ajax_cb' ),
+ 'simple_events',
+ 'se_section_woocommerce',
+ array(
+ 'action' => 'se_mark_existing_orders_as_completed',
+ 'btn_text' => __( 'Perform bulk status updates', 'simple-events' ),
+ )
+ );
+
+ // Register an "Calendar" section in the `simple_events` option group.
+ add_settings_section(
+ 'se_section_calendar',
+ esc_html__( 'Calendar', 'simple-events' ),
+ array( __CLASS__, 'section_cb_calendar' ),
+ 'simple_events'
+ );
+
+ // Register a "Custom Endpoint" field in the `se_section_calendar` section.
+ add_settings_field(
+ 'cal_download_endpoint',
+ esc_html__( 'Custom Endpoint for Downloading Calendar', 'simple-events' ),
+ array( __CLASS__, 'cal_download_cb' ),
+ 'simple_events',
+ 'se_section_calendar',
+ array(
+ 'label_for' => 'cal_download_endpoint',
+ )
+ );
+
+ // Register a "Download Calendar" field in the `se_section_calendar` section.
+ add_settings_field(
+ 'disable_download_calendar',
+ esc_html__( 'Disable Calendar Download Endpoint', 'simple-events' ),
+ array( __CLASS__, 'field_cb' ),
+ 'simple_events',
+ 'se_section_calendar',
+ array(
+ 'label_for' => 'disable_download_calendar',
+ )
+ );
+ }
+
+ /**
+ * Displays the "Editor" section.
+ *
+ * @return void
+ */
+ public static function section_cb_editor() {
+ ?>
+
+
+
+
+
+
+
+ 'Show Both', // Shows old events in feed and single view. Empty string used due to it being default value if se_options not set previously.
+ 'hide_events_on_both' => 'Hide Both', // Hides old events on both feed and single view.
+ 'hide_events_on_feed' => 'Hide on Feed, Show on Single', // Hides old events on both feed only.
+ );
+ $value = isset( $options[ $args['label_for'] ] ) ? $options[ $args['label_for'] ] : '';
+
+ foreach ( $radio_labels as $key => $label ) {
+ ?>
+
+
+ >
+
+
+ ';
+ }
+ }
+ }
+
+ /**
+ * Displays the "Past Events" field.
+ *
+ * @param array $args Display arguments.
+ *
+ * @return void
+ */
+ public static function field_cb( $args ) {
+ $options = get_option( 'se_options' );
+ ?>
+
+ >
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+ >
+
+
+
+
+
+
+ ID ); ?>
+ >
+ post_title ); ?>
+
+
+
+
+
+
+ %1$s
', 'simple-events' ),
+ esc_url( get_post_type_archive_link( SE_Event_Post_Type::$post_type ) . $value ),
+ )
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+ -1,
+ 'orderby' => 'date',
+ 'order' => 'DESC',
+ 'return' => 'ids',
+ )
+ );
+
+ // Disable email notifications
+ add_filter( 'woocommerce_defer_transactional_emails', '__return_true' );
+
+ $updated_orders = 0;
+
+ foreach ( $orders as $order ) {
+ $order = wc_get_order( $order );
+ $order_status = $order->get_status();
+
+ // Skip if order is already completed or isn't being processed.
+ if ( 'completed' === $order_status || 'processing' !== $order_status ) {
+ continue;
+ }
+
+ $items = $order->get_items();
+ $should_autocomplete = true;
+
+ // Check if order contains only tickets.
+ foreach ( $items as $item ) {
+ if ( ! wc_box_office_is_product_ticket( $item['product_id'] ) ) {
+ $should_autocomplete = false;
+ }
+ }
+
+ // Mark order as completed if it contains only tickets.
+ if ( $should_autocomplete ) {
+ $order->update_status( 'completed' );
+ ++$updated_orders;
+ }
+ }
+
+ // Enable email notifications again
+ remove_filter( 'woocommerce_defer_transactional_emails', '__return_true' );
+
+ // translators: %d is the number of orders updated.
+ wp_send_json_success( sprintf( __( '%d orders updated successfully', 'simple-events' ), $updated_orders ) );
+ }
+}
+
+SE_Settings::init();
diff --git a/OLD/src/classes/class-se-template-loader.php b/OLD/src/classes/class-se-template-loader.php
new file mode 100644
index 0000000..040388d
--- /dev/null
+++ b/OLD/src/classes/class-se-template-loader.php
@@ -0,0 +1,153 @@
+post_name ) . '.php',
+ 'single-se-event.php',
+ );
+
+ // If a custom page template has been assigned to the event, add it to the front of the list.
+ if ( '' !== $event->page_template ) {
+ array_unshift( $theme_templates, $event->page_template );
+ }
+
+ // Determine if any of these templates is available in the theme.
+ $single_event_template = get_query_template( 'singular', $theme_templates );
+
+ if ( $single_event_template ) {
+ return $single_event_template;
+ }
+
+ $fallback_template = 'single.php';
+ }
+
+ // Determine if standard se-event archive templates are available in the theme
+ // before replacing with the custom template in this plugin.
+ if ( is_archive( 'se-event' ) ) {
+ $theme_templates = array(
+ 'archive-se-event.php',
+ );
+
+ // Determine if any of these templates is available in the theme.
+ $archive_event_template = get_query_template( 'archive', $theme_templates );
+
+ if ( $archive_event_template ) {
+ return $archive_event_template;
+ }
+
+ $fallback_template = 'archive.php';
+ }
+
+ // Retrieve the full path of the fallback template in this plugin.
+ $fallback_template = self::locate_template( array( $fallback_template ) );
+
+ if ( '' !== $fallback_template ) {
+ return $fallback_template;
+ }
+
+ return $template;
+ }
+
+ /**
+ * Load a template.
+ *
+ * @param array $template_names Template file(s) to search for, in order.
+ * @param boolean $load If true the template file will be loaded if it is found.
+ * @param boolean $once Whether to require_once or require. Default true. Has no effect if $load is false.
+ * @param array $args Arguments.
+ * @param boolean $return_html Return generated HTML.
+ *
+ * @return string
+ */
+ public static function locate_template( $template_names, $load = false, $once = true, $args = array(), $return_html = false ) {
+ $located = '';
+
+ foreach ( (array) $template_names as $template_name ) {
+ if ( ! $template_name ) {
+ continue;
+ }
+
+ if ( file_exists( SE_TEMPLATE_PATH . '/' . $template_name ) ) {
+ $located = SE_TEMPLATE_PATH . '/' . $template_name;
+ break;
+ }
+ }
+
+ if ( $load && '' !== $located ) {
+ if ( $return_html ) {
+ ob_start();
+ load_template( $located, $once, $args );
+ $output = ob_get_contents();
+ ob_end_clean();
+ return $output;
+ }
+ load_template( $located, $once, $args );
+ }
+
+ return $located;
+ }
+
+ /**
+ * Loads a template part into a template.
+ *
+ * @param string $slug The slug name for the generic template.
+ * @param string $name The name of the specialised template.
+ * @param boolean $load If true the template file will be loaded if it is found.
+ * @param array $args Arguments.
+ * @param boolean $return_html Return generated HTML.
+ *
+ * @return string
+ */
+ public static function get_template_part( $slug, $name = null, $load = true, $args = array(), $return_html = false ) {
+ $templates = array();
+
+ if ( isset( $name ) ) {
+ $templates[] = $slug . '-' . $name . '.php';
+ }
+
+ $templates[] = $slug . '.php';
+
+ return self::locate_template( $templates, $load, false, $args, $return_html );
+ }
+}
+
+SE_Template_Loader::init();
diff --git a/OLD/src/event-functions.php b/OLD/src/event-functions.php
new file mode 100644
index 0000000..a089f12
--- /dev/null
+++ b/OLD/src/event-functions.php
@@ -0,0 +1,530 @@
+post_content );
+
+ foreach ( $blocks as $block ) {
+ if ( 'simple-events/event-tickets' === $block['blockName'] ) {
+ if ( ! empty( $block['attrs'] ) && ! empty( $block['attrs']['selected'] ) ) {
+ $products = $block['attrs']['selected'];
+ break;
+ }
+ }
+ }
+
+ // Validate ids.
+ foreach ( $products as $key => $product ) {
+ if ( ! wc_box_office_is_product_ticket( $product ) ) {
+ unset( $products[ $key ] );
+ }
+ }
+
+ return $products;
+}
+
+/**
+ * Get tickets attached to event.
+ *
+ * @param integer $event_id Event id.
+ *
+ * @return mixed
+ */
+function se_event_get_ticket_prices( $event_id ) {
+ $prices = array();
+
+ $tickets = se_event_get_tickets( $event_id );
+
+ if ( ! empty( $tickets ) ) {
+ // Get prices.
+ foreach ( $tickets as $ticket ) {
+ $ticket = wc_get_product( $ticket );
+
+ if ( ! $ticket ) {
+ continue;
+ }
+
+ $prices[] = $ticket->get_price();
+ }
+ }
+
+ // Sort prices.
+ sort( $prices );
+
+ if ( ! empty( $prices ) ) {
+ return $prices;
+ }
+
+ return false;
+}
+
+/**
+ * Get ticket stock for an event.
+ *
+ * @param integer $event_id Event id.
+ *
+ * @return mixed
+ */
+function se_event_get_tickets_stock( $event_id ) {
+ $stock_total = 0;
+
+ // Get ticket products.
+ $tickets = se_event_get_tickets( $event_id );
+
+ if ( ! empty( $tickets ) ) {
+ foreach ( $tickets as $ticket ) {
+ $ticket = wc_get_product( $ticket );
+
+ if ( ! $ticket ) {
+ continue;
+ }
+
+ $stock = ( $ticket->managing_stock() ? $ticket->get_stock_quantity() : false );
+
+ if ( $stock && $stock > 0 ) {
+ $stock_total += $stock;
+ }
+ }
+ }
+
+ if ( $stock_total < 1 ) {
+ return false;
+ }
+
+ return $stock_total;
+}
+
+/**
+ * Get event dates.
+ *
+ * @param integer $event_id Event id.
+ * @param array $event_dates Event dates.
+ *
+ * @return mixed
+ */
+function se_event_get_dates( $event_id, $event_dates = null ) {
+ if ( is_null( $event_dates ) ) {
+ $event_dates = get_post_meta( $event_id, 'se_event_dates', true );
+ }
+
+ if ( empty( $event_dates ) ) {
+ return apply_filters( 'se_event_dates', false, $event_id );
+ }
+
+ // Sort dates.
+ if ( count( $event_dates ) > 1 ) {
+ array_multisort(
+ array_column( $event_dates, 'datetime_start' ),
+ SORT_ASC,
+ $event_dates
+ );
+ }
+
+ return apply_filters( 'se_event_dates', $event_dates, $event_id );
+}
+
+/**
+ * Gets only the future event dates in a formatted string.
+ *
+ * @param integer $event_id Event id.
+ * @param boolean $date_only Whether to return only the date.
+ * @param boolean $time_only Whether to return only the time.
+ * @param array $event_dates Event dates.
+ *
+ * @return string
+ */
+function se_event_get_future_dates( $event_id, $date_only = false, $time_only = false, $event_dates = null ) {
+ // Get required post meta.
+ $event_dates = se_event_get_dates( $event_id, $event_dates );
+ $event_timezone = get_post_meta( $event_id, 'se_event_timezone', true );
+ $hide_end_time = get_post_meta( $event_id, 'se_event_hide_end_time', true );
+ $hide_start_time = get_post_meta( $event_id, 'se_event_hide_start_time', true );
+ $display_timezone = (bool) get_post_meta( $event_id, 'se_event_display_timezone', true );
+ $now = SE_Calendar::get_instance()->create_date_time( 'now' )->format( 'U' );
+
+ if ( ! $event_dates ) {
+ return '';
+ }
+
+ // Iterate over all the events and remove any where the start and end has passed.
+ foreach ( $event_dates as $key => $date ) {
+ if ( $date['datetime_start'] < $now && $date['datetime_end'] < $now ) {
+ unset( $event_dates[ $key ] );
+ }
+ }
+ if ( empty( $event_dates ) ) {
+ return '';
+ }
+
+ return se_event_format_dates(
+ $event_dates,
+ $event_timezone,
+ $hide_end_time,
+ $hide_start_time,
+ $display_timezone,
+ false,
+ false
+ );
+}
+/**
+ * Get the event dates in a formatted string.
+ *
+ * @param integer $event_id Event id.
+ * @param boolean $date_only Whether to return only the date.
+ * @param boolean $time_only Whether to return only the time.
+ * @param array $event_dates Event dates.
+ *
+ * @return string
+ */
+function se_event_get_formatted_dates( $event_id, $date_only = false, $time_only = false, $event_dates = null ) {
+
+ // Get required post meta.
+ $event_dates = se_event_get_dates( $event_id, $event_dates );
+ $event_timezone = get_post_meta( $event_id, 'se_event_timezone', true );
+ $hide_end_time = get_post_meta( $event_id, 'se_event_hide_end_time', true );
+ $hide_start_time = get_post_meta( $event_id, 'se_event_hide_start_time', true );
+ $display_timezone = (bool) get_post_meta( $event_id, 'se_event_display_timezone', true );
+
+ if ( ! $event_dates ) {
+ return '';
+ }
+
+ return se_event_format_dates(
+ $event_dates,
+ $event_timezone,
+ $hide_end_time,
+ $hide_start_time,
+ $display_timezone,
+ $date_only,
+ $time_only
+ );
+}
+
+/**
+ * Formats the dates for the event.
+ *
+ * @param array $event_dates Event dates.
+ * @param string $timezone Timezone.
+ * @param mixed $hide_end_time If we should hide the end time.
+ * @param mixed $hide_start_time If we should hide the start time.
+ * @param mixed $display_timezone If we should display the timezone.
+ * @param mixed $date_only If we should only show the date.
+ * @param mixed $time_only If we should only show the time.
+ *
+ * @return string
+ */
+function se_event_format_dates( $event_dates, $timezone, $hide_end_time, $hide_start_time, $display_timezone, $date_only, $time_only ) {
+ $dates_count = is_array( $event_dates ) ? count( $event_dates ) : 1;
+
+ if ( ! empty( $event_timezone ) ) {
+ $timezone = new DateTimeZone( $event_timezone );
+ } else {
+ $timezone = wp_timezone();
+ }
+
+ $timezone_date = new DateTime( '', $timezone );
+ $timezone_abbr = $timezone_date->format( 'T' );
+
+ // Begin output as a list if the count is more than 1.
+ $dates_output = ( $dates_count > 1 ) ? '