From d6804f780abfae7ba7b25e1a1f368f9d405b6a1d Mon Sep 17 00:00:00 2001
From: Scott Kingsley Clark
Date: Tue, 13 Dec 2022 09:04:38 -0600
Subject: [PATCH 01/11] Update version
---
init.php | 4 ++--
package.json | 2 +-
readme.txt | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/init.php b/init.php
index 2a81e254df..ffc7c88824 100644
--- a/init.php
+++ b/init.php
@@ -10,7 +10,7 @@
* Plugin Name: Pods - Custom Content Types and Fields
* Plugin URI: https://pods.io/
* Description: Pods is a framework for creating, managing, and deploying customized content types and fields
- * Version: 2.9.10
+ * Version: 2.9.11-a-1
* Author: Pods Framework Team
* Author URI: https://pods.io/about/
* Text Domain: pods
@@ -43,7 +43,7 @@
add_action( 'init', 'pods_deactivate_pods_ui' );
} else {
// Current version.
- define( 'PODS_VERSION', '2.9.10' );
+ define( 'PODS_VERSION', '2.9.11-a-1' );
// Current database version, this is the last version the database changed.
define( 'PODS_DB_VERSION', '2.3.5' );
diff --git a/package.json b/package.json
index e50ad421d5..85fa3fe741 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pods",
- "version": "2.9.10",
+ "version": "2.9.11-a-1",
"description": "Pods is a development framework for creating, extending, managing, and deploying customized content types in WordPress.",
"author": "Pods Foundation, Inc",
"homepage": "https://pods.io/",
diff --git a/readme.txt b/readme.txt
index 72ebd24838..a94420a811 100644
--- a/readme.txt
+++ b/readme.txt
@@ -5,7 +5,7 @@ Tags: pods, custom post types, custom taxonomies, content types, custom fields,
Requires at least: 5.7
Tested up to: 6.1
Requires PHP: 5.6
-Stable tag: 2.9.10
+Stable tag: 2.9.11-a-1
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
From c47565016a0c49abd4732f34e0da6c99b80a2a60 Mon Sep 17 00:00:00 2001
From: Scott Kingsley Clark
Date: Wed, 14 Dec 2022 10:27:13 -0600
Subject: [PATCH 02/11] Remove pods_debug() output from Repair tool
---
src/Pods/Tools/Base.php | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/Pods/Tools/Base.php b/src/Pods/Tools/Base.php
index acbb84fb4c..2b7017d1a6 100644
--- a/src/Pods/Tools/Base.php
+++ b/src/Pods/Tools/Base.php
@@ -83,7 +83,6 @@ protected function get_message_html( $tool_heading, array $results, $mode = null
}
if ( empty( $result_set ) ) {
- pods_debug( $heading );
$result_set[] = __( 'No actions were needed.', 'pods' );
}
From 46fe4a36fc5ba80ebe0295cb23085725caf4389b Mon Sep 17 00:00:00 2001
From: Scott Kingsley Clark
Date: Thu, 15 Dec 2022 10:50:39 -0600
Subject: [PATCH 03/11] Update namespace var checks to check if not empty
---
classes/PodsInit.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/classes/PodsInit.php b/classes/PodsInit.php
index e40b87aab3..fa3a5bf299 100644
--- a/classes/PodsInit.php
+++ b/classes/PodsInit.php
@@ -1564,7 +1564,7 @@ public function setup_content_types( $force = false ) {
$rest_namespace = pods_v( 'rest_namespace', $post_type );
// Get the namespace and sanitize/clean up the path.
- if ( $rest_namespace ) {
+ if ( ! empty( $rest_namespace ) ) {
$rest_namespace = str_replace( '\\', '/', $rest_namespace );
$rest_namespace = explode( '/', $rest_namespace );
$rest_namespace = array_map( 'sanitize_title', $rest_namespace );
@@ -1578,7 +1578,7 @@ public function setup_content_types( $force = false ) {
$pods_post_types[ $post_type_name ]['rest_base'] = $rest_base;
$pods_post_types[ $post_type_name ]['rest_controller_class'] = 'WP_REST_Posts_Controller';
- if ( $rest_namespace ) {
+ if ( ! empty( $rest_namespace ) ) {
$pods_post_types[ $post_type_name ]['rest_namespace'] = $rest_namespace;
}
}
@@ -1771,7 +1771,7 @@ public function setup_content_types( $force = false ) {
$rest_namespace = pods_v( 'rest_namespace', $taxonomy );
// Get the namespace and sanitize/clean up the path.
- if ( $rest_namespace ) {
+ if ( ! empty( $rest_namespace ) ) {
$rest_namespace = str_replace( '\\', '/', $rest_namespace );
$rest_namespace = explode( '/', $rest_namespace );
$rest_namespace = array_map( 'sanitize_title', $rest_namespace );
@@ -1785,7 +1785,7 @@ public function setup_content_types( $force = false ) {
$pods_taxonomies[ $taxonomy_name ]['rest_base'] = $rest_base;
$pods_taxonomies[ $taxonomy_name ]['rest_controller_class'] = 'WP_REST_Terms_Controller';
- if ( $rest_namespace ) {
+ if ( ! empty( $rest_namespace ) ) {
$pods_taxonomies[ $taxonomy_name ]['rest_namespace'] = $rest_namespace;
}
}
From aece2d1119b49aeaf14d3cb4edc2d1aa799ed94e Mon Sep 17 00:00:00 2001
From: Scott Kingsley Clark
Date: Thu, 15 Dec 2022 10:58:09 -0600
Subject: [PATCH 04/11] Update Tribe Common to 5.0.5
---
.../Editor/Full_Site/Template_Utils.php | 88 ++
.../src/Tribe/Admin/Activation_Page.php | 68 +-
.../Conditional_Content/Black_Friday.php | 84 ++
.../Datetime_Conditional_Abstract.php | 150 +++
.../Conditional_Content/End_Of_Year_Sale.php | 85 ++
.../Conditional_Content/Service_Provider.php | 49 +
tribe-common/src/Tribe/Admin/Help_Page.php | 281 +++++-
tribe-common/src/Tribe/Admin/Helpers.php | 13 +-
.../src/Tribe/Admin/Notice/Date_Based.php | 367 ++++++++
.../src/Tribe/Admin/Notice/Marketing.php | 16 +-
.../Admin/Notice/Marketing/Black_Friday.php | 72 ++
.../Notice/Marketing/End_Of_Year_Sale.php | 66 ++
.../Admin/Notice/Marketing/Stellar_Sale.php | 77 ++
.../Tribe/Admin/Notice/Plugin_Download.php | 2 +-
.../Tribe/Admin/Notice/Service_Provider.php | 63 ++
.../src/Tribe/Admin/Notice/WP_Version.php | 4 +-
tribe-common/src/Tribe/Admin/Notices.php | 75 +-
tribe-common/src/Tribe/Admin/Pages.php | 309 +++++++
tribe-common/src/Tribe/Admin/Settings.php | 82 ++
.../src/Tribe/Admin/Troubleshooting.php | 382 ++++++++
.../src/Tribe/Admin/Upsell_Notice/Main.php | 97 ++
.../src/Tribe/Admin/Upsell_Notice/README.md | 65 ++
tribe-common/src/Tribe/Ajax/Dropdown.php | 6 +-
tribe-common/src/Tribe/App_Shop.php | 62 ++
tribe-common/src/Tribe/Assets.php | 116 ++-
tribe-common/src/Tribe/Assets_Pipeline.php | 36 +-
tribe-common/src/Tribe/Cache.php | 114 ++-
tribe-common/src/Tribe/Cache_Listener.php | 322 ++++---
tribe-common/src/Tribe/Context.php | 18 +-
tribe-common/src/Tribe/Context/locations.php | 10 +-
tribe-common/src/Tribe/Cost_Utils.php | 4 +-
tribe-common/src/Tribe/Credits.php | 50 +-
tribe-common/src/Tribe/Customizer.php | 272 +++++-
.../src/Tribe/Customizer/Controls/Heading.php | 25 +-
.../src/Tribe/Customizer/Controls/Number.php | 78 ++
.../src/Tribe/Customizer/Controls/Radio.php | 73 ++
.../Customizer/Controls/Range_Slider.php | 97 ++
.../Tribe/Customizer/Controls/Separator.php | 62 ++
.../src/Tribe/Customizer/Controls/Toggle.php | 86 ++
tribe-common/src/Tribe/Customizer/Section.php | 871 ++++++++++++++++--
tribe-common/src/Tribe/Data.php | 4 -
tribe-common/src/Tribe/Date_Utils.php | 92 +-
tribe-common/src/Tribe/Dialog/View.php | 85 +-
tribe-common/src/Tribe/Editor.php | 205 +++--
tribe-common/src/Tribe/Editor/Assets.php | 156 +---
.../src/Tribe/Editor/Blocks/Abstract.php | 16 +-
.../src/Tribe/Editor/Compatibility.php | 31 +
.../Editor/Compatibility/Classic_Editor.php | 386 ++++++++
.../src/Tribe/Editor/Compatibility/Divi.php | 109 +++
.../src/Tribe/Editor/Configuration.php | 2 +-
tribe-common/src/Tribe/Editor/Provider.php | 4 +-
tribe-common/src/Tribe/Field.php | 96 +-
tribe-common/src/Tribe/JSON_LD/Abstract.php | 9 -
tribe-common/src/Tribe/Log/Admin.php | 17 +-
.../src/Tribe/Log/Service_Provider.php | 8 +-
tribe-common/src/Tribe/Main.php | 141 ++-
.../src/Tribe/Models/Post_Types/Base.php | 233 ++++-
.../src/Tribe/Models/Post_Types/Nothing.php | 2 +-
.../src/Tribe/Onboarding/Hints_Abstract.php | 98 ++
tribe-common/src/Tribe/Onboarding/Main.php | 223 +++++
tribe-common/src/Tribe/Onboarding/README.md | 242 +++++
.../src/Tribe/Onboarding/Tour_Abstract.php | 98 ++
tribe-common/src/Tribe/PUE/Checker.php | 155 +++-
tribe-common/src/Tribe/PUE/Notices.php | 27 +
tribe-common/src/Tribe/Plugin_Meta_Links.php | 4 +-
tribe-common/src/Tribe/Plugins_API.php | 38 +-
tribe-common/src/Tribe/Process/Queue.php | 2 +-
tribe-common/src/Tribe/Promoter/Connector.php | 4 +-
tribe-common/src/Tribe/REST/Main.php | 4 +-
tribe-common/src/Tribe/REST/System.php | 19 +
tribe-common/src/Tribe/Repository.php | 81 +-
.../src/Tribe/Repository/Decorator.php | 16 +
.../src/Tribe/Repository/Interface.php | 27 +
.../src/Tribe/Repository/Query_Filters.php | 2 +-
.../src/Tribe/Service_Providers/Dialog.php | 2 +-
.../Tribe/Service_Providers/Onboarding.php | 147 +++
.../src/Tribe/Service_Providers/Tooltip.php | 15 +-
tribe-common/src/Tribe/Settings.php | 445 ++++-----
tribe-common/src/Tribe/Settings_Manager.php | 88 +-
tribe-common/src/Tribe/Settings_Tab.php | 2 +-
tribe-common/src/Tribe/Support.php | 79 +-
tribe-common/src/Tribe/Template.php | 43 +-
tribe-common/src/Tribe/Template_Factory.php | 219 -----
tribe-common/src/Tribe/Timezones.php | 12 +-
tribe-common/src/Tribe/Tracker.php | 4 +
tribe-common/src/Tribe/Utils/Array.php | 63 +-
tribe-common/src/Tribe/Utils/Body_Classes.php | 2 +-
.../src/Tribe/Utils/Collection_Trait.php | 10 -
tribe-common/src/Tribe/Utils/Color.php | 25 +-
.../src/Tribe/Utils/Compatibility_Classes.php | 352 +++++++
tribe-common/src/Tribe/Utils/Date_I18n.php | 1 +
.../src/Tribe/Utils/Date_I18n_Immutable.php | 1 +
.../src/Tribe/Utils/Element_Classes.php | 6 +-
tribe-common/src/Tribe/Utils/Lazy_Events.php | 2 +-
tribe-common/src/Tribe/Utils/Lazy_String.php | 7 +-
.../src/Tribe/Utils/Post_Thumbnail.php | 4 -
tribe-common/src/Tribe/Utils/Taxonomy.php | 73 ++
.../src/Tribe/Utils/Theme_Compatibility.php | 276 ++++++
tribe-common/src/Tribe/Validate.php | 14 +
.../src/Tribe/Values/Abstract_Currency.php | 369 ++++++++
.../src/Tribe/Values/Abstract_Value.php | 455 +++++++++
.../src/Tribe/Values/Currency_Interface.php | 86 ++
.../src/Tribe/Values/Value_Calculation.php | 71 ++
.../src/Tribe/Values/Value_Formatting.php | 58 ++
.../src/Tribe/Values/Value_Interface.php | 145 +++
.../src/Tribe/Values/Value_Update.php | 58 ++
.../src/Tribe/Widget/Widget_Abstract.php | 17 +-
tribe-common/src/admin-views/app-shop.php | 53 +-
.../src/admin-views/components/icons/dot.php | 23 +
.../src/admin-views/components/loader.php | 44 +
.../src/admin-views/components/switch.php | 57 ++
.../conditional_content/black-friday.php | 35 +
.../conditional_content/end-of-year-sale.php | 35 +
.../src/admin-views/help-calendar.php | 248 +++++
.../src/admin-views/help-community.php | 241 +++++
.../src/admin-views/help-ticketing.php | 244 +++++
tribe-common/src/admin-views/help.php | 107 +++
.../admin-views/notices/end-of-year-sale.php | 30 +
.../admin-views/notices/tribe-bf-general.php | 15 +-
.../notices/tribe-stellar-sale.php | 29 +
.../src/admin-views/notices/upsell/icon.php | 21 +
.../src/admin-views/notices/upsell/main.php | 50 +
.../src/admin-views/tribe-options-display.php | 62 --
.../src/admin-views/tribe-options-general.php | 80 --
.../src/admin-views/tribe-options-network.php | 35 -
.../src/admin-views/troubleshooting.php | 55 ++
.../troubleshooting/common-issues.php | 40 +
.../troubleshooting/detected-issues.php | 54 ++
.../admin-views/troubleshooting/ea-status.php | 52 ++
.../ea-status/current-status.php | 41 +
.../ea-status/current-usage.php | 42 +
.../troubleshooting/ea-status/eventbrite.php | 48 +
.../troubleshooting/ea-status/license-key.php | 54 ++
.../troubleshooting/ea-status/meetup.php | 39 +
.../ea-status/scheduler-status.php | 33 +
.../ea-status/server-connection.php | 55 ++
.../admin-views/troubleshooting/event-log.php | 18 +
.../troubleshooting/first-steps.php | 55 ++
.../troubleshooting/footer-logo.php | 14 +
.../troubleshooting/introduction.php | 31 +
.../admin-views/troubleshooting/notice.php | 20 +
.../recent-template-changes.php | 16 +
.../troubleshooting/support-cta.php | 31 +
.../troubleshooting/system-information.php | 42 +
.../deprecated/Tribe__Template_Factory.php | 32 +
tribe-common/src/functions/conditionals.php | 78 ++
tribe-common/src/functions/editor.php | 20 +
tribe-common/src/functions/files.php | 42 +
.../src/functions/template-tags/date.php | 6 +-
.../src/functions/template-tags/general.php | 85 +-
tribe-common/src/functions/utils.php | 92 +-
tribe-common/src/views/dialog/confirm.php | 4 +-
tribe-common/src/views/dialog/warning.php | 32 +
tribe-common/src/views/tooltip/tooltip.php | 2 +-
.../views/v2/components/icons/arrow-right.php | 2 +-
.../views/v2/components/icons/cal-export.php | 28 +
.../views/v2/components/icons/caret-down.php | 2 +-
.../views/v2/components/icons/caret-left.php | 2 +-
.../views/v2/components/icons/caret-right.php | 2 +-
.../views/v2/components/icons/close-alt.php | 2 +-
.../src/views/v2/components/icons/close.php | 2 +-
.../src/views/v2/components/icons/day.php | 2 +-
.../src/views/v2/components/icons/dot.php | 2 +-
.../src/views/v2/components/icons/error.php | 2 +-
.../views/v2/components/icons/featured.php | 11 +-
.../src/views/v2/components/icons/filter.php | 2 +-
.../src/views/v2/components/icons/hybrid.php | 34 +
.../src/views/v2/components/icons/list.php | 15 +-
.../views/v2/components/icons/location.php | 2 +-
.../src/views/v2/components/icons/mail.php | 2 +-
.../src/views/v2/components/icons/map-pin.php | 2 +-
.../src/views/v2/components/icons/map.php | 2 +-
.../components/icons/messages-not-found.php | 2 +-
.../src/views/v2/components/icons/minus.php | 2 +-
.../src/views/v2/components/icons/month.php | 2 +-
.../src/views/v2/components/icons/no-map.php | 2 +-
.../src/views/v2/components/icons/phone.php | 2 +-
.../src/views/v2/components/icons/photo.php | 2 +-
.../src/views/v2/components/icons/play.php | 2 +-
.../src/views/v2/components/icons/plus.php | 2 +-
.../views/v2/components/icons/recurring.php | 14 +-
.../src/views/v2/components/icons/reset.php | 2 +-
.../src/views/v2/components/icons/search.php | 2 +-
.../v2/components/icons/stellar-icon.php | 27 +
.../src/views/v2/components/icons/summary.php | 27 +
.../src/views/v2/components/icons/video.php | 2 +-
.../src/views/v2/components/icons/virtual.php | 14 +-
.../src/views/v2/components/icons/website.php | 2 +-
.../src/views/v2/components/icons/week.php | 2 +-
tribe-common/tribe-autoload.php | 1 +
tribe-common/vendor/autoload.php | 2 +-
tribe-common/vendor/autoload_52.php | 2 +-
.../vendor/clipboard/clipboard.min.js | 7 -
.../vendor/composer/autoload_classmap.php | 38 +
.../vendor/composer/autoload_psr4.php | 1 +
.../vendor/composer/autoload_real.php | 8 +-
.../vendor/composer/autoload_real_52.php | 6 +-
.../vendor/composer/autoload_static.php | 53 +-
tribe-common/vendor/composer/installed.json | 68 +-
.../php-jwt/src/BeforeValidException.php | 2 +-
.../firebase/php-jwt/src/ExpiredException.php | 2 +-
.../vendor/firebase/php-jwt/src/JWT.php | 526 ++++++++---
.../php-jwt/src/SignatureInvalidException.php | 2 +-
.../vendor/psr/log/Psr/Log/AbstractLogger.php | 32 +-
.../psr/log/Psr/Log/LoggerAwareTrait.php | 2 +-
.../tribe-selectWoo/dist/js/select2.full.js | 16 +-
.../vendor/tribe-selectWoo/dist/js/select2.js | 16 +-
.../tribe-selectWoo/dist/js/selectWoo.full.js | 14 +-
.../dist/js/selectWoo.full.min.js | 2 +-
.../tribe-selectWoo/dist/js/selectWoo.js | 16 +-
210 files changed, 12450 insertions(+), 1716 deletions(-)
create mode 100644 tribe-common/src/Common/Editor/Full_Site/Template_Utils.php
create mode 100644 tribe-common/src/Tribe/Admin/Conditional_Content/Black_Friday.php
create mode 100644 tribe-common/src/Tribe/Admin/Conditional_Content/Datetime_Conditional_Abstract.php
create mode 100644 tribe-common/src/Tribe/Admin/Conditional_Content/End_Of_Year_Sale.php
create mode 100644 tribe-common/src/Tribe/Admin/Conditional_Content/Service_Provider.php
create mode 100644 tribe-common/src/Tribe/Admin/Notice/Date_Based.php
create mode 100644 tribe-common/src/Tribe/Admin/Notice/Marketing/Black_Friday.php
create mode 100644 tribe-common/src/Tribe/Admin/Notice/Marketing/End_Of_Year_Sale.php
create mode 100644 tribe-common/src/Tribe/Admin/Notice/Marketing/Stellar_Sale.php
create mode 100644 tribe-common/src/Tribe/Admin/Notice/Service_Provider.php
create mode 100644 tribe-common/src/Tribe/Admin/Pages.php
create mode 100644 tribe-common/src/Tribe/Admin/Settings.php
create mode 100644 tribe-common/src/Tribe/Admin/Troubleshooting.php
create mode 100644 tribe-common/src/Tribe/Admin/Upsell_Notice/Main.php
create mode 100644 tribe-common/src/Tribe/Admin/Upsell_Notice/README.md
create mode 100644 tribe-common/src/Tribe/Customizer/Controls/Number.php
create mode 100644 tribe-common/src/Tribe/Customizer/Controls/Radio.php
create mode 100644 tribe-common/src/Tribe/Customizer/Controls/Range_Slider.php
create mode 100644 tribe-common/src/Tribe/Customizer/Controls/Separator.php
create mode 100644 tribe-common/src/Tribe/Customizer/Controls/Toggle.php
create mode 100644 tribe-common/src/Tribe/Editor/Compatibility.php
create mode 100644 tribe-common/src/Tribe/Editor/Compatibility/Classic_Editor.php
create mode 100644 tribe-common/src/Tribe/Editor/Compatibility/Divi.php
create mode 100644 tribe-common/src/Tribe/Onboarding/Hints_Abstract.php
create mode 100644 tribe-common/src/Tribe/Onboarding/Main.php
create mode 100644 tribe-common/src/Tribe/Onboarding/README.md
create mode 100644 tribe-common/src/Tribe/Onboarding/Tour_Abstract.php
create mode 100644 tribe-common/src/Tribe/Service_Providers/Onboarding.php
delete mode 100755 tribe-common/src/Tribe/Template_Factory.php
create mode 100644 tribe-common/src/Tribe/Utils/Compatibility_Classes.php
create mode 100644 tribe-common/src/Tribe/Utils/Theme_Compatibility.php
create mode 100644 tribe-common/src/Tribe/Values/Abstract_Currency.php
create mode 100644 tribe-common/src/Tribe/Values/Abstract_Value.php
create mode 100644 tribe-common/src/Tribe/Values/Currency_Interface.php
create mode 100644 tribe-common/src/Tribe/Values/Value_Calculation.php
create mode 100644 tribe-common/src/Tribe/Values/Value_Formatting.php
create mode 100644 tribe-common/src/Tribe/Values/Value_Interface.php
create mode 100644 tribe-common/src/Tribe/Values/Value_Update.php
create mode 100644 tribe-common/src/admin-views/components/icons/dot.php
create mode 100644 tribe-common/src/admin-views/components/loader.php
create mode 100644 tribe-common/src/admin-views/components/switch.php
create mode 100644 tribe-common/src/admin-views/conditional_content/black-friday.php
create mode 100644 tribe-common/src/admin-views/conditional_content/end-of-year-sale.php
create mode 100644 tribe-common/src/admin-views/help-calendar.php
create mode 100644 tribe-common/src/admin-views/help-community.php
create mode 100644 tribe-common/src/admin-views/help-ticketing.php
create mode 100755 tribe-common/src/admin-views/help.php
create mode 100644 tribe-common/src/admin-views/notices/end-of-year-sale.php
create mode 100644 tribe-common/src/admin-views/notices/tribe-stellar-sale.php
create mode 100644 tribe-common/src/admin-views/notices/upsell/icon.php
create mode 100644 tribe-common/src/admin-views/notices/upsell/main.php
delete mode 100755 tribe-common/src/admin-views/tribe-options-display.php
delete mode 100755 tribe-common/src/admin-views/tribe-options-general.php
delete mode 100755 tribe-common/src/admin-views/tribe-options-network.php
create mode 100755 tribe-common/src/admin-views/troubleshooting.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/common-issues.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/detected-issues.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/current-status.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/current-usage.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/eventbrite.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/license-key.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/meetup.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/scheduler-status.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/ea-status/server-connection.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/event-log.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/first-steps.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/footer-logo.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/introduction.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/notice.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/recent-template-changes.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/support-cta.php
create mode 100644 tribe-common/src/admin-views/troubleshooting/system-information.php
create mode 100644 tribe-common/src/deprecated/Tribe__Template_Factory.php
create mode 100644 tribe-common/src/functions/conditionals.php
create mode 100644 tribe-common/src/functions/editor.php
create mode 100644 tribe-common/src/functions/files.php
create mode 100644 tribe-common/src/views/dialog/warning.php
create mode 100644 tribe-common/src/views/v2/components/icons/cal-export.php
create mode 100644 tribe-common/src/views/v2/components/icons/hybrid.php
create mode 100644 tribe-common/src/views/v2/components/icons/stellar-icon.php
create mode 100644 tribe-common/src/views/v2/components/icons/summary.php
delete mode 100644 tribe-common/vendor/clipboard/clipboard.min.js
diff --git a/tribe-common/src/Common/Editor/Full_Site/Template_Utils.php b/tribe-common/src/Common/Editor/Full_Site/Template_Utils.php
new file mode 100644
index 0000000000..d87f398d8e
--- /dev/null
+++ b/tribe-common/src/Common/Editor/Full_Site/Template_Utils.php
@@ -0,0 +1,88 @@
+> $blocks Array of parsed block objects.
+ *
+ * @return array> Block references to the passed blocks and their inner blocks.
+ */
+ public static function flatten_blocks( &$blocks ) {
+ $all_blocks = [];
+ $queue = [];
+
+ foreach ( $blocks as &$block ) {
+ $queue[] = &$block;
+ }
+
+ $queue_count = count( $queue );
+
+ while ( $queue_count > 0 ) {
+ $block = &$queue[0];
+ array_shift( $queue );
+ $all_blocks[] = &$block;
+
+ if ( ! empty( $block['innerBlocks'] ) ) {
+ foreach ( $block['innerBlocks'] as &$inner_block ) {
+ $queue[] = &$inner_block;
+ }
+ }
+
+ $queue_count = count( $queue );
+ }
+
+ return $all_blocks;
+ }
+
+ /**
+ * Parses wp_template content and injects the current theme's stylesheet as a theme attribute into
+ * each wp_template_part.
+ *
+ * @since 4.14.18
+ *
+ * @param string $template_content serialized wp_template content.
+ *
+ * @return string Updated wp_template content.
+ */
+ public static function inject_theme_attribute_in_content( $template_content ) {
+ $has_updated_content = false;
+ $new_content = '';
+ $template_blocks = parse_blocks( $template_content );
+
+ $blocks = static::flatten_blocks( $template_blocks );
+ foreach ( $blocks as &$block ) {
+ if (
+ 'core/template-part' === $block['blockName'] &&
+ ! isset( $block['attrs']['theme'] )
+ ) {
+ $block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
+ $has_updated_content = true;
+ }
+ }
+
+ if ( $has_updated_content ) {
+ foreach ( $template_blocks as &$block ) {
+ $new_content .= serialize_block( $block );
+ }
+
+ return $new_content;
+ }
+
+ return $template_content;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Activation_Page.php b/tribe-common/src/Tribe/Admin/Activation_Page.php
index 9d83bfdb6a..dbe6d44a4a 100644
--- a/tribe-common/src/Tribe/Admin/Activation_Page.php
+++ b/tribe-common/src/Tribe/Admin/Activation_Page.php
@@ -27,6 +27,8 @@ class Tribe__Admin__Activation_Page {
public function __construct( array $args = [] ) {
$this->args = wp_parse_args( $args, [
'slug' => '',
+ 'admin_page' => '',
+ 'admin_url' => '',
'activation_transient' => '',
'version' => '',
'plugin_path' => '',
@@ -69,6 +71,11 @@ public function is_update_page() {
* Listen for opportunities to show update and welcome splash pages.
*/
public function hooks() {
+ // Never show this on the front-end.
+ if ( ! is_admin() ) {
+ return;
+ }
+
if (
tribe_is_truthy( get_option( 'tribe_skip_welcome', false ) )
|| tribe_is_truthy( tribe_get_option( 'skip_welcome', false ) )
@@ -77,7 +84,7 @@ public function hooks() {
}
add_action( 'admin_init', [ $this, 'maybe_redirect' ], 10, 0 );
- add_action( 'admin_menu', [ $this, 'register_page' ], 100, 0 ); // come in after the default page is registered
+ add_action( 'admin_menu', [ $this, 'register_page' ], 100, 0 ); // Come in after the default page is registered.
add_action( 'update_plugin_complete_actions', [ $this, 'update_complete_actions' ], 15, 2 );
add_action( 'update_bulk_plugins_complete_actions', [ $this, 'update_complete_actions' ], 15, 2 );
@@ -123,7 +130,7 @@ public function update_complete_actions( $actions, $plugin ) {
*/
public function maybe_redirect() {
if ( ! empty( $_POST ) ) {
- return; // don't interrupt anything the user's trying to do
+ return; // Don't interrupt anything the user's trying to do.
}
if ( ! is_admin() || defined( 'DOING_AJAX' ) ) {
@@ -131,18 +138,46 @@ public function maybe_redirect() {
}
if ( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) {
- return; // probably the plugin update/install iframe
+ return; // Probably the plugin update/install iframe.
}
if ( isset( $_GET[ $this->welcome_slug ] ) || isset( $_GET[ $this->update_slug ] ) ) {
- return; // no infinite redirects
+ return; // No infinite redirects.
}
if ( isset( $_GET['tribe-skip-welcome'] ) ) {
- return; // a way to skip these checks and
+ return; // A way to skip these checks and.
}
- // bail if we aren't activating a plugin
+ if ( ! $this->showed_update_message_for_current_version() && ! $this->is_new_install() ) {
+ $page = tribe_get_request_var( 'page' );
+ if ( empty( $page ) ) {
+ return;
+ }
+
+ $match_page = str_replace( 'tribe_events_page_', '', $this->args['admin_page'] );
+
+ if ( $page !== $match_page ) {
+ return;
+ }
+
+ /**
+ * Filters whether we should disable the update page redirect.
+ *
+ * @since 5.0.0
+ *
+ * @param $bypass bool
+ */
+ $bypass_update_page = apply_filters( 'tec_admin_update_page_bypass', false, $this );
+
+ if ( $bypass_update_page ) {
+ return;
+ }
+
+ $this->redirect_to_update_page();
+ }
+
+ // Bail if we aren't activating a plugin.
if ( ! get_transient( $this->args['activation_transient'] ) ) {
return;
}
@@ -153,10 +188,6 @@ public function maybe_redirect() {
return;
}
- if ( $this->showed_update_message_for_current_version() ) {
- return;
- }
-
// the redirect might be intercepted by another plugin, but
// we'll go ahead and mark it as viewed right now, just in case
// we end up in a redirect loop
@@ -171,9 +202,11 @@ public function maybe_redirect() {
/**
* Have we shown the welcome/update message for the current version?
*
+ * @since 5.0.0 Turned this method public.
+ *
* @return bool
*/
- protected function showed_update_message_for_current_version() {
+ public function showed_update_message_for_current_version() {
$message_version_displayed = Tribe__Settings_Manager::get_option( 'last-update-message-' . $this->args['slug'] );
if ( empty( $message_version_displayed ) ) {
@@ -240,12 +273,11 @@ protected function redirect_to_update_page() {
*/
protected function get_message_page_url( $slug ) {
$settings = Tribe__Settings::instance();
- // get the base settings page url
- $url = apply_filters(
- 'tribe_settings_url',
- add_query_arg( 'page', $settings->adminSlug, admin_url( 'edit.php' ) )
- );
+
+ $url = ! empty( $this->args['admin_url'] ) ? $this->args['admin_url'] : $settings->get_url();
+
$url = esc_url_raw( add_query_arg( $slug, 1, $url ) );
+
return $url;
}
@@ -263,7 +295,7 @@ public function register_page() {
$this->disable_default_settings_page();
add_filter( 'admin_body_class', [ $this, 'admin_body_class' ] );
- add_action( Tribe__Settings::instance()->admin_page, [ $this, 'display_page' ] );
+ add_action( $this->args['admin_page'], [ $this, 'display_page' ] );
}
/**
@@ -283,7 +315,7 @@ public function admin_body_class( $classes ) {
* in the Events > Settings slot instead, for this request only).
*/
protected function disable_default_settings_page() {
- remove_action( Tribe__Settings::instance()->admin_page, [ Tribe__Settings::instance(), 'generatePage' ] );
+ remove_action( $this->args['admin_page'], [ Tribe__Settings::instance(), 'generatePage' ] );
}
/**
diff --git a/tribe-common/src/Tribe/Admin/Conditional_Content/Black_Friday.php b/tribe-common/src/Tribe/Admin/Conditional_Content/Black_Friday.php
new file mode 100644
index 0000000000..eb729f67d3
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Conditional_Content/Black_Friday.php
@@ -0,0 +1,84 @@
+modify( '-3 days' );
+
+ return $date;
+ }
+
+ /**
+ * Replace the opening markup for the general settings info box.
+ *
+ * @since 4.14.7
+ * @return void
+ */
+ public function add_conditional_content( $fields ) {
+ // Check if the content should currently be displayed.
+ if( ! $this->should_display() ) {
+ return $fields;
+ }
+
+ // Set up template variables.
+ $images_dir = \Tribe__Main::instance()->plugin_url . 'src/resources/images/';
+ $template_args = [
+ 'branding_logo' => $images_dir . 'logo/tec-brand.svg',
+ 'background_image' => $images_dir . 'marketing/bf-promo.png',
+ 'button_link' => 'https://evnt.is/1aqi',
+ ];
+
+ // Get the Black Friday promo content.
+ $content = $this->get_template()->template( 'conditional_content/black-friday', $template_args, false );
+
+ // Replace starting info box markup.
+ $fields['info-start']['html'] .= $content;
+
+ return $fields;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Conditional_Content/Datetime_Conditional_Abstract.php b/tribe-common/src/Tribe/Admin/Conditional_Content/Datetime_Conditional_Abstract.php
new file mode 100644
index 0000000000..374698f362
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Conditional_Content/Datetime_Conditional_Abstract.php
@@ -0,0 +1,150 @@
+start_date, 'UTC' );
+ $date = $date->setTime( $this->start_time, 0 );
+
+ /**
+ * Allow filtering of the start date for testing.
+ *
+ * @since 4.14.7
+ * @param \DateTime $date - Unix timestamp for start date
+ * @param object $this
+ */
+ $date = apply_filters( "tec_admin_conditional_content_{$this->slug}_start_date", $date, $this );
+
+ return $date;
+ }
+
+ /**
+ * Unix datetime for content end.
+ *
+ * @since 4.14.7
+ * @return int - Unix timestamp
+ */
+ protected function get_end_time() {
+ $date = Dates::build_date_object( $this->end_date, 'UTC' );
+ $date = $date->setTime( $this->end_time, 0 );
+
+ /**
+ * Allow filtering of the end date for testing.
+ *
+ * @since 4.14.7
+ * @param \DateTime $date - Unix timestamp for end date
+ * @param object $this
+ */
+ $date = apply_filters( "tec_admin_conditional_content_{$this->slug}_end_date", $date, $this );
+
+ return $date;
+ }
+
+ /**
+ * Whether the content should display.
+ *
+ * @since 4.14.7
+ * @return boolean - Whether the content should display
+ */
+ protected function should_display() {
+ $now = Dates::build_date_object( 'now', 'UTC' );
+ $notice_start = $this->get_start_time();
+ $notice_end = $this->get_end_time();
+ $display = $notice_start <= $now && $now < $notice_end;
+
+ /**
+ * Allow filtering whether the content should display.
+ *
+ * @since 4.14.7
+ * @param bool $should_display - whether the content should display
+ * @param object $this - the conditional content object
+ */
+ $should_display = apply_filters( "tec_admin_conditional_content_{$this->slug}_should_display", $display, $this );
+
+ return $should_display;
+ }
+
+ /**
+ * Gets the template instance used to setup the rendering of the page.
+ *
+ * @since 4.14.7
+ *
+ * @return \Tribe__Template
+ */
+ public function get_template() {
+ if ( empty( $this->template ) ) {
+ $this->template = new \Tribe__Template();
+ $this->template->set_template_origin( \Tribe__Main::instance() );
+ $this->template->set_template_folder( 'src/admin-views' );
+ $this->template->set_template_context_extract( true );
+ $this->template->set_template_folder_lookup( false );
+ }
+
+ return $this->template;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Conditional_Content/End_Of_Year_Sale.php b/tribe-common/src/Tribe/Admin/Conditional_Content/End_Of_Year_Sale.php
new file mode 100644
index 0000000000..f2a51e14d4
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Conditional_Content/End_Of_Year_Sale.php
@@ -0,0 +1,85 @@
+should_display() ) {
+ return $fields;
+ }
+
+ // Set up template variables.
+ $images_dir = \Tribe__Main::instance()->plugin_url . 'src/resources/images/';
+ $template_args = [
+ 'branding_logo' => $images_dir . 'logo/tec-brand.svg',
+ 'background_image' => $images_dir . 'marketing/eoy-sale-promo.png',
+ 'button_link' => 'https://evnt.is/1a-x',
+ ];
+
+ // Get the Black Friday promo content.
+ $content = $this->get_template()->template( 'conditional_content/end-of-year-sale', $template_args, false );
+
+ // Replace starting info box markup.
+ $fields['info-start']['html'] .= $content;
+
+ return $fields;
+ }
+
+ /**
+ * Unix time for notice end.
+ *
+ * @since 4.14.9
+ *
+ * @return int $end_time The date & time the notice should stop displaying, as a Unix timestamp.
+ */
+ public function get_end_time() {
+ $date = parent::get_end_time();
+ $date = $date->setTime( 23, 59 );
+
+ return $date;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Conditional_Content/Service_Provider.php b/tribe-common/src/Tribe/Admin/Conditional_Content/Service_Provider.php
new file mode 100644
index 0000000000..64506e1968
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Conditional_Content/Service_Provider.php
@@ -0,0 +1,49 @@
+container->singleton( Black_Friday::class, Black_Friday::class, [ 'hook' ] );
+ // EOY Sale disabled for 2022
+ // $this->container->singleton( End_Of_Year_Sale::class, End_Of_Year_Sale::class, [ 'hook' ] );
+ $this->hooks();
+ }
+
+ /**
+ * Set up hooks for classes.
+ *
+ * @since 4.14.7
+ */
+ protected function hooks() {
+ add_action( 'tribe_plugins_loaded', [ $this, 'plugins_loaded' ] );
+ }
+
+ /**
+ * Setup for things that require plugins loaded first.
+ *
+ * @since 4.14.7
+ */
+ public function plugins_loaded() {
+ $this->container->make( Black_Friday::class );
+ // EOY Sale disabled for 2022
+ // $this->container->make( End_Of_Year_Sale::class );
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Help_Page.php b/tribe-common/src/Tribe/Admin/Help_Page.php
index b66b278c01..b0c2501709 100644
--- a/tribe-common/src/Tribe/Admin/Help_Page.php
+++ b/tribe-common/src/Tribe/Admin/Help_Page.php
@@ -21,6 +21,33 @@ public static function instance() {
return tribe( static::class );
}
+ /**
+ * Set up hooks.
+ *
+ * @since 4.15.0
+ */
+ public function hook() {
+ add_filter( 'admin_body_class', [ $this, 'admin_body_class' ] );
+ }
+
+ /**
+ * Hooked to admin_body_class to add a class for help page.
+ *
+ * @since 4.15.0
+ *
+ * @param string $classes A space separated string of classes to be added to body.
+ *
+ * @return string $classes A space separated string of classes to be added to body.
+ */
+ public function admin_body_class( $classes ) {
+ if ( ! $this->is_current_page() ) {
+ return $classes;
+ }
+
+ $classes .= ' tribe-help tec-help';
+ return $classes;
+ }
+
/**
* Checks if the current page is the Help one
*
@@ -29,7 +56,14 @@ public static function instance() {
* @return bool
*/
public function is_current_page() {
- return Tribe__Admin__Helpers::instance()->is_screen( 'tribe_events_page_tribe-help' ) || Tribe__Admin__Helpers::instance()->is_screen( 'settings_page_tribe-common-help-network' );
+ global $current_screen;
+
+ $help_pages = [
+ 'tribe_events_page_tec-events-help',
+ 'tickets_page_tec-tickets-help',
+ ];
+
+ return in_array( $current_screen->id, $help_pages );
}
/**
@@ -52,10 +86,13 @@ public function register_assets() {
'localize' => [
'name' => 'tribe_system_info',
'data' => [
- 'sysinfo_optin_nonce' => wp_create_nonce( 'sysinfo_optin_nonce' ),
- 'clipboard_btn_text' => __( 'Copy to clipboard', 'tribe-common' ),
- 'clipboard_copied_text' => __( 'System info copied', 'tribe-common' ),
- 'clipboard_fail_text' => __( 'Press "Cmd + C" to copy', 'tribe-common' ),
+ 'sysinfo_optin_nonce' => wp_create_nonce( 'sysinfo_optin_nonce' ),
+ 'clipboard_btn_text' => _x( 'Copy to clipboard', 'Copy to clipboard button text.', 'tribe-common' ),
+ 'clipboard_copied_text' => _x( 'System info copied', 'Copy to clipboard success message', 'tribe-common' ),
+ 'clipboard_fail_text' => _x( 'Press "Cmd + C" to copy', 'Copy to clipboard instructions', 'tribe-common' ),
+ 'sysinfo_error_message_text' => _x( 'Something has gone wrong!', 'Default error message for system info optin', 'tribe-common' ),
+ 'sysinfo_error_code_text' => _x( 'Code:', 'Error code label for system info optin', 'tribe-common'),
+ 'sysinfo_error_status_text' => _x( 'Status:', 'Error status label for system info optin', 'tribe-common'),
],
],
]
@@ -913,4 +950,238 @@ public function print_plugin_box( $plugin ) {
__( 'Can I have more than one calendar?', 'tribe-common' ),
+ 'answer' => __( 'No, but you can use event categories or tags to display certain events like having...', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arh',
+ ],
+ [
+ 'question' => __( 'What do I get with Events Calendar Pro?', 'tribe-common' ),
+ 'answer' => __( 'Events Calendar Pro runs alongside The Events Calendar and enhances...' ),
+ 'link' => 'https://evnt.is/1arj',
+ ],
+ [
+ 'question' => __( 'How do I sell tickets to events?', 'tribe-common' ),
+ 'answer' => __( 'Use our free Event Tickets plugin to get started with tickets and RSVPs.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1ark',
+ ],
+ [
+ 'question' => __( 'Where can I find a list of available shortcodes?', 'tribe-common' ),
+ 'answer' => __( 'Our plugins include many shortcodes that do everything from embedding the calendar...', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arl',
+ ],
+ ] );
+
+ return $faqs;
+ }
+
+ /**
+ * Defines calendar extensions and displays them in the UI.
+ *
+ * @since 4.14.2
+ *
+ * @return array of extensions which are displayed on the calendar and community tab of the in-app help page.
+ */
+ public function get_calendar_extensions() {
+ $extensions = apply_filters( 'tec_help_calendar_extensions', [
+ [
+ 'title' => __( 'Calendar widget areas', 'tribe-common' ),
+ 'description' => __( 'This extension creates a useful variety of WordPress widget areas (a.k.a. sidebars).', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arc',
+ 'product-slug' => 'the-events-calendar',
+ ],
+ [
+ 'title' => __( 'Event block patterns', 'tribe-common' ),
+ 'description' => __( 'This extension adds a set of block patterns for events to the WordPress block editor.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1ard',
+ 'product-slug' => 'the-events-calendar',
+ ],
+ [
+ 'title' => __( 'Alternative photo view', 'tribe-common' ),
+ 'description' => __( 'This extension replaces photo view with a tiled grid of cards featuring event images.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1are',
+ 'product-slug' => 'events-calendar-pro',
+ ],
+ [
+ 'title' => __( 'The Events Calendar Tweaks', 'tribe-common' ),
+ 'description' => __( 'This extension is a collection of tweaks and snippets for The Events Calendar.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arg',
+ 'product-slug' => 'the-events-calendar',
+ ],
+ ] );
+
+ return $extensions;
+ }
+
+ /**
+ * Defines calendar products.
+ *
+ * @since 4.14.2
+ *
+ * @return array of products which are displayed on the calendar tab of the in-app help page.
+ */
+ public function get_calendar_products() {
+ $calendar_products = apply_filters( 'tec_help_calendar_products', [
+ 'events-calendar-pro',
+ 'tribe-filterbar',
+ 'event-aggregator',
+ 'events-virtual',
+ ] );
+
+ return $calendar_products;
+ }
+
+ /**
+ * Defines ticketing frequently asked questions and displays them in the UI.
+ *
+ * @since 4.14.2
+ *
+ * @return array of FAQs which are displayed on the ticketing tab of the in-app help page.
+ */
+ public function get_ticketing_faqs() {
+ $faqs = apply_filters( 'tec_help_ticketing_faqs', [
+ [
+ 'question' => __( 'How Do I create events with Tickets or RSVP’s?', 'tribe-common' ),
+ 'answer' => __( 'We’ve put together a video tutorial showing how to create events with Tickets using our plugins. Click on the link in the link in the title to learn more.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1art',
+ ],
+ [
+ 'question' => __( 'How Do I Set Up E-Commerce Plugins for Selling Tickets?', 'tribe-common' ),
+ 'answer' => __( 'You can sell tickets using our built-in e-commerce option, or upgrade to Event Tickets Plus to use ecommerce plugins such as WooCommerce.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arq',
+ ],
+ [
+ 'question' => __( 'Can I have a seating chart associated with my tickets?', 'tribe-common' ),
+ 'answer' => __( 'Yes! You can easily accomplish this task using the stock options and multiple ticket types available with Event Tickets.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arr',
+ ],
+ [
+ 'question' => __( 'How do I process refunds for tickets?', 'tribe-common' ),
+ 'answer' => __( 'When it comes to paid tickets, these orders can be refunded through the e-commerce platform in use.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1ars',
+ ],
+ ] );
+
+ return $faqs;
+ }
+
+ /**
+ * Defines ticketing extensions and displays them in the UI.
+ *
+ * @since 4.14.2
+ *
+ * @return array of extensions which are displayed on the ticketing tab of the in-app help page.
+ */
+ public function get_ticketing_extensions() {
+ $extensions = apply_filters( 'tec_help_ticketing_extensions', [
+ [
+ 'title' => __( 'Ticket Email Settings', 'tribe-common' ),
+ 'description' => __( 'Adds a new settings panel in Events > Settings that gives more control over the ticket and rsvp emails that are sent to attendees after registration.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arx',
+ 'product-slug' => 'event-tickets',
+ ],
+ [
+ 'title' => __( 'Per Event Check In API', 'tribe-common' ),
+ 'description' => __( 'This extension shows a meta box with an API key on each Event with Ticket/RSVP.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arw',
+ 'product-slug' => 'event-tickets',
+ ],
+ [
+ 'title' => __( 'Add Event & Attendee Info to WooCommerce Order Details', 'tribe-common' ),
+ 'description' => __( 'Displays the information collected by “attendee meta fields” in the WooCommerce order screens as well.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arv',
+ 'product-slug' => 'event-tickets',
+ ],
+ [
+ 'title' => __( 'Organizer Notification Email', 'tribe-common' ),
+ 'description' => __( 'This extension will send an email to event organizers whenever a user registers for their event.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1aru',
+ 'product-slug' => 'event-tickets',
+ ],
+ ] );
+
+ return $extensions;
+ }
+
+ /**
+ * Defines ticketing products.
+ *
+ * @since 4.14.2
+ *
+ * @return array of products which are displayed on the ticketing tab of the in-app help page.
+ */
+ public function get_ticketing_products() {
+ $ticketing_products = apply_filters( 'tec_help_ticketing_products', [
+ 'event-tickets',
+ 'event-tickets-plus',
+ 'tribe-eventbrite',
+ 'promoter',
+ ] );
+
+ return $ticketing_products;
+ }
+
+ /**
+ * Defines community extensions and displays them in the UI.
+ *
+ * @since 4.14.2
+ *
+ * @return array of extensions which are displayed on the community tab of the in-app help page.
+ */
+ public function get_community_extensions() {
+ $extensions = apply_filters( 'tec_help_ticketing_extensions', [
+ [
+ 'title' => __( 'Add Cost Currency Symbol', 'tribe-common' ),
+ 'description' => __( 'This extension allows you to set default currency symbols for your users to choose from instead of having a plain text field.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arn',
+ 'product-slug' => 'community-events',
+ ],
+ [
+ 'title' => __( 'Add Google Maps Display and Link Options', 'tribe-common' ),
+ 'description' => __( 'This extension adds the “Show Google Maps” and “Show Google Maps Link” checkboxes when creating a new Venue.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arm',
+ 'product-slug' => 'community-events',
+ ],
+ [
+ 'title' => __( 'Hide Others’ Organizers and Venues', 'tribe-common' ),
+ 'description' => __( 'This extension allows you to hide the Organizers and Venues that a visitor has not created from the Community Events submission form.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1aro',
+ 'product-slug' => 'community-events',
+ ],
+ [
+ 'title' => __( 'Display Custom HTML', 'tribe-common' ),
+ 'description' => __( 'This extension allows you to add custom HTML content to the top of the Community Events submission form.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1arp',
+ 'product-slug' => 'community-events',
+ ],
+ ] );
+
+ return $extensions;
+ }
+
+ /**
+ * Defines community products.
+ *
+ * @since 4.14.2
+ *
+ * @return array of products which are displayed on the community tab of the in-app help page.
+ */
+ public function get_community_products() {
+ $community_products = apply_filters( 'tec_help_ticketing_products', [
+ 'events-community',
+ 'events-community-tickets',
+ ] );
+
+ return $community_products;
+ }
}
diff --git a/tribe-common/src/Tribe/Admin/Helpers.php b/tribe-common/src/Tribe/Admin/Helpers.php
index 21fd6817e5..7f1d832b29 100644
--- a/tribe-common/src/Tribe/Admin/Helpers.php
+++ b/tribe-common/src/Tribe/Admin/Helpers.php
@@ -119,27 +119,32 @@ public function is_screen( $id = null ) {
return false;
}
- // Avoid Notices by checking the object type of WP_Screen
+ // Avoid Notices by checking the object type of WP_Screen.
if ( ! $this->is_wp_screen() ) {
return false;
}
- // Match any screen from Tribe
+ // Match any screen from Tribe.
if ( is_null( $id ) && false !== strpos( $current_screen->id, 'tribe' ) ) {
return true;
}
+ // Match any screen from TEC.
+ if ( is_null( $id ) && false !== strpos( $current_screen->id, 'tec' ) ) {
+ return true;
+ }
+
// Match any of the pages set
if ( ! is_scalar( $id ) && in_array( $current_screen->id, (array) $id ) ) {
return true;
}
- // Match a specific page
+ // Match a specific page.
if ( $current_screen->id === $id ) {
return true;
}
- // Match any post type page in the supported post types
+ // Match any post type page in the supported post types.
$defaults = apply_filters( 'tribe_is_post_type_screen_post_types', Tribe__Main::get_post_types() );
if ( in_array( $current_screen->post_type, $defaults ) ) {
return true;
diff --git a/tribe-common/src/Tribe/Admin/Notice/Date_Based.php b/tribe-common/src/Tribe/Admin/Notice/Date_Based.php
new file mode 100644
index 0000000000..d5b47262bc
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Notice/Date_Based.php
@@ -0,0 +1,367 @@
+tec_is_active = $tribe_dependency->is_plugin_active( 'Tribe__Events__Main' );
+ $this->et_is_active = $tribe_dependency->is_plugin_active( 'Tribe__Tickets__Main' );
+
+ $now = Dates::build_date_object( 'now', 'UTC' );
+ $notice_start = $this->get_start_time();
+ $notice_end = $this->get_end_time();
+ $extension_date = $this->get_extension_time();
+
+ // If we have an extension date defined.
+ if ( ! empty( $this->get_extension_time() ) ) {
+ // If the sale has started and
+ if (
+ $notice_start <= $now
+ && $notice_end < $now
+ && $now < $extension_date
+ ) {
+ add_filter( "tribe_{$this->slug}_notice_end_date", function() {
+ return $this->get_extension_time();
+ });
+ }
+ }
+
+ $this->hook();
+ }
+
+ /**
+ * Register the various Marketing notices.
+ *
+ * @since 4.14.2
+ */
+ public function hook() {
+ $this->hook_notice();
+ }
+
+ /**
+ * Register the notice.
+ *
+ * @since 4.14.2
+ */
+ public function hook_notice() {
+ tribe_notice(
+ $this->slug,
+ [ $this, "display_notice" ],
+ [
+ 'type' => 'tribe-banner',
+ 'dismiss' => 1,
+ 'priority' => -1,
+ 'wrap' => false,
+ ],
+ [ $this, "should_display" ]
+ );
+ }
+
+ /**
+ * HTML for the notice.
+ *
+ * @since 4.14.2
+ *
+ * @return string The HTML string to be displayed.
+ */
+ abstract function display_notice();
+
+ /**
+ * Function to get and filter the screens the notice is displayed on.
+ *
+ * @since 4.15.4
+ *
+ * @return array List of allowed screens.
+ */
+ public function get_screens() {
+ $screens = $this->screens;
+
+ /**
+ * Allows filtering of the screens for all date-based notices.
+ *
+ * @since 4.15.4
+ *
+ * @param array $screens The current list of allowed screens.
+ * @param string $slug The slug for the current notice.
+ *
+ * @return array $screens The modified list of allowed screens.
+ */
+ $screens = apply_filters(
+ 'tec_date_based_notice_get_screens',
+ $screens,
+ $this->slug
+ );
+
+ /**
+ * Allows filtering of the screens for a specific date-based notice.
+ *
+ * @since 4.15.4
+ *
+ * @param array $screens The current list of allowed screens.
+ *
+ * @return array $screens The modified list of allowed screens.
+ */
+ $screens = apply_filters(
+ "tec_date_based_notice_get_screens_{$this->slug}",
+ $screens
+ );
+
+ return $screens;
+ }
+
+ /**
+ * Whether the notice should display.
+ *
+ * @since 4.14.2
+ *
+ * @return boolean $should_display Whether the notice should display or not.
+ */
+ public function should_display() {
+ // If upsells have been manually hidden, respect that.
+ if ( tec_should_hide_upsell() ) {
+ return false;
+ }
+
+ $current_screen = get_current_screen();
+
+ $screens = $this->get_screens();
+
+ // If not a valid screen, don't display.
+ if ( empty( $current_screen->id ) || ! in_array( $current_screen->id, $screens, true ) ) {
+ return false;
+ }
+
+ $now = Dates::build_date_object( 'now', 'UTC' );
+ $notice_start = $this->get_start_time();
+ $notice_end = $this->get_end_time();
+
+ $should_display = $notice_start <= $now && $now < $notice_end;
+
+
+ /**
+ * Allow filtering of whether the notice should display.
+ *
+ * @since 4.14.2
+ *
+ * @param boolean $should_display Whether the notice should display.
+ * @param Tribe__Admin__Notice_Date_Based $notice The notice object.
+ */
+ return apply_filters( "tribe_{$this->slug}_notice_should_display", $should_display, $this );
+ }
+
+ /**
+ * Unix time for notice start.
+ *
+ * @since 4.14.2
+ *
+ * @return int $start_time The date & time the notice should start displaying, as a Unix timestamp.
+ */
+ public function get_start_time() {
+ $date = Dates::build_date_object( $this->start_date, 'UTC' );
+ $date = $date->setTime( $this->start_time, 0 );
+
+ /**
+ * Allow filtering of the start date DateTime object,
+ * to allow for things like "the day before" ( $date->modify( '-1 day' ) ) and such.
+ *
+ * @since 4.14.2
+ *
+ * @param \DateTime $date Date object for the notice start.
+ */
+ $date = apply_filters( "tribe_{$this->slug}_notice_start_date", $date, $this );
+
+ return $date;
+ }
+
+ /**
+ * Unix time for notice end.
+ *
+ * @since 4.14.2
+ *
+ * @return int $end_time The date & time the notice should stop displaying, or shift to the extension datetime as a Unix timestamp.
+ */
+ public function get_end_time() {
+ $date = Dates::build_date_object( $this->end_date, 'UTC' );
+ $date = $date->setTime( $this->end_time, 0 );
+
+ /**
+ * Allow filtering of the end date DateTime object,
+ * to allow for things like "the day after" ( $date->modify( '+1 day' ) ) and such.
+ *
+ * @since 4.14.2
+ *
+ * @param \DateTime $date Date object for the notice end.
+ */
+ $date = apply_filters( "tribe_{$this->slug}_notice_end_date", $date, $this );
+
+ return $date;
+ }
+
+
+
+ /**
+ * Unix time for notice extension end.
+ *
+ * @since 4.15.4
+ *
+ * @return int $end_time The date & time the notice should stop displaying, as a Unix timestamp.
+ */
+ public function get_extension_time() {
+ $date = Dates::build_date_object( $this->extension_date, 'UTC' );
+ $date = $date->setTime( $this->extension_time, 0 );
+
+ /**
+ * Allow filtering of the extension date DateTime object,
+ * to allow for things like "the day after" ( $date->modify( '+1 day' ) ) and such.
+ *
+ * @since 4.14.2
+ *
+ * @param \DateTime $date Date object for the notice end.
+ */
+ $date = apply_filters( "tribe_{$this->slug}_notice_extension_date", $date, $this );
+
+ return $date;
+ }
+
+ /**
+ * Gets the template instance used to setup the rendering of the page.
+ *
+ * @since 4.14.7
+ *
+ * @return \Tribe__Template
+ */
+ public function get_template() {
+ if ( empty( $this->template ) ) {
+ $this->template = new \Tribe__Template();
+ $this->template->set_template_origin( \Tribe__Main::instance() );
+ $this->template->set_template_folder( 'src/admin-views' );
+ $this->template->set_template_context_extract( true );
+ $this->template->set_template_folder_lookup( false );
+ }
+
+ return $this->template;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Notice/Marketing.php b/tribe-common/src/Tribe/Admin/Notice/Marketing.php
index c4ea865516..add054cb85 100644
--- a/tribe-common/src/Tribe/Admin/Notice/Marketing.php
+++ b/tribe-common/src/Tribe/Admin/Notice/Marketing.php
@@ -1,4 +1,5 @@
plugin_url . 'src/resources/images/icons/sale-burst.svg';
$cta_url = 'https://evnt.is/bf' . date( 'Y' );
+ $screens = [
+ 'tribe_events_page_tribe-common',
+ 'tribe_events_page_tec-events-settings',
+ 'events_page_tribe-common',
+ 'toplevel_page_tribe-common',
+ ];
// If we are on the settings page or a welcome page, change the Black Friday URL.
if (
! empty( $current_screen->id )
- && (
- 'tribe_events_page_tribe-common' === $current_screen->id
- || 'events_page_tribe-common' === $current_screen->id
- || 'toplevel_page_tribe-common' === $current_screen->id
- )
+ && in_array( $current_screen->id, $screens )
) {
if ( isset( $_GET['welcome-message-the-events-calendar'] ) || isset( $_GET['welcome-message-event-tickets' ] ) ) {
$cta_url .= 'welcome';
diff --git a/tribe-common/src/Tribe/Admin/Notice/Marketing/Black_Friday.php b/tribe-common/src/Tribe/Admin/Notice/Marketing/Black_Friday.php
new file mode 100644
index 0000000000..15100faef5
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Notice/Marketing/Black_Friday.php
@@ -0,0 +1,72 @@
+enqueue( [ 'tribe-common-admin' ] );
+
+ // Set up template variables.
+ $template_args = [
+ 'icon_url' => \Tribe__Main::instance()->plugin_url . 'src/resources/images/icons/sale-burst.svg',
+ 'cta_url' => 'https://evnt.is/1aqi',
+ 'end_date' => $this->get_end_time()->format_i18n( 'F jS' ),
+ ];
+
+ // Get the Black Friday notice content.
+ $content = $this->get_template()->template( 'notices/tribe-bf-general', $template_args, false );
+
+ return $content;
+ }
+
+ /**
+ * Unix time for notice start.
+ *
+ * @since 4.14.2
+ *
+ * @return int $end_time The date & time the notice should start displaying, as a Unix timestamp.
+ */
+ public function get_start_time() {
+ $date = parent::get_start_time();
+ $date = $date->modify( '-3 days' );
+
+ return $date;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Notice/Marketing/End_Of_Year_Sale.php b/tribe-common/src/Tribe/Admin/Notice/Marketing/End_Of_Year_Sale.php
new file mode 100644
index 0000000000..d2a0ff521a
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Notice/Marketing/End_Of_Year_Sale.php
@@ -0,0 +1,66 @@
+ \Tribe__Main::instance()->plugin_url . 'src/resources/images/marketing/eoy-sale-2021.svg',
+ 'cta_url' => 'https://evnt.is/1a-x',
+ ];
+
+ // Get the sale notice content.
+ $content = $this->get_template()->template( 'notices/end-of-year-sale', $template_args, false );
+
+ return $content;
+ }
+
+ /**
+ * Unix time for notice end.
+ *
+ * @since 4.14.9
+ *
+ * @return int $end_time The date & time the notice should stop displaying, as a Unix timestamp.
+ */
+ public function get_end_time() {
+ $date = parent::get_end_time();
+ $date = $date->setTime( 23, 59 );
+
+ return $date;
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Notice/Marketing/Stellar_Sale.php b/tribe-common/src/Tribe/Admin/Notice/Marketing/Stellar_Sale.php
new file mode 100644
index 0000000000..484a417e2c
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Notice/Marketing/Stellar_Sale.php
@@ -0,0 +1,77 @@
+enqueue( [ 'tribe-common-admin' ] );
+
+ // Used in the template.
+ $cta_url = 'https://evnt.is/1aqi';
+ $icon_url = \Tribe__Main::instance()->plugin_url . 'src/resources/images/marketing/circles.svg';
+ $icon_classes = [ 'tribe-common-c-svgicon--circles' ];
+ $end_date = $this->get_end_time();
+
+ ob_start();
+
+ include \Tribe__Main::instance()->plugin_path . 'src/admin-views/notices/tribe-stellar-sale.php';
+
+ return ob_get_clean();
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Notice/Plugin_Download.php b/tribe-common/src/Tribe/Admin/Notice/Plugin_Download.php
index c17e6c37c1..8deb0a0108 100644
--- a/tribe-common/src/Tribe/Admin/Notice/Plugin_Download.php
+++ b/tribe-common/src/Tribe/Admin/Notice/Plugin_Download.php
@@ -119,7 +119,7 @@ public function show_inactive_plugins_alert() {
$plugin_names_clean_text = wp_kses( $this->implode_with_grammar( $plugin_name ), $allowed_html );
$req_plugin_names_clean_text = wp_kses( $this->implode_with_grammar( $req_plugins ), $allowed_html );
- $notice_html_content = '
' . esc_html__( 'To begin using %2$s, please install and activate %3$s.', 'tribe-common' ) . '
';
+ $notice_html_content = '
' . esc_html__( 'To begin using %2$s, please install (or upgrade) and activate %3$s.', 'tribe-common' ) . '
';
$read_more_link = '' . esc_html__( 'Read more', 'tribe-common' ) . '.';
$pue_notice_text = esc_html__( 'There’s a new version of %1$s available, but your license is expired. You’ll need to renew your license to get access to the latest version. If you plan to continue using your current version of the plugin(s), be sure to use a compatible version of The Events Calendar. %2$s', 'tribe-common' );
diff --git a/tribe-common/src/Tribe/Admin/Notice/Service_Provider.php b/tribe-common/src/Tribe/Admin/Notice/Service_Provider.php
new file mode 100644
index 0000000000..84f5d88af8
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Notice/Service_Provider.php
@@ -0,0 +1,63 @@
+hooks();
+ }
+
+ /**
+ * Set up hooks for classes.
+ *
+ * @since 4.14.2
+ */
+ private function hooks() {
+ add_action( 'tribe_plugins_loaded', [ $this, 'plugins_loaded'] );
+ }
+
+ /**
+ * Setup for things that require plugins loaded first.
+ *
+ * @since 4.14.2
+ */
+ public function plugins_loaded() {
+ tribe( 'pue.notices' );
+ tribe( 'admin.notice.php.version' );
+ tribe( WP_Version::class );
+
+ if ( defined( 'TRIBE_HIDE_MARKETING_NOTICES' ) ) {
+ return;
+ }
+
+ tribe( Marketing\Stellar_Sale::class );
+ tribe( Marketing\Black_Friday::class );
+ // EOY Sale disabled for 2022
+ // tribe( Marketing\End_Of_Year_Sale::class );
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Notice/WP_Version.php b/tribe-common/src/Tribe/Admin/Notice/WP_Version.php
index 5a61a18f71..8c3137957a 100644
--- a/tribe-common/src/Tribe/Admin/Notice/WP_Version.php
+++ b/tribe-common/src/Tribe/Admin/Notice/WP_Version.php
@@ -1,7 +1,6 @@
'tec-admin-notices',
+ ]
);
}
@@ -137,10 +140,10 @@ public function hook() {
}
/**
- * This will allow the user to Dimiss the Notice using JS.
+ * This will allow the user to Dismiss the Notice using JS.
*
* We will dismiss the notice without checking to see if the slug was already
- * registered (via a call to exists()) for the reason that, during a dismiss
+ * registered (via a call to exists()) for the reason that, during dismissal
* ajax request, some valid notices may not have been registered yet.
*
* @since 4.3
@@ -152,9 +155,9 @@ public function maybe_dismiss() {
wp_send_json( false );
}
- $slug = sanitize_title_with_dashes( $_GET[ self::$meta_key ] );
+ $slug = sanitize_key( $_GET[ self::$meta_key ] );
- // Send a JSON answer with the status of dimissal
+ // Send a JSON answer with the status of dismissal
wp_send_json( $this->dismiss( $slug ) );
}
@@ -188,12 +191,33 @@ public function __call( $name, $arguments ) {
$content = $notice->content;
$wrap = isset( $notice->wrap ) ? $notice->wrap : false;
+ if ( is_array( $content ) && isset( $content[0] ) && $content[0] instanceof __PHP_Incomplete_Class ) {
+ // From a class that no longer exists (e.g. the plugin is not active), clean and bail.
+ $this->remove( $slug );
+ $this->remove_transient( $slug );
+
+ return false;
+ }
+
if ( is_callable( $content ) ) {
$content = call_user_func_array( $content, [ $notice ] );
}
- // Return the rendered HTML
- return $this->render( $slug, $content, false, $wrap );
+ if ( empty( $content ) ) {
+ // There is nothing to render, let's avoid the empty notice frame.
+ return false;
+ }
+
+ tribe_asset_enqueue_group( 'tec-admin-notices' );
+
+ // Return the rendered HTML.
+ $html = $this->render( $slug, $content, false, $wrap );
+
+ // Remove the notice and the transient (if any) since it's been rendered.
+ $this->remove( $slug );
+ $this->remove_transient( $slug );
+
+ return $html;
}
return false;
@@ -236,6 +260,10 @@ public function render( $slug, $content = null, $return = true, $wrap = false )
$classes[] = 'is-dismissible';
}
+ if ( $notice->inline ) {
+ $classes[] = 'inline';
+ }
+
// Prevents Empty Notices
if ( empty( $content ) ) {
return false;
@@ -246,6 +274,7 @@ public function render( $slug, $content = null, $return = true, $wrap = false )
}
$html = sprintf( '
%s
', implode( ' ', $classes ), $notice->slug, $content );
+ tribe_asset_enqueue_group( 'tec-admin-notices' );
if ( ! $return ) {
echo $html;
@@ -514,7 +543,7 @@ public function undismiss_for_all( $slug ) {
*/
public function register( $slug, $callback, $arguments = [], $active_callback = null ) {
// Prevent weird stuff here
- $slug = sanitize_title_with_dashes( $slug );
+ $slug = sanitize_key( $slug );
$defaults = [
'callback' => null,
@@ -523,6 +552,7 @@ public function register( $slug, $callback, $arguments = [], $active_callback =
'priority' => 10,
'expire' => false,
'dismiss' => false,
+ 'inline' => false,
'recurring' => false,
'recurring_interval' => null,
'type' => 'error',
@@ -546,9 +576,15 @@ public function register( $slug, $callback, $arguments = [], $active_callback =
// Clean these
$notice->priority = absint( $notice->priority );
$notice->expire = (bool) $notice->expire;
- $notice->dismiss = (bool) $notice->dismiss;
$notice->recurring = (bool) $notice->recurring;
+ if ( ! is_callable( $notice->dismiss ) ) {
+ $notice->dismiss = (bool) $notice->dismiss;
+ }
+ if ( ! is_callable( $notice->inline ) ) {
+ $notice->inline = (bool) $notice->inline;
+ }
+
// Set the Notice on the array of notices
$this->notices[ $slug ] = $notice;
@@ -616,18 +652,29 @@ public function remove( $slug ) {
*
* @param string $slug
*
- * @return array|null
+ * @return object|array|null
*/
public function get( $slug = null ) {
- // Prevent weird stuff here
- $slug = sanitize_title_with_dashes( $slug );
-
if ( is_null( $slug ) ) {
return $this->notices;
}
+ // Prevent weird stuff here
+ $slug = sanitize_key( $slug );
+
if ( ! empty( $this->notices[ $slug ] ) ) {
- return $this->notices[ $slug ];
+ // I want to avoid modifying the registered value.
+ $notice = $this->notices[ $slug ];
+
+ if ( is_callable( $notice->inline ) ) {
+ $notice->inline = call_user_func( $notice->inline, $notice );
+ }
+
+ if ( is_callable( $notice->dismiss ) ) {
+ $notice->dismiss = call_user_func( $notice->dismiss, $notice );
+ }
+
+ return $notice;
}
return null;
diff --git a/tribe-common/src/Tribe/Admin/Pages.php b/tribe-common/src/Tribe/Admin/Pages.php
new file mode 100644
index 0000000000..3a05e4f847
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Pages.php
@@ -0,0 +1,309 @@
+
+ */
+ private $pages = [];
+
+ /**
+ * Get registered pages.
+ *
+ * @since 4.15.0
+ *
+ * @return array $pages {
+ * Array containing the registered pages.
+ *
+ * @type array $page_id {
+ * @type string id Id to reference the page.
+ * @type array title Page title. Used in menus and breadcrumbs.
+ * @type string|null parent Parent ID. Null for new top level page.
+ * @type string path Path for this page, full path in app context; ex /analytics/report
+ * @type string capability Capability needed to access the page.
+ * @type string icon Icon. Dashicons helper class, base64-encoded SVG, or 'none'.
+ * @type int position Menu item position.
+ * @type int order Navigation item order.
+ * @type callable callback The function to be called to output the content for the page.
+ * }
+ * }
+ */
+ public function get_pages() {
+ /**
+ * Filters the list of registered TEC admin pages.
+ *
+ * @since 4.15.0
+ *
+ * @param array $pages {
+ * Array containing the registered pages to be filtered
+ *
+ * @type array $page_id {
+ * @type string id Id to reference the page.
+ * @type array title Page title. Used in menus and breadcrumbs.
+ * @type string|null parent Parent ID. Null for new top level page.
+ * @type string path Path for this page, full path in app context; ex /analytics/report
+ * @type string capability Capability needed to access the page.
+ * @type string icon Icon. Dashicons helper class, base64-encoded SVG, or 'none'.
+ * @type int position Menu item position.
+ * @type int order Navigation item order.
+ * @type callable callback The function to be called to output the content for the page.
+ * }
+ * }
+ */
+ $pages = apply_filters( 'tec_admin_pages', $this->pages );
+
+ return $pages;
+ }
+
+ /**
+ * Adds a page to `tec-admin`.
+ *
+ * @since 4.15.0
+ *
+ * @param array $options {
+ * Array describing the page.
+ *
+ * @type string id Id to reference the page.
+ * @type string title Page title. Used in menus and breadcrumbs.
+ * @type string|null parent Parent ID. Null for new top level page.
+ * @type string path Path for this page, full path in app context; ex /analytics/report
+ * @type string capability Capability needed to access the page.
+ * @type string icon Icon. Dashicons helper class, base64-encoded SVG, or 'none'.
+ * @type int position Menu item position.
+ * @type int order Navigation item order.
+ * @type callable callback The function to be called to output the content for the page.
+ * }
+ *
+ * @return string $page The resulting page's hook_suffix.
+ *
+ */
+ public function register_page( $options = [] ) {
+ $defaults = [
+ 'id' => null,
+ 'parent' => null,
+ 'title' => '',
+ 'capability' => self::get_capability(),
+ 'path' => '',
+ 'icon' => '',
+ 'position' => null,
+ 'callback' => [ __CLASS__, 'render_page' ],
+ ];
+
+ $options = wp_parse_args( $options, $defaults );
+
+ if ( is_null( $options['parent'] ) ) {
+ $page = add_menu_page(
+ $options['title'],
+ $options['title'],
+ $options['capability'],
+ $options['path'],
+ $options['callback'],
+ $options['icon'],
+ $options['position']
+ );
+ } else {
+ $page = add_submenu_page(
+ $options['parent'],
+ $options['title'],
+ $options['title'],
+ $options['capability'],
+ $options['path'],
+ $options['callback']
+ );
+ }
+
+ $this->connect_page( $options );
+
+ return $page;
+ }
+
+ /**
+ * Get the current page.
+ *
+ * @since 4.15.0
+ *
+ * @return string|boolean Current page or false if not registered with this controller.
+ */
+ public function get_current_page() {
+ if ( is_null( $this->current_page ) ) {
+ $this->determine_current_page();
+ }
+
+ return $this->current_page;
+ }
+
+ /**
+ * Determine the current page.
+ *
+ * @since 4.15.0
+ *
+ * @return string|boolean Current page or false if not registered with this controller.
+ */
+ public function determine_current_page() {
+ $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
+
+ if ( is_null( $current_screen ) ) {
+ $this->current_page = tribe_get_request_var( 'page' );
+ return $this->current_page;
+ }
+
+ $this->current_page = $current_screen->id;
+
+ return $this->current_page;
+ }
+
+ /**
+ * Connect an existing page to wp-admin.
+ *
+ * @since 4.15.0
+ *
+ * @param array $options {
+ * Array describing the page.
+ *
+ * @type string id Id to reference the page.
+ * @type string|array title Page title. Used in menus and breadcrumbs.
+ * @type string|null parent Parent ID. Null for new top level page.
+ * @type string path Path for this page. E.g. admin.php?page=wc-settings&tab=checkout
+ * @type string capability Capability needed to access the page.
+ * @type string icon Icon. Dashicons helper class, base64-encoded SVG, or 'none'.
+ * @type int position Menu item position.
+ * }
+ */
+ public function connect_page( $options = [] ) {
+ if ( ! is_array( $options['title'] ) ) {
+ $options['title'] = array( $options['title'] );
+ }
+
+ /**
+ * Filter the options when connecting or registering a page.
+ *
+ * @param array $options {
+ * Array describing the page.
+ *
+ * @type string id Id to reference the page.
+ * @type string|array title Page title. Used in menus and breadcrumbs.
+ * @type string|null parent Parent ID. Null for new top level page.
+ * @type string screen_id The screen ID that represents the connected page. (Not required for registering).
+ * @type string path Path for this page. E.g. admin.php?page=wc-settings&tab=checkout
+ * @type string capability Capability needed to access the page.
+ * @type string icon Icon. Dashicons helper class, base64-encoded SVG, or 'none'.
+ * @type int position Menu item position.
+ * @type boolean js_page If this is a JS-powered page.
+ * }
+ */
+ $options = apply_filters( 'tec_admin_pages_connect_page_options', $options );
+
+ $this->pages[ $options['id'] ] = $options;
+ }
+
+ /**
+ * Get the capability.
+ *
+ * @param string $capability The capability required for a TEC page to be displayed to the user.
+ *
+ * @since 4.15.0
+ *
+ * @return string The capability required for a TEC page to be displayed to the user.
+ */
+ public static function get_capability( $capability = 'manage_options' ) {
+ /**
+ * Filters the default capability for Tribe admin pages.
+ *
+ * @param string $capability The capability required for a TEC page to be displayed to the user.
+ *
+ * @todo: We'll need to deprecate this one in favor of the one below.
+ */
+ $capability = apply_filters( 'tribe_common_event_page_capability', $capability );
+
+ /**
+ * Filters the default capability for TEC admin pages.
+ *
+ * @param string $capability The capability required for a TEC page to be displayed to the user.
+ *
+ * @since 4.15.0
+ */
+ $capability = apply_filters( 'tec_admin_pages_capability', $capability );
+
+ return $capability;
+ }
+
+ /**
+ * Define if is a `tec` admin page (registered).
+ *
+ * @since 4.15.0
+ *
+ * @param string $page_id The ID of the page to check if is a `tec` admin page.
+ *
+ * @return boolean True if is a `tec` admin page, false otherwise.
+ */
+ public function is_tec_page( $page_id = '' ) {
+ return in_array( $page_id, array_keys( $this->pages ), true );
+ }
+
+ /**
+ * Get pages with tabs.
+ * @since 4.15.0
+ *
+ * @param array $pages The list of pages with tabs.
+ * @return array $pages The list of pages with tabs, filtered.
+ */
+ public function get_pages_with_tabs( $pages = [] ) {
+ /**
+ * Filters the pages with tabs.
+ *
+ * @param array $pages Pages with tabs.
+ *
+ * @since 4.15.0
+ */
+ return apply_filters(
+ 'tec_admin_pages_with_tabs',
+ $pages
+ );
+ }
+
+ /**
+ * Check if the current page has tabs.
+ *
+ * @since 4.15.0
+ *
+ * @param string $page The page slug.
+ * @return boolean True if the page has tabs, false otherwise.
+ */
+ public function has_tabs( $page = '' ) {
+ if ( empty( $page ) ) {
+ $page = $this->get_current_page();
+ }
+
+ return in_array( $page, $this->get_pages_with_tabs() );
+ }
+
+ /**
+ * Generic page.
+ *
+ * @since 4.15.0
+ */
+ public static function render_page() {
+ ?>
+
+ should_setup_pages() ) {
+ return;
+ }
+
+ $page_title = esc_html__( 'Troubleshooting', 'tribe-common' );
+ $menu_title = esc_html__( 'Troubleshooting', 'tribe-common' );
+
+ $capability = $this->get_required_capability();
+
+ $where = Tribe__Settings::instance()->get_parent_slug();
+
+ $this->admin_page = add_submenu_page(
+ $where,
+ $page_title,
+ $menu_title,
+ $capability,
+ static::MENU_SLUG,
+ [
+ $this,
+ 'do_menu_page',
+ ]
+ );
+ }
+
+ /**
+ * Gets the required capability for the troubleshooting page.
+ *
+ * @since 4.14.2
+ *
+ * @return string Which capability we required for the troubleshooting page.
+ */
+ public function get_required_capability() {
+ /**
+ * Allows third party filtering of capability required to see the Troubleshooting page.
+ *
+ * @since 4.14.2
+ *
+ * @param string $capability Which capability we are using as the one required for the
+ * troubleshooting page.
+ * @param static $troubleshooting The current instance of the class that handles this page.
+ */
+ $capability = apply_filters( 'tec_troubleshooting_capability', 'install_plugins', $this );
+ return $capability;
+ }
+
+ /**
+ * Hooked to admin_body_class to add a class for troubleshooting page.
+ *
+ * @since 4.15.0
+ *
+ * @param string $classes a space separated string of classes to be added to body.
+ *
+ * @return string $classes a space separated string of classes to be added to body.
+ */
+ public function admin_body_class( $classes ) {
+ if ( ! $this->is_current_page() ) {
+ return $classes;
+ }
+
+ $classes .= ' tec-troubleshooting';
+ return $classes;
+ }
+
+ /**
+ * Adds the troubleshooting menu to the the WP admin bar under events.
+ *
+ * @since 4.14.2
+ *
+ */
+ public function add_toolbar_item() {
+ $capability = $this->get_required_capability();
+
+ if ( ! current_user_can( $capability ) ) {
+ return;
+ }
+
+ global $wp_admin_bar;
+
+ $wp_admin_bar->add_menu( [
+ 'id' => 'tec-troubleshooting',
+ 'title' => esc_html__( 'Troubleshooting', 'tribe-common' ),
+ 'href' => Tribe__Settings::instance()->get_url( [ 'page' => static::MENU_SLUG ] ),
+ 'parent' => 'tribe-events-settings-group',
+ ] );
+ }
+
+ /**
+ * Checks if the current page is the troubleshooting page.
+ *
+ * @since 4.14.2
+ *
+ * @return boolean returns true if the current page is the troubleshooting page.
+ */
+ public function is_current_page() {
+ if ( ! Tribe__Settings::instance()->should_setup_pages() || ! did_action( 'admin_menu' ) ) {
+ return false;
+ }
+
+ if ( is_null( $this->admin_page ) ) {
+ _doing_it_wrong(
+ __FUNCTION__,
+ 'Function was called before it is possible to accurately determine what the current page is.',
+ '4.5.6'
+ );
+ return false;
+ }
+
+ global $current_screen;
+
+ $troubleshooting_pages = [
+ 'tribe_events_page_tec-troubleshooting',
+ 'tickets_page_tec-tickets-troubleshooting',
+ ];
+
+ return in_array( $current_screen->id, $troubleshooting_pages );
+ }
+
+ /**
+ * Renders the Troubleshooting page.
+ *
+ * @since 4.14.2
+ *
+ */
+ public function do_menu_page() {
+ tribe_asset_enqueue( 'tribe-admin-help-page' );
+ $main = Tribe__Main::instance();
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting.php';
+ }
+
+ /**
+ * This method checks if there are any active issues that need to be flagged.
+ *
+ * @since 4.14.2
+ *
+ * @return boolean returns true if there are any active issues.
+ */
+ public function is_any_issue_active() {
+ $issues = $this->get_issues_found();
+ $active_issues = wp_list_pluck( $issues, 'active' );
+ return in_array( true, $active_issues );
+ }
+
+ /**
+ * Checks if any active TEC plugins require an update.
+ *
+ * @since 4.14.2
+ *
+ * @return boolean returns true is any of the plugins requires an update.
+ */
+ public function is_any_tec_plugin_out_of_date() {
+ $current = get_site_transient( 'update_plugins' );
+ $plugins = [];
+ if ( defined( 'TRIBE_EVENTS_FILE' ) ) {
+ $plugins[] = TRIBE_EVENTS_FILE;
+ }
+ if ( defined( 'EVENTS_CALENDAR_PRO_FILE' ) ) {
+ $plugins[] = EVENTS_CALENDAR_PRO_FILE;
+ }
+ if ( defined( 'EVENT_TICKETS_PLUS_FILE' ) ) {
+ $plugins[] = EVENT_TICKETS_PLUS_FILE;
+ }
+ if ( defined( 'EVENTS_VIRTUAL_FILE' ) ) {
+ $plugins[] = EVENTS_VIRTUAL_FILE;
+ }
+ if ( defined( 'EVENT_TICKETS_MAIN_PLUGIN_FILE' ) ) {
+ $plugins[] = EVENT_TICKETS_MAIN_PLUGIN_FILE;
+ }
+ if ( defined( 'TRIBE_EVENTS_FILTERBAR_FILE' ) ) {
+ $plugins[] = TRIBE_EVENTS_FILTERBAR_FILE;
+ }
+ if ( defined( 'EVENTS_COMMUNITY_TICKETS_FILE' ) ) {
+ $plugins[] = EVENTS_COMMUNITY_TICKETS_FILE;
+ }
+ if ( defined( 'EVENTS_COMMUNITY_FILE' ) ) {
+ $plugins[] = EVENTS_COMMUNITY_FILE;
+ }
+ if ( defined( 'EVENTBRITE_PLUGIN_FILE' ) ) {
+ $plugins[] = EVENTBRITE_PLUGIN_FILE;
+ }
+ if ( defined( 'TRIBE_APM_FILE' ) ) {
+ $plugins[] = TRIBE_APM_FILE;
+ }
+ if ( defined( 'IMAGE_WIDGET_PLUS_DIR' ) ) {
+ $plugins[] = IMAGE_WIDGET_PLUS_DIR;
+ }
+ $plugins = array_map( static function( $file ) {
+ $file = \str_replace( WP_PLUGIN_DIR . '/', '', $file );
+ return $file;
+ }, $plugins );
+
+ foreach ( $plugins as $file ) {
+ if ( ! isset( $current->response[ $file ] ) ) {
+ continue;
+ }
+ $response = $current->response[ $file ];
+ if ( ! empty( $response->new_version ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if any of the issues defined are active.
+ *
+ * @since 4.14.2
+ *
+ * @param string $slug the slug of active issue.
+ *
+ * @return boolean returns a boolean value for each individual issue depending on whether it is active or not.
+ */
+ public function is_active_issue( $slug ) {
+ if ( 'timezone' === $slug ) {
+ return Timezones::is_utc_offset( Timezones::wp_timezone_string() );
+ }
+ if ( 'geolocation' === $slug && class_exists( 'Tribe__Events__Google__Maps_API_Key' ) ) {
+ $key = \tribe_get_option( 'google_maps_js_api_key', false );
+ return empty( $key ) || Tribe__Events__Google__Maps_API_Key::$default_api_key === $key ;
+ }
+ if ( 'out-of-date' === $slug ) {
+ return $this->is_any_tec_plugin_out_of_date();
+ }
+ return false;
+ }
+
+ /**
+ * Displays issues found in the UI.
+ *
+ * @since 4.14.2
+ *
+ * @return array of issues which are displayed on the troubleshooting page.
+ */
+ public function get_issues_found() {
+ $issues_found = apply_filters( 'tec_help_troubleshooting_issues_found', [
+ [
+ 'title' => __( 'Site time zone uses UTC', 'tribe-common' ),
+ 'description' => __( 'When using The Events Calendar, we highly recommend that you use a geographic timezone such as "America/Los_Angeles" and avoid using a UTC timezone offset such as “UTC+9”. Choosing a UTC timezone for your site or individual events may cause problems when importing events or with Daylight Saving Time. Go to your the General WordPress settings to adjust your site timezone.', 'tribe-common' ),
+ 'more_info' => 'http://evnt.is/1ad3',
+ 'resolve_text' => __( 'Adjust your timezone', 'tribe-common' ),
+ 'fix' => '/wp-admin/options-general.php',
+ 'active' => $this->is_active_issue( 'timezone' ),
+ ],
+ [
+ 'title' => __( 'Install max has been reached', 'tribe-common' ),
+ 'description' => __( 'License keys can only be used on a limited number of sites, which varies depending on your license level. You\'ll need to remove the license from one or more other site\'s in order to use it on this one.', 'tribe-common' ),
+ 'more_info' => 'https://evnt.is/1aqz',
+ 'resolve_text' => __( 'Manage your licenses', 'tribe-common' ),
+ 'fix' => 'https://evnt.is/1aq-',
+ 'active' => $this->is_active_issue( 'install-max' ),
+ ],
+ [
+ 'title' => __( 'Default Google Maps API key', 'tribe-common' ),
+ 'description' => __( 'The Events Calendar comes with an API key for basic maps functionality. If you’d like to use more advanced features like custom map pins, dynamic map loads, or Events Calendar Pro\'s Location Search and advanced Map View, you’ll need to get your own Google Maps API key and add it to Events > Settings > Integrations', 'tribe-common' ),
+ 'more_info' => 'https://evnt.is/1aqx',
+ 'resolve_text' => __( 'Enter a custom API key', 'tribe-common' ),
+ 'fix' => '/wp-admin/edit.php?page=tribe-common&tab=addons&post_type=tribe_events',
+ 'active' => $this->is_active_issue( 'geolocation' ),
+ ],
+ [
+ 'title' => __( 'Plugin(s) are out of date', 'tribe-common' ),
+ 'description' => __( 'It\'s important to use the most recent versions of our plugins so that you have access to the latest features, bug fixes, and security updates. Plugin functionality can be comprimised if your site is running outdated or mis-matched versions.', 'tribe-common' ),
+ 'more_info' => 'https://evnt.is/1aqy',
+ 'resolve_text' => __( 'Check for updates', 'tribe-common' ),
+ 'fix' => '/wp-admin/update-core.php',
+ 'active' => $this->is_active_issue( 'out-of-date' ),
+ ],
+ ] );
+
+ return $issues_found;
+ }
+
+ /**
+ * Defines common troubleshooting issues and displays them in the UI.
+ *
+ * @since 4.14.2
+ *
+ * @return array of common issues which are displayed on the troubleshooting page.
+ */
+ public function get_common_issues() {
+ $common_issues = apply_filters( 'tec_help_troubleshooting_issues', [
+ [
+ 'issue' => __( 'Common Error Messages', 'tribe-common' ),
+ 'solution' => __( 'Here’s an overview of %s and what they mean.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1as0',
+ 'link_label' => 'common error messages',
+ ],
+ [
+ 'issue' => __( 'My calendar doesn’t look right.', 'tribe-common' ),
+ 'solution' => __( 'This can happen when other plugins try to improve performance. %s.' ),
+ 'link' => 'https://theeventscalendar.com/knowledgebase/k/troubleshooting-the-most-common-installation-issues/#layout-issue',
+ 'link_label' => 'More info',
+ ],
+ [
+ 'issue' => __( 'I installed the calendar and it crashed my site.', 'tribe-common' ),
+ 'solution' => __( '%s and other common installation issues.', 'tribe-common' ),
+ 'link' => 'https://theeventscalendar.com/knowledgebase/k/troubleshooting-the-most-common-installation-issues/#fatal-errors',
+ 'link_label' => 'Find solutions to this',
+ ],
+ [
+ 'issue' => __( 'I keep getting “Page Not Found” on events.', 'tribe-common' ),
+ 'solution' => __( 'There are a few %s to resolve and prevent 404 errors.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/1as2',
+ 'link_label' => 'things you can do',
+ ],
+ ] );
+
+ return $common_issues;
+ }
+
+ /**
+ * Fired to display notices in the admin pages where the method is called.
+ *
+ * @since 4.14.2
+ *
+ * @param string $page the page which the action is being applied.
+ *
+ */
+ public function admin_notice( $page ) {
+ do_action( 'tec_admin_notice_area', $page );
+ }
+}
diff --git a/tribe-common/src/Tribe/Admin/Upsell_Notice/Main.php b/tribe-common/src/Tribe/Admin/Upsell_Notice/Main.php
new file mode 100644
index 0000000000..5f220d0e58
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Upsell_Notice/Main.php
@@ -0,0 +1,97 @@
+template = new \Tribe__Template();
+ $this->template->set_template_origin( \Tribe__Main::instance() );
+ $this->template->set_template_folder( 'src/admin-views/notices/upsell' );
+ $this->template->set_template_context_extract( true );
+ $this->template->set_template_folder_lookup( false );
+ }
+
+ return $this->template;
+ }
+
+ /**
+ * Checks if upsell should be rendered.
+ *
+ * @since 4.14.17
+ *
+ * @return boolean
+ */
+ private function should_render() {
+ if ( function_exists( 'tec_should_hide_upsell' ) ) {
+ return ! tec_should_hide_upsell();
+ }
+ if ( defined( 'TRIBE_HIDE_UPSELL' ) ) {
+ return ! tribe_is_truthy( TRIBE_HIDE_UPSELL );
+ }
+ return true;
+ }
+
+ /**
+ * Render upsell notice.
+ *
+ * @since 4.14.17
+ *
+ * @param array $args Array of arguments that will ultimately be sent to the template.
+ * @param bool $echo Whether or not to echo the HTML. Defaults to true.
+ *
+ * @return string HTML of upsell notice.
+ */
+ public function render( $args, $echo = true ) {
+ // Check if upsell should be rendered.
+ if ( ! $this->should_render() ) {
+ return;
+ }
+
+ // Default args for the container.
+ $args = wp_parse_args( $args, [
+ 'classes' => [],
+ 'text' => '',
+ 'link_target' => '_blank',
+ 'icon_url' => tribe_resource_url( 'images/icons/circle-bolt.svg', false, null, \Tribe__Main::instance() ),
+ 'link' => [],
+ ] );
+
+ // Default args for the link.
+ $args['link'] = wp_parse_args( $args['link'], [
+ 'classes' => [],
+ 'text' => '',
+ 'url' => '',
+ 'target' => '_blank',
+ 'rel' => 'noopener noreferrer',
+ ] );
+
+ $template = $this->get_template();
+ return $template->template( 'main', $args, $echo );
+ }
+}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Admin/Upsell_Notice/README.md b/tribe-common/src/Tribe/Admin/Upsell_Notice/README.md
new file mode 100644
index 0000000000..aa0b1012e1
--- /dev/null
+++ b/tribe-common/src/Tribe/Admin/Upsell_Notice/README.md
@@ -0,0 +1,65 @@
+# Upsell Notices
+
+To add an upsell notice, use the following code:
+
+```
+tribe( \Tribe\Admin\Upsell_Notice\Main::class )->render( [
+ 'text' => 'Text to explain what you are promoting.',
+ 'link' => [
+ 'text' => 'Text for the link.',
+ 'url' => 'https://url.com/to/more/info',
+ ],
+] );
+```
+
+## Customizing the notice container
+
+There are a couple of classes you can add that will display the upsell notice with different styles.
+
+- `.tec-admin__upsell--rounded-corners` - Adds a rounded-corner, light gray background around the entire notice.
+- `.tec-admin__upsell--rounded-corners-text` - Adds a rounded-corner, light gray background around the notice text, only.
+
+Example:
+```
+tribe( \Tribe\Admin\Upsell_Notice\Main::class )->render( [
+ 'classes' => [
+ 'tec-admin__upsell--rounded-corners'
+ ],
+ 'text' => 'Text to explain what you are promoting.',
+ 'link' => [
+ 'text' => 'Text for the link.',
+ 'url' => 'https://url.com/to/more/info',
+ ],
+] );
+```
+
+## Customizing the notice link
+
+Likewise, you can also add these classes to the link array to change the appearance.
+
+- `.tec-admin__upsell-link--dark` - Changes the color to a dark color, instead of the default blue.
+- `.tec-admin__upsell-link--underlined` - Adds an underline to the link text.
+
+You can also change the following attributes of the link:
+
+- `target` - Default is '_blank'.
+- `rel` - Default is 'nofollow noreferrer'.
+
+Example:
+```
+tribe( \Tribe\Admin\Upsell_Notice\Main::class )->render( [
+ 'classes' => [
+ 'tec-admin__upsell--rounded-corners-text'
+ ],
+ 'text' => 'Text to explain what you are promoting.',
+ 'link' => [
+ 'classes' => [
+ 'tec-admin__upsell-link--dark',
+ 'tec-admin__upsell-link--underlined',
+ ],
+ 'text' => 'Text for the link.',
+ 'url' => 'https://url.com/to/more/info',
+ 'target' => '_parent'
+ ],
+] );
+```
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Ajax/Dropdown.php b/tribe-common/src/Tribe/Ajax/Dropdown.php
index fd490f4ef3..071d977cf0 100644
--- a/tribe-common/src/Tribe/Ajax/Dropdown.php
+++ b/tribe-common/src/Tribe/Ajax/Dropdown.php
@@ -88,6 +88,10 @@ public function search_terms( $search, $page, $args, $source ) {
}
}
+ foreach ( $results as $result ) {
+ $result->text = wp_specialchars_decode( wp_kses( $result->text, [] ) );
+ }
+
$data['results'] = $results;
$data['taxonomies'] = get_taxonomies();
@@ -271,7 +275,7 @@ public function route() {
if ( has_filter( $filter ) ) {
$data = apply_filters( $filter, [], $args->search, $args->page, $args->args, $args->source );
} else {
- $data = call_user_func_array( [ $this, $args->source ], (array) $args );
+ $data = call_user_func_array( [ $this, $args->source ], array_values( (array) $args ) );
}
// If we've got a empty dataset we return an error.
diff --git a/tribe-common/src/Tribe/App_Shop.php b/tribe-common/src/Tribe/App_Shop.php
index 154b262a61..9eb664a736 100755
--- a/tribe-common/src/Tribe/App_Shop.php
+++ b/tribe-common/src/Tribe/App_Shop.php
@@ -135,6 +135,7 @@ public function do_menu_page() {
$products = $this->get_all_products();
$bundles = $this->get_bundles();
$extensions = $this->get_extensions();
+ $stellar_brands = $this->get_stellar_brands();
include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/app-shop.php';
}
@@ -299,6 +300,67 @@ private function get_extensions() {
return $extensions;
}
+ /**
+ * Gets Stellar brands
+ *
+ * @return array|WP_Error
+ */
+ private function get_stellar_brands() {
+ $stellar_brands = [
+ (object) [
+ 'image' => 'images/shop/stellar-learndash-cta.jpg',
+ 'logo' => 'images/shop/stellar-learndash-logo.png',
+ 'title' => __( 'The online course platform created by e-learning experts.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/learndash',
+ 'linktext' => __( 'Add Courses', 'tribe-common' ),
+ 'description' => __( 'Trusted to power learning programs for major universities, startups, entrepreneurs, and bloggers worldwide.', 'tribe-common' ),
+ ],
+ (object) [
+ 'image' => 'images/shop/stellar-ithemes-cta.jpg',
+ 'logo' => 'images/shop/stellar-ithemes-logo.png',
+ 'title' => __( 'Foundational favorites: iThemes Security and Developer Toolkit.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/ithemes',
+ 'linktext' => __( 'Add Security', 'tribe-common' ),
+ 'description' => __( 'iThemes Security, the WordPress security plugin that’s easy to use. Built with performance in mind.', 'tribe-common' ),
+ ],
+ (object) [
+ 'image' => 'images/shop/stellar-rcp-cta.jpg',
+ 'logo' => 'images/shop/stellar-rcp-logo.png',
+ 'title' => __( 'Built with developers in mind.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/rcp',
+ 'linktext' => __( 'Add Content Restriction', 'tribe-common' ),
+ 'description' => __( 'Restrict Content Pro is flexible, easy to extend, and chock full of action hooks and filters, making it easy to modify and tweak to your specific needs.', 'tribe-common' ),
+ ],
+ (object) [
+ 'image' => 'images/shop/stellar-kadence-cta.jpg',
+ 'logo' => 'images/shop/stellar-kadence-logo.png',
+ 'title' => __( 'Build better WordPress websites with Kadence.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/kadencewp',
+ 'linktext' => __( 'Add Starter Templates', 'tribe-common' ),
+ 'description' => __( 'Kadence lets you unlock your creativity in the WordPress Block Editor with expertly designed blocks, a robust theme, and a massive library of starter templates.', 'tribe-common' ),
+ ],
+ (object) [
+ 'image' => 'images/shop/stellar-iconic-cta.jpg',
+ 'logo' => 'images/shop/stellar-iconic-logo.png',
+ 'title' => __( 'Sales-boosting WooCommerce plugins.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/iconic',
+ 'linktext' => __( 'Add Commerce Tools', 'tribe-common' ),
+ 'description' => __( 'Easy-to-use WooCommerce plugins work perfectly together, with any theme. Create a fast and profitable eCommerce store without any technical knowledge.
+ ', 'tribe-common' ),
+ ],
+ (object) [
+ 'image' => 'images/shop/stellar-give-cta.jpg',
+ 'logo' => 'images/shop/stellar-give-logo.png',
+ 'title' => __( 'The best WordPress donation plugin.', 'tribe-common' ),
+ 'link' => 'https://evnt.is/givewp',
+ 'linktext' => __( 'Add Donations', 'tribe-common' ),
+ 'description' => __( 'GiveWP makes it easy to raise money online with donation forms, donor databases, and fundraising reporting.', 'tribe-common' ),
+ ],
+ ];
+
+ return $stellar_brands;
+ }
+
/**
* Static Singleton Factory Method
*
diff --git a/tribe-common/src/Tribe/Assets.php b/tribe-common/src/Tribe/Assets.php
index f431e56a76..4b816f45c9 100644
--- a/tribe-common/src/Tribe/Assets.php
+++ b/tribe-common/src/Tribe/Assets.php
@@ -39,11 +39,55 @@ public function __construct() {
// Hook the actual registering of.
add_action( 'init', [ $this, 'register_in_wp' ], 1, 0 );
add_filter( 'script_loader_tag', [ $this, 'filter_tag_async_defer' ], 50, 2 );
+ add_filter( 'script_loader_tag', [ $this, 'filter_modify_to_module' ], 50, 2 );
+ add_filter( 'script_loader_tag', [ $this, 'filter_print_before_after_script' ], 100, 2 );
// Enqueue late.
add_filter( 'script_loader_tag', [ $this, 'filter_add_localization_data' ], 500, 2 );
}
+ /**
+ * Depending on how certain scripts are loaded and how much cross-compatibility is required we need to be able to
+ * create noConflict backups and restore other scripts, which normally need to be printed directly on the scripts.
+ *
+ * @since 5.0.0
+ *
+ * @param string $tag Tag we are filtering.
+ * @param string $handle Which is the ID/Handle of the tag we are about to print.
+ *
+ * @return string Script tag with the before and after strings attached to it.
+ */
+ public function filter_print_before_after_script( $tag, $handle ) : string {
+ // Only filter for our own filters.
+ if ( ! $asset = $this->get( $handle ) ) {
+ return (string) $tag;
+ }
+
+ // Bail when not dealing with JS assets.
+ if ( 'js' !== $asset->type ) {
+ return (string) $tag;
+ }
+
+ // Only go forward if there is any print before or after.
+ if ( empty( $asset->print_before ) && empty( $asset->print_after ) ) {
+ return (string) $tag;
+ }
+
+ $before = '';
+ if ( ! empty( $asset->print_before ) ) {
+ $before = (string) ( is_callable( $asset->print_before ) ? call_user_func( $asset->print_before, $asset ) : $asset->print_before );
+ }
+
+ $after = '';
+ if ( ! empty( $asset->print_after ) ) {
+ $after = (string) ( is_callable( $asset->print_after ) ? call_user_func( $asset->print_after, $asset ) : $asset->print_after );
+ }
+
+ $tag = $before . (string) $tag . $after;
+
+ return $tag;
+ }
+
/**
* Handles adding localization data, when attached to `script_loader_tag` which allows dependencies to load in their
* localization data as well.
@@ -56,7 +100,7 @@ public function __construct() {
* @return string Script tag with the localization variable HTML attached to it.
*/
public function filter_add_localization_data( $tag, $handle ) {
- // Only filter for own own filters.
+ // Only filter for own filters.
if ( ! $asset = $this->get( $handle ) ) {
return $tag;
}
@@ -122,7 +166,7 @@ public function filter_add_localization_data( $tag, $handle ) {
* @return string Script tag with the defer and/or async attached.
*/
public function filter_tag_async_defer( $tag, $handle ) {
- // Only filter for own own filters.
+ // Only filter for our own filters.
if ( ! $asset = $this->get( $handle ) ) {
return $tag;
}
@@ -149,16 +193,53 @@ public function filter_tag_async_defer( $tag, $handle ) {
$replacement .= 'defer ';
}
- $replacement_src = $replacement . 'src=';
- $replacement_type = $replacement . 'type=';
- return str_replace( [ '\n"
- . $tag
- . "\n";
+ . $tag
+ . "\n";
}
+
+ return $tag;
+ }
+
+ /**
+ * After select2 is loaded to the FE we add one scripts after to prevent select2 from breaking.
+ *
+ * @since 4.13.2
+ * @since 4.14.18 Ensure we don't run this in the admin.
+ *
+ * @param string $tag The \n";
+
return $tag;
}
}
diff --git a/tribe-common/src/Tribe/Cache.php b/tribe-common/src/Tribe/Cache.php
index b769760ce7..353466767d 100755
--- a/tribe-common/src/Tribe/Cache.php
+++ b/tribe-common/src/Tribe/Cache.php
@@ -118,7 +118,7 @@ public function get( $id, $expiration_trigger = '', $default = false, $expiratio
if ( is_callable( $default ) ) {
// A callback has been specified.
- $value = call_user_func_array( $default, $args );
+ $value = $default( ...$args );
} else {
// Default is a value.
$value = $default;
@@ -258,7 +258,7 @@ public function maybe_delete_expired_transients() {
public function get_id( $key, $expiration_trigger = '' ) {
if ( is_array( $expiration_trigger ) ) {
$triggers = $expiration_trigger;
- } else {
+ } elseif ( 'tribe-events-non-persistent' !== $expiration_trigger && 'tribe-events' !== $expiration_trigger ) {
$triggers = array_filter( explode( '|', $expiration_trigger ) );
}
@@ -434,6 +434,21 @@ public function offsetUnset( $offset ) {
$this->delete( $offset );
}
+ /**
+ * Removes a group of the cache, for now only `non_persistent` is supported.
+ *
+ * @since 4.14.13
+ *
+ * @return bool
+ */
+ public function reset( $group = 'non_persistent' ) {
+ if ( 'non_persistent' !== $group ) {
+ return false;
+ }
+ $this->non_persistent_keys = [];
+ return true;
+ }
+
/**
* Warms up the caches for a collection of posts.
*
@@ -540,4 +555,99 @@ public function data_size_over_packet_size( $value ) {
// If the size of the string is above 90% of the database `max_allowed_packet` setting, then it should not be written to the db.
return $size > ( $feature_detection->get_mysql_max_packet_size() * .9 );
}
+
+ /**
+ * Returns a transient that might have been stored, due ot its size, in chunks.
+ *
+ * @since 4.13.3
+ *
+ * @param string $id The name of the transients to return.
+ * @param string|array $expiration_trigger The transient expiration trigger(s).
+ *
+ * @return false|mixed Either the transient value, joined back into one, or `false` to indicate
+ * the transient was not found or was malformed.
+ */
+ public function get_chunkable_transient( $id, $expiration_trigger = '' ) {
+ $transient = $this->get_id( $id, $expiration_trigger );
+
+ if ( wp_using_ext_object_cache() ) {
+ return get_transient( $transient );
+ }
+
+ $chunks = [];
+ $i = 0;
+ do {
+ $chunk_transient = $transient . '_' . $i++;
+ $chunk = get_transient( $chunk_transient );
+ $chunks[ $chunk_transient ] = (string) $chunk;
+ } while ( ! empty( $chunk ) );
+
+ // Remove any piece of data that was added but is not relevant.
+ $chunks = array_filter( $chunks );
+
+ if ( empty( $chunks ) ) {
+ return false;
+ }
+
+ try {
+ $data = implode( '', $chunks );
+ $is_serialized = preg_match( '/^[aO]:\\d+:/', $data );
+ $unserialized = maybe_unserialize( implode( '', $chunks ) );
+
+ if ( is_string( $unserialized ) && $unserialized === $data && $is_serialized ) {
+ // Something was messed up.
+ return false;
+ }
+
+ return $unserialized;
+ } catch ( Exception $e ) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets a transient in the database with the knowledge that, if too large to be stored in one
+ * DB row, it will be chunked.
+ *
+ * The method will redirect to the `set_transient` function if the site is using object caching.
+ *
+ *
+ * @since 4.13.3
+ *
+ * @param string $id The transient ID.
+ * @param mixed $value The value to store, that could be chunked.
+ * @param int $expiration The transient expiration, in seconds.
+ * @param string|array $expiration_trigger The transient expiration trigger(s).
+ *
+ * @return bool Whether the transient, or the transient chunks, have been stored correctly or not.
+ */
+ public function set_chunkable_transient( $id, $value, $expiration = 0, $expiration_trigger = '' ) {
+ $transient = $this->get_id( $id, $expiration_trigger );
+
+ if ( wp_using_ext_object_cache() ) {
+ return $this->set_transient( $transient, $value, $expiration );
+ }
+
+ $inserted = [];
+ $serialized_value = maybe_serialize( $value );
+ $chunk_size = tribe( 'feature-detection' )->get_mysql_max_packet_size() * 0.9;
+ $chunks = str_split( $serialized_value, $chunk_size );
+ foreach ( $chunks as $i => $chunk ) {
+ $chunk_transient = $transient . '_' . $i;
+
+ $set = set_transient( $chunk_transient, $chunk, $expiration );
+
+ if ( ! $set ) {
+ foreach ( $inserted as $transient_to_delete ) {
+ delete_transient( $transient_to_delete );
+ }
+
+ return false;
+ }
+
+ $inserted[] = $chunk_transient;
+ }
+
+ return true;
+ }
}
diff --git a/tribe-common/src/Tribe/Cache_Listener.php b/tribe-common/src/Tribe/Cache_Listener.php
index bfa3fce112..bc428398af 100755
--- a/tribe-common/src/Tribe/Cache_Listener.php
+++ b/tribe-common/src/Tribe/Cache_Listener.php
@@ -1,171 +1,215 @@
cache = new Tribe__Cache();
+ }
- /**
- * Class constructor.
- *
- * @return void
- */
- public function __construct() {
- $this->cache = new Tribe__Cache();
- }
+ /**
+ * Run the init functionality (like add_hooks).
+ *
+ * @return void
+ */
+ public function init() {
+ $this->add_hooks();
+ }
- /**
- * Run the init functionality (like add_hooks).
- *
- * @return void
- */
- public function init() {
- $this->add_hooks();
- }
+ /**
+ * Add the hooks necessary.
+ *
+ * @return void
+ */
+ private function add_hooks() {
+ add_action( 'save_post', [ $this, 'save_post' ], 0, 2 );
+ add_action( 'updated_option', [ $this, 'update_last_updated_option' ], 10, 3 );
+ add_action( 'updated_option', [ $this, 'update_last_save_post' ], 10, 3 );
+ add_action( 'generate_rewrite_rules', [ $this, 'generate_rewrite_rules' ] );
+ add_action( 'clean_post_cache', [ $this, 'save_post' ], 0, 2 );
+ }
- /**
- * Add the hooks necessary.
- *
- * @return void
- */
- private function add_hooks() {
- add_action( 'save_post', [ $this, 'save_post' ], 0, 2 );
- add_action( 'updated_option', [ $this, 'update_last_updated_option' ], 10, 3 );
- add_action( 'updated_option', [ $this, 'update_last_save_post' ], 10, 3 );
- add_action( 'generate_rewrite_rules', [ $this, 'generate_rewrite_rules' ] );
+ /**
+ * Run the caching functionality that is executed on save post.
+ *
+ * @param int $post_id The post_id.
+ * @param WP_Post $post The current post object being saved.w
+ */
+ public function save_post( $post_id, $post ) {
+ if ( in_array( $post->post_type, Tribe__Main::get_post_types() ) ) {
+ $this->cache->set_last_occurrence( self::TRIGGER_SAVE_POST );
}
+ }
- /**
- * Run the caching functionality that is executed on save post.
- *
- * @param int $post_id The post_id.
- * @param WP_Post $post The current post object being saved.
- */
- public function save_post( $post_id, $post ) {
- if ( in_array( $post->post_type, Tribe__Main::get_post_types() ) ) {
- $this->cache->set_last_occurrence( self::TRIGGER_SAVE_POST );
- }
+ /**
+ * Run the caching functionality that is executed on saving tribe calendar options.
+ *
+ * @see 'updated_option'
+ *
+ * @param string $option_name Name of the updated option.
+ * @param mixed $old_value The old option value.
+ * @param mixed $value The new option value.
+ */
+ public function update_last_save_post( $option_name, $old_value, $value ) {
+ $triggers = [
+ 'tribe_events_calendar_options' => true,
+ 'permalink_structure' => true,
+ 'rewrite_rules' => true,
+ 'start_of_week' => true,
+ ];
+
+ $triggers = $this->filter_action_last_occurrence_triggers( $triggers, static::TRIGGER_SAVE_POST, func_get_args() );
+
+ if ( ! empty( $triggers[ $option_name ] ) ) {
+ $this->cache->set_last_occurrence( self::TRIGGER_SAVE_POST );
}
+ }
- /**
- * Run the caching functionality that is executed on saving tribe calendar options.
- *
- * @see 'updated_option'
- *
- * @param string $option_name Name of the updated option.
- * @param mixed $old_value The old option value.
- * @param mixed $value The new option value.
- */
- public function update_last_save_post( $option_name, $old_value, $value ) {
- $triggers = [
- 'tribe_events_calendar_options' => true,
- 'permalink_structure' => true,
- 'rewrite_rules' => true,
- 'start_of_week' => true,
- ];
- if ( ! empty( $triggers[ $option_name ] ) ) {
- $this->cache->set_last_occurrence( self::TRIGGER_SAVE_POST );
- }
+ /**
+ * Run the caching functionality that is executed on saving tribe calendar options.
+ *
+ * @see 'updated_option'
+ *
+ * @since 4.11.0
+ *
+ * @param string $option_name Name of the updated option.
+ * @param mixed $old_value The old option value.
+ * @param mixed $value The new option value.
+ */
+ public function update_last_updated_option( $option_name, $old_value, $value ) {
+ $triggers = [
+ 'active_plugins' => true,
+ 'tribe_events_calendar_options' => true,
+ 'permalink_structure' => true,
+ 'rewrite_rules' => true,
+ 'start_of_week' => true,
+ 'sidebars_widgets' => true,
+ 'stylesheet' => true,
+ 'template' => true,
+ 'WPLANG' => true,
+ ];
+
+ $triggers = $this->filter_action_last_occurrence_triggers( $triggers, static::TRIGGER_UPDATED_OPTION, func_get_args() );
+
+ if ( ! empty( $triggers[ $option_name ] ) ) {
+ $this->cache->set_last_occurrence( self::TRIGGER_UPDATED_OPTION );
}
+ }
+
+ /**
+ * Filtering for last occurrence triggers.
+ *
+ * @since 4.13.2
+ *
+ * @param array $triggers Which options will trigger this given action last occurrence.
+ * @param string $action Which action this trigger will set.
+ * @param array $args Which arguments from the updated option method.
+ *
+ * @return array
+ */
+ public function filter_action_last_occurrence_triggers( array $triggers = [], $action = '', array $args = [] ) {
/**
- * Run the caching functionality that is executed on saving tribe calendar options.
- *
- * @see 'updated_option'
+ * Filters the contents of which options will trigger expiring a given actions cache.
*
- * @since 4.11.0
+ * @since 4.13.2
*
- * @param string $option_name Name of the updated option.
- * @param mixed $old_value The old option value.
- * @param mixed $value The new option value.
+ * @param array $triggers Which options will trigger this given action last occurrence.
+ * @param string $action Which action this trigger will set.
+ * @param array $args Which arguments from the updated option method.
*/
- public function update_last_updated_option( $option_name, $old_value, $value ) {
- $triggers = [
- 'active_plugins' => true,
- 'tribe_events_calendar_options' => true,
- 'permalink_structure' => true,
- 'rewrite_rules' => true,
- 'start_of_week' => true,
- 'sidebars_widgets' => true,
- 'stylesheet' => true,
- 'template' => true,
- 'WPLANG' => true,
- ];
-
- if ( ! empty( $triggers[ $option_name ] ) ) {
- $this->cache->set_last_occurrence( self::TRIGGER_UPDATED_OPTION );
- }
- }
+ $triggers = apply_filters( 'tribe_cache_last_occurrence_option_triggers', $triggers, $action, $args );
/**
- * For any hook that doesn't need any additional filtering
+ * Filters the contents of which options will trigger expiring a given actions cache.
+ * Allows filtering a specific action.
*
- * @param $method
- * @param $args
- */
- public function __call( $method, $args ) {
- $this->cache->set_last_occurrence( $method );
- }
-
- /**
- * Instance method of the cache listener.
+ * @since 4.13.2
*
- * @return Tribe__Cache_Listener
+ * @param array $triggers Which options will trigger this given action last occurrence.
+ * @param string $action Which action this trigger will set.
+ * @param array $args Which arguments from the updated option method.
*/
- public static function instance() {
- if ( empty( self::$instance ) ) {
- self::$instance = self::create_listener();
- }
+ return (array) apply_filters( "tribe_cache_last_occurrence_option_triggers:{$action}", $triggers, $action, $args );
+ }
+
+ /**
+ * For any hook that doesn't need any additional filtering
+ *
+ * @param $method
+ * @param $args
+ */
+ public function __call( $method, $args ) {
+ $this->cache->set_last_occurrence( $method );
+ }
- return self::$instance;
+ /**
+ * Instance method of the cache listener.
+ *
+ * @return Tribe__Cache_Listener
+ */
+ public static function instance() {
+ if ( empty( self::$instance ) ) {
+ self::$instance = self::create_listener();
}
- /**
- * Create a cache listener.
- *
- * @return Tribe__Cache_Listener
- */
- private static function create_listener() {
- $listener = new self();
- $listener->init();
+ return self::$instance;
+ }
- return $listener;
- }
+ /**
+ * Create a cache listener.
+ *
+ * @return Tribe__Cache_Listener
+ */
+ private static function create_listener() {
+ $listener = new self();
+ $listener->init();
- /**
- * Run the caching functionality that is executed when rewrite rules are generated.
- *
- * @since 4.9.11
- */
- public function generate_rewrite_rules() {
- $this->cache->set_last_occurrence( self::TRIGGER_GENERATE_REWRITE_RULES );
- }
+ return $listener;
+ }
+
+ /**
+ * Run the caching functionality that is executed when rewrite rules are generated.
+ *
+ * @since 4.9.11
+ */
+ public function generate_rewrite_rules() {
+ $this->cache->set_last_occurrence( self::TRIGGER_GENERATE_REWRITE_RULES );
}
+}
diff --git a/tribe-common/src/Tribe/Context.php b/tribe-common/src/Tribe/Context.php
index b7188c0cfb..5429ebc5fc 100644
--- a/tribe-common/src/Tribe/Context.php
+++ b/tribe-common/src/Tribe/Context.php
@@ -282,7 +282,23 @@ public function is_editing_post( $post_or_type = null ) {
}
if ( ! empty( $post_or_type ) ) {
- $lookup = [ $_GET, $_POST, $_REQUEST ];
+ $lookup = [];
+ // Prevent a slew of warnings every time we call this.
+ if ( isset( $_REQUEST ) ) {
+ $lookup[] = (array) $_REQUEST;
+ }
+
+ if ( isset( $_GET ) ) {
+ $lookup[] = (array) $_GET;
+ }
+
+ if ( isset( $_POST ) ) {
+ $lookup[] = (array) $_POST;
+ }
+
+ if ( empty( $lookup ) ) {
+ return false;
+ }
$current_post = Tribe__Utils__Array::get_in_any( $lookup, 'post', get_post() );
diff --git a/tribe-common/src/Tribe/Context/locations.php b/tribe-common/src/Tribe/Context/locations.php
index 0df3e81e23..6a433dd97e 100644
--- a/tribe-common/src/Tribe/Context/locations.php
+++ b/tribe-common/src/Tribe/Context/locations.php
@@ -47,6 +47,14 @@ static function( $struct ){
Tribe__Context::FUNC => static function () {
global $wp_query;
+ if ( empty( $wp_query ) ) {
+ return false;
+ }
+
+ if ( ! $wp_query instanceof WP_Query ) {
+ return false;
+ }
+
return $wp_query->is_main_query();
},
],
@@ -99,7 +107,7 @@ static function( $struct ){
'objects'
);
- foreach( $post_type_objs as $post_type ) {
+ foreach ( $post_type_objs as $post_type ) {
if ( empty( $post_type->query_var ) ) {
continue;
}
diff --git a/tribe-common/src/Tribe/Cost_Utils.php b/tribe-common/src/Tribe/Cost_Utils.php
index 8e0aec0520..42a112a19f 100644
--- a/tribe-common/src/Tribe/Cost_Utils.php
+++ b/tribe-common/src/Tribe/Cost_Utils.php
@@ -111,7 +111,7 @@ public function maybe_replace_cost_with_free( $cost ) {
is_numeric( $cost_with_period )
&& '0.00' === number_format( $cost_with_period, 2, '.', ',' )
) {
- return esc_html__( 'Free', 'the-events-calendar' );
+ return esc_html__( 'Free', 'tribe-common' );
}
return $cost;
@@ -346,7 +346,7 @@ public function parse_cost_range( $costs, $max_decimals = null, $sort = true ) {
}
$output_costs = [];
- $costs = call_user_func_array( 'array_merge', $costs );
+ $costs = call_user_func_array( 'array_merge', array_values( $costs ) );
foreach ( $costs as $cost ) {
$numeric_cost = str_replace( $this->get_separators(), '.', $cost );
diff --git a/tribe-common/src/Tribe/Credits.php b/tribe-common/src/Tribe/Credits.php
index 15ae9d6fd3..c87bc1f518 100755
--- a/tribe-common/src/Tribe/Credits.php
+++ b/tribe-common/src/Tribe/Credits.php
@@ -45,33 +45,47 @@ public function rating_nudge( $footer_text ) {
add_filter( 'tribe_tickets_post_types', [ $this, 'tmp_return_tribe_events' ], 99 );
- // only display custom text on Tribe Admin Pages
+ $review_text_tec = esc_html__( 'Rate %1$sThe Events Calendar%2$s %3$s', 'tribe-common' );
+ $review_url_tec = 'https://wordpress.org/support/plugin/the-events-calendar/reviews/?filter=5';
+
+ $review_text_et = esc_html__( 'If you like %1$sEvent Tickets%2$s please leave us a %3$s. It takes a minute and it helps a lot.', 'tribe-common' );
+ $review_url_et = 'https://wordpress.org/support/plugin/event-tickets/reviews/?filter=5';
+
+ // Only display custom text on Tribe Admin Pages.
if ( $admin_helpers->is_screen() || $admin_helpers->is_post_type_screen() ) {
if ( class_exists( 'Tribe__Events__Main' ) ) {
- $review_url = 'https://wordpress.org/support/plugin/the-events-calendar/reviews/?filter=5';
-
- $footer_text = sprintf(
- esc_html__( 'Rate %1$sThe Events Calendar%2$s %3$s', 'tribe-common' ),
- '',
- '',
- '★★★★★'
- );
+ // If we have TEC and ET, split the impressions 50/50.
+ if ( class_exists( 'Tribe__Tickets__Main' ) && wp_rand( 0,1 ) ) {
+ $review_text = $review_text_et;
+ $review_url = $review_url_et;
+ } else {
+ $review_text = $review_text_tec;
+ $review_url = $review_url_tec;
+ }
} else {
- $review_url = 'https://wordpress.org/support/plugin/event-tickets/reviews/?filter=5';
-
- $footer_text = sprintf(
- esc_html__( 'Rate %1$sEvent Tickets%2$s %3$s', 'tribe-common' ),
- '',
- '',
- '★★★★★'
- );
+ $review_text = $review_text_et;
+ $review_url = $review_url_et;
}
+
+ $footer_text = sprintf(
+ $review_text,
+ '',
+ '',
+ '★★★★★'
+ );
}
remove_filter( 'tribe_tickets_post_types', [ $this, 'tmp_return_tribe_events' ], 99 );
- return $footer_text;
+ /**
+ * Filters the admin footer text.
+ *
+ * @since 4.15.0
+ *
+ * @param $footer_text The admin footer text.
+ */
+ return apply_filters( 'tec_admin_footer_text', $footer_text );
}
/**
diff --git a/tribe-common/src/Tribe/Customizer.php b/tribe-common/src/Tribe/Customizer.php
index 7ce92b9399..a153ded51f 100644
--- a/tribe-common/src/Tribe/Customizer.php
+++ b/tribe-common/src/Tribe/Customizer.php
@@ -113,6 +113,7 @@ public function __construct() {
add_action( 'customize_register', [ $this, 'register' ], 15 );
add_action( 'wp_print_footer_scripts', [ $this, 'print_css_template' ], 15 );
+ add_action( 'customize_controls_print_footer_scripts', [ $this, 'customize_controls_print_footer_scripts' ], 15 );
// front end styles from customizer
add_action( 'tribe_events_pro_widget_render', [ $this, 'inline_style' ], 101 );
@@ -191,7 +192,7 @@ public function get_loaded_sections() {
* @deprecated
* @since 4.0
*
- * @param array $selection_class
+ * @param array $sections_class
* @param self $customizer
*/
$this->sections_class = apply_filters( 'tribe_events_pro_customizer_sections_class', $this->sections_class, $this );
@@ -201,7 +202,7 @@ public function get_loaded_sections() {
*
* @since 4.4
*
- * @param array $selection_class
+ * @param array $sections_class
* @param self $customizer
*/
$this->sections_class = apply_filters( 'tribe_customizer_sections_class', $this->sections_class, $this );
@@ -209,6 +210,25 @@ public function get_loaded_sections() {
return $this->sections_class;
}
+ /**
+ * Returns the section requested by ID.
+ *
+ * @since 4.13.3
+ *
+ * @param string $id The ID of the desired section.
+ *
+ * @return boolean|Tribe__Customizer__Section The requested section or boolean false if not found.
+ */
+ public function get_section( $id ) {
+ $sections = $this->get_loaded_sections();
+
+ if ( empty( $sections[ $id ] ) ) {
+ return false;
+ }
+
+ return $sections[ $id ];
+ }
+
/**
* A easy way to check if customize is active
*
@@ -267,11 +287,14 @@ public static function search_var( $variable = null, $indexes = [], $default = n
}
/**
- * Get an option from the database, using index search you can retrieve the full panel, a section or even a setting
+ * Get an option from the database, using index search you can retrieve the full panel, a section or even a setting.
+ *
+ * @since 4.4
*
- * @param array $search Index search, array( 'section_name', 'setting_name' )
- * @param mixed $default The default, if the requested variable doesn't exits
- * @return mixed The requested option or the default
+ * @param array $search Index search, array( 'section_name', 'setting_name' ).
+ * @param mixed $default The default, if the requested variable doesn't exits.
+ *
+ * @return mixed The requested option or the default.
*/
public function get_option( $search = null, $default = null ) {
$sections = get_option( $this->ID, $default );
@@ -369,6 +392,22 @@ public function has_option() {
return ! empty( $option );
}
+ /**
+ * Add an action for some backwards compatibility.
+ *
+ * @since 4.14.2
+ *
+ * @return void
+ */
+ public function customize_controls_print_footer_scripts() {
+ /**
+ * Allows plugins to hook in and add any scripts they need at the right time.
+ *
+ * @param Tribe__Customizer $customizer The current instance of Tribe__Customizer.
+ */
+ do_action( 'tribe_enqueue_customizer_scripts', $this );
+ }
+
/**
* Print the CSS for the customizer on `wp_print_footer_scripts`
*
@@ -497,7 +536,7 @@ public function inline_style( $force = false ) {
if ( $just_print ) {
printf(
- "\n",
+ "\n",
esc_attr( $sheet ),
$inline_style
);
@@ -553,7 +592,7 @@ private function parse_css_template( $template ) {
* @return void
*/
public function register( WP_Customize_Manager $customizer ) {
- // Set the Cutomizer on a class variable
+ // Set the Customizer on a class variable
$this->manager = $customizer;
/**
@@ -647,6 +686,42 @@ private function register_panel() {
return $this->manager->get_panel( $this->ID );
}
+ /**
+ * Returns a URL to the TEC Customizer panel.
+ *
+ * @since 4.14.0
+ *
+ * @return string The URL to the TEC Customizer panel.
+ */
+ public function get_panel_url() {
+ $query['autofocus[panel]'] = 'tribe_customizer';
+ return add_query_arg( $query, admin_url( 'customize.php' ) );
+ }
+
+ /**
+ * Returns an HTML link directly to the (opened) TEC Customizer panel
+ *
+ * @since 4.14.0
+ *
+ * @param string $link_text The (pre)translated text for the link.
+ *
+ * @return string The HTML anchor element, linking to the TEC Customizer panel.
+ * An empty string is returned if missing a parameter.
+ */
+ public function get_panel_link( $link_text ) {
+ if ( empty( $link_text ) || ! is_string( $link_text ) ) {
+ return '';
+ }
+
+ $panel_url = $this->get_panel_url();
+
+ return sprintf(
+ '%2$s',
+ esc_url( $panel_url ),
+ esc_html( $link_text )
+ );
+ }
+
/**
* Use a "alias" method to register sections to allow users to filter args and the ID
*
@@ -699,18 +774,65 @@ public function register_section( $id, $args ) {
return $this->manager->get_section( $section_id );
}
+ /**
+ * Returns a URL to the a specific TEC Customizer section.
+ *
+ * @since 4.14.0
+ *
+ * @param string $section The slug for the desired section.
+ *
+ * @return string The URL to the TEC Customizer section.
+ */
+ public function get_section_url( $section ) {
+ if ( empty( $section ) ) {
+ return '';
+ }
+
+ $query['autofocus[section]'] = $section;
+ return add_query_arg( $query, admin_url( 'customize.php' ) );
+ }
+
+ /**
+ * Gets the HTML link to a section in the TEC Customizer.
+ *
+ * @since 4.14.0
+ *
+ * @param string $section The section "slug" to link to.
+ * @param string $link_text The text for the link.
+ *
+ * @return string The HTML anchor element, linking to the TEC Customizer section.
+ * An empty string is returned if missing a parameter.
+ */
+ public function get_section_link( $section, $link_text = '' ) {
+ if ( empty( $section ) || empty( $link_text ) || ! is_string($link_text ) ) {
+ return '';
+ }
+
+
+ $panel_url = $this->get_section_url( $section );
+ if ( empty( $panel_url ) ) {
+ return '';
+ }
+
+ return sprintf(
+ '%2$s',
+ esc_url( $panel_url ),
+ esc_html( $link_text )
+ );
+ }
+
/**
* Build the Setting name using the HTML format for Arrays
*
* @since 4.0
*
- * @param string $slug The actual Setting name
- * @param string|WP_Customize_Section $section [description]
+ * @param string $slug The actual Setting name
+ * @param string|WP_Customize_Section $section The section the setting lives in.
*
- * @return string HTML name Attribute name o the setting
+ * @return string HTML name Attribute name of the setting.
*/
public function get_setting_name( $slug, $section = null ) {
- $name = $this->panel->id;
+ $name = ! empty( $this->panel->id ) ? $this->panel->id : '';
// If there is a section set append it
if ( $section instanceof WP_Customize_Section ) {
@@ -725,21 +847,78 @@ public function get_setting_name( $slug, $section = null ) {
return $name;
}
-
/**
* Adds a setting field name to the Array of Possible Selective refresh fields
*
* @since 4.2
*
- * @param string $name The actual Setting name
+ * @param string $name The actual Setting name
*
- * @return array The list of existing Settings, the new one included
+ * @return array The list of existing Settings, the new one included
*/
public function add_setting_name( $name ) {
$this->settings[] = $name;
return $this->settings;
}
+ /**
+ * Gets the URL to a specific control/setting in the TEC Customizer.
+ *
+ * @since 4.14.0
+ *
+ * @param string $section The section "slug" to link into.
+ * @param string $setting The setting "slug" to link to.
+ *
+ * @return string The URL to the setting.
+ * An empty string is returned if a parameter is missing or the setting control cannot be found.
+ */
+ public function get_setting_url( $section, $setting ) {
+ // Bail if something is missing.
+ if ( empty( $setting ) || empty( $section ) ) {
+ return '';
+ }
+
+ $control = $this->get_setting_name( $setting, $section );
+
+ if ( empty( $control ) ) {
+ return '';
+ }
+
+ $query['autofocus[control]'] = $control;
+
+ return add_query_arg( $query, admin_url( 'customize.php' ) );
+ }
+
+ /**
+ * Gets the link to the a specific control/setting in the TEC Customizer.
+ *
+ * @since 4.14.0
+ *
+ * @param string $section The section "slug" to link into.
+ * @param string $setting The setting "slug" to link to.
+ * @param string $link_text The translated text for the link.
+ *
+ * @return string The HTML anchor element, linking to the TEC Customizer setting.
+ * An empty string is returned if missing a parameter or the setting control cannot be found.
+ */
+ public function get_setting_link( $section, $setting, $link_text ) {
+ // Bail if something is missing.
+ if ( empty( $setting ) || empty( $section ) || empty( $link_text ) ) {
+ return '';
+ }
+
+ $control_url = $this->get_setting_url( $section, $setting );
+
+ if ( empty( $control_url ) ) {
+ return '';
+ }
+
+ return sprintf(
+ '%2$s',
+ esc_url( $control_url ),
+ esc_html( $link_text )
+ );
+ }
/**
* Using the Previously created CSS element, we not just re-create it every setting change
@@ -828,4 +1007,67 @@ public function get_styles_scripts() {
return $result;
}
+
+ /**
+ * Inserts link to TEC Customizer section for FSE themes in admin (left) menu.
+ *
+ * @since 4.14.8
+ */
+ public function add_fse_customizer_link() {
+ _deprecated_function( __METHOD__, '4.14.18', 'No replacement. Customizer menu item is preserved as long as we activate it.');
+ // Exit early if the current theme is not a FSE theme.
+ if ( ! tec_is_full_site_editor() ) {
+ return;
+ }
+
+ // Add a link to the TEC panel in the Customizer.
+ add_submenu_page(
+ 'themes.php',
+ _x( 'Customize The Events Calendar', 'Page title for the TEC Customizer section.', 'tribe-common' ),
+ _x( 'Customize The Events Calendar', 'Menu item text for the TEC Customizer section link.', 'tribe-common' ),
+ 'edit_theme_options',
+ esc_url( add_query_arg( 'autofocus[panel]', 'tribe_customizer' , admin_url( 'customize.php' ) ) )
+ );
+ }
+
+ /**
+ * Inserts link to TEC Customizer section for FSE themes in Events > Settings > Display.
+ *
+ * @since 4.14.8
+ *
+ * @param array $settings The existing settings array.
+ *
+ * @return array $settings The modified settings array.
+ */
+ public function add_fse_customizer_link_to_display_tab( $settings ) {
+ _deprecated_function( __METHOD__, '4.14.18', 'No replacement. Customizer link is preserved as long as we activate it.');
+ // Exit early if the current theme is not a FSE theme.
+ if ( ! tec_is_full_site_editor() ) {
+ return $settings;
+ }
+
+ $new_settings = [
+ 'tribe-customizer-section-title' => [
+ 'type' => 'html',
+ 'html' => '
';
@@ -386,7 +392,7 @@ public function generatePage() {
}
/**
- * generate the tabs in the settings screen
+ * Generate the tabs in the settings screen.
*
* @return void
*/
@@ -394,13 +400,7 @@ public function generateTabs() {
if ( is_array( $this->tabs ) && ! empty( $this->tabs ) ) {
echo '
';
foreach ( $this->tabs as $tab => $name ) {
- if ( ! is_network_admin() ) {
- $url = '?page=' . $this->adminSlug . '&tab=' . urlencode( $tab );
- $url = apply_filters( 'tribe_settings_url', $url );
- }
- if ( is_network_admin() ) {
- $url = '?page=' . $this->adminSlug . '&tab=' . urlencode( $tab );
- }
+ $url = $this->get_tab_url( $tab );
$class = ( $tab == $this->currentTab ) ? ' nav-tab-active' : '';
echo '' . esc_html( $name ) . '';
}
@@ -409,76 +409,104 @@ public function generateTabs() {
}
}
+ /**
+ * Generate the URL for a tab.
+ *
+ * @since 4.15.0
+ *
+ * @param string $tab The tab slug.
+ *
+ * @return string $url The URL.
+ */
+ public function get_tab_url( $tab ) {
+ $admin_pages = tribe( 'admin.pages' );
+ $admin_page = $admin_pages->get_current_page();
+ $wp_page = is_network_admin() ? network_admin_url( 'settings.php' ) : admin_url( 'admin.php' );
+ $url = add_query_arg(
+ [
+ 'page' => $admin_page,
+ 'tab' => $tab,
+ ],
+ $wp_page
+ );
+
+ $url = apply_filters( 'tec_settings_tab_url', $url, $admin_page, $tab );
+
+ return $url;
+ }
+
/**
* validate the settings
*
* @return void
*/
public function validate() {
+ $admin_pages = tribe( 'admin.pages' );
+ $admin_page = $admin_pages->get_current_page();
- do_action( 'tribe_settings_validate_before_checks' );
+ do_action( 'tribe_settings_validate_before_checks', $admin_page );
- // check that the right POST && variables are set
+ // Check that the right POST && variables are set.
if ( isset( $_POST['tribeSaveSettings'] ) && isset( $_POST['current-settings-tab'] ) ) {
// check permissions
- if ( ! current_user_can( 'manage_options' ) ) {
+ if ( ! current_user_can( AdminPages::get_capability() ) ) {
$this->errors[] = esc_html__( "You don't have permission to do that.", 'tribe-common' );
$this->major_error = true;
}
- // check the nonce
+ // Check the nonce.
if ( ! wp_verify_nonce( $_POST['tribe-save-settings'], 'saving' ) ) {
$this->errors[] = esc_html__( 'The request was sent insecurely.', 'tribe-common' );
$this->major_error = true;
}
- // check that the request originated from the current tab
+ // check that the request originated from the current tab.
if ( $_POST['current-settings-tab'] != $this->currentTab ) {
$this->errors[] = esc_html__( "The request wasn't sent from this tab.", 'tribe-common' );
$this->major_error = true;
}
- // bail if we have errors
+ // Bail if we have errors.
if ( count( $this->errors ) ) {
remove_action( 'shutdown', [ $this, 'deleteOptions' ] );
add_option( 'tribe_settings_errors', $this->errors );
add_option( 'tribe_settings_major_error', $this->major_error );
- wp_redirect( $this->url );
+ wp_redirect( $this->get_settings_page_url() );
exit;
}
- // some hooks
- do_action( 'tribe_settings_validate' );
- do_action( 'tribe_settings_validate_tab_' . $this->currentTab );
+ // Some hooks.
+ do_action( 'tribe_settings_validate', $admin_page );
+ do_action( 'tribe_settings_validate_tab_' . $this->currentTab, $admin_page );
- // set the current tab and current fields
+ // Set the current tab and current fields.
$tab = $this->currentTab;
$fields = $this->current_fields = $this->fields_for_save[ $tab ];
if ( is_array( $fields ) ) {
- // loop through the fields and validate them
+ // Loop through the fields and validate them.
foreach ( $fields as $field_id => $field ) {
- // get the value
+ // Get the value.
$value = ( isset( $_POST[ $field_id ] ) ) ? $_POST[ $field_id ] : null;
$value = apply_filters( 'tribe_settings_validate_field_value', $value, $field_id, $field );
- // make sure it has validation set up for it, else do nothing
+ // Make sure it has validation set up for it, else do nothing.
if (
( ! isset( $field['conditional'] ) || $field['conditional'] )
&& ( ! empty( $field['validation_type'] ) || ! empty( $field['validation_callback'] ) )
) {
- // some hooks
+ // Some hooks.
do_action( 'tribe_settings_validate_field', $field_id, $value, $field );
do_action( 'tribe_settings_validate_field_' . $field_id, $value, $field );
- // validate this field
+ // Validate this field.
$validate = new Tribe__Validate( $field_id, $field, $value );
if ( isset( $validate->result->error ) ) {
- // uh oh; validation failed
+ // Uh oh; validation failed.
$this->errors[ $field_id ] = $validate->result->error;
} elseif ( $validate->result->valid ) {
- // validation passed
+ // Validation passed.
$this->validated[ $field_id ] = new stdClass;
$this->validated[ $field_id ]->field = $validate->field;
$this->validated[ $field_id ]->value = $validate->value;
@@ -486,32 +514,35 @@ public function validate() {
}
}
- // do not generate errors for dependent fields that should not show
+ // Do not generate errors for dependent fields that should not show.
if ( ! empty( $this->errors ) ) {
$keep = array_filter( array_keys( $this->errors ), [ $this, 'dependency_checks' ] );
$compare = empty( $keep ) ? [] : array_combine( $keep, $keep );
$this->errors = array_intersect_key( $this->errors, $compare );
}
- // run the saving method
+ // Run the saving method.
$this->save();
}
}
-
}
/**
- * save the settings
+ * Save the settings.
+ *
+ * @since 4.15.0 Add the current page as parameter for the actions.
*
* @return void
*/
public function save() {
+ $admin_pages = tribe( 'admin.pages' );
+ $admin_page = $admin_pages->get_current_page();
- // some hooks
- do_action( 'tribe_settings_save' );
- do_action( 'tribe_settings_save_tab_' . $this->currentTab );
+ // Some hooks.
+ do_action( 'tribe_settings_save', $admin_page );
+ do_action( 'tribe_settings_save_tab_' . $this->currentTab, $admin_page );
- // we'll need this later
+ // We'll need this later.
$parent_options = [];
/**
@@ -522,11 +553,11 @@ public function save() {
*/
if ( ! empty( $this->validated ) ) {
foreach ( $this->validated as $field_id => $validated_field ) {
- // get the value and filter it
+ // Get the value and filter it.
$value = $validated_field->value;
$value = apply_filters( 'tribe_settings_save_field_value', $value, $field_id, $validated_field );
- // figure out the parent option [could be set to false] and filter it
+ // Figure out the parent option [could be set to false] and filter it.
if ( is_network_admin() ) {
$parent_option = ( isset( $validated_field->field['parent_option'] ) ) ? $validated_field->field['parent_option'] : Tribe__Main::OPTIONNAMENETWORK;
}
@@ -537,7 +568,7 @@ public function save() {
$parent_option = apply_filters( 'tribe_settings_save_field_parent_option', $parent_option, $field_id );
$network_option = isset( $validated_field->field['network_option'] ) ? (bool) $validated_field->field['network_option'] : false;
- // some hooks
+ // Some hooks.
do_action( 'tribe_settings_save_field', $field_id, $value, $validated_field );
do_action( 'tribe_settings_save_field_' . $field_id, $value, $validated_field );
@@ -548,7 +579,7 @@ public function save() {
update_option( $field_id, $value );
}
} else {
- // set the parent option
+ // Set the parent option.
$parent_options[ $parent_option ][ $field_id ] = $value;
}
}
@@ -561,23 +592,23 @@ public function save() {
* this will save using the Tribe__Settings_Manager::set_options method.
*/
foreach ( $parent_options as $option_id => $new_options ) {
- // get the old options
+ // Get the old options.
if ( is_network_admin() ) {
$old_options = (array) get_site_option( $option_id );
} else {
$old_options = (array) get_option( $option_id );
}
- // set the options by parsing old + new and filter that
+ // Set the options by parsing old + new and filter that.
$options = apply_filters( 'tribe_settings_save_option_array', wp_parse_args( $new_options, $old_options ), $option_id );
if ( $option_id == Tribe__Main::OPTIONNAME ) {
- // save using the Tribe__Settings_Manager method
+ // Save using the Tribe__Settings_Manager method.
Tribe__Settings_Manager::set_options( $options );
} elseif ( $option_id == Tribe__Main::OPTIONNAMENETWORK ) {
Tribe__Settings_Manager::set_network_options( $options );
} else {
- // save using regular WP method
+ // Save using regular WP method.
if ( is_network_admin() ) {
update_site_option( $option_id, $options );
} else {
@@ -586,54 +617,54 @@ public function save() {
}
}
- do_action( 'tribe_settings_after_save' );
- do_action( 'tribe_settings_after_save_' . $this->currentTab );
+ do_action( 'tribe_settings_after_save', $admin_page );
+ do_action( 'tribe_settings_after_save_' . $this->currentTab, $admin_page );
remove_action( 'shutdown', [ $this, 'deleteOptions' ] );
add_option( 'tribe_settings_sent_data', $_POST );
add_option( 'tribe_settings_errors', $this->errors );
add_option( 'tribe_settings_major_error', $this->major_error );
- wp_redirect( esc_url_raw( add_query_arg( [ 'saved' => true ], $this->url ) ) );
+ wp_redirect( esc_url_raw( add_query_arg( [ 'saved' => true ], $this->get_settings_page_url() ) ) );
exit;
}
/**
- * display errors, if any, after saving
+ * Display errors, if any, after saving.
*
* @return void
*/
public function displayErrors() {
-
- // fetch the errors and filter them
+ // Fetch the errors and filter them.
$errors = (array) apply_filters( 'tribe_settings_display_errors', $this->errors );
$count = apply_filters( 'tribe_settings_count_errors', count( $errors ) );
- if ( apply_filters( 'tribe_settings_display_errors_or_not', ( $count > 0 ) ) ) {
- // output a message if we have errors
+ // Bail if we don't have errors.
+ if ( ! apply_filters( 'tribe_settings_display_errors_or_not', ( $count > 0 ) ) ) {
+ return;
+ }
- $output = '
';
- $output .= esc_html__( 'Your form had the following errors:', 'tribe-common' );
- $output .= '
';
+ $output = '
';
+ $output .= esc_html__( 'Your form had the following errors:', 'tribe-common' );
+ $output .= '
';
- // loop through each error
- foreach ( $errors as $error ) {
- $output .= '
' . (string) $error . '
';
- }
+ // Loop through each error.
+ foreach ( $errors as $error ) {
+ $output .= '
' . (string) $error . '
';
+ }
- if ( count( $errors ) ) {
- $message = ( isset( $this->major_error ) && $this->major_error )
- ? esc_html__( 'None of your settings were saved. Please try again.' )
- : esc_html( _n( 'The above setting was not saved. Other settings were successfully saved.', 'The above settings were not saved. Other settings were successfully saved.', $count, 'tribe-common' ) );
- }
+ if ( count( $errors ) ) {
+ $message = ( isset( $this->major_error ) && $this->major_error )
+ ? esc_html__( 'None of your settings were saved. Please try again.' )
+ : esc_html( _n( 'The above setting was not saved. Other settings were successfully saved.', 'The above settings were not saved. Other settings were successfully saved.', $count, 'tribe-common' ) );
+ }
- $output .= '
' . $message . '
';
+ $output .= '
' . $message . '
';
- // final output, filtered of course
- echo apply_filters( 'tribe_settings_error_message', $output );
- }
+ // Final output, filtered of course.
+ echo apply_filters( 'tribe_settings_error_message', $output );
}
/**
- * display success message after saving
+ * Display success message after saving.
*
* @return void
*/
@@ -641,7 +672,7 @@ public function displaySuccess() {
$errors = (array) apply_filters( 'tribe_settings_display_errors', $this->errors );
$count = apply_filters( 'tribe_settings_count_errors', count( $errors ) );
- // are we coming from the saving place?
+ // Are we coming from the saving place?
if ( isset( $_GET['saved'] ) && ! apply_filters( 'tribe_settings_display_errors_or_not', ( $count > 0 ) ) ) {
// output the filtered message
$message = esc_html__( 'Settings saved.', 'tribe-common' );
@@ -649,12 +680,12 @@ public function displaySuccess() {
echo apply_filters( 'tribe_settings_success_message', $output, $this->currentTab );
}
- //Delete Temporary Options After Display Errors and Success
+ // Delete Temporary Options After Display Errors and Success.
$this->deleteOptions();
}
/**
- * delete temporary options
+ * Delete temporary options.
*
* @return void
*/
@@ -694,7 +725,7 @@ public function get_url( array $args = [] ) {
public function get_parent_slug() {
$slug = self::$parent_page;
- // if we don't have an event post type, then we can just use the tribe-common slug
+ // If we don't have an event post type, then we can just use the tribe-common slug.
if ( 'edit.php' === $slug || 'admin.php?page=tribe-common' === $slug ) {
$slug = self::$parent_slug;
}
diff --git a/tribe-common/src/Tribe/Settings_Manager.php b/tribe-common/src/Tribe/Settings_Manager.php
index 80d5cfa222..74f166cf37 100644
--- a/tribe-common/src/Tribe/Settings_Manager.php
+++ b/tribe-common/src/Tribe/Settings_Manager.php
@@ -11,7 +11,7 @@ class Tribe__Settings_Manager {
public function __construct() {
$this->add_hooks();
- // Load multisite defaults
+ // Load multisite defaults.
if ( is_multisite() ) {
$tribe_events_mu_defaults = [];
if ( file_exists( WP_CONTENT_DIR . '/tribe-events-mu-defaults.php' ) ) {
@@ -26,9 +26,7 @@ public function add_hooks() {
add_action( '_network_admin_menu', [ $this, 'init_options' ] );
add_action( '_admin_menu', [ $this, 'init_options' ] );
- add_action( 'admin_menu', [ $this, 'add_help_admin_menu_item' ], 50 );
add_action( 'tribe_settings_do_tabs', [ $this, 'do_setting_tabs' ] );
- add_action( 'tribe_settings_do_tabs', [ $this, 'do_network_settings_tab' ], 400 );
add_action( 'tribe_settings_validate_tab_network', [ $this, 'save_all_tabs_hidden' ] );
add_action( 'updated_option', [ $this, 'update_options_cache' ], 10, 3 );
}
@@ -46,7 +44,7 @@ public function add_hooks() {
* @return void
*/
public function update_options_cache( $option, $old_value, $value ) {
- // Bail when no our option.
+ // Bail when not our option.
if ( Tribe__Main::OPTIONNAME !== $option ) {
return;
}
@@ -72,14 +70,6 @@ public function do_setting_tabs() {
// Make sure Thickbox is available regardless of which admin page we're on
add_thickbox();
- include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-general.php';
- include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-display.php';
-
- $showNetworkTabs = $this->get_network_option( 'showSettingsTabs', false );
-
- new Tribe__Settings_Tab( 'general', esc_html__( 'General', 'tribe-common' ), $generalTab );
- new Tribe__Settings_Tab( 'display', esc_html__( 'Display', 'tribe-common' ), $displayTab );
-
$this->do_licenses_tab();
}
@@ -151,8 +141,8 @@ public static function set_options( $options, $apply_filters = true ) {
/**
* Set an option
*
- * @param string $name
- * @param mixed $value
+ * @param string $name The option key or 'name'.
+ * @param mixed $value The value we want to set.
*
* @return bool
*/
@@ -160,7 +150,23 @@ public static function set_option( $name, $value ) {
$options = self::get_options();
$options[ $name ] = $value;
- return self::set_options( $options );
+ return static::set_options( $options );
+ }
+
+ /**
+ * Remove an option. Actually remove (unset), as opposed to setting to null/empty string/etc.
+ *
+ * @since 4.14.13
+ *
+ * @param string $name The option key or 'name'.
+ *
+ * @return bool
+ */
+ public static function remove_option( $name ) {
+ $options = self::get_options();
+ unset( $options[ $name ] );
+
+ return static::set_options( $options );
}
/**
@@ -217,8 +223,18 @@ public static function set_network_options( $options, $apply_filters = true ) {
return;
}
- if ( $apply_filters == true ) {
- $options = apply_filters( 'tribe-events-save-network-options', $options );
+ if (
+ isset( $_POST['tribeSaveSettings'] )
+ && isset( $_POST['current-settings-tab'] )
+ ) {
+ $options['hideSettingsTabs'] = tribe_get_request_var( 'hideSettingsTabs', [] );
+ }
+
+ $admin_pages = tribe( 'admin.pages' );
+ $admin_page = $admin_pages->get_current_page();
+
+ if ( true === $apply_filters ) {
+ $options = apply_filters( 'tribe-events-save-network-options', $options, $admin_page );
}
if ( update_site_option( Tribe__Main::OPTIONNAMENETWORK, $options ) ) {
@@ -234,18 +250,7 @@ public static function set_network_options( $options, $apply_filters = true ) {
* @return void
*/
public static function add_network_options_page() {
- $tribe_settings = Tribe__Settings::instance();
- add_submenu_page(
- 'settings.php',
- $tribe_settings->menuName,
- $tribe_settings->menuName,
- 'manage_network_options',
- 'tribe-common',
- [
- $tribe_settings,
- 'generatePage',
- ]
- );
+ _deprecated_function( __METHOD__, '4.15.0' );
}
/**
@@ -254,9 +259,7 @@ public static function add_network_options_page() {
* @return void
*/
public static function do_network_settings_tab() {
- include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-network.php';
-
- new Tribe__Settings_Tab( 'network', esc_html__( 'Network', 'tribe-common' ), $networkTab );
+ _deprecated_function( __METHOD__, '4.15.0' );
}
/**
@@ -305,25 +308,16 @@ public function do_help_tab() {
* Include Help tab Assets here
*/
- include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/tribe-options-help.php';
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/help.php';
}
/**
* Add help menu item to the admin (unless blocked via network admin settings).
*
- * @todo move to an admin class
+ * @deprecated 5.0.2
*/
public function add_help_admin_menu_item() {
- $hidden_settings_tabs = self::get_network_option( 'hideSettingsTabs', [] );
- if ( in_array( 'help', $hidden_settings_tabs ) ) {
- return;
- }
-
- $parent = class_exists( 'Tribe__Events__Main' ) ? Tribe__Settings::$parent_page : Tribe__Settings::$parent_slug;
- $title = esc_html__( 'Help', 'tribe-common' );
- $slug = 'tribe-help';
-
- add_submenu_page( $parent, $title, $title, 'manage_options', $slug, [ $this, 'do_help_tab' ] );
+ _deprecated_function( __METHOD__, '5.0.2', 'Now handled by Tribe\Events\Admin\Settings::add_admin_pages()' );
}
/**
@@ -347,12 +341,6 @@ public function save_all_tabs_hidden() {
$network_options = (array) get_site_option( Tribe__Main::OPTIONNAMENETWORK );
- if ( isset( $_POST['hideSettingsTabs'] ) && $_POST['hideSettingsTabs'] == $all_tabs_keys ) {
- $network_options['allSettingsTabsHidden'] = '1';
- } else {
- $network_options['allSettingsTabsHidden'] = '0';
- }
-
$this->set_network_options( $network_options );
}
diff --git a/tribe-common/src/Tribe/Settings_Tab.php b/tribe-common/src/Tribe/Settings_Tab.php
index 11e15a5aad..8b3d00ed6b 100755
--- a/tribe-common/src/Tribe/Settings_Tab.php
+++ b/tribe-common/src/Tribe/Settings_Tab.php
@@ -219,7 +219,7 @@ public function doContent() {
}
} else {
// no fields setup for this tab yet
- echo '
' . esc_html__( 'There are no fields setup for this tab yet.', 'tribe-common' ) . '
';
+ echo '
' . esc_html__( 'There are no fields set up for this tab yet.', 'tribe-common' ) . '
';
}
}
diff --git a/tribe-common/src/Tribe/Support.php b/tribe-common/src/Tribe/Support.php
index 3f94018591..1c7be122f3 100755
--- a/tribe-common/src/Tribe/Support.php
+++ b/tribe-common/src/Tribe/Support.php
@@ -15,6 +15,15 @@ class Tribe__Support {
public static $support;
public $rewrite_rules_purged = false;
+ /**
+ * The wp_options key used to store the optin_key
+ *
+ * @since 4.14.5
+ *
+ * @var string
+ */
+ public static $option_key = 'tribe_systeminfo_optin';
+
/**
* @var Tribe__Support__Obfuscator
*/
@@ -29,6 +38,9 @@ class Tribe__Support {
protected $must_escape = [
'tribeEventsAfterHTML',
'tribeEventsBeforeHTML',
+ 'dateWithYearFormat',
+ 'dateWithoutYearFormat',
+ 'monthAndYearFormat',
];
/**
@@ -216,9 +228,21 @@ public function getSupportStats() {
* Allow for customization of the array of information that's turned into the "System Information" screen in the "Help" admin page.
*
* @param array $systeminfo The array of information turned into the "System Information" screen.
+ *
+ * @deprecated 4.14.13 Using a newer format of filter.
*/
$systeminfo = apply_filters( 'tribe-events-pro-support', $systeminfo );
+ /**
+ * Allow for customization of the array of information that's turned into the "System Information" screen in the "Help" admin page.
+ *
+ * @since 4.14.13
+ *
+ * @param array $systeminfo The array of information turned into the "System Information" screen.
+ *
+ */
+ $systeminfo = apply_filters( 'tec_system_information', $systeminfo );
+
return $systeminfo;
}
@@ -256,28 +280,25 @@ public function formattedSupportStats() {
if ( empty( $v ) ) {
$output .= '
' . esc_html__( 'Your system information will only be used by The Events Calendar\'s support team. All information is stored securely. We do not share this information with any third parties.', 'tribe-common' ) . '
';
$opt_in .= '';
@@ -332,7 +350,7 @@ public static function opt_in() {
*/
public static function sysinfo_query( $query ) {
- $optin_key = get_option( 'tribe_systeminfo_optin' );
+ $optin_key = get_option( self::$option_key );
if ( ! $optin_key ) {
wp_send_json_error( __( 'Invalid Key', 'tribe-common' ) );
@@ -350,10 +368,10 @@ public static function sysinfo_query( $query ) {
}
/*
- * Create Unique Enpoint Per Site
+ * Create Unique Endpoint Per Site
*/
public static function create_sysinfo_endpoint() {
- $optin_key = get_option( 'tribe_systeminfo_optin' );
+ $optin_key = get_option( self::$option_key );
if ( $optin_key ) {
register_rest_route(
'tribe_events/v2',
@@ -377,25 +395,25 @@ public static function ajax_sysinfo_optin() {
}
if ( 'generate' == $_POST['generate_key'] ) {
-
$random = base_convert( rand( 0, getrandmax() ), 10, 36 );
$optin_key = hash( 'sha1', $random );
- update_option( 'tribe_systeminfo_optin', $optin_key );
+
+ update_option( self::$option_key, $optin_key );
//Only Connect If a License Exists
$keys = apply_filters( 'tribe-pue-install-keys', [] );
if ( is_array( $keys ) && ! empty( $keys ) ) {
- Tribe__Support::send_sysinfo_key( $optin_key );
+ self::send_sysinfo_key( $optin_key );
} else {
wp_send_json_success( __( 'Unique System Info Key Generated', 'tribe-common' ) );
}
} elseif ( 'remove' == $_POST['generate_key'] ) {
- $optin_key = get_option( 'tribe_systeminfo_optin' );
+ $optin_key = get_option( self::$option_key );
- delete_option( 'tribe_systeminfo_optin' );
+ delete_option( self::$option_key );
- Tribe__Support::send_sysinfo_key( $optin_key, null, 'remove' );
+ self::send_sysinfo_key( $optin_key, null, 'remove' );
}
@@ -411,16 +429,9 @@ public static function ajax_sysinfo_optin() {
* @param null $pueadd boolean to disable messaging when coming from pue script
*/
public static function send_sysinfo_key( $optin_key = null, $url = null, $remove = null, $pueadd = false ) {
-
- $url = $url ? $url : urlencode( str_replace( [ 'http://', 'https://' ], '', get_site_url() ) );
-
- $teccom_url = 'https://theeventscalendar.com/';
-
- if ( defined( 'TEC_URL' ) ) {
- $teccom_url = trailingslashit( TEC_URL );
- }
-
- $query = $teccom_url . 'wp-json/tribe_system/v2/customer-info/' . $optin_key . '/' . $url;
+ $url = $url ? $url : urlencode( str_replace( [ 'http://', 'https://' ], '', get_site_url() ) );
+ $teccom_url = defined( 'TEC_URL' ) ? TEC_URL : 'https://theeventscalendar.com';
+ $query = trailingslashit( $teccom_url ) . 'wp-json/tribe_system/v2/customer-info/' . $optin_key . '/' . $url;
if ( $remove ) {
$query .= '?status=remove';
@@ -434,7 +445,7 @@ public static function send_sysinfo_key( $optin_key = null, $url = null, $remove
// make sure the response came back okay
if ( ! isset( $response->success ) ) {
//on error delete the key
- delete_option( 'tribe_systeminfo_optin' );
+ delete_option( self::$option_key );
//send error response
wp_send_json_error( $response );
diff --git a/tribe-common/src/Tribe/Template.php b/tribe-common/src/Tribe/Template.php
index e24465da0b..c752eacd19 100644
--- a/tribe-common/src/Tribe/Template.php
+++ b/tribe-common/src/Tribe/Template.php
@@ -292,7 +292,7 @@ public function get_template_current_hook_name() {
* @since 4.6.2
*
* @param array|string $index Specify each nested index in order.
- * Example: array( 'lvl1', 'lvl2' );
+ * Example: [ 'lvl1', 'lvl2' ];
* @param mixed $default Default value if the search finds nothing.
* @param boolean $is_local Use the Local or Global context.
*
@@ -313,7 +313,7 @@ final public function get( $index, $default = null, $is_local = true ) {
*
* @param mixed $value The value that will be filtered.
* @param array|string $index Specify each nested index in order.
- * Example: array( 'lvl1', 'lvl2' );
+ * Example: [ 'lvl1', 'lvl2' ];
* @param mixed $default Default value if the search finds nothing.
* @param boolean $is_local Use the Local or Global context.
* @param self $template Current instance of the Tribe__Template.
@@ -356,13 +356,13 @@ final public function set( $index, $value = null, $is_local = true ) {
}
/**
- * Merges local and global context, and saves it locally
+ * Merges local and global context, and saves it locally.
*
* @since 4.6.2
*
- * @param array $context Local Context array of data
- * @param string $file Complete path to include the PHP File
- * @param array $name Template name
+ * @param array $context Local Context array of data.
+ * @param string $file Complete path to include the PHP File.
+ * @param array $name Template name.
*
* @return array
*/
@@ -376,17 +376,31 @@ public function merge_context( $context = [], $file = null, $name = null ) {
$context = wp_parse_args( (array) $context, $this->get_values() );
/**
- * Allows filtering the Local context
+ * Allows filtering the Local context.
*
* @since 4.6.2
*
- * @param array $context Local Context array of data
- * @param string $file Complete path to include the PHP File
- * @param array $name Template name
- * @param self $template Current instance of the Tribe__Template
+ * @param array $context Local Context array of data.
+ * @param string $file Complete path to include the PHP File.
+ * @param array $name Template name.
+ * @param self $template Current instance of the Tribe__Template.
*/
$this->context = apply_filters( 'tribe_template_context', $context, $file, $name, $this );
+ $hook_name = $this->get_template_current_hook_name();
+
+ /**
+ * Allows filtering the Local context specifically to the template with the hook name passed to the method.
+ *
+ * @since 4.12.13
+ *
+ * @param array $context Local Context array of data.
+ * @param string $file Complete path to include the PHP File.
+ * @param array $name Template name.
+ * @param self $template Current instance of the Tribe__Template.
+ */
+ $this->context = apply_filters( "tribe_template_context:{$hook_name}", $this->context, $file, $name, $this );
+
return $this->context;
}
@@ -483,7 +497,7 @@ protected function get_template_public_path( $base, $namespace ) {
// Craft the plugin Path
$path = array_merge( (array) $base, (array) $this->get_template_public_namespace( $namespace ) );
- // Pick up if the folder needs to be aded to the public template path.
+ // Pick up if the folder needs to be added to the public template path.
$folder = array_diff( $this->folder, $this->get_template_origin_base_folder() );
if ( ! empty( $folder ) ) {
@@ -507,7 +521,7 @@ protected function get_template_public_path( $base, $namespace ) {
/**
* Fetches the folders in which we will look for a given file
*
- * @since 4.7.20
+ * @since 4.7.20
* @since 4.12.10 Add support for common lookup.
*
* @return array A list of possible locations for the template file.
@@ -593,7 +607,7 @@ protected function get_template_theme_path_list( $namespace ) {
*
* @since 4.7.20
*
- * @param mixed $name File name we are looking for
+ * @param mixed $name File name we are looking for.
*
* @return string
*/
@@ -1463,6 +1477,7 @@ protected function actions_after_template( $file, $name, $hook_name ) {
* @param self $template Current instance of the Tribe__Template.
*/
do_action( "tribe_template_after_include:{$hook_name}", $file, $name, $this );
+
return ob_get_clean();
}
}
diff --git a/tribe-common/src/Tribe/Template_Factory.php b/tribe-common/src/Tribe/Template_Factory.php
deleted file mode 100755
index 3635e7a113..0000000000
--- a/tribe-common/src/Tribe/Template_Factory.php
+++ /dev/null
@@ -1,219 +0,0 @@
-asset_packages();
- }
-
- /**
- * Manage the asset packages defined for this template
- *
- * @deprecated 4.7.18
- *
- * @return void
- **/
- protected function asset_packages() {
- foreach ( $this->asset_packages as $asset_package ) {
- $this->asset_package( $asset_package );
- }
- }
-
- /**
- * Handles an asset package request.
- *
- * @deprecated 4.7.18
- *
- * @param string $name The asset name in the `hyphen-separated-format`
- * @param array $deps An array of dependency handles
- * @param string $vendor_url URL to vendor scripts and styles dir
- * @param string $prefix MT script and style prefix
- * @param Tribe__Main $tec An instance of the main plugin class
- */
- protected static function handle_asset_package_request( $name, $deps, $vendor_url, $prefix, $tec ) {
-
- $asset = self::get_asset_factory_instance( $name );
-
- self::prepare_asset_package_request( $asset, $name, $deps, $vendor_url, $prefix, $tec );
- }
-
- /**
- * initializes asset package request
- *
- * @deprecated 4.7.18
- *
- * @param object $asset The Tribe__*Asset object
- * @param string $name The asset name in the `hyphen-separated-format`
- * @param array $deps An array of dependency handles
- * @param string $vendor_url URL to vendor scripts and styles dir
- * @param string $prefix MT script and style prefix
- * @param Tribe__Main $common An instance of the main plugin class
- */
- protected static function prepare_asset_package_request( $asset, $name, $deps, $vendor_url, $prefix, $common ) {
- if ( ! $asset ) {
- do_action( $prefix . '-' . $name );
-
- return;
- }
-
- $asset->set_name( $name );
- $asset->set_deps( $deps );
- $asset->set_vendor_url( $vendor_url );
- $asset->set_prefix( $prefix );
- $asset->set_tec( $common );
-
- $asset->handle();
- }
-
- /**
- * Retrieves the appropriate asset factory instance
- *
- * @deprecated 4.7.18
- *
- */
- protected static function get_asset_factory_instance( $name ) {
- $asset = Tribe__Asset__Factory::instance()->make_for_name( $name );
- return $asset;
- }
-
- /**
- *
- * @deprecated 4.7.18
- *
- * @param string $script_handle A registered script handle.
- */
- public static function add_vendor_script( $script_handle ) {
- if ( in_array( $script_handle, self::$vendor_scripts ) ) {
- return;
- }
- self::$vendor_scripts[] = $script_handle;
- }
-
- /**
- * @return string[] An array of registered vendor script handles.
- */
- public static function get_vendor_scripts() {
- return self::$vendor_scripts;
- }
-
- /**
- * Asset calls for vendor packages
- *
- * @deprecated 4.7.18
- *
- * @param string $name
- * @param array $deps Dependents
- */
- public static function asset_package( $name, $deps = [] ) {
-
- $common = Tribe__Main::instance();
- $prefix = 'tribe-events';
-
- // setup plugin resources & 3rd party vendor urls
- $vendor_url = trailingslashit( $common->plugin_url ) . 'vendor/';
-
- self::handle_asset_package_request( $name, $deps, $vendor_url, $prefix, $common );
- }
-
- /**
- * Returns the path to a minified version of a js or css file, if it exists.
- * If the file does not exist, returns false.
- *
- * @deprecated 4.7.18
- *
- * @param string $url The path or URL to the un-minified file.
- * @param bool $default_to_original Whether to just return original path if min version not found.
- *
- * @return string|false The path/url to minified version or false, if file not found.
- */
- public static function getMinFile( $url, $default_to_original = false ) {
- if ( ! defined( 'SCRIPT_DEBUG' ) || SCRIPT_DEBUG === false ) {
- if ( substr( $url, - 3, 3 ) == '.js' ) {
- $url_new = substr_replace( $url, '.min', - 3, 0 );
- }
- if ( substr( $url, - 4, 4 ) == '.css' ) {
- $url_new = substr_replace( $url, '.min', - 4, 0 );
- }
- }
-
- if ( isset( $url_new ) && file_exists( str_replace( content_url(), WP_CONTENT_DIR, $url_new ) ) ) {
- return $url_new;
- } elseif ( $default_to_original ) {
- return $url;
- } else {
- return false;
- }
- }
-
- /**
- * Playing ping-pong with WooCommerce. They keep changing their script.
- *
- * @deprecated 4.7.18
- *
- * @see https://github.com/woothemes/woocommerce/issues/3623
- */
- public static function get_placeholder_handle() {
- $placeholder_handle = 'jquery-placeholder';
-
- global $woocommerce;
- if (
- class_exists( 'Woocommerce' ) &&
- version_compare( $woocommerce->version, '2.0.11', '>=' ) &&
- version_compare( $woocommerce->version, '2.0.13', '<=' )
- ) {
- $placeholder_handle = 'tribe-placeholder';
- }
-
- return $placeholder_handle;
- }
-}
diff --git a/tribe-common/src/Tribe/Timezones.php b/tribe-common/src/Tribe/Timezones.php
index 8af5da2531..2edd8f3d69 100644
--- a/tribe-common/src/Tribe/Timezones.php
+++ b/tribe-common/src/Tribe/Timezones.php
@@ -66,8 +66,16 @@ public static function wp_timezone_abbr( $date ) {
* @return string
*/
public static function wp_timezone_string() {
- $current_offset = get_option( 'gmt_offset' );
- $tzstring = get_option( 'timezone_string' );
+ $cache = tribe( 'cache' );
+ if ( empty( $cache['option_timezone_string'] ) ) {
+ $cache['option_timezone_string'] = get_option( 'timezone_string' );
+ }
+ if ( ! isset( $cache['option_gmt_offset'] ) ) {
+ $cache['option_gmt_offset'] = get_option( 'gmt_offset' );
+ }
+
+ $current_offset = $cache['option_gmt_offset'];
+ $tzstring = $cache['option_timezone_string'];
// Return the timezone string if already set
if ( ! empty( $tzstring ) ) {
diff --git a/tribe-common/src/Tribe/Tracker.php b/tribe-common/src/Tribe/Tracker.php
index 2d43c1f0e2..20e5413917 100644
--- a/tribe-common/src/Tribe/Tracker.php
+++ b/tribe-common/src/Tribe/Tracker.php
@@ -282,8 +282,12 @@ public function filter_watch_updated_meta( $check, $post_id, $meta_key, $meta_va
// If we got here we will update the Modified Meta
$modified[ $meta_key ] = $now;
+ // Avoid loops!
+ remove_filter( 'update_post_metadata', [ $this, 'filter_watch_updated_meta' ], PHP_INT_MAX - 1 );
// Actually do the Update
update_post_meta( $post->ID, self::$field_key, $modified );
+ // Safe to filter again.
+ add_filter( 'update_post_metadata', [ $this, 'filter_watch_updated_meta' ], PHP_INT_MAX - 1, 5 );
// We need to return this, because we are still on a filter
return $check;
diff --git a/tribe-common/src/Tribe/Utils/Array.php b/tribe-common/src/Tribe/Utils/Array.php
index c9ede39602..0f79618869 100644
--- a/tribe-common/src/Tribe/Utils/Array.php
+++ b/tribe-common/src/Tribe/Utils/Array.php
@@ -35,7 +35,7 @@ public static function set( array $array, $key, $value ) {
// would likely lead to unexpected problems for whatever first set it.
$error = sprintf(
'Attempted to set $array[%1$s] but %2$s is already set and is not an array.',
- implode( $key, '][' ),
+ implode( '][', $key ),
$i
);
@@ -667,5 +667,66 @@ public static function merge_recursive_query_vars( array ...$arrays ) {
// Finally destringify the keys to return something that will resemble, in shape, the original arrays.
return static::destringify_keys( $merged );
}
+
+ /**
+ * Shapes, filtering it, an array to the specified expected set of required keys.
+ *
+ * @since 5.0.0
+ *
+ * @param array $array The input array to shape.
+ * @param array $shape The shape to update the array with. It should only define keys
+ * or arrays of keys. Keys that have no values will be set to `null`.
+ * To add the key only if set, prefix the key with `?`, e.g. `?foo`.
+ *
+ * @return array The input array shaped and ordered per the shape.
+ */
+ public static function shape_filter( array $array, array $shape ): array {
+ $shaped = [];
+ foreach ( $shape as $shape_index => $shape_key ) {
+ $optional = is_array( $shape_key ) ?
+ strpos( $shape_index, '?' ) === 0
+ : strpos( $shape_key, '?' ) === 0;
+
+ if ( is_array( $shape_key ) ) {
+ $shape_index = $optional ? substr( $shape_index, 1 ) : $shape_index;
+ if ( $optional && ! isset( $array[ $shape_index ] ) ) {
+ continue;
+ }
+ $shaped[ $shape_index ] = self::shape_filter( $array[$shape_index] ?? [], $shape_key );
+ } else {
+ $shape_key = $optional ? substr( $shape_key, 1 ) : $shape_key;
+ if ( ! isset( $array[ $shape_key ] ) && $optional ) {
+ continue;
+ }
+ $shaped[ $shape_key ] = $array[ $shape_key ] ?? null;
+ }
+ }
+
+ return $shaped;
+ }
+
+ /**
+ * Searches an array using a callback and returns the index of the first match.
+ *
+ * This method fills the gap left by the non-existence of an `array_usearch` function.
+ *
+ * @since 5.0.0
+ *
+ * @param mixed $needle The element to search in the array.
+ * @param array $haystack The array to search.
+ * @param callable $callback A callback function with signature `fn($needle, $value, $key) :bool`
+ * that will be used to find the first match of needle in haystack.
+ *
+ * @return string|int|false Either the index of the first match or `false` if no match was found.
+ */
+ public static function usearch( $needle, array $haystack, callable $callback ) {
+ foreach ( $haystack as $key => $value ) {
+ if ( $callback( $needle, $value, $key ) ) {
+ return $key;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/tribe-common/src/Tribe/Utils/Body_Classes.php b/tribe-common/src/Tribe/Utils/Body_Classes.php
index 6e8b702811..7f8da5d376 100644
--- a/tribe-common/src/Tribe/Utils/Body_Classes.php
+++ b/tribe-common/src/Tribe/Utils/Body_Classes.php
@@ -340,9 +340,9 @@ private function should_add_body_classes( array $add_classes, array $existing_cl
* @since 4.12.6
*
* @param boolean $add Whether to add classes or not.
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
* @param array $add_classes The array of body class names to add.
* @param array $existing_classes An array of existing body class names from WP.
- * @param string $queue The queue we want to get 'admin', 'display', 'all'.
*
*/
return (bool)apply_filters( 'tribe_body_classes_should_add', false, $queue, $add_classes, $existing_classes );
diff --git a/tribe-common/src/Tribe/Utils/Collection_Trait.php b/tribe-common/src/Tribe/Utils/Collection_Trait.php
index aac291d51e..c0c34c4b28 100644
--- a/tribe-common/src/Tribe/Utils/Collection_Trait.php
+++ b/tribe-common/src/Tribe/Utils/Collection_Trait.php
@@ -67,7 +67,6 @@ public function nth( $n ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetExists( $offset ) {
$items = $this->all();
@@ -77,7 +76,6 @@ public function offsetExists( $offset ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetGet( $offset ) {
$items = $this->all();
@@ -89,7 +87,6 @@ public function offsetGet( $offset ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetSet( $offset, $value ) {
$this->items = $this->all();
@@ -99,7 +96,6 @@ public function offsetSet( $offset, $value ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetUnset( $offset ) {
$this->items = $this->all();
@@ -109,7 +105,6 @@ public function offsetUnset( $offset ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function next() {
$this->items_index ++;
}
@@ -117,7 +112,6 @@ public function next() {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function valid() {
$items = $this->all();
@@ -127,7 +121,6 @@ public function valid() {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function key() {
return $this->items_index;
}
@@ -135,7 +128,6 @@ public function key() {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function current() {
$items = array_values( $this->all() );
@@ -145,7 +137,6 @@ public function current() {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function rewind() {
$this->items_index = 0;
}
@@ -153,7 +144,6 @@ public function rewind() {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function count() {
return count( $this->all() );
}
diff --git a/tribe-common/src/Tribe/Utils/Color.php b/tribe-common/src/Tribe/Utils/Color.php
index de5213278c..8426e5b881 100644
--- a/tribe-common/src/Tribe/Utils/Color.php
+++ b/tribe-common/src/Tribe/Utils/Color.php
@@ -7,6 +7,8 @@
*
* @package Common
* @since 4.3
+ *
+ * @since 4.14.2 Added get_hex_with_hash function.
*/
/**
@@ -167,7 +169,6 @@ public static function hslToHex( $hsl = [] ) {
return $r.$g.$b;
}
-
/**
* Given a HEX string returns a RGB array equivalent.
* @param string $color
@@ -190,7 +191,6 @@ public static function hexToRgb( $color ) {
return $RGB;
}
-
/**
* Given an RGB associative array returns the equivalent HEX string
* @param array $rgb
@@ -209,9 +209,7 @@ public static function rgbToHex( $rgb = [] ) {
$hex[2] = dechex( $rgb['B'] );
return implode( '', $hex );
-
- }
-
+ }
/**
* Given a HEX value, returns a darker color. If no desired amount provided, then the color halfway between
@@ -272,7 +270,6 @@ public function makeGradient( $amount = self::DEFAULT_ADJUST ) {
return [ 'light' => $lightColor, 'dark' => $darkColor ];
}
-
/**
* Returns whether or not given color is considered "light"
* @param string|Boolean $color
@@ -329,12 +326,25 @@ public function complementary() {
public function getHsl() {
return $this->_hsl;
}
+
/**
- * Returns your original color
+ * Returns your original color minus any hash mark.
*/
public function getHex() {
return $this->_hex;
}
+
+ /**
+ * Returns your original color with the hash mark.
+ *
+ * @since 4.14.2
+ *
+ * @return string Hex color code with hash prefix.
+ */
+ public function get_hex_with_hash() {
+ return '#' . $this->_hex;
+ }
+
/**
* Returns your color's RGB array
*/
@@ -503,5 +513,4 @@ private static function _checkHex( $hex ) {
return $color;
}
-
}
diff --git a/tribe-common/src/Tribe/Utils/Compatibility_Classes.php b/tribe-common/src/Tribe/Utils/Compatibility_Classes.php
new file mode 100644
index 0000000000..972c0112c1
--- /dev/null
+++ b/tribe-common/src/Tribe/Utils/Compatibility_Classes.php
@@ -0,0 +1,352 @@
+ true, 'class => false ]
+ *
+ * @var array
+ */
+ protected $classes = [];
+
+ /**
+ * Stores all the admin classes.
+ * In the format: ['class' => true, 'class => false ]
+ *
+ * @var array
+ */
+ protected $admin_classes = [];
+
+ /**
+ * Queue-aware method to get the classes array.
+ * Returns the array of classes to add.
+ *
+ * @since 4.14.0
+ *
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ * @return array A map of the classes for the queue.
+ */
+ public function get_classes( $queue = 'display' ) {
+ switch( $queue ) {
+ case 'admin':
+ return $this->admin_classes;
+ break;
+ case 'all':
+ return array_merge( $this->classes, $this->admin_classes );
+ break;
+ default:
+ return $this->classes;
+ break;
+ }
+ }
+
+ /**
+ * Returns the array of classnames to add
+ *
+ * @since 4.14.0
+ *
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ * @return array The list of class names.
+ */
+ public function get_class_names( $queue = 'display' ) {
+ $classes = $this->get_classes( $queue );
+
+ return array_keys(
+ array_filter(
+ $classes,
+ static function( $v ) {
+ return $v;
+ },
+ ARRAY_FILTER_USE_KEY
+ )
+ );
+ }
+
+ /**
+ * Checks if a class is in the queue,
+ * wether it's going to be added or not.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class The class we are checking for.
+ * @param string $queue The queue we want to check 'admin', 'display', 'all'
+ * @return boolean Whether a class exists or not in the queue.
+ */
+ public function class_in_queue( $class, $queue = 'display' ) {
+ $classes = $this->get_classes( $queue );
+
+ return array_key_exists( $class, $classes );
+ }
+
+ /**
+ * Checks if a class is in the queue and going to be added.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class The class we are checking for.
+ * @param string $queue The queue we want to check 'admin', 'display', 'all'
+ * @return boolean Whether a class is currently queued or not.
+ */
+ public function class_is_enqueued( $class, $queue = 'display' ) {
+ $classes = $this->get_classes( $queue );
+ if ( ! $this->class_in_queue( $class, $queue ) ) {
+ return false;
+ }
+
+ return $classes[ $class ];
+ }
+
+ /**
+ * Dequeues a class.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class
+ * @param string $queue The queue we want to alter 'admin', 'display', 'all'
+ * @return boolean
+ */
+ public function dequeue_class( $class, $queue = 'display' ) {
+ if ( ! $this->class_in_queue( $class, $queue ) ) {
+ return false;
+ }
+
+ if ( 'admin' !== $queue ) {
+ $this->classes[ $class ] = false;
+ }
+
+ if ( 'display' !== $queue ) {
+ $this->admin_classes[ $class ] = false;
+ }
+
+ return true;
+
+ }
+
+ /**
+ * Enqueues a class.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class
+ * @param string $queue The queue we want to alter 'admin', 'display', 'all'
+ * @return false
+ */
+ public function enqueue_class( $class, $queue = 'display' ) {
+ if ( ! $this->class_in_queue( $class, $queue ) ) {
+ return false;
+ }
+
+ if ( 'admin' !== $queue ) {
+ $this->classes[ $class ] = true;
+ return true;
+ }
+
+ if ( 'display' !== $queue ) {
+ $this->admin_classes[ $class ] = true;
+ return true;
+ }
+
+ // Something went wrong.
+ return false;
+ }
+
+ /**
+ * Add a single class to the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class The class to add.
+ * @param string $queue The queue we want to alter 'admin', 'display', 'all'
+ * @return void
+ */
+ public function add_class( $class, $queue = 'display' ) {
+ if ( empty( $class ) ) {
+ return;
+ }
+
+ if ( is_array( $class ) ) {
+ $this->add_classes( $class, $queue );
+ } elseif ( $this->should_add_compatibility_class_to_queue( $class, $queue ) ) {
+
+ $class = sanitize_html_class( $class );
+
+ if ( 'admin' !== $queue ) {
+ $this->classes[ $class ] = true ;
+ }
+
+ if ( 'display' !== $queue ) {
+ $this->admin_classes[ $class ] = true ;
+ }
+ }
+ }
+
+ /**
+ * Add an array of classes to the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param array $class The classes to add.
+ * @return void
+ */
+ public function add_classes( array $classes, $queue = 'display' ) {
+ foreach ( $classes as $key => $value ) {
+ // If the classes are passed as class => bool, only add ones set to true.
+ if ( is_bool( $value ) && false !== $value ) {
+ $this->add_class( $key, $queue );
+ } else {
+ $this->add_class( $value, $queue );
+ }
+ }
+ }
+
+ /**
+ * Remove a single class from the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class The class to remove.
+ * @return void
+ */
+ public function remove_class( $class, $queue = 'display' ) {
+ if ( 'admin' !== $queue ) {
+ $this->classes = array_filter(
+ $this->classes,
+ static function( $k ) use ( $class ) {
+ return $k !== $class;
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+
+ if ( 'display' !== $queue ) {
+ $this->admin_classes = array_filter(
+ $this->admin_classes,
+ static function( $k ) use ( $class ) {
+ return $k !== $class;
+ },
+ ARRAY_FILTER_USE_KEY
+ );
+ }
+ }
+
+ /**
+ * Remove an array of classes from the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param array $classes The classes to remove.
+ * @return void
+ */
+ public function remove_classes( array $classes, $queue = 'display' ) {
+ if ( empty( $classes ) || ! is_array( $classes) ) {
+ return;
+ }
+
+ foreach ( $classes as $class ) {
+ $this->remove_class( $class, $queue );
+ }
+ }
+
+ /**
+ * Adds the enqueued classes to the compatibility class array.
+ *
+ * @since 4.14.0
+ *
+ * @param array $classes An array of compatibility class names.
+ * @return array Array of compatibility classes.
+ */
+ public function add_compatibility_classes( $classes = [] ) {
+ // Make sure they should be added.
+ if( ! $this->should_add_compatibility_classes( $this->get_class_names(), (array) $classes, 'display' ) ) {
+ return $classes;
+ }
+
+ $element_classes = new Element_Classes( $this->get_class_names() );
+
+ return array_merge( $classes, $element_classes->get_classes() );
+ }
+
+ /**
+ * Adds the enqueued classes to the compatibility class array.
+ *
+ * @since 4.14.0
+ *
+ * @param string $classes The existing compatibility class names.
+ *
+ * @return string String of admin compatibility classes.
+ */
+ public function add_admin_compatibility_classes( $classes ) {
+ $existing_classes = explode( ' ', $classes );
+ // Make sure they should be added.
+ if ( ! $this->should_add_compatibility_classes( $this->get_class_names( 'admin' ), (array) $existing_classes, 'admin' ) ) {
+ // Ensure we return the current string on false!
+ return $classes;
+ }
+
+ $element_classes = new Element_Classes( array_merge( $existing_classes, $this->get_class_names( 'admin' ) ) );
+
+ return $element_classes->get_classes_as_string();
+
+ }
+
+ /**
+ * Should a individual class be added to the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param string $class The compatibility class we wish to add.
+ *
+ * @return boolean Whether to add tribe compatibility classes to the queue.
+ */
+ private function should_add_compatibility_class_to_queue( $class, $queue = 'display' ) {
+ /**
+ * Filter whether to add the compatibility class to the queue or not.
+ *
+ * @since 4.14.0
+ *
+ * @param boolean $add Whether to add the class to the queue or not.
+ * @param array $class The array of compatibility class names to add.
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ */
+ return (bool) apply_filters( 'tribe_compatibility_class_should_add_to_queue', false, $class, $queue );
+ }
+
+ /**
+ * Logic for whether the compatibility classes, as a whole, should be added.
+ *
+ * @since 4.14.0
+ *
+ * @param array $add_classes An array of compatibility class names to add.
+ * @param array $existing_classes An array of existing compatibility class names from WP.
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ *
+ * @return boolean Whether to add tribe compatibility classes.
+ */
+ private function should_add_compatibility_classes( array $add_classes, array $existing_classes, $queue ) {
+ /**
+ * Filter whether to add tribe compatibility classes or not.
+ *
+ * @since 4.14.0
+ *
+ * @param boolean $add Whether to add classes or not.
+ * @param array $add_classes The array of compatibility class names to add.
+ * @param array $existing_classes An array of existing compatibility class names from WP.
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ *
+ */
+ return (bool)apply_filters( 'tribe_compatibility_classes_should_add', false, $queue, $add_classes, $existing_classes );
+ }
+}
diff --git a/tribe-common/src/Tribe/Utils/Date_I18n.php b/tribe-common/src/Tribe/Utils/Date_I18n.php
index f61244626f..bd159c0114 100644
--- a/tribe-common/src/Tribe/Utils/Date_I18n.php
+++ b/tribe-common/src/Tribe/Utils/Date_I18n.php
@@ -23,6 +23,7 @@ class Date_I18n extends DateTime {
*
* @return Date_I18n Localizable variation of DateTime.
*/
+ #[\ReturnTypeWillChange]
public static function createFromImmutable( $datetime ) {
$date_object = new self;
$date_object->setTimestamp( $datetime->getTimestamp() );
diff --git a/tribe-common/src/Tribe/Utils/Date_I18n_Immutable.php b/tribe-common/src/Tribe/Utils/Date_I18n_Immutable.php
index 3a1965552b..5175ecc4b1 100644
--- a/tribe-common/src/Tribe/Utils/Date_I18n_Immutable.php
+++ b/tribe-common/src/Tribe/Utils/Date_I18n_Immutable.php
@@ -22,6 +22,7 @@ class Date_I18n_Immutable extends DateTimeImmutable {
*
* @return Date_I18n_Immutable Localizable variation of DateTimeImmutable.
*/
+ #[\ReturnTypeWillChange]
public static function createFromMutable( $datetime ) {
$date_object = new self;
$date_object = $date_object->setTimestamp( $datetime->getTimestamp() );
diff --git a/tribe-common/src/Tribe/Utils/Element_Classes.php b/tribe-common/src/Tribe/Utils/Element_Classes.php
index 0f5af1b39b..18496b0a96 100644
--- a/tribe-common/src/Tribe/Utils/Element_Classes.php
+++ b/tribe-common/src/Tribe/Utils/Element_Classes.php
@@ -207,11 +207,11 @@ protected function parse_array( array $values ) {
$this->parse( $value );
}
} elseif ( is_string( $key ) ) {
- if ( ! is_bool( $value ) ) {
- throw new \UnexpectedValueException( 'Value for key ' . $key . ' must be of type boolean' );
+ if ( $value instanceof \Closure || is_callable( $value ) ) {
+ $value = $value( $this->results );
}
- $this->parse_string( $key, $value );
+ $this->parse_string( $key, tribe_is_truthy( $value ) );
}
}
}
diff --git a/tribe-common/src/Tribe/Utils/Lazy_Events.php b/tribe-common/src/Tribe/Utils/Lazy_Events.php
index 10f9cc41a2..279288ecb0 100644
--- a/tribe-common/src/Tribe/Utils/Lazy_Events.php
+++ b/tribe-common/src/Tribe/Utils/Lazy_Events.php
@@ -160,7 +160,7 @@ protected function resolved() {
$hooked = has_action( $action, $this->lazy_resolve_callback );
- // Let's play it safe and move the resoloution as late as possible.
+ // Let's play it safe and move the resolution as late as possible.
$new_priority = false !== $hooked ? max( $hooked, $priority ) : $priority;
if ( is_numeric( $hooked ) && $hooked !== $new_priority ) {
diff --git a/tribe-common/src/Tribe/Utils/Lazy_String.php b/tribe-common/src/Tribe/Utils/Lazy_String.php
index 8597763769..04d465df73 100644
--- a/tribe-common/src/Tribe/Utils/Lazy_String.php
+++ b/tribe-common/src/Tribe/Utils/Lazy_String.php
@@ -71,7 +71,12 @@ public function __construct( callable $callback, $escape_callback = 'esc_html' )
*/
public function __toString() {
if ( null === $this->string ) {
- $this->string = call_user_func( $this->value_callback );
+ $value = call_user_func( $this->value_callback );
+ if ( ! $value instanceof \Generator ) {
+ $value = (string) $value;
+ }
+
+ $this->string = $value;
$this->resolved();
}
diff --git a/tribe-common/src/Tribe/Utils/Post_Thumbnail.php b/tribe-common/src/Tribe/Utils/Post_Thumbnail.php
index 7dfed2c16f..fb4a3b8aa2 100644
--- a/tribe-common/src/Tribe/Utils/Post_Thumbnail.php
+++ b/tribe-common/src/Tribe/Utils/Post_Thumbnail.php
@@ -230,7 +230,6 @@ static function( $size ) use ( $thumbnail_id ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetExists( $offset ) {
$this->data = $this->fetch_data();
@@ -240,7 +239,6 @@ public function offsetExists( $offset ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetGet( $offset ) {
$this->data = $this->fetch_data();
@@ -252,7 +250,6 @@ public function offsetGet( $offset ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetSet( $offset, $value ) {
$this->data = $this->fetch_data();
@@ -262,7 +259,6 @@ public function offsetSet( $offset, $value ) {
/**
* {@inheritDoc}
*/
- #[\ReturnTypeWillChange]
public function offsetUnset( $offset ) {
$this->data = $this->fetch_data();
diff --git a/tribe-common/src/Tribe/Utils/Taxonomy.php b/tribe-common/src/Tribe/Utils/Taxonomy.php
index 15537ed18e..bbd3956e36 100644
--- a/tribe-common/src/Tribe/Utils/Taxonomy.php
+++ b/tribe-common/src/Tribe/Utils/Taxonomy.php
@@ -123,4 +123,77 @@ public static function normalize_to_term_ids( $terms, $taxonomy ) {
return $terms;
}
+
+
+ /**
+ * When dealing with templates that make use of `get_post_class` the taxonomy + terms queries are very inefficient
+ * so this method primes the caching by doing a single query that will build the cache for all Posts involved on
+ * the template we are about to render, reducing about 2 queries for each Post that we prime the cache for.
+ *
+ * Important to note that
+ *
+ * @since 5.0.0
+ *
+ * @param array $posts
+ * @param array $taxonomies
+ * @param bool $prime_term_meta
+ *
+ * @return array
+ */
+ public static function prime_term_cache( array $posts = [], array $taxonomies = [ 'post_tag', \Tribe__Events__Main::TAXONOMY ], bool $prime_term_meta = false ): array {
+ $first = reset( $posts );
+ $is_numeric = ( ! $first instanceof \WP_Post );
+ if ( $is_numeric ) {
+ $ids = $posts;
+ } else {
+ $ids = wp_list_pluck( $posts, 'ID' );
+ }
+ $cache = [];
+
+ // Build the base cache.
+ foreach ( $ids as $id ) {
+ foreach ( $taxonomies as $taxonomy ) {
+ $cache[ $id ][ $taxonomy ] = [];
+ }
+ }
+
+ $args = [
+ 'fields' => 'all_with_object_id',
+ 'object_ids' => $ids,
+ 'taxonomy' => $taxonomies,
+ ];
+ $terms = get_terms( $args );
+
+ // Drop invalid results.
+ $valid_terms = array_filter( $terms, static function ( $term ) {
+ return $term instanceof \WP_Term;
+ } );
+
+ $term_ids = wp_list_pluck( $valid_terms, 'term_id' );
+
+ foreach ( $valid_terms as $term ) {
+ $cache[ $term->object_id ][ $term->taxonomy ][] = $term->term_id;
+ }
+
+ foreach ( $cache as $id => $object_taxonomies ) {
+ // Skip when invalid object id is passed.
+ if ( empty( $id ) ) {
+ continue;
+ }
+
+ foreach ( $object_taxonomies as $taxonomy => $term_ids ) {
+ // Skip when invalid taxonomy is passed.
+ if ( empty( $taxonomy ) ) {
+ continue;
+ }
+
+ // Do not skip when `term_ids` are empty.
+ wp_cache_add( $id, $term_ids, $taxonomy . '_relationships' );
+ }
+ }
+
+ _prime_term_caches( $term_ids, $prime_term_meta );
+
+ return $cache;
+ }
}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Utils/Theme_Compatibility.php b/tribe-common/src/Tribe/Utils/Theme_Compatibility.php
new file mode 100644
index 0000000000..6e61a36508
--- /dev/null
+++ b/tribe-common/src/Tribe/Utils/Theme_Compatibility.php
@@ -0,0 +1,276 @@
+get_template() ) ) {
+ return false;
+ }
+
+ $theme = $current_theme->get_template();
+ }
+
+ $required = in_array( $theme, static::get_registered_themes() );
+
+ /**
+ * Allows hooking in to enforce compatibility by other plugins.
+ *
+ * @since 4.14.0
+ *
+ * @param boolean $required If compatibility is required.
+ * @param null|string $theme The optional theme name string.
+ */
+ $required = apply_filters( 'tribe_compatibility_required', $required, $theme );
+
+ return tribe_is_truthy( $required );
+ }
+
+ /**
+ * Contains the logic for if this object's classes should be added to the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param boolean $add Whether to add the class to the queue or not.
+ * @param array $class The array of compatibility class names to add.
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ *
+ * @return boolean Whether compatibility classes should be added or not.
+ */
+ public static function should_add_compatibility_class_to_queue( $add, $class, $queue ) {
+ if (
+ 'admin' === $queue
+ || ! static::is_compatibility_required()
+ ) {
+ return $add;
+ }
+
+ if ( in_array( $class, static::get_compatibility_classes() ) ) {
+ $add = true;
+ }
+
+ /**
+ * Filters whether we should add a specific class to the queue.
+ *
+ * @since 4.14.0
+ *
+ * @param boolean $add Whether to add the class to the queue or not.
+ * @param array $class The array of compatibility class names to add.
+ * @param string $queue The queue we want to get 'admin', 'display', 'all'.
+ */
+ return apply_filters( 'tribe_compatibility_add_class', $add, $class, $queue );
+ }
+
+ /**
+ * Add compatibility classes.
+ *
+ * @since 4.14.0
+ *
+ * @return void
+ */
+ public static function add_compatibility_classes() {
+ tribe( Compatibility_Classes::class )->add_classes( static::get_compatibility_classes() );
+ }
+
+ /**
+ * Fetches the correct class strings for theme and child theme if available + the container class.
+ *
+ * @since 4.14.0
+ *
+ * @return array $classes
+ */
+ public static function get_container_classes() {
+ $classes = [ 'tribe-compatibility-container' ];
+
+ if ( static::is_compatibility_required() ) {
+ $classes = array_merge( $classes, static::get_compatibility_classes() );
+ }
+
+ /**
+ * Filters the HTML classes applied to a compatibility container.
+ *
+ * @since 4.14.0
+ *
+ * @param array $html_classes Array of classes used for this container.
+ */
+ return apply_filters( 'tribe_compatibility_container_classes', $classes );
+ }
+
+ /**
+ * Fetches the correct class strings for theme and child theme if available.
+ *
+ * @since 4.14.0
+ *
+ * @return array $classes
+ */
+ public static function get_compatibility_classes() {
+ $classes = [];
+ $current_theme = static::get_current_theme( true );
+
+ if ( empty( $current_theme ) || empty( $current_theme->get_template() ) ) {
+ return $classes;
+ }
+
+ // Detect if we're using a child theme.
+ if ( $parent = $current_theme->parent ) {
+ $classes[] = sanitize_html_class( 'tribe-theme-' . $parent->get_template() );
+ $classes[] = sanitize_html_class( 'tribe-theme-child-' . $current_theme->get_template() );
+ } else {
+ $classes[] = sanitize_html_class( 'tribe-theme-' . $current_theme->get_template() );
+ }
+
+ /**
+ * Filters the list of classes we're adding.
+ *
+ * @since 4.14.0
+ *
+ * @param array $classes An array of classes in the shape `[ => boolean ]`.
+ */
+ return apply_filters( 'tribe_compatibility_classes', $classes );
+ }
+
+ /**
+ * Returns a list of themes registered for compatibility with our Views.
+ *
+ * @since 4.14.0
+ *
+ * @return array An array of the themes registered.
+ */
+ public static function get_registered_themes() {
+ /**
+ * Filters the list of themes that are registered for compatibility.
+ *
+ * @since 4.14.0
+ *
+ * @param array $registered An array of views in the shape `[ ]`.
+ */
+ return (array) apply_filters( 'tribe_theme_compatibility_registered', self::$themes );
+ }
+
+ /**
+ * Returns an array of active themes (parent and child).
+ *
+ * @since 4.14.0
+ *
+ * @return array $themes An array in the format [ 'parent' => 'theme name', 'child' => 'theme name' ].
+ * Empty array if none found.
+ */
+ public static function get_active_themes() {
+ $themes = [];
+ $current_theme = static::get_current_theme( true );
+
+ if ( empty( $current_theme ) ) {
+ return $themes;
+ }
+
+ $parent_theme = $current_theme->parent();
+
+ // No parent theme.
+ if ( empty( $parent_theme ) ) {
+ $themes['parent'] = strtolower( $current_theme->get_template() );
+ return $themes;
+ }
+
+ $themes['parent'] = strtolower( $parent_theme->get_template() );
+ $child_theme = $current_theme->get( 'stylesheet' );
+
+ // if the 2 options are the same, then there is no child theme.
+ if ( $child_theme !== $parent_theme ) {
+ $themes['child'] = strtolower( $child_theme );
+ }
+
+ return $themes;
+ }
+
+ /**
+ * Get the current theme.
+ *
+ * @since 4.14.0
+ *
+ * @param boolean $object Pass true if you want the theme object returned instead of the name.
+ *
+ * @return string|object|boolean Will return the theme name by default.
+ * Will return the theme object if passed boolean true as the parameter.
+ * Will return boolean false if the theme is not found.
+ */
+ public static function get_current_theme( $object = false ) {
+ $current_theme = wp_get_theme();
+
+ // If we can't get it for some reason...
+ if ( ! $current_theme instanceof WP_Theme || ! $current_theme->exists() ) {
+ return false;
+ }
+
+ if ( $object ) {
+ return $current_theme;
+ }
+
+ return $current_theme->get_template();
+ }
+
+ /**
+ * Checks if the provided theme is active.
+ *
+ * @since 4.14.0
+ *
+ * @param string $theme The theme name like 'avada' or 'twentytwenty',
+ *
+ * @return boolean True if the requested theme is active,
+ * false if the current theme could not be found or is not the requested theme.
+ */
+ public static function is_active_theme( $check ) {
+ $current_theme = wp_get_theme();
+
+ // Current theme is not
+ if ( ! $current_theme instanceof \WP_Theme ) {
+ $theme = false;
+ } elseif ( ! $current_theme->exists() ) {
+ $theme = false;
+ } else {
+ $theme = $current_theme->get_template();
+ }
+
+ return ! empty( $theme ) && strtolower( $check ) === strtolower( $theme );
+ }
+}
diff --git a/tribe-common/src/Tribe/Validate.php b/tribe-common/src/Tribe/Validate.php
index 63a65042d2..a83b84e08f 100755
--- a/tribe-common/src/Tribe/Validate.php
+++ b/tribe-common/src/Tribe/Validate.php
@@ -499,5 +499,19 @@ public function email( ) {
}
}
+ /**
+ * Validates and sanitizes a HTML color codes, including hex, rgb, rgba, hsl and hsla.
+ *
+ * @since 5.0.0
+ */
+ public function color() {
+ if ( preg_match( '/^(#(?:[0-9a-f]{2}){2,4}|#[0-9a-f]{3}|(?:rgba?|hsla?)\((?:\d+%?(?:deg|rad|grad|turn)?(?:,|\s)+){2,3}[\s\/]*[\d\.]+%?\))$/i', $this->value ) ) {
+ $this->result->valid = true;
+ } else {
+ $this->result->valid = false;
+ $this->result->error = sprintf( esc_html__( '%s must be a valid HTML color code.', 'tribe-common' ), $this->label );
+ }
+ }
+
} // end class
} // endif class_exists
diff --git a/tribe-common/src/Tribe/Values/Abstract_Currency.php b/tribe-common/src/Tribe/Values/Abstract_Currency.php
new file mode 100644
index 0000000000..89f03622a4
--- /dev/null
+++ b/tribe-common/src/Tribe/Values/Abstract_Currency.php
@@ -0,0 +1,369 @@
+set_up_currency_details();
+
+ parent::__construct( $amount );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_currency() {
+
+ /**
+ * Filter the value returned for get_currency() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $currency = apply_filters( "tec_common_value_{$this->get_value_type()}_get_currency", $this->currency, $this );
+
+ /**
+ * Filter the value returned for get_currency() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_currency', $currency, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_decimal() {
+
+ /**
+ * Filter the value returned for get_decimal() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param float $decimal the float representation of the value, rounded to precision
+ * @param Abstract_Currency the object instance
+ *
+ * @return float
+ */
+ $decimal = apply_filters( "tec_common_value_{$this->get_value_type()}_get_decimal", $this->decimal, $this );
+
+ /**
+ * Filter the value returned for get_decimal() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param float $decimal the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return float
+ */
+ return apply_filters( 'tec_common_value_get_decimal', $decimal, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_string() {
+
+ /**
+ * Filter the value returned for get_string() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $string the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $string = apply_filters( "tec_common_value_{$this->get_value_type()}_get_string", $this->string, $this );
+
+ /**
+ * Filter the value returned for get_string() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $string the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_string', $string, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_currency_code() {
+
+ /**
+ * Filter the value returned for get_currency_code() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_code the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $currency_code = apply_filters( "tec_common_value_{$this->get_value_type()}_get_currency_code", $this->currency_code, $this );
+
+ /**
+ * Filter the value returned for get_currency_code() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_code the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_currency_code', $currency_code, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_currency_symbol() {
+
+ /**
+ * Filter the value returned for get_currency_symbol() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_symbol the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $currency_symbol = apply_filters( "tec_common_value_{$this->get_value_type()}_get_currency_symbol", $this->currency_symbol, $this );
+
+ /**
+ * Filter the value returned for get_currency_symbol() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_symbol the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_currency_symbol', $currency_symbol, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_currency_symbol_position() {
+
+ /**
+ * Filter the value returned for get_currency_symbol_position() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_symbol_position the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $currency_symbol_position = apply_filters( "tec_common_value_{$this->get_value_type()}_get_currency_symbol_position", $this->currency_symbol_position, $this );
+
+ /**
+ * Filter the value returned for get_currency_symbol_position() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_symbol_position the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_currency_symbol_position', $currency_symbol_position, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_currency_separator_decimal() {
+
+ /**
+ * Filter the value returned for get_currency_separator_decimal() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_separator_decimal the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $currency_separator_decimal = apply_filters( "tec_common_value_{$this->get_value_type()}_get_currency_separator_decimal", $this->currency_separator_decimal, $this );
+
+ /**
+ * Filter the value returned for get_currency_separator_decimal() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_separator_decimal the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_currency_separator_decimal', $currency_separator_decimal, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_currency_separator_thousands() {
+
+ /**
+ * Filter the value returned for get_currency_separator_thousands() when implemented in a specific class type
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_separator_thousands the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ $currency_separator_thousands = apply_filters( "tec_common_value_{$this->get_value_type()}_get_currency_separator_thousands", $this->currency_separator_thousands, $this );
+
+ /**
+ * Filter the value returned for get_currency_separator_thousands() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param string $currency_separator_thousands the string representation of the value
+ * @param Abstract_Currency the object instance
+ *
+ * @return string
+ */
+ return apply_filters( 'tec_common_value_get_currency_separator_thousands', $currency_separator_thousands, $this );
+ }
+
+ /**
+ * Protected setter for the string representation of the object amount. This is a formatted string, including the
+ * currency symbol.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value use the public setter `$obj->set_value( $amount )`
+ */
+ protected function set_currency_value() {
+ $this->currency = $this->to_currency( $this->get_normalized_value() );
+ }
+
+ /**
+ * Protected setter for the decimal representation of the object amount. This is a float, rounded to the precision.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value use the public setter `$obj->set_value( $amount )`
+ */
+ protected function set_decimal_value() {
+ $this->decimal = $this->to_decimal( $this->get_normalized_value() );
+ }
+
+ /**
+ * Protected setter for the string representation of the object amount. This is a formatted string, without the
+ * currency symbol.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value use the public setter `$obj->set_value( $amount )`
+ */
+ protected function set_string_value() {
+ $this->string = $this->to_string( $this->get_normalized_value() );
+ }
+}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Values/Abstract_Value.php b/tribe-common/src/Tribe/Values/Abstract_Value.php
new file mode 100644
index 0000000000..34ecb0ca11
--- /dev/null
+++ b/tribe-common/src/Tribe/Values/Abstract_Value.php
@@ -0,0 +1,455 @@
+set_initial_representation( $amount );
+ $this->set_normalized_amount( $amount );
+ $this->update();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function create( $value = 0 ) {
+ $class = get_called_class();
+ return new $class( $value );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function set_value( $amount ) {
+ $this->set_normalized_amount( $amount );
+ $this->update();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function set_precision( $amount ) {
+ $this->precision = $amount;
+ }
+
+
+ /**
+ * @inheritDoc
+ */
+ public function get_integer() {
+ /**
+ * Filter the value returned for get_integer() when implemented in a specific class name
+ *
+ * @since 4.14.9
+ *
+ * @param int $integer the integer representation of the value
+ * @param Abstract_Value the object instance
+ *
+ * @return int
+ */
+ $integer = apply_filters( "tec_common_value_{$this->get_value_type()}_get_integer", $this->integer, $this );
+
+ /**
+ * Filter the value returned for get_integer() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param int $integer the integer representation of the value
+ * @param Abstract_Value the object instance
+ *
+ * @return int
+ */
+ return apply_filters( 'tec_common_value_get_integer', $integer, $this );
+
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_float() {
+ /**
+ * Filter the value returned for get_float() when implemented in a specific class name
+ *
+ * @since 4.14.9
+ *
+ * @param float $float the float representation of the value
+ * @param Abstract_Value the object instance
+ *
+ * @return float
+ */
+ $float = apply_filters( "tec_common_value_{$this->get_value_type()}_get_float", $this->float, $this );
+
+ /**
+ * Filter the value returned for get_float() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param float $float the float representation of the value
+ * @param Abstract_Value the object instance
+ *
+ * @return float
+ */
+ return apply_filters( 'tec_common_value_get_float', $float, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_precision() {
+ /**
+ * Filter the value returned for get_precision() when implemented in a specific class name
+ *
+ * @since 4.14.9
+ *
+ * @param int $precision the precision to which values will be calculated
+ * @param Abstract_Value the object instance
+ *
+ * @return int
+ */
+ $precision = apply_filters( "tec_common_value_{$this->get_value_type()}_get_precision", $this->precision, $this );
+
+ /**
+ * Filter the value returned for get_precision() when implemented in any class
+ *
+ * @since 4.14.9
+ *
+ * @param int $precision the precision to which values will be calculated
+ * @param Abstract_Value the object instance
+ *
+ * @return int
+ */
+ return (int) apply_filters( 'tec_common_value_get_precision', $precision, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_normalized_value() {
+ return $this->normalized_amount;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_initial_representation() {
+ return $this->initial_value;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get_value_type() {
+ return $this->value_type;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function normalize( $value ) {
+
+ if ( is_numeric( $value ) ) {
+ return (float) $value;
+ }
+
+ if ( $this->is_character_block( $value ) ) {
+ return (float) 0;
+ }
+
+ $value = $this->remove_character_blocks( $value );
+ $value = $this->remove_html( $value );
+
+ // Get all non-digits from the amount
+ preg_match_all( '/[^\d]/', $value, $non_digits );
+
+ // if the string is all digits, it is numeric
+ if ( empty( $non_digits[0] ) ) {
+ return (float) $value;
+ }
+
+ $pieces = $this->remove_non_digits( $value, $non_digits );
+
+ return (float) $this->assemble_normalized_value( $pieces );
+ }
+
+ /**
+ * Removes any blocks composed of all non-digit characters from the numeric string. These will usually represent
+ * the currency code and any other pieces of text that may have been sent with the value.
+ *
+ * This is specially important in case the currency unit contains the same characters as the decimal/thousands
+ * separators such as in Moroccan Dirham (1,234.56 .د.م.) or Danish Krone (kr. 1.234,56)
+ *
+ * @since 4.14.9
+ *
+ * @param string $value the numeric string being normalized
+ *
+ * @return string
+ */
+ private function remove_character_blocks( $value ) {
+ foreach ( explode( ' ', $value ) as $block ) {
+ if ( ! $this->is_character_block( $block ) ) {
+ continue;
+ }
+
+ $value = str_replace( $block, '', $value );
+ }
+
+ return $value;
+ }
+
+ /**
+ * Removes all html tags and html entities from the value string
+ *
+ * @since 4.14.9
+ *
+ * @param string $value the value being normalized
+ *
+ * @return string
+ */
+ private function remove_html( $value ) {
+ return wp_strip_all_tags( preg_replace( '/&[^;]+;/', '', trim( $value ) ) );
+ }
+
+ /**
+ * Takes the value string and a list of non-digit characters and removes any of those characters. If the character
+ * is found to be a decimal separator, normalize it to a dot, so the number translates to a float.
+ *
+ * @since 4.14.9
+ *
+ * @param string $value the value being normalized
+ * @param string[] $non_digits a list of non-digit characters present in $value
+ * @param string $separator a default separator to use when splitting the string
+ *
+ * @return string[]
+ */
+ private function remove_non_digits( $value, $non_digits, $separator = '>>>' ) {
+
+ $tokens = array_unique( $non_digits[0] );
+
+ foreach ( $tokens as $token ) {
+ if ( $this->is_decimal_separator( $token, $value ) ) {
+ $separator = $token;
+ continue;
+ }
+
+ $value = str_replace( $token, '', $value );
+ }
+
+ return explode( $separator, $value );
+ }
+
+ /**
+ * Re-assemble the normalized value to store.
+ *
+ * @since 4.14.9
+ *
+ * @param int[] $pieces the normalized value split in an array.
+ *
+ * @return float
+ */
+ private function assemble_normalized_value( $pieces ) {
+
+ // If the initial amount did not have decimals specified, $pieces will be an array of a single
+ // numeric value, so we just return it as a float.
+ if ( 1 === count( $pieces ) && is_numeric( reset( $pieces ) ) ) {
+ return (float) reset( $pieces );
+ }
+
+ $decimal = array_pop( $pieces );
+
+ return (float) implode( '', array_merge( $pieces, [ '.', $decimal ] ) );
+ }
+
+ /**
+ * Private setter for the initial value the object was created with. This value cannot be changed during the object
+ * lifecycle.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value discard the original object and create a new one.
+ */
+ private function set_initial_representation( $amount ) {
+ if ( empty( $this->initial_value ) ) {
+ $this->initial_value = $amount;
+ }
+ }
+
+ /**
+ * Private setter for the normalized amount extracted from the initial value.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value use the public setter `$obj->set_value( $amount )`
+ */
+ private function set_normalized_amount( $amount ) {
+
+ $normalized_value = $this->normalize( $amount );
+
+ /**
+ * Filter the value to be set as $normalized_amount for a specific implementation.
+ *
+ * @since 4.14.9
+ *
+ * @param float $normalized_value the normalized value
+ * @param Abstract_Value the object instance
+ *
+ * @return float
+ */
+ $normalized_value = (float) apply_filters( "tec_common_{$this->get_value_type()}_value_normalized", $normalized_value, $this );
+
+ /**
+ * Filter the value to be set as $normalized_amount for all implementations.
+ *
+ * @since 4.14.9
+ *
+ * @param float $normalized_value the normalized value
+ * @param Abstract_Value the object instance
+ *
+ * @return float
+ */
+ $normalized_value = (float) apply_filters( "tec_common_value_normalized", $normalized_value, $this );
+
+ /**
+ * Fire action right before setting the normalized value
+ *
+ * @since 4.14.9
+ *
+ * @param float $normalized_value the normalized value
+ * @param Abstract_Value the object instance
+ */
+ do_action( 'tec_common_value_normalized', $normalized_value, $this );
+
+ $this->normalized_amount = $normalized_value;
+ }
+
+ /**
+ * Private setter for the integer representation of the object amount.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value use the public setter `$obj->set_value( $amount )`
+ */
+ protected function set_integer_value() {
+ $this->integer = $this->to_integer( $this->normalized_amount );
+ }
+
+ /**
+ * Private setter for the floating point representation of the object amount.
+ *
+ * @since 4.14.9
+ *
+ * To set a new value use the public setter `$obj->set_value( $amount )`
+ */
+ protected function set_float_value() {
+ $this->float = $this->normalized_amount;
+ }
+
+ /**
+ * Tries to determine if a token is serving as a decimal separator or something else
+ * in a string;
+ *
+ * The rule to determine a decimal is straightforward. It needs to exist only once
+ * in the string and the piece of the string after the separator cannot be longer
+ * than 2 digits. Anything else is serving another purpose.
+ *
+ * @since 4.14.9
+ *
+ * @param $separator string a separator token, like . or ,
+ * @param $value string a number formatted as a string
+ *
+ * @return bool
+ */
+ private function is_decimal_separator( $separator, $value ) {
+ $pieces = array_filter( explode( $separator, $value ) );
+
+ foreach ( $pieces as $i => $block ) {
+ if ( $this->is_character_block( $block ) ) {
+ unset( $pieces[ $i ] );
+ }
+ }
+
+ if ( 2 === count( $pieces ) ) {
+ return strlen( array_pop( $pieces ) ) < 3;
+ }
+
+ return false;
+ }
+
+ /**
+ * Tests if a string is composed entirely of non-digit characters
+ *
+ * @since 4.14.9
+ *
+ * @param string $block the string to check
+ *
+ * @return bool
+ */
+ private function is_character_block( $block ) {
+ return empty( preg_replace( '/\D/', '', $block ) );
+ }
+}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Values/Currency_Interface.php b/tribe-common/src/Tribe/Values/Currency_Interface.php
new file mode 100644
index 0000000000..aadf963642
--- /dev/null
+++ b/tribe-common/src/Tribe/Values/Currency_Interface.php
@@ -0,0 +1,86 @@
+set_value( $this->multiply( $multiplier ) );
+
+ return $this;
+ }
+
+ /**
+ * Sets the current object value to be the sum of its current value plus the values of all objects received in
+ * $values.
+ *
+ * @since 4.14.9
+ *
+ * @param Abstract_Value[] $values a list of Value objects
+ *
+ * @return $this
+ */
+ public function total( $values ) {
+ $num = array_map( function ( $obj ) {
+ return $obj->get_float();
+ }, $values );
+
+ $this->set_value( $this->sum( $num ) );
+
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function sum( $values ) {
+ $values[] = $this->get_float();
+
+ return array_sum( $values );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function multiply( $multiplier ) {
+ return $this->get_float() * $multiplier;
+ }
+
+ /**
+ * Rounds the current value to its precision and multiplies it by 10^precision to get an integer representation
+ * including decimals.
+ *
+ * @since 4.14.9
+ *
+ * @param int|float $value the value to transform
+ *
+ * @return int
+ */
+ public function to_integer( $value ) {
+ return (int) ( round( $value, $this->get_precision() ) * pow( 10, $this->get_precision() ) );
+ }
+}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Values/Value_Formatting.php b/tribe-common/src/Tribe/Values/Value_Formatting.php
new file mode 100644
index 0000000000..33cbdd1198
--- /dev/null
+++ b/tribe-common/src/Tribe/Values/Value_Formatting.php
@@ -0,0 +1,58 @@
+to_decimal( $value ),
+ $this->get_precision(),
+ $this->get_currency_separator_decimal(),
+ $this->get_currency_separator_thousands()
+ );
+ }
+
+ /**
+ * Transforms a normalized value into a decimal representation by rounding the significant digits to the precision.
+ *
+ * @since 4.14.9
+ *
+ * @param float $value the normalized value to transform
+ *
+ * @param float|\WP_Error the value rounded to the specified precision
+ */
+ private function to_decimal( $value ) {
+ return round( $value, $this->get_precision() );
+ }
+
+ /**
+ * Transforms a normalized value into a currency representation using the defined currency symbol, position,
+ * separators and precision.
+ *
+ * @since 4.14.9
+ *
+ * @param float $value the normalized value to transform
+ *
+ * @return string|\WP_Error the currency-formatted string
+ */
+ private function to_currency( $value ) {
+ $value = $this->to_string( $value );
+
+ if ( 'prefix' === $this->get_currency_symbol_position() ) {
+ return $this->get_currency_symbol() . $value;
+ }
+
+ return $value . $this->get_currency_symbol();
+ }
+}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Values/Value_Interface.php b/tribe-common/src/Tribe/Values/Value_Interface.php
new file mode 100644
index 0000000000..aaba5247b8
--- /dev/null
+++ b/tribe-common/src/Tribe/Values/Value_Interface.php
@@ -0,0 +1,145 @@
+get_value_type()}_value_get_setters", $setters, $this );
+
+ /**
+ * Filter the value returned for get_setters() for all class names.
+ *
+ * @since 4.14.9
+ *
+ * @param string[] $setters the list of setter methods returned
+ * @param Abstract_Value the object instance
+ *
+ * @return string[]
+ */
+ return apply_filters( 'tec_tickets_commerce_value_get_setters', $setters, $this );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function update() {
+ foreach ( $this->get_setters() as $setter ) {
+ call_user_func( [ $this, $setter ] );
+ }
+ }
+}
\ No newline at end of file
diff --git a/tribe-common/src/Tribe/Widget/Widget_Abstract.php b/tribe-common/src/Tribe/Widget/Widget_Abstract.php
index a8dafb798c..89c4cd7ae4 100644
--- a/tribe-common/src/Tribe/Widget/Widget_Abstract.php
+++ b/tribe-common/src/Tribe/Widget/Widget_Abstract.php
@@ -285,11 +285,11 @@ public function form( $instance ) {
// Specifically on the admin we force the admin fields into the arguments.
$this->arguments['admin_fields'] = $this->get_admin_fields();
- $this->toggle_hooks( true );
+ $this->toggle_hooks( true, 'form' );
$html = $this->get_admin_html( $this->get_arguments() );
- $this->toggle_hooks( false );
+ $this->toggle_hooks( false, 'form' );
return $html;
}
@@ -302,11 +302,11 @@ public function widget( $args, $instance ) {
$this->setup( $args, $instance );
- $this->toggle_hooks( true );
+ $this->toggle_hooks( true, 'display' );
$html = $this->get_html();
- $this->toggle_hooks( false );
+ $this->toggle_hooks( false, 'display' );
echo $html;
@@ -792,15 +792,20 @@ public function get_admin_html( $arguments ) {
*
* @since 4.13.0
*
- * @param bool $toggle Whether to turn the hooks on or off.
+ * @param bool $toggle Whether to turn the hooks on or off.
+ * @param string $location If we are doing the form (admin) or the display (front end)
*
* @return void
*/
- public function toggle_hooks( $toggle ) {
+ public function toggle_hooks( $toggle, $location = 'display' ) {
+ $slug = static::get_widget_slug();
+
if ( $toggle ) {
+ do_action( 'tec_start_widget_' . $location, $slug );
$this->add_hooks();
} else {
$this->remove_hooks();
+ do_action( 'tec_end_widget_' . $location, $slug );
}
/**
diff --git a/tribe-common/src/admin-views/app-shop.php b/tribe-common/src/admin-views/app-shop.php
index ec1e6adff8..c88d12ebdc 100644
--- a/tribe-common/src/admin-views/app-shop.php
+++ b/tribe-common/src/admin-views/app-shop.php
@@ -12,10 +12,16 @@
$all_products['for-sale'][] = $product;
}
}
+
+use \Tribe\Admin\Troubleshooting;
?>
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/conditional_content/end-of-year-sale.php b/tribe-common/src/admin-views/conditional_content/end-of-year-sale.php
new file mode 100644
index 0000000000..d947bb3c15
--- /dev/null
+++ b/tribe-common/src/admin-views/conditional_content/end-of-year-sale.php
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+ Save 30% on all our plugins. Offer expires soon!', 'tribe-common' ); ?>
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/notices/end-of-year-sale.php b/tribe-common/src/admin-views/notices/end-of-year-sale.php
new file mode 100644
index 0000000000..e853cf9482
--- /dev/null
+++ b/tribe-common/src/admin-views/notices/end-of-year-sale.php
@@ -0,0 +1,30 @@
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/notices/tribe-bf-general.php b/tribe-common/src/admin-views/notices/tribe-bf-general.php
index 4bca7f8126..ec7750ce44 100644
--- a/tribe-common/src/admin-views/notices/tribe-bf-general.php
+++ b/tribe-common/src/admin-views/notices/tribe-bf-general.php
@@ -6,6 +6,7 @@
*
* @var string $icon_url The local URL for the notice's image.
* @var string $cta_url The short URL for black friday.
+ * @var string $end_date - the end date of the sale.
*/
?>
@@ -13,10 +14,18 @@
-
Save 40% on Every. Single. Plugin.
+
- Black Friday Sale now through November 30.
- Shop now
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/notices/tribe-stellar-sale.php b/tribe-common/src/admin-views/notices/tribe-stellar-sale.php
new file mode 100644
index 0000000000..161786fe00
--- /dev/null
+++ b/tribe-common/src/admin-views/notices/tribe-stellar-sale.php
@@ -0,0 +1,29 @@
+
+
'
- . sprintf(
- __( 'The following three fields accept the date format options available to the PHP %1$s function. Learn how to make your own date format here.', 'tribe-common' ),
- 'date()',
- 'https://wordpress.org/support/article/formatting-date-and-time/'
- )
- . '
' . sprintf( esc_html__( 'Thank you for using Event Tickets! All of us at The Events Calendar sincerely appreciate your support and we\'re excited to see you using our plugins. Check out our handy %1$sNew User Primer%2$s to get started.', 'tribe-common' ), '', '' ) . '
' . esc_html__( 'Are you thinking "Wow, this plugin is amazing! I should say thanks to The Events Calendar for all their hard work." The greatest thanks we could ask for is recognition. Add a small text-only link at the bottom of your calendar pointing to The Events Calendar project.', 'tribe-common' ) . ' ' . esc_html__( 'See an example of the link', 'tribe-common' ) . '.
',
- ],
-];
-
-if ( is_super_admin() ) {
- $generalTabFields['debugEvents'] = [
- 'type' => 'checkbox_bool',
- 'label' => esc_html__( 'Debug mode', 'tribe-common' ),
- 'tooltip' => sprintf(
- esc_html__(
- 'Enable this option to log debug information. By default this will log to your server PHP error log. If you\'d like to see the log messages in your browser, then we recommend that you install the %s and look for the "Tribe" tab in the debug output.',
- 'tribe-common'
- ),
- '' . esc_html__( 'Debug Bar Plugin', 'tribe-common' ) . ''
- ),
- 'default' => false,
- 'validation_type' => 'boolean',
- ];
-}
-
-// Closes form
-$generalTabFields['tribe-form-content-end'] = [
- 'type' => 'html',
- 'html' => '
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/detected-issues.php b/tribe-common/src/admin-views/troubleshooting/detected-issues.php
new file mode 100644
index 0000000000..7dfe39015d
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/detected-issues.php
@@ -0,0 +1,54 @@
+get_issues_found();
+
+if ( tribe( Troubleshooting::class )->is_any_issue_active() ) : //checks is there are any active issues before printing ?>
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status.php b/tribe-common/src/admin-views/troubleshooting/ea-status.php
new file mode 100644
index 0000000000..8aa4a99469
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status.php
@@ -0,0 +1,52 @@
+ 'images/help/success-icon.svg',
+ 'warning' => 'images/help/warning-icon.svg',
+ 'error' => 'images/help/error-icon.svg',
+];
+
+$show_third_party_accounts = ! is_network_admin();
+?>
+
+
+
+
+
+
+ plugin_path . 'src/admin-views/troubleshooting/ea-status/license-key.php';
+ // if EA is not active, bail out of the rest of this
+ if ( $ea_active ) {
+ // current usage
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting/ea-status/current-usage.php';
+ // current status
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting/ea-status/current-status.php';
+ // server connection
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting/ea-status/server-connection.php';
+ // scheduler status
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting/ea-status/scheduler-status.php';
+
+ if ( $show_third_party_accounts ) :
+ // eventbrite
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting/ea-status/eventbrite.php';
+ // meetup
+ include_once Tribe__Main::instance()->plugin_path . 'src/admin-views/troubleshooting/ea-status/meetup.php';
+ endif;
+ }
+ ?>
+
+
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/current-status.php b/tribe-common/src/admin-views/troubleshooting/ea-status/current-status.php
new file mode 100644
index 0000000000..31bc99a1e6
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/current-status.php
@@ -0,0 +1,41 @@
+ $status_icons An array of icons for the EA Status table.
+ * @param \Tribe__Main $main An instance of the main class of Tribe Common.
+ *
+ */
+
+$icon = 'success';
+$notes = ' ';
+$message = esc_html_x( 'Imports Enabled in Settings', '', 'tribe-common' );
+$disabled = tribe_get_option( 'tribe_aggregator_disable', false );
+
+if ( $disabled ) {
+ $icon = 'error';
+ $message = _x( 'Imports disabled in Settings', '', 'tribe-common' );
+ $settings_url = Tribe__Settings::instance()->get_url( array( 'tab' => 'imports' ) );
+ $notes = sprintf(
+ '%2$s',
+ esc_url( $settings_url ),
+ _x( 'Edit Import Settings', '','tribe-common' )
+ );
+}
+?>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/current-usage.php b/tribe-common/src/admin-views/troubleshooting/ea-status/current-usage.php
new file mode 100644
index 0000000000..d87bf7de9b
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/current-usage.php
@@ -0,0 +1,42 @@
+get_limit( 'import' );
+$import_available = $service->get_limit_remaining();
+$import_count = $service->get_limit_usage();
+$icon = 'success';
+$notes = ' ';
+
+if ( 0 === $import_limit || $import_count >= $import_limit ) {
+ $icon = 'error';
+ $notes = esc_html__( 'You have reached your daily import limit. Scheduled imports will be paused until tomorrow.', 'tribe-common' );
+} elseif ( $import_count / $import_limit >= 0.8 ) {
+ $icon = 'warning';
+ $notes = esc_html__( 'You are approaching your daily import limit. You may want to adjust your Scheduled Import frequencies.', 'tribe-common' );
+}
+
+$message = sprintf( // import count and limit
+ _n( '%1$d import used out of %2$d available today', '%1$d imports used out of %2$d available today', $import_count, 'tribe-common' ),
+ intval( $import_count ),
+ intval( $import_limit )
+);
+?>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/eventbrite.php b/tribe-common/src/admin-views/troubleshooting/ea-status/eventbrite.php
new file mode 100644
index 0000000000..98b683b9c6
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/eventbrite.php
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+api( 'origins' )->is_oauth_enabled( 'eventbrite' ) ) {
+ if ( ! tribe( 'events-aggregator.settings' )->has_eb_security_key() ) {
+ $icon = 'warning';
+ $message = __( 'You have not connected Event Aggregator to Eventbrite', 'tribe-common' );
+ $eventbrite_auth_url = Tribe__Events__Aggregator__Record__Eventbrite::get_auth_url(
+ [ 'back' => 'settings' ]
+ );
+ $notes = '' . esc_html_x( 'Connect to Eventbrite', 'link for connecting eventbrite', 'tribe-common' ) . '';
+ }
+ } else {
+ $icon = 'warning';
+ $message = __( 'Limited connectivity with Eventbrite', 'tribe-common' );
+ $notes = esc_html__( 'The service has disabled oAuth. Some types of events may not import.', 'tribe-common' );
+ }
+?>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/license-key.php b/tribe-common/src/admin-views/troubleshooting/ea-status/license-key.php
new file mode 100644
index 0000000000..0f29301527
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/license-key.php
@@ -0,0 +1,54 @@
+offsetExists( 'events-aggregator.main' ) ) {
+ return;
+};
+
+if ( tribe( 'events-aggregator.main' )->is_service_active() ) {
+ $icon = 'success';
+ $message = __( 'Your license is valid', 'tribe-common' );
+ $ea_active = true;
+} else {
+ $service_status = tribe( 'events-aggregator.service' )->api()->get_error_code();
+
+ $icon = 'error';
+ if ( 'core:aggregator:invalid-service-key' == $service_status ) {
+ $message = __( 'You do not have a license', 'tribe-common' );
+ $notes = '';
+ $notes .= esc_html__( 'Buy Event Aggregator to access more event sources and automatic imports!', 'tribe-common' );
+ $notes .= '';
+ } else {
+ $message = __( 'Your license is invalid', 'tribe-common' );
+ $notes = '' . esc_html__( 'Check your license key', 'tribe-common' ) . '';
+ }
+}
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/meetup.php b/tribe-common/src/admin-views/troubleshooting/ea-status/meetup.php
new file mode 100644
index 0000000000..4eaad68224
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/meetup.php
@@ -0,0 +1,39 @@
+api( 'origins' )->is_oauth_enabled( 'meetup' ) ) {
+ if ( ! tribe( 'events-aggregator.settings' )->has_meetup_security_key() ) {
+ $icon = 'warning';
+ $message = __( 'You have not connected Event Aggregator to Meetup', 'tribe-common' );
+ $meetup_auth_url = Tribe__Events__Aggregator__Record__Meetup::get_auth_url( [ 'back' => 'settings' ] );
+ $notes = '' . esc_html_x( 'Connect to Meetup', 'link for connecting meetup', 'tribe-common' ) . '';
+ }
+} else {
+ $icon = 'warning';
+ $message = __( 'Limited connectivity with Meetup', 'tribe-common' );
+ $notes = esc_html__( 'The service has disabled oAuth. Some types of events may not import.', 'tribe-common' );
+}
+?>
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/scheduler-status.php b/tribe-common/src/admin-views/troubleshooting/ea-status/scheduler-status.php
new file mode 100644
index 0000000000..95adb99ed2
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/scheduler-status.php
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/ea-status/server-connection.php b/tribe-common/src/admin-views/troubleshooting/ea-status/server-connection.php
new file mode 100644
index 0000000000..8c5fc41db9
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/ea-status/server-connection.php
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+api()->domain;
+ $up = tribe( 'events-aggregator.service' )->get( 'status/up' );
+
+ if ( ! $up || is_wp_error( $up ) ) {
+ $icon = 'error';
+ /* translators: %s: Event Aggregator Server URL */
+ $message = sprintf( __( 'Not connected to %s', 'tribe-common' ), $ea_server );
+ $notes = esc_html__( 'The server is not currently responding', 'tribe-common' );
+ } elseif ( is_object( $up ) && is_object( $up->data ) && isset( $up->data->status ) && 400 <= $up->data->status ) {
+ // this is a rare condition that should never happen
+ // An example case: the route is not defined on the EA server
+ $icon = 'warning';
+
+ /* translators: %s: Event Aggregator Server URL */
+ $message = sprintf( __( 'Not connected to %s', 'tribe-common' ), $ea_server );
+ $notes = __( 'The server is responding with an error:', 'tribe-common' );
+ $notes .= '
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/event-log.php b/tribe-common/src/admin-views/troubleshooting/event-log.php
new file mode 100644
index 0000000000..a41f57a00f
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/event-log.php
@@ -0,0 +1,18 @@
+display_log();
+?>
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/first-steps.php b/tribe-common/src/admin-views/troubleshooting/first-steps.php
new file mode 100644
index 0000000000..90f8cb536e
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/first-steps.php
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html__( 'View article', 'tribe-common' ) . '';
+ echo sprintf( __( 'Most issues are caused by conflicts with the theme or other plugins. Follow these steps as a first point of action. %s', 'tribe-common' ), $link );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html__( 'View article', 'tribe-common' ) . '';
+ echo sprintf( __( 'Providing the details of your calendar plugin and settings (located below) helps our support team troubleshoot an issue faster. %s', 'tribe-common' ), $link );
+ ?>
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/footer-logo.php b/tribe-common/src/admin-views/troubleshooting/footer-logo.php
new file mode 100644
index 0000000000..138c6491fc
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/footer-logo.php
@@ -0,0 +1,14 @@
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/introduction.php b/tribe-common/src/admin-views/troubleshooting/introduction.php
new file mode 100644
index 0000000000..02c7744e73
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/introduction.php
@@ -0,0 +1,31 @@
+
+
+ admin_notice( 'troubleshooting' );
+ ?>
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/notice.php b/tribe-common/src/admin-views/troubleshooting/notice.php
new file mode 100644
index 0000000000..63d98076d8
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/notice.php
@@ -0,0 +1,20 @@
+' . esc_html__( 'Help page?', 'tribe-common' ) . '';
+?>
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/recent-template-changes.php b/tribe-common/src/admin-views/troubleshooting/recent-template-changes.php
new file mode 100644
index 0000000000..c46054380a
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/recent-template-changes.php
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tribe-common/src/admin-views/troubleshooting/support-cta.php b/tribe-common/src/admin-views/troubleshooting/support-cta.php
new file mode 100644
index 0000000000..b7bd7a646c
--- /dev/null
+++ b/tribe-common/src/admin-views/troubleshooting/support-cta.php
@@ -0,0 +1,31 @@
+
+
')},i.prototype.focusOnSearch=function(){var e=this;void 0!==e.$search&&setTimeout(function(){e._keyUpPrevented=!0,e.$search.focus()},1)},i.prototype.update=function(e){if(this.clear(),0!==e.length){for(var t=[],i=0;i1||n)return e.call(this,t);this.clear();var i=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(i)},t}),t.define("select2/selection/allowClear",["jquery","../keys"],function(e,t){function n(){}return n.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(e){i._handleClear(e)}),t.on("keypress",function(e){i._handleKeyboardClear(e,t)})},n.prototype._handleClear=function(e,t){if(!this.options.get("disabled")){var n=this.$selection.find(".select2-selection__clear");if(0!==n.length){t.stopPropagation();for(var i=n.data("data"),o=0;o0||0===n.length)){var i=e('×');i.data("data",n),this.$selection.find(".select2-selection__rendered").prepend(i)}},n}),t.define("select2/selection/search",["jquery","../utils","../keys"],function(e,t,n){function i(e,t,n){e.call(this,t,n)}return i.prototype.render=function(t){var n=e('');this.$searchContainer=n,this.$search=n.find("input");var i=t.call(this);return this._transferTabIndex(),i},i.prototype.bind=function(e,t,i){var o=this,s=t.id+"-results";e.call(this,t,i),t.on("open",function(){o.$search.attr("aria-owns",s),o.$search.trigger("focus")}),t.on("close",function(){o.$search.val(""),o.$search.removeAttr("aria-activedescendant"),o.$search.removeAttr("aria-owns"),o.$search.trigger("focus")}),t.on("enable",function(){o.$search.prop("disabled",!1),o._transferTabIndex()}),t.on("disable",function(){o.$search.prop("disabled",!0)}),t.on("focus",function(e){o.$search.trigger("focus")}),t.on("results:focus",function(e){o.$search.attr("aria-activedescendant",e.data._resultId)}),this.$selection.on("focusin",".select2-search--inline",function(e){o.trigger("focus",e)}),this.$selection.on("focusout",".select2-search--inline",function(e){o._handleBlur(e)}),this.$selection.on("keydown",".select2-search--inline",function(e){if(e.stopPropagation(),o.trigger("keypress",e),o._keyUpPrevented=e.isDefaultPrevented(),e.which===n.BACKSPACE&&""===o.$search.val()){var i=o.$searchContainer.prev(".select2-selection__choice");if(i.length>0){var s=i.data("data");o.searchRemoveChoice(s),e.preventDefault()}}else e.which===n.ENTER&&(t.open(),e.preventDefault())});var r=document.documentMode,a=r&&r<=11;this.$selection.on("input.searchcheck",".select2-search--inline",function(e){a?o.$selection.off("input.search input.searchcheck"):o.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(e){if(a&&"input"===e.type)o.$selection.off("input.search input.searchcheck");else{var t=e.which;t!=n.SHIFT&&t!=n.CTRL&&t!=n.ALT&&t!=n.TAB&&o.handleSearch(e)}})},i.prototype._transferTabIndex=function(e){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},i.prototype.createPlaceholder=function(e,t){this.$search.attr("placeholder",t.text)},i.prototype.update=function(e,t){var n=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),e.call(this,t),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),n&&this.$search.focus()},i.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var e=this.$search.val();this.trigger("query",{term:e})}this._keyUpPrevented=!1},i.prototype.searchRemoveChoice=function(e,t){this.trigger("unselect",{data:t}),this.$search.val(t.text),this.handleSearch()},i.prototype.resizeSearch=function(){this.$search.css("width","25px");var e="";""!==this.$search.attr("placeholder")?e=this.$selection.find(".select2-selection__rendered").innerWidth():e=.75*(this.$search.val().length+1)+"em";this.$search.css("width",e)},i}),t.define("select2/selection/eventRelay",["jquery"],function(e){function t(){}return t.prototype.bind=function(t,n,i){var o=this,s=["open","opening","close","closing","select","selecting","unselect","unselecting"],r=["opening","closing","selecting","unselecting"];t.call(this,n,i),n.on("*",function(t,n){if(-1!==e.inArray(t,s)){n=n||{};var i=e.Event("select2:"+t,{params:n});o.$element.trigger(i),-1!==e.inArray(t,r)&&(n.prevented=i.isDefaultPrevented())}})},t}),t.define("select2/translation",["jquery","require"],function(e,t){function n(e){this.dict=e||{}}return n.prototype.all=function(){return this.dict},n.prototype.get=function(e){return this.dict[e]},n.prototype.extend=function(t){this.dict=e.extend({},t.all(),this.dict)},n._cache={},n.loadPath=function(e){if(!(e in n._cache)){var i=t(e);n._cache[e]=i}return new n(n._cache[e])},n}),t.define("select2/diacritics",[],function(){return{"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"}}),t.define("select2/data/base",["../utils"],function(e){function t(e,n){t.__super__.constructor.call(this)}return e.Extend(t,e.Observable),t.prototype.current=function(e){throw new Error("The `current` method must be defined in child classes.")},t.prototype.query=function(e,t){throw new Error("The `query` method must be defined in child classes.")},t.prototype.bind=function(e,t){},t.prototype.destroy=function(){},t.prototype.generateResultId=function(t,n){var i="";return i+=null!=t?t.id:e.generateChars(4),i+="-result-",i+=e.generateChars(4),null!=n.id?i+="-"+n.id.toString():i+="-"+e.generateChars(4),i},t}),t.define("select2/data/select",["./base","../utils","jquery"],function(e,t,n){function i(e,t){this.$element=e,this.options=t,i.__super__.constructor.call(this)}return t.Extend(i,e),i.prototype.current=function(e){var t=[],i=this;this.$element.find(":selected").each(function(){var e=n(this),o=i.item(e);t.push(o)}),e(t)},i.prototype.select=function(e){var t=this;if(e.selected=!0,n(e.element).is("option"))return e.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(i){var o=[];(e=[e]).push.apply(e,i);for(var s=0;s=0){var u=o.filter(a(c)),d=this.item(u),p=n.extend(!0,{},c,d),h=this.option(p);u.replaceWith(h)}else{var f=this.option(c);if(c.children){var g=this.convertToOptions(c.children);t.appendMany(f,g)}r.push(f)}}return r},i}),t.define("select2/data/ajax",["./array","../utils","jquery"],function(e,t,n){function i(e,t){this.ajaxOptions=this._applyDefaults(t.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),i.__super__.constructor.call(this,e,t)}return t.Extend(i,e),i.prototype._applyDefaults=function(e){var t={data:function(e){return n.extend({},e,{q:e.term})},transport:function(e,t,i){var o=n.ajax(e);return o.then(t),o.fail(i),o}};return n.extend({},t,e,!0)},i.prototype.processResults=function(e){return e},i.prototype.query=function(e,t){var i=this;null!=this._request&&("function"==typeof this._request.abort&&this._request.abort(),this._request=null);var o=n.extend({type:"GET"},this.ajaxOptions);function s(){var n=o.transport(o,function(n){var o=i.processResults(n,e);i.options.get("debug")&&window.console&&console.error&&(o&&o.results&&Array.isArray(o.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),t(o),i.container.focusOnActiveElement()},function(){n.status&&"0"===n.status||i.trigger("results:message",{message:"errorLoading"})});i._request=n}"function"==typeof o.url&&(o.url=o.url.call(this.$element,e)),"function"==typeof o.data&&(o.data=o.data.call(this.$element,e)),this.ajaxOptions.delay&&null!=e.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(s,this.ajaxOptions.delay)):s()},i}),t.define("select2/data/tags",["jquery"],function(e){function t(e,t,n){var i=n.get("tags"),o=n.get("createTag");void 0!==o&&(this.createTag=o);var s=n.get("insertTag");if(void 0!==s&&(this.insertTag=s),e.call(this,t,n),Array.isArray(i))for(var r=0;r0&&t.term.length>this.maximumInputLength?this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),t.define("select2/data/maximumSelectionLength",[],function(){function e(e,t,n){this.maximumSelectionLength=n.get("maximumSelectionLength"),e.call(this,t,n)}return e.prototype.query=function(e,t,n){var i=this;this.current(function(o){var s=null!=o?o.length:0;i.maximumSelectionLength>0&&s>=i.maximumSelectionLength?i.trigger("results:message",{message:"maximumSelected",args:{maximum:i.maximumSelectionLength}}):e.call(i,t,n)})},e}),t.define("select2/dropdown",["jquery","./utils"],function(e,t){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return t.Extend(n,t.Observable),n.prototype.render=function(){var t=e('');return t.attr("dir",this.options.get("dir")),this.$dropdown=t,t},n.prototype.bind=function(){},n.prototype.position=function(e,t){},n.prototype.destroy=function(){this.$dropdown.remove()},n}),t.define("select2/dropdown/search",["jquery","../utils"],function(e,t){function n(){}return n.prototype.render=function(t){var n=t.call(this),i=e('');return this.$searchContainer=i,this.$search=i.find("input"),n.prepend(i),n},n.prototype.bind=function(t,n,i){var o=this,s=n.id+"-results";t.call(this,n,i),this.$search.on("keydown",function(e){o.trigger("keypress",e),o._keyUpPrevented=e.isDefaultPrevented()}),this.$search.on("input",function(t){e(this).off("keyup")}),this.$search.on("keyup input",function(e){o.handleSearch(e)}),n.on("open",function(){o.$search.attr("tabindex",0),o.$search.attr("aria-owns",s),o.$search.focus(),window.setTimeout(function(){o.$search.focus()},0)}),n.on("close",function(){o.$search.attr("tabindex",-1),o.$search.removeAttr("aria-activedescendant"),o.$search.removeAttr("aria-owns"),o.$search.val("")}),n.on("focus",function(){n.isOpen()||o.$search.focus()}),n.on("results:all",function(e){null!=e.query.term&&""!==e.query.term||(o.showSearch(e)?o.$searchContainer.removeClass("select2-search--hide"):o.$searchContainer.addClass("select2-search--hide"))}),n.on("results:focus",function(e){o.$search.attr("aria-activedescendant",e.data._resultId)})},n.prototype.handleSearch=function(e){if(!this._keyUpPrevented){var t=this.$search.val();this.trigger("query",{term:t})}this._keyUpPrevented=!1},n.prototype.showSearch=function(e,t){return!0},n}),t.define("select2/dropdown/hidePlaceholder",[],function(){function e(e,t,n,i){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n,i)}return e.prototype.append=function(e,t){t.results=this.removePlaceholder(t.results),e.call(this,t)},e.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},e.prototype.removePlaceholder=function(e,t){for(var n=t.slice(0),i=t.length-1;i>=0;i--){var o=t[i];this.placeholder.id===o.id&&n.splice(i,1)}return n},e}),t.define("select2/dropdown/infiniteScroll",["jquery"],function(e){function t(e,t,n,i){this.lastParams={},e.call(this,t,n,i),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return t.prototype.append=function(e,t){this.$loadingMore.remove(),this.loading=!1,e.call(this,t),this.showLoadingMore(t)&&this.$results.append(this.$loadingMore)},t.prototype.bind=function(t,n,i){var o=this;t.call(this,n,i),n.on("query",function(e){o.lastParams=e,o.loading=!0}),n.on("query:append",function(e){o.lastParams=e,o.loading=!0}),this.$results.on("scroll",function(){var t=e.contains(document.documentElement,o.$loadingMore[0]);!o.loading&&t&&(o.$results.offset().top+o.$results.outerHeight(!1)+50>=o.$loadingMore.offset().top+o.$loadingMore.outerHeight(!1)&&o.loadMore())})},t.prototype.loadMore=function(){this.loading=!0;var t=e.extend({},{page:1},this.lastParams);t.page++,this.trigger("query:append",t)},t.prototype.showLoadingMore=function(e,t){return t.pagination&&t.pagination.more},t.prototype.createLoadingMore=function(){var t=e(''),n=this.options.get("translations").get("loadingMore");return t.html(n(this.lastParams)),t},t}),t.define("select2/dropdown/attachBody",["jquery","../utils"],function(e,t){function n(t,n,i){this.$dropdownParent=i.get("dropdownParent")||e(document.body),t.call(this,n,i)}return n.prototype.bind=function(e,t,n){var i=this,o=!1;e.call(this,t,n),t.on("open",function(){i._showDropdown(),i._attachPositioningHandler(t),o||(o=!0,t.on("results:all",function(){i._positionDropdown(),i._resizeDropdown()}),t.on("results:append",function(){i._positionDropdown(),i._resizeDropdown()}))}),t.on("close",function(){i._hideDropdown(),i._detachPositioningHandler(t)}),this.$dropdownContainer.on("mousedown",function(e){e.stopPropagation()})},n.prototype.destroy=function(e){e.call(this),this.$dropdownContainer.remove()},n.prototype.position=function(e,t,n){t.attr("class",n.attr("class")),t.removeClass("select2"),t.addClass("select2-container--open"),t.css({position:"absolute",top:-999999}),this.$container=n},n.prototype.render=function(t){var n=e(""),i=t.call(this);return n.append(i),this.$dropdownContainer=n,n},n.prototype._hideDropdown=function(e){this.$dropdownContainer.detach()},n.prototype._attachPositioningHandler=function(n,i){var o=this,s="scroll.select2."+i.id,r="resize.select2."+i.id,a="orientationchange.select2."+i.id,l=this.$container.parents().filter(t.hasScroll);l.each(function(){e(this).data("select2-scroll-position",{x:e(this).scrollLeft(),y:e(this).scrollTop()})}),l.on(s,function(t){var n=e(this).data("select2-scroll-position");e(this).scrollTop(n.y)}),e(window).on(s+" "+r+" "+a,function(e){o._positionDropdown(),o._resizeDropdown()})},n.prototype._detachPositioningHandler=function(n,i){var o="scroll.select2."+i.id,s="resize.select2."+i.id,r="orientationchange.select2."+i.id;this.$container.parents().filter(t.hasScroll).off(o),e(window).off(o+" "+s+" "+r)},n.prototype._positionDropdown=function(){var t=e(window),n=this.$dropdown.hasClass("select2-dropdown--above"),i=this.$dropdown.hasClass("select2-dropdown--below"),o=null,s=this.$container.offset();s.bottom=s.top+this.$container.outerHeight(!1);var r={height:this.$container.outerHeight(!1)};r.top=s.top,r.bottom=s.top+r.height;var a=this.$dropdown.outerHeight(!1),l=t.scrollTop(),c=t.scrollTop()+t.height(),u=ls.bottom+a,p={left:s.left,top:r.bottom},h=this.$dropdownParent;"static"===h.css("position")&&(h=h.offsetParent());var f=h.offset();p.top-=f.top,p.left-=f.left,n||i||(o="below"),d||!u||n?!u&&d&&n&&(o="below"):o="above",("above"==o||n&&"below"!==o)&&(p.top=r.top-f.top-a),null!=o&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+o),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+o)),this.$dropdownContainer.css(p)},n.prototype._resizeDropdown=function(){var e={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(e.minWidth=e.width,e.position="relative",e.width="auto"),this.$dropdown.css(e)},n.prototype._showDropdown=function(e){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},n}),t.define("select2/dropdown/minimumResultsForSearch",[],function(){function e(e,t,n,i){this.minimumResultsForSearch=n.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),e.call(this,t,n,i)}return e.prototype.showSearch=function(e,t){return!(function e(t){for(var n=0,i=0;i0&&(d.dataAdapter=c.Decorate(d.dataAdapter,v)),d.maximumInputLength>0&&(d.dataAdapter=c.Decorate(d.dataAdapter,y)),d.maximumSelectionLength>0&&(d.dataAdapter=c.Decorate(d.dataAdapter,w)),d.tags&&(d.dataAdapter=c.Decorate(d.dataAdapter,g)),null==d.tokenSeparators&&null==d.tokenizer||(d.dataAdapter=c.Decorate(d.dataAdapter,m)),null!=d.query){var E=t(d.amdBase+"compat/query");d.dataAdapter=c.Decorate(d.dataAdapter,E)}if(null!=d.initSelection){var D=t(d.amdBase+"compat/initSelection");d.dataAdapter=c.Decorate(d.dataAdapter,D)}}if(null==d.resultsAdapter&&(d.resultsAdapter=n,null!=d.ajax&&(d.resultsAdapter=c.Decorate(d.resultsAdapter,x)),null!=d.placeholder&&(d.resultsAdapter=c.Decorate(d.resultsAdapter,b)),d.selectOnClose&&(d.resultsAdapter=c.Decorate(d.resultsAdapter,S))),null==d.dropdownAdapter){if(d.multiple)d.dropdownAdapter=_;else{var T=c.Decorate(_,$);d.dropdownAdapter=T}if(0!==d.minimumResultsForSearch&&(d.dropdownAdapter=c.Decorate(d.dropdownAdapter,C)),d.closeOnSelect&&(d.dropdownAdapter=c.Decorate(d.dropdownAdapter,O)),null!=d.dropdownCssClass||null!=d.dropdownCss||null!=d.adaptDropdownCssClass){var q=t(d.amdBase+"compat/dropdownCss");d.dropdownAdapter=c.Decorate(d.dropdownAdapter,q)}d.dropdownAdapter=c.Decorate(d.dropdownAdapter,A)}if(null==d.selectionAdapter){if(d.multiple?d.selectionAdapter=o:d.selectionAdapter=i,null!=d.placeholder&&(d.selectionAdapter=c.Decorate(d.selectionAdapter,s)),d.allowClear&&(d.selectionAdapter=c.Decorate(d.selectionAdapter,r)),d.multiple&&(d.selectionAdapter=c.Decorate(d.selectionAdapter,a)),null!=d.containerCssClass||null!=d.containerCss||null!=d.adaptContainerCssClass){var j=t(d.amdBase+"compat/containerCss");d.selectionAdapter=c.Decorate(d.selectionAdapter,j)}d.selectionAdapter=c.Decorate(d.selectionAdapter,l)}if("string"==typeof d.language)if(d.language.indexOf("-")>0){var L=d.language.split("-")[0];d.language=[d.language,L]}else d.language=[d.language];if(Array.isArray(d.language)){var k=new u;d.language.push("en");for(var P=d.language,I=0;I0){for(var s=e.extend(!0,{},o),r=o.children.length-1;r>=0;r--)null==n(i,o.children[r])&&s.children.splice(r,1);return s.children.length>0?s:n(i,s)}var a=t(o.text).toUpperCase(),l=t(i.term).toUpperCase();return a.indexOf(l)>-1?o:null},minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(e){return e},templateResult:function(e){return e.text},templateSelection:function(e){return e.text},theme:"default",width:"resolve"}},D.prototype.set=function(t,n){var i={};i[e.camelCase(t)]=n;var o=c._convertData(i);e.extend(this.defaults,o)},new D}),t.define("select2/options",["require","jquery","./defaults","./utils"],function(e,t,n,i){function o(t,o){if(this.options=t,null!=o&&this.fromElement(o),this.options=n.apply(this.options),o&&o.is("input")){var s=e(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=i.Decorate(this.options.dataAdapter,s)}}return o.prototype.fromElement=function(e){var n=["select2"];null==this.options.multiple&&(this.options.multiple=e.prop("multiple")),null==this.options.disabled&&(this.options.disabled=e.prop("disabled")),null==this.options.language&&(e.prop("lang")?this.options.language=e.prop("lang").toLowerCase():e.closest("[lang]").prop("lang")&&(this.options.language=e.closest("[lang]").prop("lang"))),null==this.options.dir&&(e.prop("dir")?this.options.dir=e.prop("dir"):e.closest("[dir]").prop("dir")?this.options.dir=e.closest("[dir]").prop("dir"):this.options.dir="ltr"),e.prop("disabled",this.options.disabled),e.prop("multiple",this.options.multiple),e.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),e.data("data",e.data("select2Tags")),e.data("tags",!0)),e.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),e.attr("ajax--url",e.data("ajaxUrl")),e.data("ajax--url",e.data("ajaxUrl")));var o={};o=t.fn.jquery&&"1."==t.fn.jquery.substr(0,2)&&e[0].dataset?t.extend(!0,{},e[0].dataset,e.data()):e.data();var s=t.extend(!0,{},o);for(var r in s=i._convertData(s))t.inArray(r,n)>-1||(t.isPlainObject(this.options[r])?t.extend(this.options[r],s[r]):this.options[r]=s[r]);return this},o.prototype.get=function(e){return this.options[e]},o.prototype.set=function(e,t){this.options[e]=t},o}),t.define("select2/core",["jquery","./options","./utils","./keys"],function(e,t,n,i){var o=function(e,n){null!=e.data("select2")&&e.data("select2").destroy(),this.$element=e,this.id=this._generateId(e),n=n||{},this.options=new t(n,e),o.__super__.constructor.call(this);var i=e.attr("tabindex")||0;e.data("old-tabindex",i),e.attr("tabindex","-1");var s=this.options.get("dataAdapter");this.dataAdapter=new s(e,this.options);var r=this.render();this._placeContainer(r);var a=this.options.get("selectionAdapter");this.selection=new a(e,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,r);var l=this.options.get("dropdownAdapter");this.dropdown=new l(e,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,r);var c=this.options.get("resultsAdapter");this.results=new c(e,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var u=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(e){u.trigger("selection:update",{data:e})}),e.addClass("select2-hidden-accessible"),e.attr("aria-hidden","true"),this._syncAttributes(),e.data("select2",this)};return n.Extend(o,n.Observable),o.prototype._generateId=function(e){return"select2-"+(null!=e.attr("id")?e.attr("id"):null!=e.attr("name")?e.attr("name")+"-"+n.generateChars(2):n.generateChars(4)).replace(/(:|\.|\[|\]|,)/g,"")},o.prototype._placeContainer=function(e){e.insertAfter(this.$element);var t=this._resolveWidth(this.$element,this.options.get("width"));null!=t&&e.css("width",t)},o.prototype._resolveWidth=function(e,t){var n=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==t){var i=this._resolveWidth(e,"style");return null!=i?i:this._resolveWidth(e,"element")}if("element"==t){var o=e.outerWidth(!1);return o<=0?"auto":o+"px"}if("style"==t){var s=e.attr("style");if("string"!=typeof s)return null;for(var r=s.split(";"),a=0,l=r.length;a=1)return c[1]}return null}return t},o.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},o.prototype._registerDomEvents=function(){var t=this;this.$element.on("change.select2",function(){t.dataAdapter.current(function(e){t.trigger("selection:update",{data:e})})}),this.$element.on("focus.select2",function(e){t.trigger("focus",e)}),this._syncA=n.bind(this._syncAttributes,this),this._syncS=n.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var i=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=i?(this._observer=new i(function(n){e.each(n,t._syncA),e.each(n,t._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",t._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",t._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",t._syncS,!1))},o.prototype._registerDataEvents=function(){var e=this;this.dataAdapter.on("*",function(t,n){e.trigger(t,n)})},o.prototype._registerSelectionEvents=function(){var t=this,n=["toggle","focus"];this.selection.on("toggle",function(){t.toggleDropdown()}),this.selection.on("focus",function(e){t.focus(e)}),this.selection.on("*",function(i,o){-1===e.inArray(i,n)&&t.trigger(i,o)})},o.prototype._registerDropdownEvents=function(){var e=this;this.dropdown.on("*",function(t,n){e.trigger(t,n)})},o.prototype._registerResultsEvents=function(){var e=this;this.results.on("*",function(t,n){e.trigger(t,n)})},o.prototype._registerEvents=function(){var t=this;this.on("open",function(){t.$container.addClass("select2-container--open")}),this.on("close",function(){t.$container.removeClass("select2-container--open")}),this.on("enable",function(){t.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){t.$container.addClass("select2-container--disabled")}),this.on("blur",function(){t.$container.removeClass("select2-container--focus")}),this.on("query",function(e){t.isOpen()||t.trigger("open",{}),this.dataAdapter.query(e,function(n){t.trigger("results:all",{data:n,query:e})})}),this.on("query:append",function(e){this.dataAdapter.query(e,function(n){t.trigger("results:append",{data:n,query:e})})}),this.on("open",function(){setTimeout(function(){t.focusOnActiveElement()},1)}),e(document).on("keydown",function(e){var n=e.which;if(t.isOpen()){n===i.ESC||n===i.TAB||n===i.UP&&e.altKey?(t.close(),e.preventDefault()):n===i.ENTER?(t.trigger("results:select",{}),e.preventDefault()):n===i.SPACE&&e.ctrlKey?(t.trigger("results:toggle",{}),e.preventDefault()):n===i.UP?(t.trigger("results:previous",{}),e.preventDefault()):n===i.DOWN&&(t.trigger("results:next",{}),e.preventDefault());var o=t.$dropdown.find(".select2-search__field");o.length||(o=t.$container.find(".select2-search__field")),n===i.DOWN||n===i.UP?t.focusOnActiveElement():(o.focus(),setTimeout(function(){t.focusOnActiveElement()},1e3))}else t.hasFocus()&&(n!==i.ENTER&&n!==i.SPACE&&n!==i.DOWN||(t.open(),e.preventDefault()))})},o.prototype.focusOnActiveElement=function(){this.isOpen()&&!n.isTouchscreen()&&this.$results.find("li.select2-results__option--highlighted").focus()},o.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},o.prototype._syncSubtree=function(e,t){var n=!1,i=this;if(!e||!e.target||"OPTION"===e.target.nodeName||"OPTGROUP"===e.target.nodeName){if(t)if(t.addedNodes&&t.addedNodes.length>0)for(var o=0;o0&&(n=!0);else n=!0;n&&this.dataAdapter.current(function(e){i.trigger("selection:update",{data:e})})}},o.prototype.trigger=function(e,t){var n=o.__super__.trigger,i={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===t&&(t={}),e in i){var s=i[e],r={prevented:!1,name:e,args:t};if(n.call(this,s,r),r.prevented)return void(t.prevented=!0)}n.call(this,e,t)},o.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},o.prototype.open=function(){this.isOpen()||this.trigger("query",{})},o.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},o.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},o.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},o.prototype.focus=function(e){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},o.prototype.enable=function(e){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),null!=e&&0!==e.length||(e=[!0]);var t=!e[0];this.$element.prop("disabled",t)},o.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var e=[];return this.dataAdapter.current(function(t){e=t}),e},o.prototype.val=function(t){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==t||0===t.length)return this.$element.val();var n=t[0];Array.isArray(n)&&(n=e.map(n,function(e){return e.toString()})),this.$element.val(n).trigger("change")},o.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},o.prototype.render=function(){var t=e('');return t.attr("dir",this.options.get("dir")),this.$container=t,this.$container.addClass("select2-container--"+this.options.get("theme")),t.data("element",this.$element),t},o}),t.define("select2/compat/utils",["jquery"],function(e){return{syncCssClasses:function(t,n,i){var o,s,r=[];(o=t.attr("class").trim())&&e((o=""+o).split(/\s+/)).each(function(){0===this.indexOf("select2-")&&r.push(this)}),(o=n.attr("class").trim())&&e((o=""+o).split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&null!=(s=i(this))&&r.push(s)}),t.attr("class",r.join(" "))}}}),t.define("select2/compat/containerCss",["jquery","./utils"],function(e,t){function n(e){return null}function i(){}return i.prototype.render=function(e){var i=e.call(this),o=this.options.get("containerCssClass")||"";"function"==typeof o&&(o=o(this.$element));var s=this.options.get("adaptContainerCssClass");if(s=s||n,-1!==o.indexOf(":all:")){o=o.replace(":all:","");var r=s;s=function(e){var t=r(e);return null!=t?t+" "+e:e}}var a=this.options.get("containerCss")||{};return"function"==typeof a&&(a=a(this.$element)),t.syncCssClasses(i,this.$element,s),i.css(a),i.addClass(o),i},i}),t.define("select2/compat/dropdownCss",["jquery","./utils"],function(e,t){function n(e){return null}function i(){}return i.prototype.render=function(e){var i=e.call(this),o=this.options.get("dropdownCssClass")||"";"function"==typeof o&&(o=o(this.$element));var s=this.options.get("adaptDropdownCssClass");if(s=s||n,-1!==o.indexOf(":all:")){o=o.replace(":all:","");var r=s;s=function(e){var t=r(e);return null!=t?t+" "+e:e}}var a=this.options.get("dropdownCss")||{};return"function"==typeof a&&(a=a(this.$element)),t.syncCssClasses(i,this.$element,s),i.css(a),i.addClass(o),i},i}),t.define("select2/compat/initSelection",["jquery"],function(e){function t(e,t,n){n.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=n.get("initSelection"),this._isInitialized=!1,e.call(this,t,n)}return t.prototype.current=function(e,t){var n=this;this._isInitialized?e.call(this,t):this.initSelection.call(null,this.$element,function(e){n._isInitialized=!0,Array.isArray(e)||(e=[e]),t(e)})},t}),t.define("select2/compat/inputData",["jquery"],function(e){function t(e,t,n){this._currentData=[],this._valueSeparator=n.get("valueSeparator")||",","hidden"===t.prop("type")&&n.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a `