diff --git a/assets/src/css/admin/settings.scss b/assets/src/css/admin/settings.scss index 2d2af68567..5f027d4c0f 100644 --- a/assets/src/css/admin/settings.scss +++ b/assets/src/css/admin/settings.scss @@ -1153,6 +1153,11 @@ a.give-delete { display: none; } +.give-subsubsub .givewp-feature-flag-notification-counter { + translate: 0 -0.125rem; + vertical-align: middle; +} + .give-admin-beta-features-message { background-color: #fff; padding: 1rem; @@ -1166,6 +1171,16 @@ a.give-delete { font-family: 'Inter' ,sans-serif; font-weight: 400; line-height: 1.5rem; + margin-bottom: 1.5rem; + + .givewp-beta-icon { + margin-right: 0.25rem; + } + + p { + line-height: 1.6; + margin: 0; + } } .give-admin-beta-features-feedback-link { diff --git a/src/EventTickets/Actions/AddEventTicketsToDonationConfirmationPageDonationTotal.php b/src/EventTickets/Actions/AddEventTicketsToDonationConfirmationPageDonationTotal.php index c978a3d1e6..cd00caf565 100644 --- a/src/EventTickets/Actions/AddEventTicketsToDonationConfirmationPageDonationTotal.php +++ b/src/EventTickets/Actions/AddEventTicketsToDonationConfirmationPageDonationTotal.php @@ -2,11 +2,9 @@ namespace Give\EventTickets\Actions; -use Give\EventTickets\Models\EventTicket; use Give\EventTickets\Repositories\EventTicketRepository; use Give\Framework\Receipts\DonationReceipt; use Give\Framework\Receipts\Properties\ReceiptDetail; -use Give\Framework\Support\ValueObjects\Money; /** * @since 3.6.0 @@ -14,26 +12,18 @@ class AddEventTicketsToDonationConfirmationPageDonationTotal { /** + * @unreleased Refactored to use getTotalByDonation method * @since 3.6.0 */ public function __invoke(DonationReceipt $receipt): void { - $eventTickets = give(EventTicketRepository::class)->queryByDonationId($receipt->donation->id)->getAll(); - - if (!empty($eventTickets)) { - $currency = $receipt->donation->amount->getCurrency(); - $total = array_reduce($eventTickets, function (Money $carry, EventTicket $eventTicket) { - $ticketType = $eventTicket->ticketType()->get(); - - return $carry->add( - $ticketType->price - ); - }, new Money(0, $currency)); + $totalTicketAmount = give(EventTicketRepository::class)->getTotalByDonation($receipt->donation); + if ($totalTicketAmount->getAmount() > 0) { $receipt->donationDetails->addDetail( new ReceiptDetail( __('Event Tickets', 'give'), - $total->formatToLocale() + $totalTicketAmount->formatToLocale() ) ); } diff --git a/src/EventTickets/Actions/ConvertEventTicketsBlockToFieldsApi.php b/src/EventTickets/Actions/ConvertEventTicketsBlockToFieldsApi.php index e766fa588f..e167fe4a22 100644 --- a/src/EventTickets/Actions/ConvertEventTicketsBlockToFieldsApi.php +++ b/src/EventTickets/Actions/ConvertEventTicketsBlockToFieldsApi.php @@ -14,6 +14,7 @@ class ConvertEventTicketsBlockToFieldsApi { /** + * @unreleased Set event end date and time. * @since 3.12.2 Remove event ID from field name * @since 3.6.0 * @@ -32,6 +33,7 @@ public function __invoke(BlockModel $block, int $formId) $eventTicketsField ->title($event->title) ->startDateTime($event->startDateTime->format('Y-m-d H:i:s')) + ->endDateTime($event->endDateTime->format('Y-m-d H:i:s')) ->description($event->description) ->ticketTypes($ticketTypes); diff --git a/src/EventTickets/Actions/GenerateTicketsFromPurchaseData.php b/src/EventTickets/Actions/GenerateTicketsFromPurchaseData.php index 8b838cd842..87f1ed6127 100644 --- a/src/EventTickets/Actions/GenerateTicketsFromPurchaseData.php +++ b/src/EventTickets/Actions/GenerateTicketsFromPurchaseData.php @@ -26,6 +26,7 @@ public function __construct(Donation $donation) } /** + * @unreleased Add "amount" to the array of props * @since 3.6.0 */ public function __invoke(TicketPurchaseData $data) @@ -35,6 +36,7 @@ public function __invoke(TicketPurchaseData $data) 'eventId' => $data->ticketType->eventId, 'ticketTypeId' => $data->ticketType->id, 'donationId' => $this->donation->id, + 'amount' => $data->ticketType->price, ]); } } diff --git a/src/EventTickets/Factories/EventTicketFactory.php b/src/EventTickets/Factories/EventTicketFactory.php index ffb649acd6..e14ac9ee7d 100644 --- a/src/EventTickets/Factories/EventTicketFactory.php +++ b/src/EventTickets/Factories/EventTicketFactory.php @@ -12,15 +12,20 @@ class EventTicketFactory extends ModelFactory { /** + * @unreleased Add amount to the properties array using the price from the ticket type * @since 3.6.0 + * * @throws Exception */ public function definition(): array { + $ticketType = EventTicketType::factory()->create(); + return [ 'eventId' => Event::factory()->create()->id, - 'ticketTypeId' => EventTicketType::factory()->create()->id, + 'ticketTypeId' => $ticketType->id, 'donationId' => Donation::factory()->create()->id, + 'amount' => $ticketType->price, 'createdAt' => new DateTime(), 'updatedAt' => new DateTime(), ]; diff --git a/src/EventTickets/Fields/EventTickets.php b/src/EventTickets/Fields/EventTickets.php index 9088a965dc..77fd86d85d 100644 --- a/src/EventTickets/Fields/EventTickets.php +++ b/src/EventTickets/Fields/EventTickets.php @@ -7,8 +7,9 @@ class EventTickets extends Field { protected $title; - protected $startDateTime; protected $description; + protected $startDateTime; + protected $endDateTime; protected $ticketTypes = []; const TYPE = 'eventTickets'; @@ -50,59 +51,53 @@ public function startDateTime(string $date): EventTickets /** * @since 3.6.0 */ - public function getDescription(): string + public function getEndDateTime(): string { - return $this->description; + return $this->endDateTime; } /** - * @since 3.6.0 + * @unreleased */ - public function description(string $description): EventTickets + public function endDateTime(string $date): EventTickets { - $this->description = $description; + $this->endDateTime = $date; + return $this; } /** * @since 3.6.0 */ - public function getTicketTypes(): array + public function getDescription(): string { - return $this->ticketTypes; + return $this->description; } /** * @since 3.6.0 */ - public function ticketTypes(array $ticketTypes): EventTickets + public function description(string $description): EventTickets { - $this->ticketTypes = $ticketTypes; + $this->description = $description; return $this; } /** * @since 3.6.0 */ - public function getTicketsLabel(): string + public function getTicketTypes(): array { - return apply_filters( - 'givewp_event_tickets_block/tickets_label', - __('Select Tickets', 'give') - ); + return $this->ticketTypes; } /** * @since 3.6.0 */ - public function getSoldOutMessage(): string + public function ticketTypes(array $ticketTypes): EventTickets { - return apply_filters( - 'givewp_event_tickets_block/sold_out_message', - __( - 'Thank you for supporting our cause. Our fundraising event tickets are officially sold out. You can still contribute by making a donation.', - 'give' - ) - ); + $this->ticketTypes = $ticketTypes; + return $this; } + } diff --git a/src/EventTickets/ListTable/Columns/DescriptionColumn.php b/src/EventTickets/ListTable/Columns/DescriptionColumn.php index 009c5cfd48..9908e5ff59 100644 --- a/src/EventTickets/ListTable/Columns/DescriptionColumn.php +++ b/src/EventTickets/ListTable/Columns/DescriptionColumn.php @@ -37,12 +37,20 @@ public function getLabel(): string /** * @inheritDoc * + * @unreleased Truncate description to 200 characters * @since 3.6.0 * * @param Event $model */ public function getCellValue($model): string { - return wpautop($model->description); + $maxChars = 200; + $truncatedDescription = mb_substr($model->description, 0, $maxChars); + + if (mb_strlen($model->description) > $maxChars) { + $truncatedDescription .= '...'; + } + + return sprintf('
%s
', wpautop($truncatedDescription)); } } diff --git a/src/EventTickets/ListTable/Columns/SalesAmountColumn.php b/src/EventTickets/ListTable/Columns/SalesAmountColumn.php index 41d3ecd977..df533b2507 100644 --- a/src/EventTickets/ListTable/Columns/SalesAmountColumn.php +++ b/src/EventTickets/ListTable/Columns/SalesAmountColumn.php @@ -38,6 +38,7 @@ public function getLabel(): string /** * @inheritDoc * + * @unreleased Refactored to use event ticket amount instead of ticket type price * @since 3.6.0 * * @param Event $model @@ -46,22 +47,26 @@ public function getCellValue($model, $locale = ''): string { $ticketTypes = []; foreach ($model->ticketTypes()->getAll() ?? [] as $ticketType) { + $salesCount = $ticketType->eventTickets()->count(); + $ticketsAvailable = $ticketType->capacity - $salesCount; + $ticketTypes[$ticketType->id] = [ 'price' => $ticketType->price, 'capacity' => $ticketType->capacity, + 'ticketsAvailable' => $ticketsAvailable, ]; } $salesTotal = array_reduce( $model->eventTickets()->getAll() ?? [], - function (Money $carry, $eventTicket) use ($ticketTypes) { - return $carry->add($ticketTypes[$eventTicket->ticketTypeId]['price']); + function (Money $carry, $eventTicket) { + return $carry->add($eventTicket->amount); }, new Money(0, give_get_currency()) ); $maxCapacitySales = array_reduce($ticketTypes, function (Money $carry, $ticketType) { - return $carry->add($ticketType['price']->multiply($ticketType['capacity'])); - }, new Money(0, give_get_currency())); + return $carry->add($ticketType['price']->multiply($ticketType['ticketsAvailable'])); + }, $salesTotal); $salesPercentage = $maxCapacitySales->formatToMinorAmount() > 0 ? max( min($salesTotal->formatToMinorAmount() / $maxCapacitySales->formatToMinorAmount(), 100), diff --git a/src/EventTickets/Migrations/AddAmountColumnToEventTicketsTable.php b/src/EventTickets/Migrations/AddAmountColumnToEventTicketsTable.php new file mode 100644 index 0000000000..b9542636a7 --- /dev/null +++ b/src/EventTickets/Migrations/AddAmountColumnToEventTicketsTable.php @@ -0,0 +1,74 @@ +give_event_tickets; + $eventTicketTypesTable = $wpdb->give_event_ticket_types; + + $this->addAmountColumn($wpdb, $eventTicketsTable); + $this->migrateTicketPrices($wpdb, $eventTicketsTable, $eventTicketTypesTable); + } + + /** + * @throws DatabaseMigrationException + */ + private function addAmountColumn($wpdb, $eventTicketsTable) { + $sql = "ALTER TABLE $eventTicketsTable + ADD COLUMN amount INT UNSIGNED NOT NULL AFTER donation_id"; + + try { + $wpdb->query($sql); + } catch (DatabaseQueryException $exception) { + throw new DatabaseMigrationException( "An error occurred while adding the amount column to the $eventTicketsTable table", 0, $exception ); + } + } + + /** + * @throws DatabaseMigrationException + */ + private function migrateTicketPrices($wpdb, $eventTicketsTable, $eventTicketTypesTable) { + $sql = "UPDATE $eventTicketsTable eventTickets + JOIN $eventTicketTypesTable evenTicketTypes + ON eventTickets.ticket_type_id = evenTicketTypes.id + SET eventTickets.amount = evenTicketTypes.price"; + + try { + $wpdb->query($sql); + } catch (DatabaseQueryException $exception) { + throw new DatabaseMigrationException( "An error occurred while migrating data to the amount column in the $eventTicketsTable table", 0, $exception ); + } + } +}; diff --git a/src/EventTickets/Models/EventTicket.php b/src/EventTickets/Models/EventTicket.php index 836cc57dad..59380c12e1 100644 --- a/src/EventTickets/Models/EventTicket.php +++ b/src/EventTickets/Models/EventTicket.php @@ -15,6 +15,7 @@ use Give\Framework\Models\ModelQueryBuilder; use Give\Framework\Models\ValueObjects\Relationship; use Give\Framework\Support\Facades\DateTime\Temporal; +use Give\Framework\Support\ValueObjects\Money; /** * @since 3.6.0 @@ -23,12 +24,16 @@ class EventTicket extends Model implements ModelCrud /*, ModelHasFactory */ { /** * @inheritdoc + * + * @unreleased Add amount to the properties array + * @since 3.6.0 */ protected $properties = [ 'id' => 'int', // @todo Maybe use UUID instead of auto-incrementing integer 'eventId' => 'int', 'ticketTypeId' => 'int', 'donationId' => 'int', + 'amount' => Money::class, 'createdAt' => DateTime::class, 'updatedAt' => DateTime::class, ]; @@ -144,7 +149,6 @@ public function ticketType(): ModelQueryBuilder return give(EventTicketTypeRepository::class)->queryById($this->ticketTypeId); } - /** * @since 3.6.0 * @@ -156,6 +160,7 @@ public function donation(): ModelQueryBuilder } /** + * @unreleased Add amount to the properties array * @since 3.6.0 * * @param object $object @@ -167,6 +172,7 @@ public static function fromQueryBuilderObject($object): EventTicket 'eventId' => (int)$object->event_id, 'ticketTypeId' => (int)$object->ticket_type_id, 'donationId' => (int)$object->donation_id, + 'amount' => new Money($object->amount, give_get_currency()), 'createdAt' => Temporal::toDateTime($object->created_at), 'updatedAt' => Temporal::toDateTime($object->updated_at), ]); diff --git a/src/EventTickets/Repositories/EventTicketRepository.php b/src/EventTickets/Repositories/EventTicketRepository.php index 16de242b86..79a4f891fc 100644 --- a/src/EventTickets/Repositories/EventTicketRepository.php +++ b/src/EventTickets/Repositories/EventTicketRepository.php @@ -20,6 +20,7 @@ class EventTicketRepository { /** + * @unreleased Add "amount" column to the properties array * @since 3.6.0 * * @var string[] @@ -28,6 +29,7 @@ class EventTicketRepository 'eventId', 'ticketTypeId', 'donationId', + 'amount', ]; /** @@ -50,6 +52,7 @@ public function queryById(int $id): ModelQueryBuilder } /** + * @unreleased Add "amount" column to the insert statement * @since 3.6.0 * * @throws Exception|InvalidArgumentException @@ -70,6 +73,7 @@ public function insert(EventTicket $eventTicket) 'event_id' => $eventTicket->eventId, 'ticket_type_id' => $eventTicket->ticketTypeId, 'donation_id' => $eventTicket->donationId, + 'amount' => $eventTicket->amount->formatToMinorAmount(), 'created_at' => $createdDateTime->format('Y-m-d H:i:s'), 'updated_at' => $createdDateTime->format('Y-m-d H:i:s'), ]); @@ -93,6 +97,7 @@ public function insert(EventTicket $eventTicket) } /** + * @unreleased Add "amount" column to the update statement * @since 3.6.0 * * @throws Exception|InvalidArgumentException @@ -115,6 +120,7 @@ public function update(EventTicket $eventTicket) 'event_id' => $eventTicket->eventId, 'ticket_type_id' => $eventTicket->ticketTypeId, 'donation_id' => $eventTicket->donationId, + 'amount' => $eventTicket->amount->formatToMinorAmount(), 'updated_at' => $updatedDateTime->format('Y-m-d H:i:s'), ]); } catch (Exception $exception) { @@ -175,6 +181,8 @@ private function validate(EventTicket $eventTicket): void } /** + * @unreleased Add "amount" column to the select statement + * @since 3.6.0 * @return ModelQueryBuilder */ public function prepareQuery(): ModelQueryBuilder @@ -187,6 +195,7 @@ public function prepareQuery(): ModelQueryBuilder 'event_id', 'ticket_type_id', 'donation_id', + 'amount', 'created_at', 'updated_at' ); @@ -224,6 +233,7 @@ public function queryByDonationId(int $donationId): ModelQueryBuilder } /** + * @unreleased Refactored to use event ticket amount instead of ticket type price * @since 3.6.0 */ public function getTotalByDonation(Donation $donation): Money @@ -232,10 +242,8 @@ public function getTotalByDonation(Donation $donation): Money $currency = $donation->amount->getCurrency(); return array_reduce($eventTickets, static function (Money $carry, EventTicket $eventTicket) { - $ticketType = $eventTicket->ticketType()->get(); - return $carry->add( - $ticketType->price + $eventTicket->amount ); }, new Money(0, $currency)); } diff --git a/src/EventTickets/Routes/CreateEvent.php b/src/EventTickets/Routes/CreateEvent.php index c3887a5b30..91024a3f0a 100644 --- a/src/EventTickets/Routes/CreateEvent.php +++ b/src/EventTickets/Routes/CreateEvent.php @@ -6,6 +6,7 @@ use Give\EventTickets\Models\Event; use WP_REST_Request; use WP_REST_Response; +use WP_REST_Server; /** * @since 3.6.0 @@ -17,6 +18,9 @@ class CreateEvent implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "publish_give_payments" and description's sanitize callback to "textarea". + * @since 3.6.0 */ public function registerRoute() { @@ -25,10 +29,10 @@ public function registerRoute() $this->endpoint, [ [ - 'methods' => 'POST', + 'methods' => WP_REST_Server::CREATABLE, 'callback' => [$this, 'handleRequest'], 'permission_callback' => function () { - return current_user_can( 'manage_options' ); + return current_user_can('edit_give_forms'); } ], 'args' => [ @@ -40,7 +44,7 @@ public function registerRoute() 'description' => [ 'type' => 'string', 'required' => false, - 'sanitize_callback' => 'sanitize_text_field', + 'sanitize_callback' => 'sanitize_textarea_field', ], 'startDateTime' => [ 'type' => 'string', diff --git a/src/EventTickets/Routes/CreateEventTicketType.php b/src/EventTickets/Routes/CreateEventTicketType.php index dca9169696..ace522f701 100644 --- a/src/EventTickets/Routes/CreateEventTicketType.php +++ b/src/EventTickets/Routes/CreateEventTicketType.php @@ -9,6 +9,7 @@ use Give\Framework\Support\ValueObjects\Money; use WP_REST_Request; use WP_REST_Response; +use WP_REST_Server; /** * @since 3.6.0 @@ -20,6 +21,9 @@ class CreateEventTicketType implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "publish_give_payments" and description's sanitize callback to "textarea". + * @since 3.6.0 */ public function registerRoute() { @@ -28,10 +32,10 @@ public function registerRoute() $this->endpoint, [ [ - 'methods' => 'POST', + 'methods' => WP_REST_Server::CREATABLE, 'callback' => [$this, 'handleRequest'], 'permission_callback' => function () { - return current_user_can( 'manage_options' ); + return current_user_can('edit_give_forms'); } ], 'args' => [ @@ -39,7 +43,7 @@ public function registerRoute() 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => function ($eventId) { - return Event::find($eventId); + return Event::find($eventId) !== null; }, 'required' => true, ], @@ -51,7 +55,7 @@ public function registerRoute() 'description' => [ 'type' => 'string', 'required' => false, - 'sanitize_callback' => 'sanitize_text_field', + 'sanitize_callback' => 'sanitize_textarea_field', ], 'price' => [ 'type' => 'integer', diff --git a/src/EventTickets/Routes/DeleteEventTicketType.php b/src/EventTickets/Routes/DeleteEventTicketType.php index c3222ede49..cf00fd03a2 100644 --- a/src/EventTickets/Routes/DeleteEventTicketType.php +++ b/src/EventTickets/Routes/DeleteEventTicketType.php @@ -20,6 +20,9 @@ class DeleteEventTicketType implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "delete_give_payments". + * @since 3.6.0 */ public function registerRoute() { @@ -30,14 +33,37 @@ public function registerRoute() [ 'methods' => WP_REST_Server::DELETABLE, 'callback' => [$this, 'handleRequest'], - 'permission_callback' => [$this, 'permissionsCheck'], + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'ticket_type_id' => [ 'type' => 'integer', 'sanitize_callback' => 'absint', - 'validate_callback' => function ($eventId) { - return EventTicketType::find($eventId); + 'validate_callback' => function ($ticketTypeId) { + $eventTicketType = EventTicketType::find( + $ticketTypeId + ); + + if (is_null($eventTicketType)) { + return false; + } + + $salesCount = $eventTicketType->eventTickets( + )->count(); + if ($salesCount > 0) { + return new WP_Error( + 'event_ticket_type_sold_delete_failed', + __( + 'This ticket type has been sold and cannot be deleted.', + 'give' + ), + ['status' => 403] + ); + } + + return true; }, 'required' => true, ], @@ -53,32 +79,8 @@ public function registerRoute() */ public function handleRequest(WP_REST_Request $request): WP_REST_Response { - $ticketType = EventTicketType::find($request->get_param('ticket_type_id')); - - $salesCount = $ticketType->eventTickets()->count(); - - if ($salesCount > 0) { - return new WP_REST_Response([ - 'message' => __('This ticket type has been sold and cannot be deleted.', 'give'), - ], 400); - } - - $ticketType->delete(); + EventTicketType::find($request->get_param('ticket_type_id'))->delete(); return new WP_REST_Response(); } - - /** - * @since 3.6.0 - * - * @return bool|WP_Error - */ - public function permissionsCheck() - { - return current_user_can('delete_posts') ?: new WP_Error( - 'rest_forbidden', - esc_html__("You don't have permission to delete Event Ticket Types", 'give'), - ['status' => is_user_logged_in() ? 403 : 401] - ); - } } diff --git a/src/EventTickets/Routes/DeleteEventsListTable.php b/src/EventTickets/Routes/DeleteEventsListTable.php index 7faa9d428d..bac5eab93f 100644 --- a/src/EventTickets/Routes/DeleteEventsListTable.php +++ b/src/EventTickets/Routes/DeleteEventsListTable.php @@ -31,6 +31,9 @@ class DeleteEventsListTable /** * @inheritDoc + * + * @unreleased Set the permission callback to "delete_give_payments". + * @since 3.6.0 */ public function registerRoute() { @@ -41,7 +44,9 @@ public function registerRoute() [ 'methods' => WP_REST_Server::DELETABLE, 'callback' => [$this, 'handleRequest'], - 'permission_callback' => [$this, 'permissionsCheck'], + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'ids' => [ @@ -73,8 +78,13 @@ public function handleRequest(WP_REST_Request $request): WP_Rest_Response $successes = []; foreach ($ids as $id) { - $soldTicketsCount = give(EventRepository::class)->getById($id)->eventTickets()->count() ?? 0; + $event = give(EventRepository::class)->getById($id); + if ( ! $event) { + $errors[] = $id; + continue; + } + $soldTicketsCount = $event->eventTickets()->count() ?? 0; if ($soldTicketsCount > 0) { $errors[] = $id; continue; @@ -103,18 +113,4 @@ protected function splitString(string $ids): array return [trim($ids)]; } - - /** - * @since 3.6.0 - * - * @return bool|\WP_Error - */ - public function permissionsCheck() - { - return current_user_can('delete_posts')?: new \WP_Error( - 'rest_forbidden', - esc_html__("You don't have permission to delete Events", 'give'), - ['status' => is_user_logged_in() ? 403 : 401] - ); - } } diff --git a/src/EventTickets/Routes/GetEventForms.php b/src/EventTickets/Routes/GetEventForms.php index 647c4e7d3e..774589ced1 100644 --- a/src/EventTickets/Routes/GetEventForms.php +++ b/src/EventTickets/Routes/GetEventForms.php @@ -18,6 +18,9 @@ class GetEventForms implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "read". + * @since 3.6.0 */ public function registerRoute() { @@ -28,14 +31,16 @@ public function registerRoute() [ 'methods' => 'GET', 'callback' => [$this, 'handleRequest'], - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'event_id' => [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => function ($eventId) { - return Event::find($eventId); + return Event::find($eventId) !== null; }, 'required' => true, ], diff --git a/src/EventTickets/Routes/GetEventTicketTypeTickets.php b/src/EventTickets/Routes/GetEventTicketTypeTickets.php index fb035b737a..e532c96f88 100644 --- a/src/EventTickets/Routes/GetEventTicketTypeTickets.php +++ b/src/EventTickets/Routes/GetEventTicketTypeTickets.php @@ -3,7 +3,6 @@ namespace Give\EventTickets\Routes; use Give\API\RestRoute; -use Give\EventTickets\Models\Event; use Give\EventTickets\Models\EventTicket; use Give\EventTickets\Models\EventTicketType; use Give\Framework\Models\Model; @@ -20,6 +19,9 @@ class GetEventTicketTypeTickets implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "read". + * @since 3.6.0 */ public function registerRoute() { @@ -30,14 +32,18 @@ public function registerRoute() [ 'methods' => 'GET', 'callback' => [$this, 'handleRequest'], - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'ticket_type_id' => [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => function ($ticketTypeId) { - return EventTicketType::find($ticketTypeId); + return EventTicketType::find( + $ticketTypeId + ) !== null; }, 'required' => true, ], diff --git a/src/EventTickets/Routes/GetEventTicketTypes.php b/src/EventTickets/Routes/GetEventTicketTypes.php index abf40a957c..a6d9336766 100644 --- a/src/EventTickets/Routes/GetEventTicketTypes.php +++ b/src/EventTickets/Routes/GetEventTicketTypes.php @@ -4,7 +4,6 @@ use Give\API\RestRoute; use Give\EventTickets\Models\Event; -use Give\EventTickets\Models\EventTicket; use Give\EventTickets\Models\EventTicketType; use Give\Framework\Models\Model; use WP_REST_Request; @@ -20,6 +19,9 @@ class GetEventTicketTypes implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "read". + * @since 3.6.0 */ public function registerRoute() { @@ -30,14 +32,16 @@ public function registerRoute() [ 'methods' => 'GET', 'callback' => [$this, 'handleRequest'], - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'event_id' => [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => function ($eventId) { - return Event::find($eventId); + return Event::find($eventId) !== null; }, 'required' => true, ], diff --git a/src/EventTickets/Routes/GetEventTickets.php b/src/EventTickets/Routes/GetEventTickets.php index 53beb16551..bb9fda3b96 100644 --- a/src/EventTickets/Routes/GetEventTickets.php +++ b/src/EventTickets/Routes/GetEventTickets.php @@ -19,6 +19,9 @@ class GetEventTickets implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "read". + * @since 3.6.0 */ public function registerRoute() { @@ -29,14 +32,16 @@ public function registerRoute() [ 'methods' => 'GET', 'callback' => [$this, 'handleRequest'], - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'event_id' => [ 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => function ($eventId) { - return Event::find($eventId); + return Event::find($eventId) !== null; }, 'required' => true, ], diff --git a/src/EventTickets/Routes/GetEvents.php b/src/EventTickets/Routes/GetEvents.php index b9f76d9750..51c50e13e3 100644 --- a/src/EventTickets/Routes/GetEvents.php +++ b/src/EventTickets/Routes/GetEvents.php @@ -17,6 +17,9 @@ class GetEvents implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "read". + * @since 3.6.0 */ public function registerRoute() { @@ -27,7 +30,9 @@ public function registerRoute() [ 'methods' => 'GET', 'callback' => [$this, 'handleRequest'], - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'page' => [ diff --git a/src/EventTickets/Routes/GetEventsListTable.php b/src/EventTickets/Routes/GetEventsListTable.php index d0c809cc07..2102e83089 100644 --- a/src/EventTickets/Routes/GetEventsListTable.php +++ b/src/EventTickets/Routes/GetEventsListTable.php @@ -32,6 +32,7 @@ class GetEventsListTable /** * @inheritDoc * + * @unreleased Set the permission callback to "read". * @since 3.6.0 */ public function registerRoute(): void @@ -43,7 +44,9 @@ public function registerRoute(): void [ 'methods' => WP_REST_Server::READABLE, 'callback' => [$this, 'handleRequest'], - 'permission_callback' => [$this, 'permissionsCheck'], + 'permission_callback' => function () { + return current_user_can('edit_give_forms'); + }, ], 'args' => [ 'page' => [ @@ -167,18 +170,4 @@ private function getWhereConditions(QueryBuilder $query): QueryBuilder return $query; } - - /** - * @since 3.6.0 - * - * @return bool|\WP_Error - */ - public function permissionsCheck() - { - return current_user_can('edit_posts')?: new \WP_Error( - 'rest_forbidden', - esc_html__("You don't have permission to view Events", 'give'), - ['status' => is_user_logged_in() ? 403 : 401] - ); - } } diff --git a/src/EventTickets/Routes/UpdateEvent.php b/src/EventTickets/Routes/UpdateEvent.php index d4a33da727..faa1594476 100644 --- a/src/EventTickets/Routes/UpdateEvent.php +++ b/src/EventTickets/Routes/UpdateEvent.php @@ -19,6 +19,9 @@ class UpdateEvent implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "edit_give_payments" and description's sanitize callback to "textarea". + * @since 3.6.0 */ public function registerRoute() { @@ -38,7 +41,7 @@ public function registerRoute() 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => function ($eventId) { - return Event::find($eventId); + return Event::find($eventId) !== null; }, 'required' => true, ], @@ -50,7 +53,7 @@ public function registerRoute() 'description' => [ 'type' => 'string', 'required' => false, - 'sanitize_callback' => 'sanitize_text_field', + 'sanitize_callback' => 'sanitize_textarea_field', ], 'startDateTime' => [ 'type' => 'string', diff --git a/src/EventTickets/Routes/UpdateEventTicketType.php b/src/EventTickets/Routes/UpdateEventTicketType.php index dcea73838b..87dbbd0693 100644 --- a/src/EventTickets/Routes/UpdateEventTicketType.php +++ b/src/EventTickets/Routes/UpdateEventTicketType.php @@ -21,6 +21,9 @@ class UpdateEventTicketType implements RestRoute /** * @inheritDoc + * + * @unreleased Set the permission callback to "edit_give_payments" and description's sanitize callback to "textarea". + * @since 3.6.0 */ public function registerRoute() { @@ -39,8 +42,10 @@ public function registerRoute() 'ticket_type_id' => [ 'type' => 'integer', 'sanitize_callback' => 'absint', - 'validate_callback' => function ($eventId) { - return EventTicketType::find($eventId); + 'validate_callback' => function ($ticketTypeId) { + return EventTicketType::find( + $ticketTypeId + ) !== null; }, 'required' => true, ], @@ -52,7 +57,7 @@ public function registerRoute() 'description' => [ 'type' => 'string', 'required' => false, - 'sanitize_callback' => 'sanitize_text_field', + 'sanitize_callback' => 'sanitize_textarea_field', ], 'price' => [ 'type' => 'integer', diff --git a/src/EventTickets/ServiceProvider.php b/src/EventTickets/ServiceProvider.php index 13d9acf779..3898f809b3 100644 --- a/src/EventTickets/ServiceProvider.php +++ b/src/EventTickets/ServiceProvider.php @@ -12,8 +12,6 @@ use Give\EventTickets\Repositories\EventTicketRepository; use Give\EventTickets\Repositories\EventTicketTypeRepository; use Give\Framework\Migrations\MigrationsRegister; -use Give\Framework\Receipts\DonationReceipt; -use Give\Framework\Support\ValueObjects\Money; use Give\Helpers\Hooks; use Give\ServiceProviders\ServiceProvider as ServiceProviderInterface; @@ -69,6 +67,7 @@ private function registerMigrations(): void Migrations\CreateEventsTable::class, Migrations\CreateEventTicketTypesTable::class, Migrations\CreateEventTicketsTable::class, + Migrations\AddAmountColumnToEventTicketsTable::class, ]); } diff --git a/src/EventTickets/resources/admin/components/EventDetailsPage/SectionTable/SectionTable.module.scss b/src/EventTickets/resources/admin/components/EventDetailsPage/SectionTable/SectionTable.module.scss index 6dc06cd40d..858ee13e20 100644 --- a/src/EventTickets/resources/admin/components/EventDetailsPage/SectionTable/SectionTable.module.scss +++ b/src/EventTickets/resources/admin/components/EventDetailsPage/SectionTable/SectionTable.module.scss @@ -102,6 +102,7 @@ &.description { font-weight: 400; + line-height: 1.43; } } diff --git a/src/EventTickets/resources/admin/components/EventDetailsPage/TicketTypesSection/index.tsx b/src/EventTickets/resources/admin/components/EventDetailsPage/TicketTypesSection/index.tsx index 844ccc5e53..a86f184352 100644 --- a/src/EventTickets/resources/admin/components/EventDetailsPage/TicketTypesSection/index.tsx +++ b/src/EventTickets/resources/admin/components/EventDetailsPage/TicketTypesSection/index.tsx @@ -40,6 +40,7 @@ export default function TicketTypesSection() { const { apiRoot, apiNonce, + currencyCode, event, event: {ticketTypes}, }: GiveEventTicketsDetails = window.GiveEventTicketsDetails; @@ -101,7 +102,7 @@ export default function TicketTypesSection() { })} /> { /** * Event Form Modal component * + * @unreleased Added placeholder to event description field * @since 3.6.0 */ export default function EventFormModal({isOpen, handleClose, apiSettings, title, event}: EventModalProps) { @@ -96,7 +97,11 @@ export default function EventFormModal({isOpen, handleClose, apiSettings, title,
-