diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css
index c7a8bb643a..9e64dc5394 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css
@@ -19,6 +19,18 @@
margin-top: 1em;
}
+.translation-event-form .event-end-fields {
+ display: inline-block;
+ vertical-align: top;
+}
+
+.translation-event-form label.event-end-open-ended-toggle {
+ display: block;
+ width: auto;
+ margin-top: 0.5em;
+ font-weight: normal;
+}
+
.translation-event-form #submit-event {
margin-left: 10%;
width: 30%;
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js
index 5052977ddc..c0fdfee1f6 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js
@@ -8,6 +8,7 @@
selectUserTimezone();
}
validateEventDates();
+ bindOpenEndedToggle();
convertToUserLocalTime();
setInterval( convertToUserLocalTime, 10000 );
@@ -94,7 +95,9 @@
$( '#event-title' ).val( wordcamp.title.rendered );
$( '#event-description' ).val( wordcamp.content.rendered );
$( '#event-start' ).val( new Date( 1000 * wordcamp['Start Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '09:00' );
- $( '#event-end' ).val( new Date( 1000 * wordcamp['End Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '18:00' );
+ if ( ! $( '#event-is-open-ended' ).is( ':checked' ) ) {
+ $( '#event-end' ).val( new Date( 1000 * wordcamp['End Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '18:00' );
+ }
$( '#event-timezone' ).val( wordcamp['Event Timezone'] );
}
@@ -123,13 +126,16 @@
$gp.notices.error( 'Event start date and time must be set.' );
return;
}
- if ( '' === $( '#event-end' ).val() ) {
- $gp.notices.error( 'Event end date and time must be set.' );
- return;
- }
- if ( $( '#event-end' ).val() <= $( '#event-start' ).val() ) {
- $gp.notices.error( 'Event end date and time must be later than event start date and time.' );
- return;
+ const isOpenEnded = $( '#event-is-open-ended' ).is( ':checked' );
+ if ( ! isOpenEnded ) {
+ if ( '' === $( '#event-end' ).val() ) {
+ $gp.notices.error( 'Event end date and time must be set.' );
+ return;
+ }
+ if ( $( '#event-end' ).val() <= $( '#event-start' ).val() ) {
+ $gp.notices.error( 'Event end date and time must be later than event start date and time.' );
+ return;
+ }
}
if ( eventStatus === 'publish' && isDraft ) {
const submitPrompt = 'Are you sure you want to publish this event?';
@@ -197,6 +203,37 @@
);
}
+ function bindOpenEndedToggle() {
+ const $checkbox = $( '#event-is-open-ended' );
+ const $endInput = $( '#event-end' );
+ if ( ! $checkbox.length || ! $endInput.length ) {
+ return;
+ }
+
+ $checkbox.on(
+ 'change',
+ function () {
+ if ( $checkbox.is( ':checked' ) ) {
+ $endInput.prop( 'disabled', true ).val( '' );
+ return;
+ }
+ $endInput.prop( 'disabled', false );
+ if ( '' === $endInput.val() ) {
+ const startVal = $( '#event-start' ).val();
+ if ( startVal ) {
+ const start = new Date( startVal );
+ start.setHours( start.getHours() + 1 );
+ const pad = ( n ) => String( n ).padStart( 2, '0' );
+ $endInput.val(
+ start.getFullYear() + '-' + pad( start.getMonth() + 1 ) + '-' + pad( start.getDate() )
+ + 'T' + pad( start.getHours() ) + ':' + pad( start.getMinutes() )
+ );
+ }
+ }
+ }
+ );
+ }
+
function validateEventDates() {
const startDateTimeInput = $( '#event-start' );
const endDateTimeInput = $( '#event-end' );
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-adder.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-adder.php
index f510118ab3..925fe8e57c 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-adder.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-adder.php
@@ -5,6 +5,7 @@
use Exception;
use Wporg\TranslationEvents\Event\Event;
use Wporg\TranslationEvents\Stats\Stats_Listener;
+use Wporg\TranslationEvents\Translation_Events;
class Attendee_Adder {
private Attendee_Repository $attendee_repository;
@@ -37,6 +38,20 @@ public function add_to_event( Event $event, Attendee $attendee ): void {
private function import_stats( Event $event, Attendee $attendee ): void {
global $wpdb, $gp_table_prefix;
+
+ $now = Translation_Events::now();
+ $end = $event->end() ? $event->end()->utc() : $now;
+ $start = $event->start()->utc();
+
+ // Open-ended events can span arbitrary durations. Cap the lookback to avoid
+ // a full table scan over `translations` when a new attendee joins.
+ if ( null === $event->end() ) {
+ $one_year_ago = $now->modify( '-1 year' );
+ if ( $start < $one_year_ago ) {
+ $start = $one_year_ago;
+ }
+ }
+
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
@@ -57,8 +72,8 @@ private function import_stats( Event $event, Attendee $attendee ): void {
'event_id' => $event->id(),
'action' => Stats_Listener::ACTION_CREATE,
'user_id' => $attendee->user_id(),
- 'date_added_after' => $event->start()->utc()->format( 'Y-m-d H:i:s' ),
- 'date_added_before' => $event->end()->utc()->format( 'Y-m-d H:i:s' ),
+ 'date_added_after' => $start->format( 'Y-m-d H:i:s' ),
+ 'date_added_before' => $end->format( 'Y-m-d H:i:s' ),
),
),
);
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php
index a4abcaea50..ce395ef0b1 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php
@@ -266,8 +266,6 @@ private function has_edit_attendees( WP_User $user, Event $event ): bool {
* @return bool
*/
private function has_edit_field( WP_User $user, Event $event, $cap ): bool {
- $event_end_plus_1_hr = $event->end()->modify( '+1 hour' );
-
if ( self::EDIT_DESCRIPTION === $cap ) {
return true;
}
@@ -284,10 +282,11 @@ private function has_edit_field( WP_User $user, Event $event, $cap ): bool {
return ( self::EDIT_TITLE === $cap || self::EDIT_END === $cap );
}
- if ( $event->end()->is_in_the_past() && $this->now < $event_end_plus_1_hr ) {
- return ( self::EDIT_TITLE === $cap || self::EDIT_END === $cap );
- }
- if ( $event->end()->is_in_the_past() && $this->now > $event_end_plus_1_hr ) {
+ if ( $event->is_past() ) {
+ $event_end_plus_1_hr = $event->end()->modify( '+1 hour' );
+ if ( $this->now < $event_end_plus_1_hr ) {
+ return ( self::EDIT_TITLE === $cap || self::EDIT_END === $cap );
+ }
return ( self::EDIT_DESCRIPTION === $cap );
}
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php
index 56580801e8..a6ac2ede99 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php
@@ -201,10 +201,11 @@ private function parse_form_data( array $data ): Event {
// This will be sanitized by sanitize_post which is called in wp_insert_post.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$description = isset( $data['event_description'] ) ? force_balance_tags( wp_unslash( $data['event_description'] ) ) : '';
- $event_start = isset( $data['event_start'] ) ? sanitize_text_field( wp_unslash( $data['event_start'] ) ) : '';
- $event_end = isset( $data['event_end'] ) ? sanitize_text_field( wp_unslash( $data['event_end'] ) ) : '';
- $event_timezone = isset( $data['event_timezone'] ) ? sanitize_text_field( wp_unslash( $data['event_timezone'] ) ) : '';
- $attendance_mode = isset( $data['event_attendance_mode'] ) ? sanitize_text_field( wp_unslash( $data['event_attendance_mode'] ) ) : 'onsite';
+ $event_start = isset( $data['event_start'] ) ? sanitize_text_field( wp_unslash( $data['event_start'] ) ) : '';
+ $event_end = isset( $data['event_end'] ) ? sanitize_text_field( wp_unslash( $data['event_end'] ) ) : '';
+ $event_is_open_ended = ! empty( $data['event_is_open_ended'] );
+ $event_timezone = isset( $data['event_timezone'] ) ? sanitize_text_field( wp_unslash( $data['event_timezone'] ) ) : '';
+ $attendance_mode = isset( $data['event_attendance_mode'] ) ? sanitize_text_field( wp_unslash( $data['event_attendance_mode'] ) ) : 'onsite';
$event_status = '';
if ( isset( $data['event_form_action'] ) && in_array( $data['event_form_action'], array( 'draft', 'publish', 'trash' ), true ) ) {
@@ -223,16 +224,21 @@ private function parse_form_data( array $data ): Event {
throw new InvalidStart();
}
- try {
- $end = new Event_End_Date( $event_end, $timezone );
- } catch ( Exception $e ) {
- throw new InvalidEnd();
+ if ( $event_is_open_ended ) {
+ $end = null;
+ } else {
+ try {
+ $end = new Event_End_Date( $event_end, $timezone );
+ } catch ( Exception $e ) {
+ throw new InvalidEnd();
+ }
+ $end = $end->setTimezone( new DateTimeZone( 'UTC' ) );
}
$event = new Event(
get_current_user_id(),
$start->setTimezone( new DateTimeZone( 'UTC' ) ),
- $end->setTimezone( new DateTimeZone( 'UTC' ) ),
+ $end,
$timezone,
$event_status,
$title,
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php
index 594f907a4f..8032dd29ea 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php
@@ -61,7 +61,10 @@ public function get_current_events( int $page = -1, int $page_size = -1 ): Event
array_filter(
$events,
function ( $event ) {
- return $event->start() <= $this->now && $this->now <= $event->end();
+ if ( $event->start() > $this->now ) {
+ return false;
+ }
+ return null === $event->end() || $this->now <= $event->end();
}
)
);
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php
index 183220da1c..1bc702a577 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php
@@ -227,12 +227,7 @@ public function get_current_and_upcoming_events( int $page = - 1, int $page_size
$page_size,
array(
'meta_query' => array(
- array(
- 'key' => '_event_end',
- 'value' => $this->now->format( 'Y-m-d H:i:s' ),
- 'compare' => '>',
- 'type' => 'DATETIME',
- ),
+ $this->meta_query_end_after_or_unbounded( $this->now ),
),
'meta_key' => '_event_start',
'orderby' => array(
@@ -290,12 +285,7 @@ public function get_current_events_for_user( int $user_id, int $page = -1, int $
'compare' => '<=',
'type' => 'DATETIME',
),
- array(
- 'key' => '_event_end',
- 'value' => $this->now->format( 'Y-m-d H:i:s' ),
- 'compare' => '>=',
- 'type' => 'DATETIME',
- ),
+ $this->meta_query_end_after_or_unbounded( $this->now, '>=' ),
),
'meta_key' => '_event_start',
'orderby' => array(
@@ -318,12 +308,7 @@ public function get_current_and_upcoming_events_for_user( int $user_id, int $pag
$page_size,
array(
'meta_query' => array(
- array(
- 'key' => '_event_end',
- 'value' => $this->now->format( 'Y-m-d H:i:s' ),
- 'compare' => '>',
- 'type' => 'DATETIME',
- ),
+ $this->meta_query_end_after_or_unbounded( $this->now ),
),
'meta_key' => '_event_start',
'orderby' => array(
@@ -454,12 +439,7 @@ protected function get_events_active_between(
'compare' => '<=',
'type' => 'DATETIME',
),
- array(
- 'key' => '_event_end',
- 'value' => $boundary_start->format( 'Y-m-d H:i:s' ),
- 'compare' => '>',
- 'type' => 'DATETIME',
- ),
+ $this->meta_query_end_after_or_unbounded( $boundary_start ),
),
'meta_key' => '_event_start',
'meta_type' => 'DATETIME',
@@ -604,18 +584,43 @@ private function get_event_meta( int $event_id ): ?array {
$meta = get_post_meta( $event_id );
$utc = new DateTimeZone( 'UTC' );
- if ( ! isset( $meta['_event_start'][0], $meta['_event_end'][0], $meta['_event_timezone'][0] ) ) {
+ if ( ! isset( $meta['_event_start'][0], $meta['_event_timezone'][0] ) ) {
return null;
}
+ $end = null;
+ if ( isset( $meta['_event_end'][0] ) && '' !== $meta['_event_end'][0] ) {
+ $end = new Event_End_Date( $meta['_event_end'][0], $utc );
+ }
+
return array(
'start' => new Event_Start_Date( $meta['_event_start'][0], $utc ),
- 'end' => new Event_End_Date( $meta['_event_end'][0], $utc ),
+ 'end' => $end,
'timezone' => new DateTimeZone( $meta['_event_timezone'][0] ),
'attendance_mode' => ! isset( $meta['_event_attendance_mode'][0] ) ? 'onsite' : $meta['_event_attendance_mode'][0],
);
}
+ /**
+ * Returns a meta_query fragment that matches events whose _event_end is after
+ * the given boundary, OR which have no _event_end meta (open-ended events).
+ */
+ private function meta_query_end_after_or_unbounded( DateTimeImmutable $boundary, string $compare = '>' ): array {
+ return array(
+ 'relation' => 'OR',
+ array(
+ 'key' => '_event_end',
+ 'value' => $boundary->format( 'Y-m-d H:i:s' ),
+ 'compare' => $compare,
+ 'type' => 'DATETIME',
+ ),
+ array(
+ 'key' => '_event_end',
+ 'compare' => 'NOT EXISTS',
+ ),
+ );
+ }
+
private function update_event_meta( Event $event ) {
$hosts = $this->attendee_repository->get_hosts( $event->id() );
$hosts_ids = array_map(
@@ -626,7 +631,11 @@ function ( $host ) {
);
$hosts_ids = implode( ', ', $hosts_ids );
update_post_meta( $event->id(), '_event_start', $event->start()->utc()->format( 'Y-m-d H:i:s' ) );
- update_post_meta( $event->id(), '_event_end', $event->end()->utc()->format( 'Y-m-d H:i:s' ) );
+ if ( null === $event->end() ) {
+ delete_post_meta( $event->id(), '_event_end' );
+ } else {
+ update_post_meta( $event->id(), '_event_end', $event->end()->utc()->format( 'Y-m-d H:i:s' ) );
+ }
update_post_meta( $event->id(), '_event_timezone', $event->timezone()->getName() );
update_post_meta( $event->id(), '_hosts', $hosts_ids );
update_post_meta( $event->id(), '_event_attendance_mode', $event->attendance_mode() );
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php
index c013ccf18b..1759dcfda3 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php
@@ -36,7 +36,7 @@ class Event {
private int $id = 0;
private int $author_id;
private Event_Start_Date $start;
- private Event_End_Date $end;
+ private ?Event_End_Date $end;
private DateTimeZone $timezone;
private string $slug = '';
private string $status;
@@ -53,7 +53,7 @@ class Event {
public function __construct(
int $author_id,
Event_Start_Date $start,
- Event_End_Date $end,
+ ?Event_End_Date $end,
DateTimeZone $timezone,
string $status,
string $title,
@@ -85,10 +85,14 @@ public function start(): Event_Start_Date {
return $this->start;
}
- public function end(): Event_End_Date {
+ public function end(): ?Event_End_Date {
return $this->end;
}
+ public function is_open_ended(): bool {
+ return null === $this->end;
+ }
+
public function is_published(): bool {
return 'publish' === $this->status;
}
@@ -103,11 +107,14 @@ public function is_trashed(): bool {
public function is_active(): bool {
$now = Translation_Events::now();
- return $now >= $this->start->utc() && $now < $this->end->utc();
+ if ( $now < $this->start->utc() ) {
+ return false;
+ }
+ return null === $this->end || $now < $this->end->utc();
}
public function is_past(): bool {
- return $this->end->is_in_the_past();
+ return null !== $this->end && $this->end->is_in_the_past();
}
public function is_remote(): bool {
@@ -154,7 +161,7 @@ public function set_start( Event_Start_Date $start ): void {
$this->start = $start;
}
- public function set_end( Event_End_Date $end ): void {
+ public function set_end( ?Event_End_Date $end ): void {
$this->end = $end;
}
@@ -196,13 +203,16 @@ public function set_attendance_mode( string $attendance_mode ): void {
* @throws InvalidStart
* @throws InvalidEnd
*/
- public function validate_times( Event_Start_Date $start, Event_End_Date $end ) {
- if ( $end <= $start ) {
- throw new InvalidEnd();
- }
+ public function validate_times( Event_Start_Date $start, ?Event_End_Date $end ) {
if ( ! $start->getTimezone() || 'UTC' !== $start->getTimezone()->getName() ) {
throw new InvalidStart();
}
+ if ( null === $end ) {
+ return;
+ }
+ if ( $end <= $start ) {
+ throw new InvalidEnd();
+ }
if ( ! $end->getTimezone() || 'UTC' !== $end->getTimezone()->getName() ) {
throw new InvalidEnd();
}
diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/rss.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/rss.php
index cdd78fb914..cdc6989f3d 100644
--- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/rss.php
+++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/rss.php
@@ -82,7 +82,9 @@ private function get_item( Event $event ) {
$item .= '
- end()->is_in_the_past() ) : ?> + is_past() ) : ?> diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/parts/event-form.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/parts/event-form.php index 8032eb650b..7dfc2a567d 100644 --- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/parts/event-form.php +++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/parts/event-form.php @@ -52,7 +52,22 @@
' . esc_html__( 'Ongoing', 'gp-translation-events' ) . '
'; + } $end = $event->end()->format( 'F j, Y' ); return '' . esc_html( $end ) . '
'; }, diff --git a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php index 36077bca0f..5f99329d32 100644 --- a/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php +++ b/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php @@ -211,8 +211,8 @@ public function event_dates_meta_box( WP_Post $post ) { ?>