\WP_REST_Server::READABLE, 'callback' => [$this, 'events'], 'permission_callback' => '__return_true', 'args' => $this->queryArgs(), ]); register_rest_route('pro-calendar-wp/v1', '/events/(?P\d+)', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'event'], 'permission_callback' => '__return_true', 'args' => ['id' => ['type' => 'integer', 'minimum' => 1]], ]); register_rest_route('pro-calendar-wp/v1', '/occurrences', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [$this, 'occurrences'], 'permission_callback' => '__return_true', 'args' => $this->queryArgs(), ]); register_rest_route('pro-calendar-wp/v1', '/events/(?P\d+)/duplicate', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [$this, 'duplicate'], 'permission_callback' => static fn (\WP_REST_Request $request): bool => current_user_can('edit_post', (int) $request['id']) && \ProCalendarWP\Infrastructure\Capabilities::canCreatePosts(), ]); } public function events(\WP_REST_Request $request): \WP_REST_Response { $items = []; $seen = []; foreach ($this->events->occurrences($this->queryInput($request)) as $occurrence) { if (isset($seen[$occurrence->eventId])) { continue; } $event = $this->events->find($occurrence->eventId); if ($event !== null) { $items[] = $this->publicEvent($event->toArray()); $seen[$occurrence->eventId] = true; } } return new \WP_REST_Response($items); } public function event(\WP_REST_Request $request): \WP_REST_Response|\WP_Error { $event = $this->events->find((int) $request['id']); if ($event === null || ! \ProCalendarWP\Events\EventVisibility::isPublic($event->id)) { return new \WP_Error('pcwp_event_not_found', __('Event not found.', 'proeventswp'), ['status' => 404]); } return new \WP_REST_Response($this->publicEvent($event->toArray())); } public function occurrences(\WP_REST_Request $request): \WP_REST_Response { $items = array_map(static fn ($occurrence): array => [ 'id' => $occurrence->id, 'event_id' => $occurrence->eventId, 'series_id' => $occurrence->seriesId, 'start_utc' => $occurrence->startUtc->format(DATE_ATOM), 'end_utc' => $occurrence->endUtc->format(DATE_ATOM), 'start_local' => $occurrence->startLocal->format(DATE_ATOM), 'end_local' => $occurrence->endLocal->format(DATE_ATOM), 'timezone' => $occurrence->timezone, 'all_day' => $occurrence->allDay, 'status' => $occurrence->status, ], $this->events->occurrences($this->queryInput($request))); return new \WP_REST_Response($items); } public function duplicate(\WP_REST_Request $request): \WP_REST_Response|\WP_Error { $source = get_post((int) $request['id']); if (! $source instanceof \WP_Post || $source->post_type !== 'pcwp_event') { return new \WP_Error('pcwp_event_not_found', __('Event not found.', 'proeventswp'), ['status' => 404]); } $newId = wp_insert_post([ 'post_type' => 'pcwp_event', 'post_status' => 'draft', /* translators: %s is the original event title. */ 'post_title' => sprintf(__('%s (copy)', 'proeventswp'), $source->post_title), 'post_content' => $source->post_content, 'post_excerpt' => $source->post_excerpt, 'post_author' => get_current_user_id(), ], true); if (is_wp_error($newId)) { return $newId; } foreach (['_pcwp_timezone', '_pcwp_all_day', '_pcwp_mode', '_pcwp_featured', '_pcwp_cost', '_pcwp_currency', '_pcwp_website', '_pcwp_virtual_url', '_pcwp_show_map', '_pcwp_latitude', '_pcwp_longitude', '_pcwp_location'] as $key) { $value = get_post_meta($source->ID, $key, true); if ($value !== '') { update_post_meta($newId, $key, $key === \ProCalendarWP\Maps\Location::KEY ? wp_slash($value) : $value); } } update_post_meta($newId, '_pcwp_status', 'scheduled'); set_post_thumbnail($newId, get_post_thumbnail_id($source->ID)); wp_set_object_terms($newId, wp_get_object_terms($source->ID, 'pcwp_event_category', ['fields' => 'ids']), 'pcwp_event_category'); wp_set_object_terms($newId, wp_get_object_terms($source->ID, 'pcwp_event_tag', ['fields' => 'ids']), 'pcwp_event_tag'); global $wpdb; $entities = $wpdb->prefix . 'pcwp_event_entities'; $wpdb->query($wpdb->prepare("INSERT IGNORE INTO {$entities} (event_id,entity_id,entity_type,sort_order) SELECT %d,entity_id,entity_type,sort_order FROM {$entities} WHERE event_id=%d", $newId, $source->ID)); return new \WP_REST_Response(['id' => (int) $newId, 'edit_link' => get_edit_post_link($newId, 'raw')], 201); } /** @return array> */ private function queryInput(\WP_REST_Request $request): array { $input = $request->get_params(); foreach (['pcwpIncludeEvents' => 'include_events', 'pcwpExcludeEvents' => 'exclude_events'] as $source => $target) { if (isset($input[$source])) { $input[$target] = $input[$source]; } } return $input; } /** @return array> */ private function queryArgs(): array { return [ 'start' => ['type' => 'string'], 'end' => ['type' => 'string'], 'category' => ['type' => 'string', 'sanitize_callback' => 'sanitize_title'], 'tag' => ['type' => 'string', 'sanitize_callback' => 'sanitize_title'], 'taxonomies' => ['type' => 'object'], 'include_events' => ['type' => 'array', 'items' => ['type' => 'integer']], 'exclude_events' => ['type' => 'array', 'items' => ['type' => 'integer']], 'pcwpIncludeEvents' => ['type' => 'array', 'items' => ['type' => 'integer']], 'pcwpExcludeEvents' => ['type' => 'array', 'items' => ['type' => 'integer']], 'keyword' => ['type' => 'string', 'sanitize_callback' => 'sanitize_text_field'], 'featured' => ['type' => 'boolean'], 'status' => ['type' => 'string'], 'mode' => ['type' => 'string', 'enum' => ['in-person', 'online', 'hybrid']], 'venue' => ['type' => 'integer', 'minimum' => 1], 'organizer' => ['type' => 'integer', 'minimum' => 1], 'series' => ['type' => 'integer', 'minimum' => 1], 'related_to' => ['type' => 'integer', 'minimum' => 1], 'min_price' => ['type' => 'number'], 'max_price' => ['type' => 'number'], 'weekday' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 6], 'time_from' => ['type' => 'string'], 'time_to' => ['type' => 'string'], 'latitude' => ['type' => 'number', 'minimum' => -90, 'maximum' => 90], 'longitude' => ['type' => 'number', 'minimum' => -180, 'maximum' => 180], 'radius' => ['type' => 'number', 'minimum' => 0], 'past_events' => ['type' => 'boolean', 'default' => true], 'limit' => ['type' => 'integer', 'minimum' => 1, 'maximum' => 500, 'default' => 100], ]; } /** * @param array $event Event data. * @return array */ private function publicEvent(array $event): array { $event['image'] = get_the_post_thumbnail_url((int) $event['id'], 'large') ?: null; $event['categories'] = wp_get_post_terms((int) $event['id'], 'pcwp_event_category', ['fields' => 'slugs']); $event['tags'] = wp_get_post_terms((int) $event['id'], 'pcwp_event_tag', ['fields' => 'slugs']); $event['series_id'] = absint(get_post_meta((int) $event['id'], '_pcwp_series_id', true)) ?: null; return apply_filters('pcwp_rest_event', $event); } }