diff --git a/common b/common index e03e060016..840b64db19 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e03e0600165c683820705de736115c210a4200c4 +Subproject commit 840b64db1971513b5bedd509a4cde8ecbc8adad7 diff --git a/event-tickets.php b/event-tickets.php index e5090432b5..4b5114b79b 100644 --- a/event-tickets.php +++ b/event-tickets.php @@ -3,7 +3,7 @@ Plugin Name: Event Tickets Plugin URI: https://evnt.is/1acb Description: Event Tickets allows you to sell basic tickets and collect RSVPs from any post, page, or event. -Version: 5.9.1.1 +Version: 5.9.2 Author: The Events Calendar Author URI: https://evnt.is/1aor License: GPLv2 or later diff --git a/lang/event-tickets.pot b/lang/event-tickets.pot index a4d7dfc071..180c392620 100644 --- a/lang/event-tickets.pot +++ b/lang/event-tickets.pot @@ -2,15 +2,15 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Event Tickets 5.9.1.1\n" +"Project-Id-Version: Event Tickets 5.9.2\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/event-tickets\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-04-25T08:44:10-07:00\n" -"PO-Revision-Date: 2024-04-25 15:44\n" +"POT-Creation-Date: 2024-05-06T11:23:29-07:00\n" +"PO-Revision-Date: 2024-05-06 18:23\n" "X-Generator: WP-CLI 2.7.1\n" "X-Domain: event-tickets\n" @@ -19,7 +19,7 @@ msgstr "" #: src/admin-views/admin-welcome-message.php:73 #: src/Tickets/Site_Health/Info_Section.php:73 #: src/Tribe/Admin/Notices.php:85 -#: src/Tribe/Main.php:764 +#: src/Tribe/Main.php:767 #: src/Tribe/Privacy.php:59 #: src/views/emails/template-parts/body/footer/credit.php:41 msgid "Event Tickets" @@ -67,7 +67,7 @@ msgstr "" #: src/admin-views/tribe-commerce-settings.php:4 #: src/Tickets/Commerce/Payments_Tab.php:300 #: src/Tribe/Admin/Notices.php:207 -#: src/Tribe/Main.php:739 +#: src/Tribe/Main.php:742 msgid "Event Tickets Plus" msgstr "" @@ -461,23 +461,23 @@ msgstr "" msgid "Leave blank for unlimited" msgstr "" -#: src/admin-views/commerce/metabox/sale-price.php:33 +#: src/admin-views/commerce/metabox/sale-price.php:43 msgid "Add Sale Price" msgstr "" -#: src/admin-views/commerce/metabox/sale-price.php:42 -#: src/admin-views/editor/fieldset/price.php:108 +#: src/admin-views/commerce/metabox/sale-price.php:52 +#: src/admin-views/editor/fieldset/price.php:120 #: src/admin-views/legacy-ticket-fields.php:24 #: src/admin-views/price-fields.php:12 msgid "Sale Price:" msgstr "" -#: src/admin-views/commerce/metabox/sale-price.php:58 +#: src/admin-views/commerce/metabox/sale-price.php:66 msgid "On sale from:" msgstr "" -#: src/admin-views/commerce/metabox/sale-price.php:72 -#: src/Tickets/Commerce/Hooks.php:789 +#: src/admin-views/commerce/metabox/sale-price.php:80 +#: src/Tickets/Commerce/Hooks.php:791 msgid "to" msgstr "" @@ -536,7 +536,7 @@ msgid "View Orders" msgstr "" #: src/admin-views/editor/column-body-price.php:22 -#: src/admin-views/editor/fieldset/price.php:71 +#: src/admin-views/editor/fieldset/price.php:83 #: src/admin-views/legacy-ticket-fields.php:15 #: src/admin-views/price-fields.php:3 msgid "Price:" @@ -584,26 +584,26 @@ msgstr "" msgid "Ti" msgstr "" +#: src/admin-views/editor/fieldset/price.php:31 +msgctxt "price description" +msgid "Leave blank for free %s" +msgstr "" + #. Translators: %s: singular version of the Ticket label. -#: src/admin-views/editor/fieldset/price.php:10 +#: src/admin-views/editor/fieldset/price.php:39 msgctxt "ticket price validation error" msgid "%s price must be greater than zero." msgstr "" -#: src/admin-views/editor/fieldset/price.php:26 -msgctxt "price description" -msgid "Leave blank for free %s" -msgstr "" - -#: src/admin-views/editor/fieldset/price.php:109 +#: src/admin-views/editor/fieldset/price.php:121 msgid "Current sale price. This can be managed via the product editor." msgstr "" -#: src/admin-views/editor/fieldset/price.php:112 +#: src/admin-views/editor/fieldset/price.php:124 msgid "Sale/Member Price:" msgstr "" -#: src/admin-views/editor/fieldset/price.php:113 +#: src/admin-views/editor/fieldset/price.php:125 msgid "Current sale or member price. This can be managed via the product editor." msgstr "" @@ -2468,65 +2468,65 @@ msgstr "" msgid "Unexpected PayPal response when updating webhook" msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:73 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:76 msgid "PayPal" msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:83 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:86 msgid "PayPal is now connected." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:88 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:91 msgid "Failed to disconnect PayPal account." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:93 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:96 msgid "Disconnected PayPal account." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:98 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:101 msgid "Failed to refresh PayPal access token." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:103 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:106 msgid "PayPal access token was refreshed successfully." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:108 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:111 msgid "Failed to refresh PayPal user info." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:113 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:116 msgid "PayPal user info was refreshed successfully." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:118 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:121 msgid "Failed to refresh PayPal webhooks." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:123 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:126 msgid "PayPal webhooks refreshed successfully." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:128 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:131 msgid "A valid SSL certificate is required to set up your PayPal account and accept payments" msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:147 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:150 msgid "Enable payments through PayPal, Venmo, and credit card." msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:218 -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:195 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:221 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:197 msgid "here" msgstr "" -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:220 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:223 msgid "PayPal doesn't support your selected currency" msgstr "" #. Translators: %1$s: Currency Name. %2$s: Link to gateway provider's currency documentation. -#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:223 +#: src/Tickets/Commerce/Gateways/PayPal/Gateway.php:226 msgid "Unfortunately PayPal doesn't support payments in %1$s. Please try using a different gateway or adjusting your Tickets Commerce currency setting. You can see a list of supported currencies %2$s." msgstr "" @@ -6724,41 +6724,41 @@ msgstr "" msgid "Change %1$s in PayPal from webhook: %2$s" msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:75 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:77 msgid "Stripe" msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:85 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:87 msgid "Stripe wasn't able to complete your connection request. Try again." msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:90 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:92 msgid "Stripe signup was successful but the authentication tokens could not be retrieved. Try refreshing the tokens." msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:95 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:97 msgid "Disconnecting from Stripe failed. Please try again." msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:105 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:107 msgid "Due to Regulatory Issues between Stripe and the country listed in your Stripe account, the free version of Event Tickets cannot accept connections from accounts in your country. Please use a Stripe account from a different country or purchase Event Tickets Plus to continue." msgstr "" #. Translators: %1$s is the opening tag for the Payments Tab page link. %2$s is the closing tag. -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:112 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:114 msgid "Your stripe account was disconnected from the Stripe dashboard. If you believe this is an error, you can re-connect in the %1$sPayments Tab of the Settings Page%2$s." msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:134 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:136 msgid "Enable credit card payments, Afterpay, AliPay, Giropay, Klarna and more." msgstr "" -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:197 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:199 msgid "Stripe doesn't support your selected currency" msgstr "" #. Translators: %1$s: Currency Name. %2$s: Link to gateway provider's currency documentation. -#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:200 +#: src/Tickets/Commerce/Gateways/Stripe/Gateway.php:202 msgid "Unfortunately, Stripe doesn't support payments in %1$s. Please try using a different gateway or adjusting your Tickets Commerce currency setting. You can see a list of supported currencies %2$s." msgstr "" @@ -7019,23 +7019,23 @@ msgstr "" msgid "Payment Intent %s does not require an update or is a duplicate of a past event." msgstr "" -#: src/Tickets/Commerce/Hooks.php:786 +#: src/Tickets/Commerce/Hooks.php:788 msgid "Add sale price" msgstr "" -#: src/Tickets/Commerce/Hooks.php:787 +#: src/Tickets/Commerce/Hooks.php:789 msgid "Sale Price" msgstr "" -#: src/Tickets/Commerce/Hooks.php:788 +#: src/Tickets/Commerce/Hooks.php:790 msgid "On sale from" msgstr "" -#: src/Tickets/Commerce/Hooks.php:790 +#: src/Tickets/Commerce/Hooks.php:792 msgid "Sale price must be lower than the regular ticket price." msgstr "" -#: src/Tickets/Commerce/Hooks.php:791 +#: src/Tickets/Commerce/Hooks.php:793 #: src/views/v2/tickets/item/content/sale-label.php:36 msgid "On Sale" msgstr "" @@ -7987,6 +7987,11 @@ msgstr "" msgid "From the 1st of January 2023, the euro became the official currency for Croatia. We have removed the Croatian Kuna from our currency settings and updated your settings to start selling with Euro." msgstr "" +#: src/Tickets/Commerce/Utils/Value.php:90 +msgctxt "No cost" +msgid "Free" +msgstr "" + #: src/Tickets/Custom_Tables/V1/admin-views/migration/maintenance-mode/ticket-updates.php:6 msgid "Your changes will not be saved." msgstr "" @@ -8838,7 +8843,7 @@ msgid "Includes ticketed attendees with orders marked Completed." msgstr "" #: src/Tribe/Admin/Home/Service_Provider.php:80 -#: src/Tribe/Main.php:899 +#: src/Tribe/Main.php:902 msgid "Welcome to Event Tickets!" msgstr "" @@ -9505,11 +9510,11 @@ msgstr "" msgid "Invalid nonce" msgstr "" -#: src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php:268 +#: src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php:273 msgid "Invalid price" msgstr "" -#: src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php:311 +#: src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php:316 msgid "%s was not able to be updated" msgstr "" @@ -9558,83 +9563,83 @@ msgctxt "list view buy now ticket button" msgid "Get %s" msgstr "" -#: src/Tribe/Main.php:163 +#: src/Tribe/Main.php:166 msgctxt "provider_plugin_name" msgid "Tickets" msgstr "" #. Translators: %1$s is the min required version of The Events Calendar. %2$s Is the update link opening ``. %3$s Is the update link closing ``. -#: src/Tribe/Main.php:502 +#: src/Tribe/Main.php:505 msgid "When The Events Calendar and Event Tickets are both activated, The Events Calendar must be running version %1$s or greater. Please %2$supdate now.%3$s" msgstr "" -#: src/Tribe/Main.php:565 +#: src/Tribe/Main.php:568 msgid "Sorry, Event Tickets requires WordPress %s or higher. Please upgrade your WordPress install." msgstr "" -#: src/Tribe/Main.php:569 +#: src/Tribe/Main.php:572 msgid "Sorry, Event Tickets requires PHP %s or higher. Talk to your Web host about moving you to a newer version of PHP." msgstr "" -#: src/Tribe/Main.php:694 +#: src/Tribe/Main.php:697 msgid "Support for Event Tickets" msgstr "" -#: src/Tribe/Main.php:696 +#: src/Tribe/Main.php:699 msgid "Settings overview" msgstr "" -#: src/Tribe/Main.php:697 +#: src/Tribe/Main.php:700 msgid "Features overview" msgstr "" -#: src/Tribe/Main.php:698 +#: src/Tribe/Main.php:701 msgid "Troubleshooting common problems" msgstr "" -#: src/Tribe/Main.php:699 +#: src/Tribe/Main.php:702 msgid "Customizing Event Tickets" msgstr "" -#: src/Tribe/Main.php:716 +#: src/Tribe/Main.php:719 msgid "New User Primer" msgstr "" -#: src/Tribe/Main.php:718 +#: src/Tribe/Main.php:721 msgctxt "help feature box section" msgid "We are committed to helping you sell %1$s for your event. Check out our handy %2$s to get started." msgstr "" -#: src/Tribe/Main.php:735 +#: src/Tribe/Main.php:738 msgid "open-source forum on WordPress.org" msgstr "" -#: src/Tribe/Main.php:736 +#: src/Tribe/Main.php:739 msgid "If you have tried the above steps and are still having trouble, you can post a new thread to our %s. Our support staff monitors these forums once a week and would be happy to assist you there." msgstr "" -#: src/Tribe/Main.php:738 +#: src/Tribe/Main.php:741 msgid "premium support on our website" msgstr "" -#: src/Tribe/Main.php:740 +#: src/Tribe/Main.php:743 msgid "Looking for more immediate support? We offer %1$s with the purchase of any of our premium plugins (like %2$s). Pick up a license and you can post there directly and expect a response within 24-48 hours during weekdays." msgstr "" -#: src/Tribe/Main.php:742 -#: src/Tribe/Main.php:747 +#: src/Tribe/Main.php:745 +#: src/Tribe/Main.php:750 msgid "post a thread" msgstr "" -#: src/Tribe/Main.php:743 +#: src/Tribe/Main.php:746 msgid "Already have Event Tickets Plus? You can %s in our premium support forums. Our support team monitors the forums and will respond to your thread within 24-48 hours (during the week)." msgstr "" -#: src/Tribe/Main.php:748 +#: src/Tribe/Main.php:751 msgid "If you have a valid license for one of our paid plugins, you can %s in our premium support forums. Our support team monitors the forums and will respond to your thread within 24-48 hours (during the week)." msgstr "" -#: src/Tribe/Main.php:1075 +#: src/Tribe/Main.php:1078 msgid "Buy" msgstr "" @@ -9767,7 +9772,7 @@ msgid "Order Number" msgstr "" #: src/Tribe/Privacy.php:549 -#: src/views/emails/template-parts/body/order/order-total.php:36 +#: src/views/emails/template-parts/body/order/order-total.php:37 #: src/views/tickets/tpp-success.php:137 msgid "Order Total" msgstr "" @@ -10998,17 +11003,17 @@ msgid "The following attempted purchase has failed because:" msgstr "" #. Translators: %s - The order gateway ID. -#: src/views/emails/template-parts/body/order/order-gateway-data.php:44 +#: src/views/emails/template-parts/body/order/order-gateway-data.php:50 msgid "Gateway Order #%s" msgstr "" #. Translators: %s - Payment provider's name. -#: src/views/emails/template-parts/body/order/payment-info.php:36 +#: src/views/emails/template-parts/body/order/payment-info.php:41 msgid "Payment unsuccessful with %s" msgstr "" #. Translators: %s - Payment provider's name. -#: src/views/emails/template-parts/body/order/payment-info.php:40 +#: src/views/emails/template-parts/body/order/payment-info.php:45 msgid "Payment completed with %s" msgstr "" @@ -11431,6 +11436,11 @@ msgstr "" msgid "Person purchasing tickets:" msgstr "" +#. Translators: %1$s: Plural `Tickets` label. +#: src/views/v2/commerce/gateway/free/button.php:28 +msgid "Get %1$s" +msgstr "" + #: src/views/v2/commerce/gateway/paypal/advanced-payments/fields/card-name.php:35 #: src/views/v2/commerce/gateway/paypal/advanced-payments/fields/card-name.php:43 msgid "Name on Card" @@ -11485,7 +11495,7 @@ msgstr "" msgid "Order number:" msgstr "" -#: src/views/v2/commerce/order/details/payment-method.php:34 +#: src/views/v2/commerce/order/details/payment-method.php:37 msgid "Payment method:" msgstr "" diff --git a/package.json b/package.json index f1c09a7a84..d4e1bf091b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "event-tickets", - "version": "5.9.1", + "version": "5.9.2", "repository": "git@github.com:the-events-calendar/event-tickets.git", "_zipname": "event-tickets", "_zipfoldername": "event-tickets", diff --git a/readme.txt b/readme.txt index 09492555a0..59e6e652d0 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Contributors: theeventscalendar, brianjessee, camwynsp, redscar, tribalmike, raf Tags: tickets, event registration, RSVP, ticket sales, attendee management Requires at least: 6.2 Tested up to: 6.5.2 -Stable tag: 5.9.1.1 +Stable tag: 5.9.2 Requires PHP: 7.4 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -196,6 +196,17 @@ Check out our extensive [knowledgebase](https://evnt.is/18wm) for articles on us == Changelog == += [5.10.0] 2024-05-08 = + +* Feature - Added support for adding Free tickets using Tickets Commerce. [ET-1218] +* Tweak - When using Events Calendar Pro, the duplicate event function will now duplicate tickets as well. [ET-2073] +* Fix - Corrected an issue where PayPal orders had an extra slash on the order table page. [ET-2076] +* Fix - Updated sale label font to be uniform with other Event Tickets elements. [ET-2074] +* Fix - Fixed showing error on Order report export data for Tickets Commerce. +* Tweak - Added filters: `tec_tickets_attendees_page_url`, `tec_tickets_commerce_is_free_ticket_allowed`, `tec_tickets_commerce_value_get_currency_display`, `tec_tickets_attendees_table_column_check_in`, `tec_tickets_attendees_table_query_args`, `tec_tickets_attendees_page_is_enabled` +* Tweak - Changed views: `emails/template-parts/body/order/order-gateway-data`, `emails/template-parts/body/order/order-total`, `emails/template-parts/body/order/payment-info`, `emails/template-parts/body/ticket/number-from-total`, `emails/template-parts/body/tickets-total`, `tickets/attendees-email`, `tickets/email-non-attendance`, `tickets/email-ticket-type-moved`, `tickets/email-tickets-moved`, `tickets/email`, `tickets/my-tickets`, `tickets/my-tickets/attendee-label`, `tickets/my-tickets/orders-list`, `tickets/my-tickets/ticket-information`, `tickets/my-tickets/tickets-list`, `tickets/my-tickets/title`, `tickets/my-tickets/user-details`, `tickets/orders-pp-tickets`, `tickets/orders-rsvp`, `tickets/orders-tc-tickets`, `tickets/orders`, `tickets/rsvp`, `tickets/tpp-return-to-cart`, `tickets/tpp-success`, `tickets/tpp`, `tickets/view-link`, `v2/commerce/gateway/free/button`, `v2/commerce/gateway/free/container`, `v2/commerce/order/details/payment-method`, `v2/commerce/ticket/regular-price`, `v2/commerce/ticket/sale-price` +* Language - 2 new strings added, 68 updated, 0 fuzzied, and 0 obsoleted + = [5.9.1.1] 2024-04-25 = * Fix - Corrected the Attendees page when languages other than English are used. [GTRIA-1268] diff --git a/src/Tickets/Admin/Attendees/Hooks.php b/src/Tickets/Admin/Attendees/Hooks.php index 0cd77c5131..e29473bca2 100644 --- a/src/Tickets/Admin/Attendees/Hooks.php +++ b/src/Tickets/Admin/Attendees/Hooks.php @@ -18,7 +18,7 @@ /** * Class Hooks. * - * @since TBD + * @since 5.10.0 * * @package TEC\Tickets\Admin */ @@ -26,7 +26,7 @@ class Hooks extends \tad_DI52_ServiceProvider { /** * Binds and sets up implementations. * - * @since TBD + * @since 5.10.0 */ public function register() { $this->add_actions(); @@ -36,7 +36,7 @@ public function register() { /** * Adds the actions for the Admin Attendees page. * - * @since TBD + * @since 5.10.0 */ protected function add_actions() { add_action( 'admin_menu', tribe_callback( Page::class, 'add_tec_tickets_attendees_page' ), 15 ); @@ -47,7 +47,7 @@ protected function add_actions() { /** * Adds the filters for the Admin Attendees page. * - * @since TBD + * @since 5.10.0 */ protected function add_filters() { add_filter( 'tribe_tickets_attendee_table_columns', tribe_callback( Page::class, 'filter_attendee_table_columns' ) ); diff --git a/src/Tickets/Admin/Attendees/Modal.php b/src/Tickets/Admin/Attendees/Modal.php index 0431b34420..ef2914120f 100644 --- a/src/Tickets/Admin/Attendees/Modal.php +++ b/src/Tickets/Admin/Attendees/Modal.php @@ -69,7 +69,7 @@ public function render_modal() { /** * Get the default modal args. * - * @since TBD + * @since 5.10.0 * * @param array $args Override default args by sending them in the `$args`. * @@ -97,7 +97,7 @@ public function get_modal_args( $args = [] ): array { /** * Get the default modal contents. * - * @since TBD + * @since 5.10.0 * * @param array $args Override default args by sending them in the `$args`. * @@ -128,7 +128,7 @@ public function get_modal_content( $args = [] ): string { /** * Get the default modal button args. * - * @since TBD + * @since 5.10.0 * * @param array $args Override default args by sending them in the `$args`. * @@ -163,7 +163,7 @@ public static function get_modal_button_args( $args = [] ): array { /** * Get the default modal button. * - * @since TBD + * @since 5.10.0 * * @param array $args Override default args by sending them in the `$args`. * @@ -180,7 +180,7 @@ public static function get_modal_button( $args = [] ): string { * Get the `Attendee Details` modal content, * depending on the request. * - * @since TBD + * @since 5.10.0 * * @param string|\WP_Error $render_response The render response HTML content or WP_Error with list of errors. * @param array $vars The request variables. diff --git a/src/Tickets/Admin/Attendees/Page.php b/src/Tickets/Admin/Attendees/Page.php index ee21f7b53e..f005eb57dd 100644 --- a/src/Tickets/Admin/Attendees/Page.php +++ b/src/Tickets/Admin/Attendees/Page.php @@ -111,7 +111,7 @@ public function add_tec_tickets_attendees_page() { /** * Render the `Attendees` page. * - * @since TBD. + * @since 5.10.0. * * @return void */ @@ -132,7 +132,7 @@ public function render_tec_tickets_attendees_page() { /** * Filters the columns for the Attendees table. * - * @since TBD + * @since 5.10.0 * * @param array $columns The columns for the Attendees table. * @@ -153,7 +153,7 @@ public function filter_attendee_table_columns( $columns ) { /** * Render the `Associated post` column value. * - * @since TBD + * @since 5.10.0 * * @param string $value Row item value. * @param array $item Row item data. @@ -189,7 +189,7 @@ public function render_column_attendee_event( $value, $item, $column ) { /** * Return if the page can be accessed. * - * @since TBD + * @since 5.10.0 * * @return bool True if the page can be accessed. */ diff --git a/src/Tickets/Admin/Attendees/Provider.php b/src/Tickets/Admin/Attendees/Provider.php index 29cd03d787..7e7254b5ef 100644 --- a/src/Tickets/Admin/Attendees/Provider.php +++ b/src/Tickets/Admin/Attendees/Provider.php @@ -52,7 +52,7 @@ protected function register_hooks() { /** * Registers the assets for the Tickets Attendees area. * - * @since TBD + * @since 5.10.0 */ protected function register_assets() { $assets = new Assets( $this->container ); diff --git a/src/Tickets/Blocks/Ticket/app/editor/container-content/price/style.pcss b/src/Tickets/Blocks/Ticket/app/editor/container-content/price/style.pcss index 40705ac2eb..f977dc62a8 100644 --- a/src/Tickets/Blocks/Ticket/app/editor/container-content/price/style.pcss +++ b/src/Tickets/Blocks/Ticket/app/editor/container-content/price/style.pcss @@ -6,12 +6,6 @@ .tribe-editor__ticket__price-label { flex: none; width: 140px; - - &:after { - color: var(--tec-color-icon-error); - content: "*"; - margin-left: var(--tec-spacer-0); - } } .tribe-editor__input.tribe-editor__ticket__price-input { diff --git a/src/Tickets/Commerce/Admin_Tables/Orders.php b/src/Tickets/Commerce/Admin_Tables/Orders.php index 92942eda3e..266a2572ce 100644 --- a/src/Tickets/Commerce/Admin_Tables/Orders.php +++ b/src/Tickets/Commerce/Admin_Tables/Orders.php @@ -694,8 +694,8 @@ public function format_for_csv( array $items ): array { $header ); } - - $csv_row[] = $this->sanitize_and_format_csv_value( $value ); + + $csv_row[] = empty( $value ) ? $value : $this->sanitize_and_format_csv_value( $value ); } $csv_data[] = $csv_row; } diff --git a/src/Tickets/Commerce/Cart.php b/src/Tickets/Commerce/Cart.php index 08ddca3d32..62d007fab4 100644 --- a/src/Tickets/Commerce/Cart.php +++ b/src/Tickets/Commerce/Cart.php @@ -341,33 +341,7 @@ public function set_cart_hash_cookie( $value = '' ) { * @return array List of items. */ public function get_items_in_cart( $full_item_params = false ) { - $cart = $this->get_repository(); - $items = $cart->get_items(); - - // When Items is empty in any capacity return an empty array. - if ( empty( $items ) ) { - return []; - } - - if ( $full_item_params ) { - $items = array_map( static function ( $item ) { - $item['obj'] = \Tribe__Tickets__Tickets::load_ticket_object( $item['ticket_id'] ); - // If it's an invalid ticket we just remove it. - if ( ! $item['obj'] instanceof \Tribe__Tickets__Ticket_Object ) { - return null; - } - - $sub_total_value = Commerce\Utils\Value::create(); - $sub_total_value->set_value( $item['obj']->price ); - - $item['event_id'] = $item['obj']->get_event_id(); - $item['sub_total'] = $sub_total_value->sub_total( $item['quantity'] ); - - return $item; - }, $items ); - } - - return array_filter( $items ); + return $this->get_repository()->get_items_in_cart( $full_item_params ); } /** @@ -660,4 +634,14 @@ public function process( array $data = [] ) { return $this->get_repository()->process( $data ); } + /** + * Get the total of the cart. + * + * @since 5.10.0 + * + * @return null|float + */ + public function get_cart_total() { + return $this->get_repository()->get_cart_total(); + } } \ No newline at end of file diff --git a/src/Tickets/Commerce/Cart/Abstract_Cart.php b/src/Tickets/Commerce/Cart/Abstract_Cart.php new file mode 100644 index 0000000000..360e7f2c6c --- /dev/null +++ b/src/Tickets/Commerce/Cart/Abstract_Cart.php @@ -0,0 +1,96 @@ + List of items. + */ + public function get_items_in_cart( $full_item_params = false ): array { + $items = $this->get_items(); + + // When Items is empty in any capacity return an empty array. + if ( empty( $items ) ) { + return []; + } + + if ( $full_item_params ) { + $items = array_map( + static function ( $item ) { + $item['obj'] = Tickets::load_ticket_object( $item['ticket_id'] ); + // If it's an invalid ticket we just remove it. + if ( ! $item['obj'] instanceof Ticket_Object ) { + return null; + } + + $sub_total_value = Value::create(); + $sub_total_value->set_value( $item['obj']->price ); + + $item['event_id'] = $item['obj']->get_event_id(); + $item['sub_total'] = $sub_total_value->sub_total( $item['quantity'] ); + + return $item; + }, + $items + ); + } + + return array_filter( $items ); + } + + /** + * Get the total of the cart. + * + * @since 5.10.0 + * + * @return null|float + */ + public function get_cart_total() { + if ( null !== $this->cart_total ) { + return $this->cart_total; + } + + $items = $this->get_items_in_cart( true ); + + if ( empty( $items ) ) { + return null; + } + + foreach ( $items as $item ) { + $this->cart_total += $item['sub_total']->get_decimal(); + } + + return $this->cart_total; + } +} diff --git a/src/Tickets/Commerce/Cart/Unmanaged_Cart.php b/src/Tickets/Commerce/Cart/Unmanaged_Cart.php index 07af7bc289..0c04afaeda 100644 --- a/src/Tickets/Commerce/Cart/Unmanaged_Cart.php +++ b/src/Tickets/Commerce/Cart/Unmanaged_Cart.php @@ -12,7 +12,7 @@ * * @since 5.1.9 */ -class Unmanaged_Cart implements Cart_Interface { +class Unmanaged_Cart extends Abstract_Cart { /** * @var string The Cart hash for this cart. @@ -128,6 +128,7 @@ public function clear() { // clear cart items data. $this->items = []; + $this->cart_total = null; } /** @@ -178,7 +179,7 @@ public function add_item( $item_id, $quantity, array $extra_data = [] ) { if ( 0 < $new_quantity ) { $item['ticket_id'] = $item_id; $item['quantity'] = $new_quantity; - $item['extra'] = $extra_data; + $item['extra'] = $extra_data; $this->items[ $item_id ] = $item; } else { diff --git a/src/Tickets/Commerce/Editor/Metabox.php b/src/Tickets/Commerce/Editor/Metabox.php index e236029c89..8cd5fd30c5 100644 --- a/src/Tickets/Commerce/Editor/Metabox.php +++ b/src/Tickets/Commerce/Editor/Metabox.php @@ -203,6 +203,7 @@ public function render_sale_price_fields( $ticket_id, $post_id, $context ): void 'end_date_errors' => [ 'is-greater-or-equal-to' => __( 'Sale to date cannot be less than Sale from date', 'event-tickets' ), ], + 'is_free_ticket_allowed' => tec_tickets_commerce_is_free_ticket_allowed(), ]; tribe( 'tickets.admin.views' )->template( 'commerce/metabox/sale-price', $args ); diff --git a/src/Tickets/Commerce/Gateways/Contracts/Traits/Paid_Gateway.php b/src/Tickets/Commerce/Gateways/Contracts/Traits/Paid_Gateway.php new file mode 100644 index 0000000000..b174427db8 --- /dev/null +++ b/src/Tickets/Commerce/Gateways/Contracts/Traits/Paid_Gateway.php @@ -0,0 +1,26 @@ +get_cart_total(); + return $cart_total > 0; + } +} diff --git a/src/Tickets/Commerce/Gateways/Free/Assets.php b/src/Tickets/Commerce/Gateways/Free/Assets.php new file mode 100644 index 0000000000..b5c3b2c865 --- /dev/null +++ b/src/Tickets/Commerce/Gateways/Free/Assets.php @@ -0,0 +1,94 @@ + [ + 'tec-tickets-commerce-gateway-free', + ], + 'conditionals' => [ $this, 'should_enqueue_assets' ], + 'localize' => [ + 'name' => 'tecTicketsCommerceGatewayFreeCheckout', + 'data' => static function () { + return [ + 'orderEndpoint' => tribe( Order_Endpoint::class )->get_route_url(), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ]; + }, + ], + ] + ); + + tribe_asset( + $plugin, + 'tribe-tickets-commerce-free-style', + 'tickets-commerce/gateway/free.css', + [ + 'tribe-common-skeleton-style', + 'tribe-common-full-style', + ], + null, + [ + 'groups' => [ + 'tribe-tickets-commerce', + 'tribe-tickets-commerce-checkout', + ], + 'print' => true, + ] + ); + } + + /** + * Define if the assets should be enqueued or not. + * + * @since 5.10.0 + * + * @return bool If the assets should be enqueued or not. + */ + public function should_enqueue_assets(): bool { + return tribe( Checkout::class )->is_current_page() && tribe( Gateway::class )->is_enabled() && tribe( Gateway::class )->is_active(); + } +} diff --git a/src/Tickets/Commerce/Gateways/Free/Gateway.php b/src/Tickets/Commerce/Gateways/Free/Gateway.php new file mode 100644 index 0000000000..2ad69761e1 --- /dev/null +++ b/src/Tickets/Commerce/Gateways/Free/Gateway.php @@ -0,0 +1,102 @@ +is_checkout_page() && static::should_show(); + } + + /** + * @inheritDoc + */ + public static function should_show(): bool { + if ( is_admin() ) { + return false; + } + + $cart_total = tribe( Cart::class )->get_cart_total(); + return 0 == $cart_total; + } + + /** + * @inheritDoc + */ + public function get_admin_notices() { + return []; + } + + /** + * Render the checkout template. + * + * @since 5.10.0 + * + * @param Template $template The template object. + */ + public function render_checkout_template( Template $template ): string { + $gateway_key = static::get_key(); + $template_path = "gateway/{$gateway_key}/container"; + + $template_vars = [ + 'must_login' => ! is_user_logged_in() && tribe( Module::class )->login_required(), + ]; + + return $template->template( $template_path, $template_vars ); + } +} diff --git a/src/Tickets/Commerce/Gateways/Free/Hooks.php b/src/Tickets/Commerce/Gateways/Free/Hooks.php new file mode 100644 index 0000000000..473afd8ced --- /dev/null +++ b/src/Tickets/Commerce/Gateways/Free/Hooks.php @@ -0,0 +1,63 @@ +add_filters(); + } + + /** + * Adds the filters required by each Tickets Commerce component. + * + * @since 5.10.0 + */ + protected function add_filters() { + add_filter( 'tec_tickets_commerce_gateways', [ $this, 'filter_add_gateway' ], 10, 2 ); + add_action( 'rest_api_init', [ $this, 'register_endpoints' ] ); + } + + /** + * Add this gateway to the list of available. + * + * @since 5.10.0 + * + * @param array $gateways List of available gateways. + * + * @return array + */ + public function filter_add_gateway( array $gateways = [] ) { + return $this->container->make( Gateway::class )->register_gateway( $gateways ); + } + + /** + * Register the REST API endpoints. + * + * @since 5.10.0 + */ + public function register_endpoints() { + $this->container->make( REST\Order_Endpoint::class )->register(); + } +} diff --git a/src/Tickets/Commerce/Gateways/Free/Provider.php b/src/Tickets/Commerce/Gateways/Free/Provider.php new file mode 100644 index 0000000000..10d6ea3e4e --- /dev/null +++ b/src/Tickets/Commerce/Gateways/Free/Provider.php @@ -0,0 +1,60 @@ +container->singleton( Gateway::class ); + $this->container->singleton( Order::class ); + $this->container->singleton( REST\Order_Endpoint::class ); + + $this->register_assets(); + $this->register_hooks(); + } + + /** + * Registers the provider handling all the 1st level filters and actions for this Service Provider + * + * @since 5.10.0 + */ + protected function register_assets() { + $assets = new Assets( $this->container ); + $assets->register(); + + $this->container->singleton( Assets::class, $assets ); + } + + /** + * Registers the provider handling all the 1st level filters and actions for this Service Provider. + * + * @since 5.10.0 + */ + protected function register_hooks() { + $hooks = new Hooks( $this->container ); + $hooks->register(); + + $this->container->singleton( Hooks::class, $hooks ); + } +} diff --git a/src/Tickets/Commerce/Gateways/Free/REST/Order_Endpoint.php b/src/Tickets/Commerce/Gateways/Free/REST/Order_Endpoint.php new file mode 100644 index 0000000000..b18d345b05 --- /dev/null +++ b/src/Tickets/Commerce/Gateways/Free/REST/Order_Endpoint.php @@ -0,0 +1,120 @@ +get_events_route_namespace(); + $documentation = tribe( 'tickets.rest-v1.endpoints.documentation' ); + + register_rest_route( + $namespace, + $this->get_endpoint_path(), + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'handle_create_order' ], + 'permission_callback' => '__return_true', + ] + ); + + $documentation->register_documentation_provider( $this->get_endpoint_path(), $this ); + } + + /** + * Handles the request that creates an order with Tickets Commerce and the Free gateway. + * + * @since 5.10.0 + * + * @param WP_REST_Request $request The request object. + * + * @return WP_Error|WP_REST_Response An array containing the data on success or a WP_Error instance on failure. + */ + public function handle_create_order( WP_REST_Request $request ) { + $response = [ + 'success' => false, + ]; + + $data = $request->get_json_params(); + $purchaser = tribe( Order::class )->get_purchaser_data( $data ); + + if ( is_wp_error( $purchaser ) ) { + return $purchaser; + } + + $order = tribe( Order::class )->create_from_cart( tribe( Gateway::class ), $purchaser ); + $meta = [ + 'gateway_order_id' => $order->ID, + ]; + + $created = tribe( Order::class )->modify_status( + $order->ID, + Pending::SLUG, + $meta + ); + + if ( is_wp_error( $created ) ) { + return $created; + } + + $updated = tribe( Order::class )->modify_status( + $order->ID, + Completed::SLUG, + ); + + if ( is_wp_error( $updated ) ) { + return $updated; + } + + tribe( Cart::class )->clear_cart(); + + $response['success'] = true; + $response['id'] = $order->ID; + $response['redirect_url'] = add_query_arg( [ 'tc-order-id' => $order->ID ], tribe( Success::class )->get_url() ); + + return new WP_REST_Response( $response ); + } +} diff --git a/src/Tickets/Commerce/Gateways/PayPal/Gateway.php b/src/Tickets/Commerce/Gateways/PayPal/Gateway.php index dde5036358..3e73167ad9 100644 --- a/src/Tickets/Commerce/Gateways/PayPal/Gateway.php +++ b/src/Tickets/Commerce/Gateways/PayPal/Gateway.php @@ -3,6 +3,7 @@ namespace TEC\Tickets\Commerce\Gateways\PayPal; use TEC\Tickets\Commerce\Gateways\Contracts\Abstract_Gateway; +use TEC\Tickets\Commerce\Gateways\Contracts\Traits\Paid_Gateway; use TEC\Tickets\Commerce\Notice_Handler; use TEC\Tickets\Commerce\Settings as TC_Settings; use TEC\Tickets\Commerce\Status\Status_Handler; @@ -17,6 +18,8 @@ * @package TEC\Tickets\Commerce\Gateways\PayPal */ class Gateway extends Abstract_Gateway { + use Paid_Gateway; + /** * @inheritDoc */ diff --git a/src/Tickets/Commerce/Gateways/PayPal/Order.php b/src/Tickets/Commerce/Gateways/PayPal/Order.php index 7487207ecc..f5c1a2da42 100644 --- a/src/Tickets/Commerce/Gateways/PayPal/Order.php +++ b/src/Tickets/Commerce/Gateways/PayPal/Order.php @@ -16,10 +16,12 @@ class Order extends Abstract_Order { /** * @inheritDoc + * + * @since 5.10.0 Fixed extra trailing slash. */ public function get_gateway_dashboard_url_by_order( \WP_Post $order ): string { - $status = tribe( Status_Handler::class )->get_by_wp_slug( $order->post_status ); - $payload = isset( $order->gateway_payload[ $status::SLUG ] ) ? $order->gateway_payload[ $status::SLUG ] : current( $order->gateway_payload ); + $status = tribe( Status_Handler::class )->get_by_wp_slug( $order->post_status ); + $payload = $order->gateway_payload[ $status::SLUG ] ?? current( $order->gateway_payload ); if ( ! is_array( $payload ) || empty( $payload ) ) { return ''; @@ -31,11 +33,11 @@ public function get_gateway_dashboard_url_by_order( \WP_Post $order ): string { $paypal_base_url = 'https://www.paypal.com/'; $capture_link = Arr::get( $capture_payload, [ 'links', 0, 'href' ] ); - // check if the link contains sandbox + // Check if the link contains sandbox. if ( strpos( $capture_link, 'sandbox' ) !== false ) { $paypal_base_url = 'https://sandbox.paypal.com/'; } - return sprintf( '%1$s/activity/payment/%2$s', $paypal_base_url, $capture_id ); + return sprintf( '%1$s/activity/payment/%2$s', untrailingslashit( $paypal_base_url ), $capture_id ); } } diff --git a/src/Tickets/Commerce/Gateways/Stripe/Gateway.php b/src/Tickets/Commerce/Gateways/Stripe/Gateway.php index 2824d07d76..b3a83af360 100644 --- a/src/Tickets/Commerce/Gateways/Stripe/Gateway.php +++ b/src/Tickets/Commerce/Gateways/Stripe/Gateway.php @@ -3,6 +3,7 @@ namespace TEC\Tickets\Commerce\Gateways\Stripe; use TEC\Tickets\Commerce\Gateways\Contracts\Abstract_Gateway; +use TEC\Tickets\Commerce\Gateways\Contracts\Traits\Paid_Gateway; use TEC\Tickets\Commerce\Gateways\Stripe\REST\Return_Endpoint; use TEC\Tickets\Commerce\Payments_Tab; use TEC\Tickets\Commerce\Settings as TC_Settings; @@ -19,6 +20,7 @@ * @package TEC\Tickets\Commerce\Gateways\Stripe */ class Gateway extends Abstract_Gateway { + use Paid_Gateway; /** * @inheritDoc diff --git a/src/Tickets/Commerce/Gateways/Stripe/Hooks.php b/src/Tickets/Commerce/Gateways/Stripe/Hooks.php index af71cf38b1..e7e1d2300c 100644 --- a/src/Tickets/Commerce/Gateways/Stripe/Hooks.php +++ b/src/Tickets/Commerce/Gateways/Stripe/Hooks.php @@ -167,7 +167,7 @@ public function include_admin_notices( $messages ) { */ public function maybe_create_stripe_payment_intent() { - if ( ! tribe( Module::class )->is_checkout_page() || ! tribe( Merchant::class )->is_connected() ) { + if ( ! tribe( Module::class )->is_checkout_page() || ! tribe( Gateway::class )->is_enabled() || ! tribe( Merchant::class )->is_connected() ) { return; } diff --git a/src/Tickets/Commerce/Hooks.php b/src/Tickets/Commerce/Hooks.php index b26e46ab2e..8930eb608b 100644 --- a/src/Tickets/Commerce/Hooks.php +++ b/src/Tickets/Commerce/Hooks.php @@ -132,6 +132,8 @@ protected function add_filters() { add_filter( 'wp_redirect', [ $this, 'filter_redirect_url' ] ); add_filter( 'tec_tickets_editor_configuration_localized_data', [ $this, 'filter_block_editor_localized_data' ] ); + + add_action( 'tribe_editor_config', [ $this, 'filter_tickets_editor_config' ] ); } /** @@ -793,4 +795,27 @@ public function filter_block_editor_localized_data( $localized ) { return $localized; } + + /** + * Filters the data used to render the Tickets Block Editor control. + * + * @since 5.10.0 + * + * @param array $data The data used to render the Tickets Block Editor control. + * + * @return array The data used to render the Tickets Block Editor control. + */ + public function filter_tickets_editor_config( $data ) { + if ( ! isset( $data['tickets'] ) ) { + $data['tickets'] = []; + } + + if ( ! isset( $data['tickets']['commerce'] ) ) { + $data['tickets']['commerce'] = []; + } + + $data['tickets']['commerce']['isFreeTicketAllowed'] = tec_tickets_commerce_is_free_ticket_allowed(); + + return $data; + } } diff --git a/src/Tickets/Commerce/Provider.php b/src/Tickets/Commerce/Provider.php index 5a6c4c97f2..722868fbd5 100644 --- a/src/Tickets/Commerce/Provider.php +++ b/src/Tickets/Commerce/Provider.php @@ -75,6 +75,7 @@ public function register() { $this->container->register( Gateways\Stripe\Provider::class ); $this->container->register( Gateways\PayPal\Provider::class ); $this->container->register( Gateways\Manual\Provider::class ); + $this->container->register( Gateways\Free\Provider::class ); // Register and add hooks for admin notices. $this->container->register( Admin\Notices::class ); diff --git a/src/Tickets/Commerce/Settings.php b/src/Tickets/Commerce/Settings.php index f0ae307a26..e38a292eda 100644 --- a/src/Tickets/Commerce/Settings.php +++ b/src/Tickets/Commerce/Settings.php @@ -609,4 +609,22 @@ public static function is_licensed_plugin( $revalidate = false ) { return $is_license_valid; } + + /** + * Determine if free ticket is allowed in Tickets Commerce. + * + * @since 5.10.0 + * + * @return bool + */ + public static function is_free_ticket_allowed() { + /** + * Filter to allow free tickets in Tickets Commerce. + * + * @since 5.10.0 + * + * @param bool $is_free_ticket_allowed Whether free tickets are allowed in Tickets Commerce. + */ + return apply_filters( 'tec_tickets_commerce_is_free_ticket_allowed', true ); + } } diff --git a/src/Tickets/Commerce/Ticket.php b/src/Tickets/Commerce/Ticket.php index 3787f79067..ae6076bfb7 100644 --- a/src/Tickets/Commerce/Ticket.php +++ b/src/Tickets/Commerce/Ticket.php @@ -1108,7 +1108,7 @@ public function process_sale_price_data( Ticket_Object $ticket, array $raw_data $sale_price = Arr::get( $raw_data, 'ticket_sale_price', false ); $regular_price = Arr::get( $raw_data, 'ticket_price', false ); - if ( empty( $sale_price ) || $sale_price >= $regular_price ) { + if ( $sale_price >= $regular_price ) { $this->remove_sale_price_data( $ticket ); return; } diff --git a/src/Tickets/Commerce/Utils/Value.php b/src/Tickets/Commerce/Utils/Value.php index 54a89834d7..1667cfe38c 100644 --- a/src/Tickets/Commerce/Utils/Value.php +++ b/src/Tickets/Commerce/Utils/Value.php @@ -75,4 +75,29 @@ public function get_shortcode_price_html() { ); } + + /** + * Get the display currency. + * + * @since 5.10.0 + * + * @return string The display text for this value. + */ + public function get_currency_display() { + $currency_display = $this->get_currency(); + + if ( $this->get_decimal() == 0 ) { + $currency_display = _x( 'Free', 'No cost', 'event-tickets' ); + } + + /** + * Filter the currency display. + * + * @since 5.10.0 + * + * @param string $currency_display The currency display. + * @param Value $value The value object. + */ + return apply_filters( 'tec_tickets_commerce_value_get_currency_display', $currency_display, $this ); + } } diff --git a/src/Tickets/Integrations/Plugins/Events_Pro/Duplicate_Post.php b/src/Tickets/Integrations/Plugins/Events_Pro/Duplicate_Post.php new file mode 100644 index 0000000000..fecf8af6ee --- /dev/null +++ b/src/Tickets/Integrations/Plugins/Events_Pro/Duplicate_Post.php @@ -0,0 +1,89 @@ +ID; + $tickets = Tribe__Tickets__Tickets::get_all_event_tickets( $post->ID ); + + // If we have no tickets, return. + if ( empty( $tickets ) ) { + return; + } + + // You technically can have multiple providers if you have RSVP + a ticket provider. + foreach ( $tickets as $ticket ) { + + $provider = tribe( $ticket->provider_class ); + + if ( empty( $provider ) || ! $provider instanceof Tribe__Tickets__Tickets ) { + continue; + } + + $provider->clone_ticket_to_new_post( $post->ID, $new_post_id, $ticket->ID ); + } + } +} diff --git a/src/Tickets/Integrations/Provider.php b/src/Tickets/Integrations/Provider.php index ed51bc8ce0..5d21f1a81f 100644 --- a/src/Tickets/Integrations/Provider.php +++ b/src/Tickets/Integrations/Provider.php @@ -28,8 +28,10 @@ class Provider extends Service_Provider { public function register() { $this->container->singleton( static::class, $this ); - $this->container->register( Plugins\Yoast_Duplicate_Post\Duplicate_Post::class); + $this->container->register( Plugins\Yoast_Duplicate_Post\Duplicate_Post::class ); - $this->container->register( Themes\Divi\Provider::class); + $this->container->register_on_action( 'tec_events_pro_custom_tables_v1_before_duplicate_event', Plugins\Events_Pro\Duplicate_Post::class ); + + $this->container->register( Themes\Divi\Provider::class ); } } diff --git a/src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php b/src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php index ea3226c744..23c8e30e0d 100644 --- a/src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php +++ b/src/Tribe/Editor/REST/V1/Endpoints/Single_Ticket.php @@ -256,8 +256,13 @@ public function add_ticket( WP_REST_Request $request, $nonce_action ) { ); } + // If price field is left blank, we create a free ticket. + if ( isset( $body['price'] ) && '' === trim( $body['price'] ) ) { + $body['price'] = '0'; + } + $is_paypal_ticket = $provider instanceof Tribe__Tickets__Commerce__PayPal__Main || $provider instanceof \TEC\Tickets\Commerce\Module; - $is_invalid_price = ( empty( $body['price'] ) || ! is_numeric( $body['price'] ) || (float) $body['price'] <= 0 ); + $is_invalid_price = ! is_numeric( $body['price'] ) || (float) $body['price'] < 0; if ( $is_paypal_ticket diff --git a/src/Tribe/Main.php b/src/Tribe/Main.php index be475caf61..0f70b53abd 100644 --- a/src/Tribe/Main.php +++ b/src/Tribe/Main.php @@ -8,7 +8,8 @@ class Tribe__Tickets__Main { /** * Current version of this plugin. */ - const VERSION = '5.9.1.1'; + + const VERSION = '5.9.2'; /** * Used to store the version history. @@ -56,7 +57,7 @@ class Tribe__Tickets__Main { protected $min_tec_version = '6.3.0-dev'; /** - * Name of the provider + * Name of the provider. * @var string */ public $plugin_name; diff --git a/src/admin-views/attendees/modal/attendee.php b/src/admin-views/attendees/modal/attendee.php index cd16178209..f4fd1506ae 100644 --- a/src/admin-views/attendees/modal/attendee.php +++ b/src/admin-views/attendees/modal/attendee.php @@ -2,7 +2,7 @@ /** * Attendees modal. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/attendee-info.php b/src/admin-views/attendees/modal/attendee/attendee-info.php index 39820a3402..b1fd3061ba 100644 --- a/src/admin-views/attendees/modal/attendee/attendee-info.php +++ b/src/admin-views/attendees/modal/attendee/attendee-info.php @@ -2,7 +2,7 @@ /** * Attendees modal - Attendee information * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/attendee-info/email.php b/src/admin-views/attendees/modal/attendee/attendee-info/email.php index 5bb8b69d25..20f8beb2ff 100644 --- a/src/admin-views/attendees/modal/attendee/attendee-info/email.php +++ b/src/admin-views/attendees/modal/attendee/attendee-info/email.php @@ -2,7 +2,7 @@ /** * Attendees modal - Attendee information > Email. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/attendee-info/id.php b/src/admin-views/attendees/modal/attendee/attendee-info/id.php index e266ee767e..e6425f2831 100644 --- a/src/admin-views/attendees/modal/attendee/attendee-info/id.php +++ b/src/admin-views/attendees/modal/attendee/attendee-info/id.php @@ -2,7 +2,7 @@ /** * Attendees modal - Attendee information > ID. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/attendee-info/rsvp-going.php b/src/admin-views/attendees/modal/attendee/attendee-info/rsvp-going.php index 9143bd7b21..2967d4120a 100644 --- a/src/admin-views/attendees/modal/attendee/attendee-info/rsvp-going.php +++ b/src/admin-views/attendees/modal/attendee/attendee-info/rsvp-going.php @@ -2,7 +2,7 @@ /** * Attendees modal - Attendee information > RSVP "Going". * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/attendee-info/security-code.php b/src/admin-views/attendees/modal/attendee/attendee-info/security-code.php index 3bed460b7a..a2f72c618e 100644 --- a/src/admin-views/attendees/modal/attendee/attendee-info/security-code.php +++ b/src/admin-views/attendees/modal/attendee/attendee-info/security-code.php @@ -2,7 +2,7 @@ /** * Attendees modal - Attendee information > Security code. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/check-in.php b/src/admin-views/attendees/modal/attendee/check-in.php index b843c3d945..6362fb8691 100644 --- a/src/admin-views/attendees/modal/attendee/check-in.php +++ b/src/admin-views/attendees/modal/attendee/check-in.php @@ -2,7 +2,7 @@ /** * Attendees modal - Check-in details. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/check-in/checked-in.php b/src/admin-views/attendees/modal/attendee/check-in/checked-in.php index 8d22c4fa24..d66085d6a3 100644 --- a/src/admin-views/attendees/modal/attendee/check-in/checked-in.php +++ b/src/admin-views/attendees/modal/attendee/check-in/checked-in.php @@ -2,7 +2,7 @@ /** * Attendees modal - Check-in details > Checked-in. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/check-in/not-checked-in.php b/src/admin-views/attendees/modal/attendee/check-in/not-checked-in.php index 1884cbcdfa..70b4f35af2 100644 --- a/src/admin-views/attendees/modal/attendee/check-in/not-checked-in.php +++ b/src/admin-views/attendees/modal/attendee/check-in/not-checked-in.php @@ -2,7 +2,7 @@ /** * Attendees modal - Check-in details > Not Checked-in. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/gravatar.php b/src/admin-views/attendees/modal/attendee/gravatar.php index 9d8dfe3029..46839070f1 100644 --- a/src/admin-views/attendees/modal/attendee/gravatar.php +++ b/src/admin-views/attendees/modal/attendee/gravatar.php @@ -2,7 +2,7 @@ /** * Attendees modal - Gravatar * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/order-info.php b/src/admin-views/attendees/modal/attendee/order-info.php index 1adb4f2d6a..14f99fe3f1 100644 --- a/src/admin-views/attendees/modal/attendee/order-info.php +++ b/src/admin-views/attendees/modal/attendee/order-info.php @@ -2,7 +2,7 @@ /** * Attendees modal - Purchase info. * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/order-info/date.php b/src/admin-views/attendees/modal/attendee/order-info/date.php index e2b491ed57..a1f90acdd4 100644 --- a/src/admin-views/attendees/modal/attendee/order-info/date.php +++ b/src/admin-views/attendees/modal/attendee/order-info/date.php @@ -2,7 +2,7 @@ /** * Attendees modal - Order information > date * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/order-info/id.php b/src/admin-views/attendees/modal/attendee/order-info/id.php index da60f0df01..eeeec2b6be 100644 --- a/src/admin-views/attendees/modal/attendee/order-info/id.php +++ b/src/admin-views/attendees/modal/attendee/order-info/id.php @@ -2,7 +2,7 @@ /** * Attendees modal - Order information > Order ID * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/order-info/status.php b/src/admin-views/attendees/modal/attendee/order-info/status.php index 58821ab265..75126a7ce6 100644 --- a/src/admin-views/attendees/modal/attendee/order-info/status.php +++ b/src/admin-views/attendees/modal/attendee/order-info/status.php @@ -2,7 +2,7 @@ /** * Attendees modal - Order information > Status * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/order-info/status/icon.php b/src/admin-views/attendees/modal/attendee/order-info/status/icon.php index b0bddd1598..a3824056c7 100644 --- a/src/admin-views/attendees/modal/attendee/order-info/status/icon.php +++ b/src/admin-views/attendees/modal/attendee/order-info/status/icon.php @@ -2,7 +2,7 @@ /** * Attendees modal - Order information > Status > Icon * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/order-info/ticket.php b/src/admin-views/attendees/modal/attendee/order-info/ticket.php index 439da94813..cfa1327f74 100644 --- a/src/admin-views/attendees/modal/attendee/order-info/ticket.php +++ b/src/admin-views/attendees/modal/attendee/order-info/ticket.php @@ -2,7 +2,7 @@ /** * Attendees modal - Order information > Ticket * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/attendees/modal/attendee/qr-image.php b/src/admin-views/attendees/modal/attendee/qr-image.php index 18d75f76f0..8f94d67846 100644 --- a/src/admin-views/attendees/modal/attendee/qr-image.php +++ b/src/admin-views/attendees/modal/attendee/qr-image.php @@ -2,7 +2,7 @@ /** * Attendees modal - QR Image * - * @since TBD + * @since 5.10.0 * * @var Tribe_Template $this Current template object. * @var \WP_Post $attendee The attendee object. diff --git a/src/admin-views/commerce/metabox/sale-price.php b/src/admin-views/commerce/metabox/sale-price.php index 44257caf15..c49ab51b7e 100644 --- a/src/admin-views/commerce/metabox/sale-price.php +++ b/src/admin-views/commerce/metabox/sale-price.php @@ -3,6 +3,7 @@ * Renders the sale price fields for Tickets Commerce. * * @since 5.9.0 + * @since 5.10.0 Added the $is_free_ticket_allowed parameter and updated validation for the Sale Price input field. * * @var Ticket_Object $ticket The ticket object. * @var string $sale_price The sale price. @@ -12,9 +13,18 @@ * @var array $start_date_errors The start date errors. * @var array $end_date_errors The end date errors. * @var array $sale_price_errors The sale price errors. + * @var bool $is_free_ticket_allowed Whether free tickets are allowed. */ -use Tribe__Tickets__Ticket_Object as Ticket_Object; +$sale_price_validation_attrs = [ + 'data-validation-is-less-than="#ticket_price"', + 'data-validation-error="' . esc_attr( wp_json_encode( $sale_price_errors ) ) . '"', +]; + +// Do not allow free ticket in sale price if not allowed. +if ( ! $is_free_ticket_allowed ) { + $sale_price_validation_attrs[] = 'data-validation-is-greater-than="0"'; +} ?>
@@ -48,9 +58,7 @@ class="ticket_form_label" class="ticket_field" size="7" value="" - data-validation-is-greater-than="0" - data-validation-is-less-than="#ticket_price" - data-validation-error="" + />
diff --git a/src/admin-views/editor/fieldset/price.php b/src/admin-views/editor/fieldset/price.php index 435aae34ad..a09f2efd54 100644 --- a/src/admin-views/editor/fieldset/price.php +++ b/src/admin-views/editor/fieldset/price.php @@ -4,13 +4,7 @@ $post_id = get_the_ID(); } -$validation_attrs = [ - 'data-validation-error="' . esc_attr( sprintf( - // Translators: %s: singular version of the Ticket label. - _x( '%s price must be greater than zero.', 'ticket price validation error', 'event-tickets' ), - tribe_get_ticket_label_singular( 'ticket_price_validation_error' ) - ) ) . '"' -]; +$validation_attrs = []; $ticket = null; $is_paypal_ticket = false; @@ -23,11 +17,29 @@ $is_paypal_ticket = $provider instanceof Tribe__Tickets__Commerce__PayPal__Main || $provider instanceof \TEC\Tickets\Commerce\Module; +// Determine whether or not free tickets are allowed. +$is_free_ticket_allowed = true; + +if ( $provider instanceof Tribe__Tickets__Commerce__PayPal__Main ) { + $is_free_ticket_allowed = false; +} + +if ( $provider instanceof \TEC\Tickets\Commerce\Module ) { + $is_free_ticket_allowed = tec_tickets_commerce_is_free_ticket_allowed(); +} + $description_string = sprintf( _x( 'Leave blank for free %s', 'price description', 'event-tickets' ), tribe_get_ticket_label_singular( 'price_description' ) ); $description_string = esc_html( apply_filters( 'tribe_tickets_price_description', $description_string, $ticket_id ) ); -$price_description = $is_paypal_ticket ? '' : $description_string; +$price_description = ! $is_free_ticket_allowed ? '' : $description_string; -if ( $is_paypal_ticket ) { +if ( ! $is_free_ticket_allowed ) { + $validation_attrs[] = 'data-validation-error="' . esc_attr( + sprintf( + // Translators: %s: singular version of the Ticket label. + _x( '%s price must be greater than zero.', 'ticket price validation error', 'event-tickets' ), + tribe_get_ticket_label_singular( 'ticket_price_validation_error' ) + ) + ) . '"'; $validation_attrs[] = 'data-required'; $validation_attrs[] = 'data-validation-is-greater-than="0"'; } diff --git a/src/functions/attendees/provider.php b/src/functions/attendees/provider.php index c0ea858e78..886932a4fa 100644 --- a/src/functions/attendees/provider.php +++ b/src/functions/attendees/provider.php @@ -9,7 +9,7 @@ * In order the function will check the `TEC_TICKETS_ATTENDEES_PAGE` constant, * the `TEC_TICKETS_ATTENDEES_PAGE` environment variable, * - * @since TBD + * @since 5.10.0 * * @return bool Whether "Attendees" page is enabled or not. */ @@ -26,7 +26,7 @@ function tec_tickets_attendees_page_is_enabled(): bool { /** * Allows filtering of the Attendees page provider. * - * @since TBD + * @since 5.10.0 * * @param boolean $enabled Determining if the "Attendees" page is enabled. */ diff --git a/src/functions/commerce/tickets.php b/src/functions/commerce/tickets.php index db5dbb576e..9e31b476c9 100644 --- a/src/functions/commerce/tickets.php +++ b/src/functions/commerce/tickets.php @@ -6,6 +6,7 @@ */ use TEC\Tickets\Commerce\Models\Ticket_Model; +use TEC\Tickets\Commerce\Settings; /** * Fetches and returns a decorated post object representing an ticket. @@ -126,3 +127,14 @@ function tec_tc_get_ticket( $ticket = null, $output = OBJECT, $filter = 'raw', $ return $post; } + +/** + * Checks if the free ticket is allowed. + * + * @since 5.10.0 + * + * @return bool + */ +function tec_tickets_commerce_is_free_ticket_allowed() { + return Settings::is_free_ticket_allowed(); +} diff --git a/src/modules/data/blocks/ticket/constants.js b/src/modules/data/blocks/ticket/constants.js index 542a4cfdc7..03be05d95b 100644 --- a/src/modules/data/blocks/ticket/constants.js +++ b/src/modules/data/blocks/ticket/constants.js @@ -52,3 +52,6 @@ export const PRICE_POSITIONS = [ PREFIX, SUFFIX ]; // eslint-disable-next-line no-undef export const TICKET_LABELS = window?.tribe_editor_config?.tickets?.ticketLabels; export const SALE_PRICE_LABELS = window?.tribe_editor_config?.tickets?.salePrice; + +// eslint-disable-next-line max-len +export const IS_FREE_TC_TICKET_ALLOWED = window?.tribe_editor_config?.tickets?.commerce?.isFreeTicketAllowed; diff --git a/src/modules/data/blocks/ticket/selectors.js b/src/modules/data/blocks/ticket/selectors.js index 5ab1b6a45f..8b9546a16c 100644 --- a/src/modules/data/blocks/ticket/selectors.js +++ b/src/modules/data/blocks/ticket/selectors.js @@ -17,6 +17,7 @@ const { INDEPENDENT, SHARED, TICKET_TYPES, + IS_FREE_TC_TICKET_ALLOWED, } = constants; const { tickets: ticketsConfig, post: postConfig } = globals; @@ -716,8 +717,16 @@ export const isTempSharedCapacityValid = createSelector( export const isZeroPriceValid = createSelector( [ getTicketTempPrice, getTicketsProvider ], ( price, provider ) => { - return 0 < parseInt( price, 10 ) || - ! [ constants.TC_CLASS, constants.TICKETS_COMMERCE_MODULE_CLASS ].includes( provider ); + if ( 0 < parseInt( price, 10 ) ) { + return true; + } + if ( constants.TC_CLASS === provider ) { + return false; + } + if ( constants.TICKETS_COMMERCE_MODULE_CLASS === provider ) { + return IS_FREE_TC_TICKET_ALLOWED; + } + return true; }, ); diff --git a/src/resources/js/admin/tickets-attendees.js b/src/resources/js/admin/tickets-attendees.js index 231f253216..6ea52065ce 100644 --- a/src/resources/js/admin/tickets-attendees.js +++ b/src/resources/js/admin/tickets-attendees.js @@ -1,7 +1,7 @@ /** * Makes sure we have all the required levels on the Tribe Object * - * @since TBD + * @since 5.10.0 * @type {Object} */ tribe.tickets = tribe.tickets || {}; @@ -11,7 +11,7 @@ tribe.dialogs.events = tribe.dialogs.events || {}; /** * Configures ET Attendees Object in the Global Tribe variable * - * @since TBD + * @since 5.10.0 * @type {Object} */ tribe.tickets.attendees = {}; @@ -19,7 +19,7 @@ tribe.tickets.attendees = {}; /** * Initializes in a Strict env the code that manages the plugin Attendees library. * - * @since TBD + * @since 5.10.0 * @param {Object} $ jQuery * @param {Object} obj tribe.tickets.attendees * @return {void} @@ -30,7 +30,7 @@ tribe.tickets.attendees = {}; /* * Manual Attendees Selectors. * - * @since TBD + * @since 5.10.0 */ obj.selectors = { modalWrapper: '.tribe-modal__wrapper--attendee-details', @@ -42,7 +42,7 @@ tribe.tickets.attendees = {}; /** * Handler for when the modal is being "closed". * - * @since TBD + * @since 5.10.0 * @param {Object} event The close event. * @param {Object} dialogEl The dialog element. * @return {void} @@ -57,7 +57,7 @@ tribe.tickets.attendees = {}; /** * Bind handler for when the modal is being "closed". * - * @since TBD + * @since 5.10.0 * @return {void} */ obj.bindModalClose = function() { @@ -70,7 +70,7 @@ tribe.tickets.attendees = {}; /** * Unbinds events for the modal content container. * - * @since TBD + * @since 5.10.0 * @param {jQuery} $container jQuery object of the container. */ obj.unbindModalEvents = function( $container ) { @@ -81,7 +81,7 @@ tribe.tickets.attendees = {}; /** * Handler for when the modal is opened. * - * @since TBD + * @since 5.10.0 * @param {Object} event The show event. * @param {Object} dialogEl The dialog element. * @param {Object} trigger The event. @@ -130,7 +130,7 @@ tribe.tickets.attendees = {}; /** * Get context to send on the request. * - * @since TBD + * @since 5.10.0 * @return {Object} */ obj.getContext = function() { @@ -142,7 +142,7 @@ tribe.tickets.attendees = {}; /** * Bind handler for when the modal is being "opened". * - * @since TBD + * @since 5.10.0 * @return {void} */ obj.bindModalOpen = function() { @@ -155,7 +155,7 @@ tribe.tickets.attendees = {}; /** * Handles the initialization of the scripts when Document is ready. * - * @since TBD + * @since 5.10.0 * @return {void} */ obj.ready = function() { diff --git a/src/resources/js/commerce/gateway/free/checkout.js b/src/resources/js/commerce/gateway/free/checkout.js new file mode 100644 index 0000000000..ce0e96af4f --- /dev/null +++ b/src/resources/js/commerce/gateway/free/checkout.js @@ -0,0 +1,248 @@ +/* global tribe, jQuery, tecTicketsCommerceGatewayFreeCheckout */ + +/** + * Path to this script in the global tribe Object. + * + * @since 5.10.0 + * + * @type {Object} + */ +tribe.tickets.commerce.gateway.free = tribe.tickets.commerce.gateway.free || {}; + +/** + * This script Object for public usage of the methods. + * + * @since 5.10.0 + * + * @type {Object} + */ +tribe.tickets.commerce.gateway.free.checkout = {}; + +( ( $, obj, ky ) => { + 'use strict'; + + /** + * Pull the variables from the PHP backend. + * + * @since 5.10.0 + * + * @type {Object} + */ + obj.checkout = tecTicketsCommerceGatewayFreeCheckout; + + /** + * Checkout Selectors. + * + * @since 5.10.0 + * + * @type {Object} + */ + obj.selectors = { + submitButton: '#tec-tc-gateway-free-checkout-button', + hiddenElement: '.tribe-common-a11y-hidden' + }; + + /** + * Loader container. + * + * @since 5.10.0 + * + * @type {Object|null} + */ + obj.checkoutContainer = null; + + /** + * Preventing errors to be thrown when using Ky + * + * @since 5.10.0 + * + * @param {Object} error + * + * @return {*} + */ + obj.onBeforeRetry = async ( error ) => { + console.log( error ); + + return ky.stop; + }; + + /** + * Preventing errors to be thrown when using Ky + * + * @since 5.10.0 + * + * @param {Object} error + * + * @return {*} + */ + obj.onBeforeError = async ( error ) => { + console.log( error ); + + return ky.stop; + }; + + /** + * Get the request arguments to setup the calls. + * + * @since 5.10.0 + * + * @param data + * @param headers + * + * @return {{headers: {"X-WP-Nonce"}, throwHttpErrors: boolean, json, hooks: {beforeError: (function(*): *)[]}}} + */ + obj.getRequestArgs = ( data, headers ) => { + if ( 'undefined' === typeof headers ) { + headers = { + 'X-WP-Nonce': obj.checkout.nonce + }; + } + + const args = { + headers: headers, + hooks: { + beforeRetry: [ + obj.onBeforeRetry + ], + beforeError: [ + obj.onBeforeError + ] + }, + timeout: 30000, + throwHttpErrors: false + }; + + if ( data ) { + args.json = data; + } + + return args; + }; + + /** + * Hides the notice for the checkout container. + * + * @since 5.10.0 + * + * @param {jQuery} $container Parent container of notice element. + */ + obj.hideNotice = ( $container ) => { + if ( ! $container.length ) { + $container = $( tribe.tickets.commerce.selectors.checkoutContainer ); + } + + const notice = tribe.tickets.commerce.notice; + const $item = $container.find( notice.selectors.item ); + notice.hide( $item ); + }; + + /** + * Shows the notice for the checkout container. + * + * @since 5.10.0 + * + * @param {jQuery} $container Parent container of notice element. + * @param {string} title Notice Title. + * @param {string} content Notice message content. + */ + obj.showNotice = ( $container, title, content ) => { + if ( ! $container || ! $container.length ) { + $container = $( tribe.tickets.commerce.selectors.checkoutContainer ); + } + const notice = tribe.tickets.commerce.notice; + const $item = $container.find( notice.selectors.item ); + notice.populate( $item, title, content ); + notice.show( $item ); + }; + + /** + * Toggle the submit button enabled/disabled + * + * @param enable + */ + obj.submitButton = ( enable ) => { + $( obj.selectors.submitButton ).prop( 'disabled', ! enable ); + }; + + /** + * Starts the process to submit a payment. + * + * @since 5.10.0 + * + * @param {Event} event The Click event from the payment. + */ + obj.handlePayment = async ( event ) => { + event.preventDefault(); + + obj.checkoutContainer = $( event.target ).closest( tribe.tickets.commerce.selectors.checkoutContainer ); + + obj.hideNotice( obj.checkoutContainer ); + + tribe.tickets.loader.show( obj.checkoutContainer ); + + let order = await obj.handleCreateOrder(); + obj.submitButton( false ); + + if ( order.success ) { + window.location.replace( order.redirect_url ); + } else { + tribe.tickets.loader.hide( obj.checkoutContainer ); + obj.showNotice( {}, order.message, '' ); + } + + obj.submitButton( true ); + }; + + /** + * Create an order and start the payment process. + * + * @since 5.10.0 + * + * @return {Promise<*>} + */ + obj.handleCreateOrder = async () => { + const args = obj.getRequestArgs( { + purchaser: obj.getPurchaserData(), + } ); + let response; + + try { + response = await tribe.ky.post( obj.checkout.orderEndpoint, args ).json(); + } catch( error ) { + response = error; + } + + tribe.tickets.debug.log( 'free', 'createOrder', response ); + + return response; + }; + + /** + * Get purchaser form data. + * + * @since 5.10.0 + * + * @return {Object} + */ + obj.getPurchaserData = () => tribe.tickets.commerce.getPurchaserData( $( tribe.tickets.commerce.selectors.purchaserFormContainer ) ); + + /** + * Bind script loader to trigger script dependent methods. + * + * @since 5.10.0 + */ + obj.bindEvents = () => { + $( obj.selectors.submitButton ).on( 'click', obj.handlePayment ); + }; + + /** + * When the page is ready. + * + * @since 5.10.0 + */ + obj.ready = () => { + obj.bindEvents(); + }; + + $( obj.ready ); +} )( jQuery, tribe.tickets.commerce.gateway.free, tribe.ky ); diff --git a/src/resources/postcss/tickets-admin/attendees/_modal.pcss b/src/resources/postcss/tickets-admin/attendees/_modal.pcss index ecf6ab1b13..de0c7a61a8 100644 --- a/src/resources/postcss/tickets-admin/attendees/_modal.pcss +++ b/src/resources/postcss/tickets-admin/attendees/_modal.pcss @@ -1,7 +1,7 @@ /** * Event Tickets Admin Attendees - Modal * - * @since TBD + * @since 5.10.0 */ .event-tickets { diff --git a/src/resources/postcss/tickets-commerce/gateway/free.pcss b/src/resources/postcss/tickets-commerce/gateway/free.pcss new file mode 100644 index 0000000000..065b29edc2 --- /dev/null +++ b/src/resources/postcss/tickets-commerce/gateway/free.pcss @@ -0,0 +1,8 @@ +/** + * Event Tickets - Tickets Commerce Free Gateway Stylesheet. + * + * @since 5.10.0 + */ + +@import '../../tickets-common.pcss'; +@import 'free/_all.pcss'; diff --git a/src/resources/postcss/tickets-commerce/gateway/free/_all.pcss b/src/resources/postcss/tickets-commerce/gateway/free/_all.pcss new file mode 100644 index 0000000000..ac56a1208c --- /dev/null +++ b/src/resources/postcss/tickets-commerce/gateway/free/_all.pcss @@ -0,0 +1,10 @@ +/** + * Event Tickets - Tickets Commerce Free Gateway Stylesheet. + * + * @since 5.10.0 + */ +.event-tickets { + .tribe-tickets__commerce-checkout-section-header { + display: none; + } +} diff --git a/src/resources/postcss/tickets/_block.pcss b/src/resources/postcss/tickets/_block.pcss index 3a604ef214..8a826a33c4 100644 --- a/src/resources/postcss/tickets/_block.pcss +++ b/src/resources/postcss/tickets/_block.pcss @@ -125,6 +125,7 @@ border-radius: var(--tec-spacer-2); color: var(--tec-color-icon-focus); display: inline-block; + font-family: var(--tec-font-family-sans-serif); font-size: var(--tec-font-size-0); font-weight: var(--tec-font-weight-bold); margin-bottom: 5px; diff --git a/src/views/emails/template-parts/body/order/order-gateway-data.php b/src/views/emails/template-parts/body/order/order-gateway-data.php index 7552dfdd23..75603e2110 100644 --- a/src/views/emails/template-parts/body/order/order-gateway-data.php +++ b/src/views/emails/template-parts/body/order/order-gateway-data.php @@ -12,6 +12,7 @@ * @version 5.6.0 * * @since 5.6.0 + * @since 5.10.0 Don't show if gateway order number is same as regular order number. * * @var Tribe__Template $this Current template object. * @var \TEC\Tickets\Emails\Email_Abstract $email The email object. @@ -28,6 +29,11 @@ return; } +// No need to show gateway ID if it's the same as the order ID. +if ( is_numeric( $order->gateway_order_id ) && intval( $order->ID ) === intval( $order->gateway_order_id ) ) { + return; +} + $gateway = tribe( Manager::class )->get_gateway_by_key( $order->gateway ); $link_or_id = $order->gateway_order_id; if ( $gateway ) { diff --git a/src/views/emails/template-parts/body/order/order-total.php b/src/views/emails/template-parts/body/order/order-total.php index 3c188e7d08..36356e9863 100644 --- a/src/views/emails/template-parts/body/order/order-total.php +++ b/src/views/emails/template-parts/body/order/order-total.php @@ -12,6 +12,7 @@ * @version 5.5.11 * * @since 5.5.11 + * @since 5.10.0 Allow for zero value total. * * @var Tribe__Template $this Current template object. * @var \TEC\Tickets\Emails\Email_Abstract $email The email object. @@ -23,7 +24,7 @@ * @var \WP_Post $order The order object. */ -if ( empty( $order ) || empty( $order->total ) ) { +if ( empty( $order ) || empty( $order->total_value ) ) { return; } diff --git a/src/views/emails/template-parts/body/order/payment-info.php b/src/views/emails/template-parts/body/order/payment-info.php index ceb706ff25..7107dbf7e1 100644 --- a/src/views/emails/template-parts/body/order/payment-info.php +++ b/src/views/emails/template-parts/body/order/payment-info.php @@ -13,6 +13,7 @@ * * @since 5.5.11 * @since 5.6.4 Capitalize payment gateway name. + * @since 5.10.0 Don't show if gateway name is blank. * * @var Tribe__Template $this Current template object. * @var \TEC\Tickets\Emails\Email_Abstract $email The email object. @@ -30,6 +31,10 @@ $gateway_name = tribe( TEC\Tickets\Commerce\Order::class )->get_gateway_label( $order ); +if ( empty( $gateway_name ) ) { + return; +} + $payment_info = empty( $order->status_slug ) || 'completed' !== strtolower( $order->status_slug ) ? sprintf( // Translators: %s - Payment provider's name. diff --git a/src/views/v2/commerce/gateway/free/button.php b/src/views/v2/commerce/gateway/free/button.php new file mode 100644 index 0000000000..10cc573baf --- /dev/null +++ b/src/views/v2/commerce/gateway/free/button.php @@ -0,0 +1,33 @@ + + diff --git a/src/views/v2/commerce/gateway/free/container.php b/src/views/v2/commerce/gateway/free/container.php new file mode 100644 index 0000000000..7927a52bab --- /dev/null +++ b/src/views/v2/commerce/gateway/free/container.php @@ -0,0 +1,26 @@ + +
+
+ template( 'gateway/free/button' ); ?> +
+
diff --git a/src/views/v2/commerce/order/details/payment-method.php b/src/views/v2/commerce/order/details/payment-method.php index 2082a4f1f6..9ad82273e8 100644 --- a/src/views/v2/commerce/order/details/payment-method.php +++ b/src/views/v2/commerce/order/details/payment-method.php @@ -7,13 +7,14 @@ * * See more documentation about our views templating system. * - * @link https://evnt.is/1amp Help article for RSVP & Ticket template files. + * @link https://evnt.is/1amp Help article for RSVP & Ticket template files. * - * @since 5.1.10 + * @since 5.1.10 * * @since 5.2.0 Added Payment method label. + * @since 5.10.0 Check if payment method is empty before rendering. * - * @version 5.1.10 + * @version 5.10.0 * * @var \Tribe__Template $this [Global] Template object. * @var Module $provider [Global] The tickets provider instance. @@ -24,7 +25,9 @@ * @var string $payment_method [Global] The payment method label. */ -if ( empty( $order->gateway ) ) { +use TEC\Tickets\Commerce\Module; + +if ( empty( $payment_method ) ) { return; } diff --git a/src/views/v2/commerce/ticket/regular-price.php b/src/views/v2/commerce/ticket/regular-price.php index 51d7840db1..0519107690 100644 --- a/src/views/v2/commerce/ticket/regular-price.php +++ b/src/views/v2/commerce/ticket/regular-price.php @@ -23,4 +23,4 @@ return; } -echo esc_html( $price->get_currency() ); +echo esc_html( $price->get_currency_display() ); diff --git a/src/views/v2/commerce/ticket/sale-price.php b/src/views/v2/commerce/ticket/sale-price.php index ea7166baa4..ebad701ddb 100644 --- a/src/views/v2/commerce/ticket/sale-price.php +++ b/src/views/v2/commerce/ticket/sale-price.php @@ -21,11 +21,12 @@ if ( empty( $on_sale ) ) { return; } + ?> - get_currency() ); ?> + get_currency_display() ); ?> diff --git a/tests/ft_integration/Tribe/Tickets/__snapshots__/MetaboxTest__test_get_panels__post with RSVP__0.snapshot.html b/tests/ft_integration/Tribe/Tickets/__snapshots__/MetaboxTest__test_get_panels__post with RSVP__0.snapshot.html index 2e408be6fa..b5a7a73075 100644 --- a/tests/ft_integration/Tribe/Tickets/__snapshots__/MetaboxTest__test_get_panels__post with RSVP__0.snapshot.html +++ b/tests/ft_integration/Tribe/Tickets/__snapshots__/MetaboxTest__test_get_panels__post with RSVP__0.snapshot.html @@ -431,8 +431,10 @@

Ticket Settings

class="ticket_field ticket_form_right" size="7" value="" - data-validation-error="Ticket price must be greater than zero." data-required data-validation-is-greater-than="0" /> -
+ /> +

+ Leave blank for free Ticket

+
Ticket Settings class="ticket_field" size="7" value="" - data-validation-is-greater-than="0" - data-validation-is-less-than="#ticket_price" - data-validation-error="{"is-greater-than":"Sale price must be greater than 0","is-less-than":"Sale price must be less than the regular price"}" - /> + data-validation-is-less-than="#ticket_price" data-validation-error="{"is-greater-than":"Sale price must be greater than 0","is-less-than":"Sale price must be less than the regular price"}" />