Changeset 13683
- Timestamp:
- 05/09/2024 08:33:51 AM (21 months ago)
- Location:
- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events
- Files:
-
- 20 added
- 24 edited
-
assets/css/translation-events.css (modified) (8 diffs)
-
assets/js/translation-events.js (modified) (5 diffs)
-
autoload.php (modified) (2 diffs)
-
includes/attendee/attendee-repository.php (modified) (4 diffs)
-
includes/attendee/attendee.php (modified) (4 diffs)
-
includes/event/event-capabilities.php (added)
-
includes/event/event-form-handler.php (modified) (8 diffs)
-
includes/event/event-repository-cached.php (modified) (1 diff)
-
includes/event/event-repository-interface.php (modified) (2 diffs)
-
includes/event/event-repository.php (modified) (7 diffs)
-
includes/event/event.php (modified) (2 diffs)
-
includes/notifications (added)
-
includes/notifications/notifications-schedule.php (added)
-
includes/notifications/notifications-send.php (added)
-
includes/project (added)
-
includes/project/project-repository.php (added)
-
includes/routes/attendee (added)
-
includes/routes/attendee/list.php (added)
-
includes/routes/event/create.php (modified) (1 diff)
-
includes/routes/event/delete.php (added)
-
includes/routes/event/details.php (modified) (5 diffs)
-
includes/routes/event/edit.php (modified) (3 diffs)
-
includes/routes/event/list-trashed.php (added)
-
includes/routes/event/translations.php (added)
-
includes/routes/event/trash.php (added)
-
includes/routes/user/attend-event.php (modified) (3 diffs)
-
includes/routes/user/host-event.php (modified) (3 diffs)
-
includes/stats/stats-calculator.php (modified) (5 diffs)
-
includes/translation (added)
-
includes/translation/translation-repository.php (added)
-
includes/upgrade.php (modified) (3 diffs)
-
includes/urls.php (added)
-
templates/event-translations-footer.php (added)
-
templates/event-translations-header.php (added)
-
templates/event.php (modified) (11 diffs)
-
templates/events-attendees.php (added)
-
templates/events-form.php (modified) (5 diffs)
-
templates/events-header.php (modified) (2 diffs)
-
templates/events-list-trashed.php (added)
-
templates/events-list.php (modified) (5 diffs)
-
templates/events-my-events.php (modified) (3 diffs)
-
templates/helper-functions.php (modified) (2 diffs)
-
templates/translations.php (added)
-
wporg-gp-translation-events.php (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css
r13541 r13683 7 7 .translation-event-form #event-title, 8 8 .translation-event-form #event-description { 9 width: 30%; 9 width: auto; 10 resize: both; 10 11 } 11 12 … … 54 55 margin: 1rem; 55 56 } 57 56 58 .event-details-stats table { 57 59 width: 100%; … … 84 86 } 85 87 86 .event-contributors li, .event-attendees li { 88 .event-contributors li, 89 .event-attendees li { 87 90 display: inline-block; 88 91 list-style-type: none; … … 92 95 } 93 96 94 .event-contributors li .avatar, .event-attendees li .avatar { 97 .event-contributors li .avatar, 98 .event-attendees li .avatar, 99 td a.attendee-avatar .avatar { 95 100 border-radius: 50%; 96 101 vertical-align: middle; … … 107 112 font-size: smaller; 108 113 } 114 109 115 .hide-event-url { 110 116 display: none; … … 247 253 } 248 254 249 input[type="submit"].remove-as-host, input[type="submit"].convert-to-host { 255 input[type="submit"].remove-as-host, 256 input[type="submit"].convert-to-host { 250 257 text-align: center; 251 258 display: inline; … … 280 287 } 281 288 282 .event-page-edit-link {289 .event-page-edit-link, .event-page-attendees-link { 283 290 float: right; 284 291 text-decoration: none; … … 349 356 text-decoration: none; 350 357 } 358 351 359 ul.text-snippets { 352 360 padding: 0; 353 361 margin-left: 160px; 354 362 } 363 355 364 .first-time-contributor-tada::after { 356 365 content: ' 🎉'; 366 } 367 368 ul#translation-links li { 369 margin-bottom: .5em; 370 } 371 372 .icons li .name { 373 display: none; 374 } 375 376 .icons li { 377 width: auto !important; 378 } 379 380 .event-attendees h2, 381 .event-contributors h2 { 382 cursor: pointer; 383 display: inline-block; 384 } 385 386 ul.event-attendees-filter { 387 padding-left: 1rem; 388 } 389 390 ul.event-attendees-filter li { 391 display: inline; 392 margin-right: .4em; 393 } 394 395 form.translation-event-form { 396 float: left; 397 width: 85%; 398 } 399 400 div.event-edit-right { 401 float: right; 402 width: 15%; 403 text-align: right; 404 } 405 406 .view-event-page { 407 float: right; 408 } 409 410 a.active-filter { 411 font-weight: bold; 412 } 413 414 .event-edit-right a.manage-attendees-btn { 415 margin-top: 1em; 416 } 417 418 #quick-add.loading::after { 419 content: 'Loading...'; 420 } 421 422 #quick-add summary { 423 cursor: pointer; 424 } 425 426 #quick-add { 427 padding: 0 .5em; 428 border-left: #d9d8d8 thin solid; 429 border-bottom: #d9d8d8 thin solid; 430 max-height: 8em; 431 overflow: auto; 357 432 } 358 433 -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js
r13529 r13683 22 22 ); 23 23 24 $( '. delete-event' ).on(24 $( '.trash-event' ).on( 25 25 'click', 26 26 function ( e ) { 27 27 e.preventDefault(); 28 handle Delete()28 handleTrash() 29 29 } 30 30 ); … … 39 39 } 40 40 ); 41 42 $( '.event-attendees h2, .event-contributors h2' ).on( 43 'click', 44 function ( e ) { 45 e.preventDefault(); 46 $( this ).closest( 'body' ).toggleClass( 'icons' ); 47 $( '.convert-to-host, .remove-as-host' ).toggle(); 48 } 49 ); 50 51 $( '#quick-add' ).on( 52 'toggle', 53 function () { 54 if ( $( this ).data( 'loaded' ) ) { 55 return; 56 } 57 $( this ).addClass( 'loading' ); 58 const options = { 59 weekday: 'short', 60 day: 'numeric', 61 month: 'short', 62 year: 'numeric' 63 }; 64 65 fetch( 'https://central.wordcamp.org/wp-json/wp/v2/wordcamps?per_page=30&status=wcpt-scheduled' ).then( 66 response => response.json() 67 ).then( 68 function ( data ) { 69 data.sort( ( a, b ) => a['Start Date (YYYY-mm-dd)'] - b['Start Date (YYYY-mm-dd)'] ); 70 const ul = $( '<ul>' ); 71 for ( const wordcamp of data ) { 72 const li = $( '<li>' ).data( 'wordcamp', wordcamp ); 73 li.append( $( '<a>' ).attr( 'href', wordcamp.link ).text( wordcamp.title.rendered ) ); 74 li.append( $( '<span>' ).text( ' ' + new Date( 1000 * wordcamp['Start Date (YYYY-mm-dd)'] ).toLocaleDateString( navigator.language, options ) + ' - ' + new Date( 1000 * wordcamp['End Date (YYYY-mm-dd)'] ).toLocaleDateString( navigator.language, options ) ) ); 75 ul.append( li ); 76 } 77 $( '#quick-add' ).data( 'loaded', true ).removeClass( 'loading' ).append( ul ); 78 } 79 ); 80 } 81 ); 82 $( document ).on( 83 'click', 84 '#quick-add a', 85 function ( e ) { 86 e.preventDefault(); 87 e.stopPropagation(); 88 89 const wordcamp = $( e.target ).closest( 'li' ).data( 'wordcamp' ); 90 if ( ! wordcamp ) { 91 return; 92 } 93 94 $( '#event-title' ).val( wordcamp.title.rendered ); 95 $( '#event-description' ).val( wordcamp.content.rendered ); 96 $( '#event-start' ).val( new Date( 1000 * wordcamp['Start Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '09:00' ); 97 $( '#event-end' ).val( new Date( 1000 * wordcamp['End Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '18:00' ); 98 $( '#event-timezone' ).val( wordcamp['Event Timezone'] ); 99 100 } 101 ); 102 41 103 } 42 104 ); … … 100 162 $( '#event-url' ).removeClass( 'hide-event-url' ).find( 'a' ).attr( 'href', response.data.eventUrl ).text( response.data.eventUrl ); 101 163 if ( $is_creation ) { 102 $( '# delete-button' ).toggle();164 $( '#trash-button' ).toggle(); 103 165 } 104 166 $gp.notices.success( response.data.message ); … … 114 176 } 115 177 116 function handle Delete() {178 function handleTrash() { 117 179 if ( ! confirm( 'Are you sure you want to delete this event?' ) ) { 118 180 return; 119 181 } 120 182 const $form = $( '.translation-event-form' ); 121 $( '#form-name' ).val( ' delete_event' );122 $( '#event-form-action' ).val( ' delete' );183 $( '#form-name' ).val( 'trash_event' ); 184 $( '#event-form-action' ).val( 'trash' ); 123 185 $.ajax( 124 186 { … … 127 189 data:$form.serialize(), 128 190 success: function ( response ) { 129 window.location = response.data.event DeleteUrl;191 window.location = response.data.eventTrashUrl; 130 192 }, 131 193 error: function ( error ) { -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/autoload.php
r13541 r13683 2 2 3 3 require_once __DIR__ . '/includes/upgrade.php'; 4 require_once __DIR__ . '/includes/urls.php'; 4 5 require_once __DIR__ . '/templates/helper-functions.php'; 5 6 require_once __DIR__ . '/includes/routes/route.php'; 7 require_once __DIR__ . '/includes/routes/attendee/list.php'; 6 8 require_once __DIR__ . '/includes/routes/event/create.php'; 7 9 require_once __DIR__ . '/includes/routes/event/details.php'; 8 10 require_once __DIR__ . '/includes/routes/event/edit.php'; 11 require_once __DIR__ . '/includes/routes/event/trash.php'; 12 require_once __DIR__ . '/includes/routes/event/delete.php'; 9 13 require_once __DIR__ . '/includes/routes/event/list.php'; 14 require_once __DIR__ . '/includes/routes/event/list-trashed.php'; 15 require_once __DIR__ . '/includes/routes/event/translations.php'; 10 16 require_once __DIR__ . '/includes/routes/user/attend-event.php'; 11 17 require_once __DIR__ . '/includes/routes/user/host-event.php'; … … 19 25 require_once __DIR__ . '/includes/event/event-repository-cached.php'; 20 26 require_once __DIR__ . '/includes/event/event-form-handler.php'; 27 require_once __DIR__ . '/includes/notifications/notifications-schedule.php'; 28 require_once __DIR__ . '/includes/notifications/notifications-send.php'; 29 require_once __DIR__ . '/includes/event/event-capabilities.php'; 21 30 require_once __DIR__ . '/includes/stats/stats-calculator.php'; 22 31 require_once __DIR__ . '/includes/stats/stats-importer.php'; 23 32 require_once __DIR__ . '/includes/stats/stats-listener.php'; 33 require_once __DIR__ . '/includes/project/project-repository.php'; 34 require_once __DIR__ . '/includes/translation/translation-repository.php'; 24 35 require_once __DIR__ . '/includes/event-text-snippet.php'; -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-repository.php
r13541 r13683 4 4 5 5 use Exception; 6 use WP_User;7 6 8 7 class Attendee_Repository { … … 83 82 $wpdb->prepare( 84 83 " 85 select * 86 from {$gp_table_prefix}event_attendees 84 select 85 user_id, 86 is_host, 87 ( 88 select group_concat( distinct locale ) 89 from {$gp_table_prefix}event_actions 90 where event_id = attendees.event_id 91 and user_id = attendees.user_id 92 ) as locales 93 from {$gp_table_prefix}event_attendees attendees 87 94 where event_id = %d 88 95 and user_id = %d … … 100 107 } 101 108 102 $attendee = new Attendee( $row->event_id, $row->user_id ); 103 if ( '1' === $row->is_host ) { 104 $attendee->mark_as_host(); 105 } 106 107 return $attendee; 108 } 109 110 /** 111 * @return Attendee[] Attendees of the event. 112 */ 113 public function get_attendees( int $event_id ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found 114 // TODO. 115 return array(); 116 } 117 118 /** 119 * Get the hosts' users for an event. 120 * 121 * @param int $event_id The id of the event. 122 * 123 * @return Attendee[] The hosts of the event. 124 * @throws Exception 125 */ 126 public function get_hosts( int $event_id ): array { 109 return new Attendee( 110 $event_id, 111 $row->user_id, 112 '1' === $row->is_host, 113 null === $row->locales ? array() : explode( ',', $row->locales ), 114 ); 115 } 116 117 /** 118 * Retrieve all the attendees of an event. 119 * 120 * @return Attendee[] Attendees of the event. Associative array with user_id as key. 121 * @throws Exception 122 */ 123 public function get_attendees( int $event_id ): array { 127 124 global $wpdb, $gp_table_prefix; 128 125 … … 133 130 $wpdb->prepare( 134 131 " 135 select event_id, user_id 136 from {$gp_table_prefix}event_attendees 137 where event_id = %d and is_host = 1 132 select 133 user_id, 134 is_host, 135 ( 136 select group_concat( distinct locale ) 137 from {$gp_table_prefix}event_actions 138 where event_id = attendees.event_id 139 and user_id = attendees.user_id 140 ) as locales 141 from {$gp_table_prefix}event_attendees attendees 142 where event_id = %d 138 143 ", 139 144 array( 140 145 $event_id, 141 146 ) 142 ) 143 ); 144 // phpcs:enable 145 146 $hosts = array(); 147 foreach ( $rows as $row ) { 148 $host = new Attendee( $row->event_id, $row->user_id ); 149 $host->mark_as_host(); 150 $hosts[] = $host; 151 } 152 return $hosts; 147 ), 148 OBJECT_K, 149 ); 150 // phpcs:enable 151 152 return array_map( 153 function ( $row ) use ( $event_id ) { 154 return new Attendee( 155 $event_id, 156 $row->user_id, 157 '1' === $row->is_host, 158 null === $row->locales ? array() : explode( ',', $row->locales ), 159 ); 160 }, 161 $rows, 162 ); 163 } 164 165 /** 166 * Get attendees without contributions for an event. 167 * 168 * @param int $event_id The id of the event. 169 * 170 * @return Attendee[] Associative array with user_id as key. 171 * @throws Exception 172 */ 173 public function get_attendees_not_contributing( int $event_id ): array { 174 return array_filter( 175 $this->get_attendees( $event_id ), 176 function ( Attendee $attendee ) { 177 return ! $attendee->is_contributor(); 178 } 179 ); 180 } 181 182 /** 183 * Get the hosts' users for an event. 184 * 185 * @param int $event_id The id of the event. 186 * 187 * @return Attendee[] The hosts of the event. Associative array with user_id as key. 188 * @throws Exception 189 */ 190 public function get_hosts( int $event_id ): array { 191 return array_filter( 192 $this->get_attendees( $event_id ), 193 function ( Attendee $attendee ) { 194 return $attendee->is_host(); 195 } 196 ); 153 197 } 154 198 -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee.php
r13529 r13683 11 11 12 12 /** 13 * @var string[] 14 */ 15 private array $contributed_locales; 16 17 /** 13 18 * @throws Exception 14 19 */ 15 public function __construct( int $event_id, int $user_id ) {20 public function __construct( int $event_id, int $user_id, bool $is_host = false, array $contributed_locales = array() ) { 16 21 if ( $event_id < 1 ) { 17 22 throw new Exception( 'invalid event id' ); … … 21 26 } 22 27 23 $this->event_id = $event_id; 24 $this->user_id = $user_id; 25 $this->is_host = false; 28 $this->event_id = $event_id; 29 $this->user_id = $user_id; 30 $this->is_host = $is_host; 31 $this->contributed_locales = $contributed_locales; 26 32 } 27 33 … … 38 44 } 39 45 46 public function is_contributor(): bool { 47 return ! empty( $this->contributed_locales ); 48 } 49 40 50 public function mark_as_host(): void { 41 51 $this->is_host = true; … … 45 55 $this->is_host = false; 46 56 } 57 58 /** 59 * @return string[] 60 */ 61 public function contributed_locales(): array { 62 return $this->contributed_locales; 63 } 47 64 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php
r13541 r13683 6 6 use DateTimeZone; 7 7 use Exception; 8 use GP;9 8 use WP_Error; 10 use Wporg\TranslationEvents\Attendee\Attendee; 11 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 9 use Wporg\TranslationEvents\Notifications\Notifications_Schedule; 12 10 use Wporg\TranslationEvents\Stats\Stats_Calculator; 11 use Wporg\TranslationEvents\Urls; 13 12 14 13 class Event_Form_Handler { 15 14 private Event_Repository_Interface $event_repository; 16 private Attendee_Repository $attendee_repository;17 18 public function __construct( Event_Repository_Interface $event_repository , Attendee_Repository $attendee_repository) {19 $this->event_repository = $event_repository;20 $this-> attendee_repository = $attendee_repository;15 private Notifications_Schedule $notifications_schedule; 16 17 public function __construct( Event_Repository_Interface $event_repository ) { 18 $this->event_repository = $event_repository; 19 $this->notifications_schedule = new Notifications_Schedule( $this->event_repository ); 21 20 } 22 21 … … 25 24 wp_send_json_error( esc_html__( 'The user must be logged in.', 'gp-translation-events' ), 403 ); 26 25 } 27 $action = isset( $form_data['form_name'] ) ? sanitize_text_field( wp_unslash( $form_data['form_name'] ) ) : ''; 28 $response_message = ''; 29 $is_nonce_valid = false; 30 $nonce_name = '_event_nonce'; 31 if ( ! in_array( $action, array( 'create_event', 'edit_event', 'delete_event' ), true ) ) { 26 27 $action = isset( $form_data['form_name'] ) ? sanitize_text_field( wp_unslash( $form_data['form_name'] ) ) : ''; 28 if ( ! in_array( $action, array( 'create_event', 'edit_event', 'trash_event' ), true ) ) { 32 29 wp_send_json_error( esc_html__( 'Invalid form name.', 'gp-translation-events' ), 403 ); 33 30 } 34 /** 35 * Filter the ability to create, edit, or delete an event. 36 * 37 * @param bool $can_crud_event Whether the user can create, edit, or delete an event. 38 */ 39 $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) ); 40 if ( 'create_event' === $action && ( ! $can_crud_event ) ) { 41 wp_send_json_error( esc_html__( 'The user does not have permission to create an event.', 'gp-translation-events' ), 403 ); 42 } 43 if ( 'edit_event' === $action ) { 44 $event_id = isset( $form_data['event_id'] ) ? sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) : ''; 45 $event = $this->event_repository->get_event( $event_id ); 46 $attendee = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() ); 47 if ( ! ( $can_crud_event || ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event_id ) || $event->author_id() === get_current_user_id() ) ) { 48 wp_send_json_error( esc_html__( 'The user does not have permission to edit or delete the event.', 'gp-translation-events' ), 403 ); 49 } 50 } 51 if ( 'delete_event' === $action ) { 52 $event_id = isset( $form_data['event_id'] ) ? sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) : ''; 53 $event = $this->event_repository->get_event( $event_id ); 54 $attendee = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() ); 55 $stats_calculator = new Stats_Calculator(); 56 if ( $stats_calculator->event_has_stats( $event->id() ) ) { 57 wp_send_json_error( esc_html__( 'The event has stats so it cannot be deleted.', 'gp-translation-events' ), 422 ); 58 } 59 if ( ! ( $can_crud_event || ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'delete_post', $event_id ) || get_current_user_id() === $event->author_id() ) ) { 60 wp_send_json_error( esc_html__( 'You do not have permission to delete this event.', 'gp-translation-events' ), 403 ); 61 } 62 } 31 32 $event_id = isset( $form_data['event_id'] ) ? sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) : 0; 33 34 if ( 'create_event' === $action && ( ! current_user_can( 'create_translation_event' ) ) ) { 35 wp_send_json_error( esc_html__( 'You do not have permissions to create events.', 'gp-translation-events' ), 403 ); 36 } 37 if ( 'edit_event' === $action && ( ! current_user_can( 'edit_translation_event', $event_id ) ) ) { 38 wp_send_json_error( esc_html__( 'You do not have permissions to edit this event.', 'gp-translation-events' ), 403 ); 39 } 40 if ( 'trash_event' === $action && ( ! current_user_can( 'trash_translation_event', $event_id ) ) ) { 41 wp_send_json_error( esc_html__( 'You do not have permissions to delete this event.', 'gp-translation-events' ), 403 ); 42 } 43 44 $is_nonce_valid = false; 45 $nonce_name = '_event_nonce'; 63 46 if ( isset( $form_data[ $nonce_name ] ) ) { 64 47 $nonce_value = sanitize_text_field( wp_unslash( $form_data[ $nonce_name ] ) ); … … 71 54 } 72 55 73 if ( 'delete_event' === $action ) { 74 // Delete event. 56 $response_message = ''; 57 if ( 'trash_event' === $action ) { 58 // Trash event. 75 59 $event_id = intval( sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) ); 76 60 $event = $this->event_repository->get_event( $event_id ); … … 89 73 } 90 74 91 if ( false === $this->event_repository-> delete_event( $event ) ) {75 if ( false === $this->event_repository->trash_event( $event ) ) { 92 76 $response_message = esc_html__( 'Failed to delete event.', 'gp-translation-events' ); 93 77 $event_status = $event->status(); 94 78 } else { 95 79 $response_message = esc_html__( 'Event deleted successfully.', 'gp-translation-events' ); 96 $event_status = 'deleted'; 80 $event_status = 'trashed'; 81 $this->notifications_schedule->delete_scheduled_emails( $event_id ); 97 82 } 98 83 } else { … … 135 120 } 136 121 $response_message = esc_html__( 'Event created successfully.', 'gp-translation-events' ); 122 $this->notifications_schedule->schedule_emails( $result ); 137 123 } 138 124 if ( 'edit_event' === $action ) { … … 159 145 } 160 146 $response_message = esc_html__( 'Event updated successfully', 'gp-translation-events' ); 147 $this->notifications_schedule->schedule_emails( $result ); 161 148 } 162 149 … … 165 152 } 166 153 167 list( $permalink, $post_name ) = get_sample_permalink( $event_id );168 $permalink = str_replace( '%pagename%', $post_name, $permalink );169 154 wp_send_json_success( 170 155 array( 171 'message' => $response_message,172 'eventId' => $event_id,173 'event Url' => str_replace( '%pagename%', $post_name, $permalink ),174 'event Status' => $event_status,175 'eventEditUrl' => esc_url( gp_url( '/events/edit/' . $event_id )),176 'event DeleteUrl' => esc_url( gp_url( '/events/my-events/' ) ),156 'message' => $response_message, 157 'eventId' => $event_id, 158 'eventStatus' => $event_status, 159 'eventUrl' => Urls::event_details_absolute( $event_id ), 160 'eventEditUrl' => Urls::event_edit( $event_id ), 161 'eventTrashUrl' => Urls::my_events(), // The URL the user is redirected to after trashing. 177 162 ) 178 163 ); … … 201 186 202 187 $event_status = ''; 203 if ( isset( $data['event_form_action'] ) && in_array( $data['event_form_action'], array( 'draft', 'publish', ' delete' ), true ) ) {188 if ( isset( $data['event_form_action'] ) && in_array( $data['event_form_action'], array( 'draft', 'publish', 'trash' ), true ) ) { 204 189 $event_status = sanitize_text_field( wp_unslash( $data['event_form_action'] ) ); 205 190 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php
r13529 r13683 30 30 $this->invalidate_cache(); 31 31 return $event_id_or_error; 32 } 33 34 public function trash_event( Event $event ) { 35 parent::trash_event( $event ); 36 $this->invalidate_cache(); 32 37 } 33 38 -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-interface.php
r13529 r13683 26 26 27 27 /** 28 * Deletean Event.28 * Trash an Event. 29 29 * 30 * @param Event $event Event to delete. 30 * @param Event $event Event to trash. 31 * 32 * @return Event|false Trashed event or false on error. 33 */ 34 public function trash_event( Event $event ); 35 36 /** 37 * Permanently delete an Event. 38 * 39 * @param Event $event Event to permanently delete. 31 40 * 32 41 * @return Event|false Deleted event or false on error. … … 81 90 82 91 /** 92 * Get events that are trashed. 93 * 94 * @param int $page Index of the page to return. 95 * @param int $page_size Page size. 96 * 97 * @return Events_Query_Result 98 * @throws Exception 99 */ 100 public function get_trashed_events( int $page = -1, int $page_size = -1 ): Events_Query_Result; 101 102 /** 83 103 * Get events that are currently active for a given user. 104 * 105 * @param int $user_id Id of the user. 106 * @param int $page Index of the page to return. 107 * @param int $page_size Page size. 108 * 109 * @return Events_Query_Result 110 * @throws Exception 111 */ 112 public function get_current_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result; 113 114 /** 115 * Get events that are currently active or happening in the future, for a given user. 84 116 * 85 117 * @param int $user_id Id of the user. -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php
r13529 r13683 21 21 } 22 22 23 /** 24 * Get or create the parent post for the year. 25 * 26 * @return int|WP_Error 27 */ 28 private function get_year_post_id( string $year ) { 29 $year_post = get_page_by_path( $year, OBJECT, self::POST_TYPE ); 30 if ( ! $year_post ) { 31 return wp_insert_post( 32 array( 33 'post_type' => self::POST_TYPE, 34 'post_title' => $year, 35 'post_name' => $year, 36 'post_status' => 'publish', 37 'post_content' => '', 38 ) 39 ); 40 } 41 return $year_post->ID; 42 } 43 23 44 public function insert_event( Event $event ) { 45 $post_parent = $this->get_year_post_id( $event->start()->utc()->format( 'Y' ) ); 46 if ( is_wp_error( $post_parent ) ) { 47 return $post_parent; 48 } 49 24 50 $event_id_or_error = wp_insert_post( 25 51 array( … … 29 55 'post_content' => $event->description(), 30 56 'post_status' => $event->status(), 57 'post_parent' => $post_parent, 31 58 ) 32 59 ); … … 41 68 42 69 public function update_event( Event $event ) { 70 $post_parent = $this->get_year_post_id( $event->start()->utc()->format( 'Y' ) ); 71 if ( is_wp_error( $post_parent ) ) { 72 return $post_parent; 73 } 43 74 $event_id_or_error = wp_update_post( 44 75 array( … … 48 79 'post_content' => $event->description(), 49 80 'post_status' => $event->status(), 81 'post_parent' => $post_parent, 50 82 ) 51 83 ); … … 58 90 } 59 91 60 public function delete_event( Event $event ) {92 public function trash_event( Event $event ) { 61 93 $result = wp_trash_post( $event->id() ); 62 94 if ( ! $result ) { 63 95 return false; 64 96 } 97 return $event; 98 } 99 100 public function delete_event( Event $event ) { 101 $result = wp_delete_post( $event->id(), true ); 102 if ( ! $result ) { 103 return false; 104 } 105 106 // Delete attendees. 107 $attendees = $this->attendee_repository->get_attendees( $event->id() ); 108 foreach ( $attendees as $attendee ) { 109 $this->attendee_repository->remove_attendee( $event->id(), $attendee->user_id() ); 110 } 111 112 // Delete stats. 113 global $wpdb, $gp_table_prefix; 114 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery 115 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching 116 $wpdb->delete( 117 "{$gp_table_prefix}event_actions", 118 array( 'event_id' => $event->id() ), 119 array( '%d' ), 120 ); 121 // phpcs:enable 122 65 123 return $event; 66 124 } … … 153 211 } 154 212 213 public function get_trashed_events( int $page = - 1, int $page_size = - 1 ): Events_Query_Result { 214 return $this->execute_events_query( 215 $page, 216 $page_size, 217 array( 218 'post_status' => 'trash', 219 ) 220 ); 221 } 222 223 public function get_current_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result { 224 $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) ); 225 226 // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query 227 // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key 228 return $this->execute_events_query( 229 $page, 230 $page_size, 231 array( 232 'meta_query' => array( 233 array( 234 'key' => '_event_start', 235 'value' => $now->format( 'Y-m-d H:i:s' ), 236 'compare' => '<=', 237 'type' => 'DATETIME', 238 ), 239 array( 240 'key' => '_event_end', 241 'value' => $now->format( 'Y-m-d H:i:s' ), 242 'compare' => '>=', 243 'type' => 'DATETIME', 244 ), 245 ), 246 'meta_key' => '_event_start', 247 'orderby' => 'meta_value', 248 'order' => 'ASC', 249 ), 250 $this->attendee_repository->get_events_for_user( $user_id ), 251 ); 252 // phpcs:enable 253 } 254 155 255 public function get_current_and_upcoming_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result { 156 256 $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) ); … … 414 514 415 515 private function update_event_meta( Event $event ) { 516 $hosts = $this->attendee_repository->get_hosts( $event->id() ); 517 $hosts_ids = array_map( 518 function ( $host ) { 519 return $host->user_id(); 520 }, 521 $hosts 522 ); 523 $hosts_ids = implode( ', ', $hosts_ids ); 416 524 update_post_meta( $event->id(), '_event_start', $event->start()->utc()->format( 'Y-m-d H:i:s' ) ); 417 525 update_post_meta( $event->id(), '_event_end', $event->end()->utc()->format( 'Y-m-d H:i:s' ) ); 418 526 update_post_meta( $event->id(), '_event_timezone', $event->timezone()->getName() ); 527 update_post_meta( $event->id(), '_hosts', $hosts_ids ); 419 528 } 420 529 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php
r13541 r13683 88 88 } 89 89 90 public function is_trashed(): bool { 91 return 'trash' === $this->status; 92 } 93 90 94 public function is_active(): bool { 91 95 $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) ); … … 142 146 */ 143 147 public function set_status( string $status ): void { 144 if ( ! in_array( $status, array( 'draft', 'publish' ), true ) ) {148 if ( ! in_array( $status, array( 'draft', 'publish', 'trash' ), true ) ) { 145 149 throw new InvalidStatus(); 146 150 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/create.php
r13529 r13683 17 17 exit; 18 18 } 19 $event_page_title = 'Create Event'; 20 $event_form_name = 'create_event'; 21 $css_show_url = 'hide-event-url'; 22 $event_id = null; 23 $event_title = ''; 24 $event_description = ''; 25 $event_url = ''; 26 $create_delete_button = true; 27 $visibility_delete_button = 'none'; 28 $event_timezone = null; 29 $event_start = new Event_Start_Date( date_i18n( 'Y - m - d H:i' ) ); 30 $event_end = new Event_End_Date( date_i18n( 'Y - m - d H:i' ) ); 19 20 if ( ! current_user_can( 'create_translation_event' ) ) { 21 $this->die_with_error( 'You do not have permission to create events.' ); 22 } 23 24 $event_page_title = 'Create Event'; 25 $event_form_name = 'create_event'; 26 $css_show_url = 'hide-event-url'; 27 $event_id = null; 28 $event_title = ''; 29 $event_description = ''; 30 $event_url = ''; 31 $create_trash_button = true; 32 $visibility_trash_button = 'none'; 33 $event_timezone = null; 34 $event_start = new Event_Start_Date( date_i18n( 'Y - m - d H:i' ) ); 35 $event_end = new Event_End_Date( date_i18n( 'Y - m - d H:i' ) ); 36 $event_slug = ''; 31 37 32 38 $this->tmpl( 'events-form', get_defined_vars() ); -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/details.php
r13541 r13683 4 4 5 5 use Exception; 6 use GP;7 6 use Wporg\TranslationEvents\Attendee\Attendee; 8 7 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 9 8 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 9 use Wporg\TranslationEvents\Project\Project_Repository; 10 10 use Wporg\TranslationEvents\Routes\Route; 11 11 use Wporg\TranslationEvents\Stats\Stats_Calculator; 12 use Wporg\TranslationEvents\Translation\Translation_Repository; 12 13 use Wporg\TranslationEvents\Translation_Events; 13 14 … … 18 19 private Event_Repository_Interface $event_repository; 19 20 private Attendee_Repository $attendee_repository; 21 private Translation_Repository $translation_repository; 22 private Project_Repository $project_repository; 23 private Stats_Calculator $stats_calculator; 20 24 21 25 public function __construct() { 22 26 parent::__construct(); 23 $this->event_repository = Translation_Events::get_event_repository(); 24 $this->attendee_repository = Translation_Events::get_attendee_repository(); 27 $this->event_repository = Translation_Events::get_event_repository(); 28 $this->attendee_repository = Translation_Events::get_attendee_repository(); 29 $this->translation_repository = new Translation_Repository(); 30 $this->project_repository = new Project_Repository(); 31 $this->stats_calculator = new Stats_Calculator(); 25 32 } 26 33 … … 36 43 } 37 44 38 /** 39 * Filter the ability to create, edit, or delete an event. 40 * 41 * @param bool $can_crud_event Whether the user can create, edit, or delete an event. 42 */ 43 $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) ); 44 if ( 'publish' !== $event->status() && ! $can_crud_event ) { 45 if ( ! current_user_can( 'view_translation_event', $event->id() ) ) { 45 46 $this->die_with_error( esc_html__( 'You are not authorized to view this page.', 'gp-translation-events' ), 403 ); 46 47 } … … 52 53 $event_end = $event->end(); 53 54 54 $attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID ); 55 $user_is_attending = $attendee instanceof Attendee; 55 $projects = $this->project_repository->get_for_event( $event->id() ); 56 $attendees = $this->attendee_repository->get_attendees( $event->id() ); 57 $current_user_attendee = $attendees[ $user->ID ] ?? null; 58 $user_is_attending = $current_user_attendee instanceof Attendee; 59 $user_is_contributor = $user_is_attending && $current_user_attendee->is_contributor(); 56 60 57 $stats_calculator = new Stats_Calculator(); 61 $hosts = array_filter( 62 $attendees, 63 function ( Attendee $attendee ) { 64 return $attendee->is_host(); 65 } 66 ); 67 68 $contributors = array_filter( 69 $attendees, 70 function ( Attendee $attendee ) { 71 return $attendee->is_contributor(); 72 } 73 ); 74 75 $attendees_not_contributing = array_filter( 76 $attendees, 77 function ( Attendee $attendee ) { 78 return ! $attendee->is_contributor(); 79 } 80 ); 81 82 $contributor_ids = array_map( 83 function ( Attendee $contributor ) { 84 return $contributor->user_id(); 85 }, 86 $contributors 87 ); 88 89 $new_contributor_ids = array(); 90 $translations_counts = $this->translation_repository->count_translations_before( $contributor_ids, $event->start() ); 91 foreach ( $translations_counts as $user_id => $count ) { 92 if ( $count <= 10 ) { 93 $new_contributor_ids[ $user_id ] = true; 94 } 95 } 96 58 97 try { 59 $event_stats = $stats_calculator->for_event( $event->id() ); 60 $contributors = $stats_calculator->get_contributors( $event->id() ); 61 $attendees = $stats_calculator->get_attendees_not_contributing( $event->id() ); 62 $attendee_repo = $this->attendee_repository; 63 $hosts = $this->attendee_repository->get_hosts( $event->id() ); 64 $projects = $stats_calculator->get_projects( $event->id() ); 98 $event_stats = $this->stats_calculator->for_event( $event->id() ); 65 99 } catch ( Exception $e ) { 66 100 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log … … 69 103 } 70 104 71 $is_editable_event = true;72 if ( $event_end->is_in_the_past() || $stats_calculator->event_has_stats( $event->id() ) ) {73 $is_editable_event = false;74 }75 76 105 $this->tmpl( 'event', get_defined_vars() ); 77 106 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/edit.php
r13541 r13683 3 3 namespace Wporg\TranslationEvents\Routes\Event; 4 4 5 use Wporg\TranslationEvents\Attendee\Attendee;6 use Wporg\TranslationEvents\Attendee\Attendee_Repository;7 5 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 8 6 use Wporg\TranslationEvents\Routes\Route; 9 use Wporg\TranslationEvents\Stats\Stats_Calculator;10 7 use Wporg\TranslationEvents\Translation_Events; 8 use Wporg\TranslationEvents\Urls; 11 9 12 10 /** … … 15 13 class Edit_Route extends Route { 16 14 private Event_Repository_Interface $event_repository; 17 private Attendee_Repository $attendee_repository;18 15 19 16 public function __construct() { 20 17 parent::__construct(); 21 $this->event_repository = Translation_Events::get_event_repository(); 22 $this->attendee_repository = Translation_Events::get_attendee_repository(); 18 $this->event_repository = Translation_Events::get_event_repository(); 23 19 } 24 20 … … 29 25 exit; 30 26 } 31 $event = $this->event_repository->get_event( $event_id );32 $attendee = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() );33 27 34 if ( ! $event || ! ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event->id() ) || $event->author_id() === get_current_user_id() ) ) { 35 $this->die_with_error( esc_html__( 'Event does not exist, or you do not have permission to edit it.', 'gp-translation-events' ), 403 ); 28 $event = $this->event_repository->get_event( $event_id ); 29 if ( ! $event ) { 30 $this->die_with_404(); 36 31 } 37 if ( 'trash' === $event->status() ) { 38 $this->die_with_error( esc_html__( 'You cannot edit a trashed event', 'gp-translation-events' ), 403 ); 32 33 if ( ! current_user_can( 'edit_translation_event', $event->id() ) ) { 34 $this->die_with_error( esc_html__( 'You do not have permission to edit this event.', 'gp-translation-events' ), 403 ); 39 35 } 40 36 41 37 include ABSPATH . 'wp-admin/includes/post.php'; 42 $event_page_title = 'Edit Event'; 43 $event_form_name = 'edit_event'; 44 $css_show_url = ''; 45 $event_title = $event->title(); 46 $event_description = $event->description(); 47 $event_status = $event->status(); 48 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 49 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 50 $event_url = get_site_url() . gp_url( wp_make_link_relative( $permalink ) ); 51 $event_timezone = $event->timezone(); 52 $event_start = $event->start(); 53 $event_end = $event->end(); 54 $create_delete_button = false; 55 $visibility_delete_button = 'inline-flex'; 56 57 if ( $event->end()->is_in_the_past() ) { 58 $this->die_with_error( esc_html__( 'You cannot edit a past event.', 'gp-translation-events' ), 403 ); 59 } 60 61 $stats_calculator = new Stats_Calculator(); 62 63 if ( $stats_calculator->event_has_stats( $event->id() ) ) { 64 $this->die_with_error( esc_html__( 'You cannot edit an event with translations.', 'gp-translation-events' ), 403 ); 65 } 66 67 if ( ! $stats_calculator->event_has_stats( $event->id() ) ) { 68 $current_user = wp_get_current_user(); 69 if ( ( $current_user->ID === $event->author_id() || ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) ) && ! $event->end()->is_in_the_past() ) { 70 $create_delete_button = true; 71 } 72 } 38 $event_page_title = 'Edit Event'; 39 $event_form_name = 'edit_event'; 40 $css_show_url = ''; 41 $event_title = $event->title(); 42 $event_description = $event->description(); 43 $event_status = $event->status(); 44 $event_url = Urls::event_details_absolute( $event_id ); 45 $event_timezone = $event->timezone(); 46 $event_start = $event->start(); 47 $event_end = $event->end(); 48 $event_slug = $event->slug(); 49 $create_trash_button = current_user_can( 'trash_translation_event', $event->id() ); 50 $visibility_trash_button = 'inline-flex'; 73 51 74 52 $this->tmpl( 'events-form', get_defined_vars() ); -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/attend-event.php
r13541 r13683 9 9 use Wporg\TranslationEvents\Stats\Stats_Importer; 10 10 use Wporg\TranslationEvents\Translation_Events; 11 use Wporg\TranslationEvents\Urls; 11 12 12 13 /** … … 48 49 $attendee = $this->attendee_repository->get_attendee( $event->id(), $user_id ); 49 50 if ( $attendee instanceof Attendee ) { 51 if ( $attendee->is_contributor() ) { 52 $this->die_with_error( esc_html__( 'Contributors cannot un-attend the event', 'gp-translation-events' ), 403 ); 53 } 50 54 $this->attendee_repository->remove_attendee( $event->id(), $user_id ); 51 55 } else { … … 60 64 } 61 65 62 wp_safe_redirect( gp_url( "/events/{$event->slug()}") );66 wp_safe_redirect( Urls::event_details( $event->id() ) ); 63 67 exit; 64 68 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/host-event.php
r13541 r13683 8 8 use Wporg\TranslationEvents\Routes\Route; 9 9 use Wporg\TranslationEvents\Translation_Events; 10 use Wporg\TranslationEvents\Urls; 10 11 11 12 /** … … 37 38 $current_user = wp_get_current_user(); 38 39 if ( ! $current_user->exists() ) { 39 $this->die_with_error( esc_html__( "Only logged-in users can manage event's hosts.", 'gp-translation-events' ), 403 );40 $this->die_with_error( esc_html__( "Only logged-in users can manage the event's hosts.", 'gp-translation-events' ), 403 ); 40 41 } 41 42 42 $current_user_attendee = $this->attendee_repository->get_attendee( $event_id, $current_user->ID ); 43 if ( ! current_user_can( 'manage_options' ) && ! $current_user_attendee->is_host() ) { 44 $this->die_with_error( esc_html__( "This user does not have permissions to manage event's hosts.", 'gp-translation-events' ), 403 ); 43 if ( ! current_user_can( 'edit_translation_event', $event_id ) ) { 44 $this->die_with_error( esc_html__( "You do not have permissions to manage the event's hosts.", 'gp-translation-events' ), 403 ); 45 45 } 46 46 … … 60 60 61 61 $this->attendee_repository->update_attendee( $affected_attendee ); 62 $this->event_repository->update_event( $event ); 62 63 } 63 64 64 wp_safe_redirect( gp_url( "/events/{$event->slug()}") );65 wp_safe_redirect( Urls::event_attendees( $event->id() ) ); 65 66 exit; 66 67 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats/stats-calculator.php
r13565 r13683 4 4 5 5 use Exception; 6 use WP_Post;7 use WP_User;8 use GP;9 6 use GP_Locale; 10 7 use GP_Locales; 11 use DateTimeImmutable;12 use DateTimeZone;13 8 14 9 class Stats_Row { 15 10 public int $created; 16 11 public int $reviewed; 12 public int $waiting; 17 13 public int $users; 18 14 public ?GP_Locale $language = null; 19 15 20 public function __construct( $created, $reviewed, $ users, ?GP_Locale $language = null ) {16 public function __construct( $created, $reviewed, $waiting, $users, ?GP_Locale $language = null ) { 21 17 $this->created = $created; 22 18 $this->reviewed = $reviewed; 19 $this->waiting = $waiting; 23 20 $this->users = $users; 24 21 $this->language = $language; … … 104 101 sum(action = 'create') as created, 105 102 count(*) as total, 106 count(distinct user_id) as users 107 from {$gp_table_prefix}event_actions 103 sum(t.status = 'waiting') as waiting, 104 count(distinct ea.user_id) as users 105 from {$gp_table_prefix}event_actions ea 106 left join {$gp_table_prefix}translations t ON ea.original_id = t.original_id and ea.user_id = t.user_id 108 107 where event_id = %d 109 108 group by locale with rollup … … 131 130 } 132 131 132 if ( is_null( $row->waiting ) ) { 133 // The corresponding translations are missing. Could be a unit test or data corruption. 134 $row->waiting = 0; 135 } 136 133 137 $stats_row = new Stats_Row( 134 138 $row->created, 135 139 $row->total - $row->created, 140 $row->waiting, 136 141 $row->users, 137 142 $lang … … 146 151 147 152 return $stats; 148 }149 150 /**151 * Get contributors for an event.152 */153 public function get_contributors( int $event_id ): array {154 global $wpdb, $gp_table_prefix;155 156 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared157 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery158 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching159 // phpcs thinks we're doing a schema change but we aren't.160 // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange161 $rows = $wpdb->get_results(162 $wpdb->prepare(163 "164 select user_id, group_concat( distinct locale ) as locales165 from {$gp_table_prefix}event_actions166 where event_id = %d167 group by user_id168 ",169 array(170 $event_id,171 )172 )173 );174 // phpcs:enable175 176 $users = array();177 foreach ( $rows as $row ) {178 $user = new WP_User( $row->user_id );179 $user->locales = explode( ',', $row->locales );180 $users[] = $user;181 }182 183 uasort(184 $users,185 function ( $a, $b ) {186 return strcasecmp( $a->display_name, $b->display_name );187 }188 );189 190 return $users;191 }192 193 /**194 * Get attendees without contributions for an event.195 */196 public function get_attendees_not_contributing( int $event_id ): array {197 global $wpdb, $gp_table_prefix;198 199 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared200 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery201 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching202 $all_attendees_ids = $wpdb->get_col(203 $wpdb->prepare(204 "205 select distinct user_id206 from {$gp_table_prefix}event_attendees207 where event_id = %d208 ",209 array(210 $event_id,211 )212 ),213 );214 215 $contributing_ids = $wpdb->get_col(216 $wpdb->prepare(217 "218 select distinct user_id219 from {$gp_table_prefix}event_actions220 where event_id = %d221 ",222 array(223 $event_id,224 )225 )226 );227 228 $attendees_not_contributing_ids = array_diff( $all_attendees_ids, $contributing_ids );229 230 $attendees_not_contributing = array();231 foreach ( $attendees_not_contributing_ids as $user_id ) {232 $attendees_not_contributing[] = new WP_User( $user_id );233 }234 235 return $attendees_not_contributing;236 }237 238 /**239 * Get projects for an event.240 */241 public function get_projects( int $event_id ): array {242 global $wpdb, $gp_table_prefix;243 244 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared245 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery246 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching247 // phpcs thinks we're doing a schema change but we aren't.248 // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange249 $rows = $wpdb->get_results(250 $wpdb->prepare(251 "252 select253 o.project_id as project,254 group_concat( distinct e.locale ) as locales,255 sum(action = 'create') as created,256 count(*) as total,257 count(distinct user_id) as users258 from {$gp_table_prefix}event_actions e, {$gp_table_prefix}originals o259 where e.event_id = %d and e.original_id = o.id260 group by o.project_id261 ",262 array(263 $event_id,264 )265 )266 );267 // phpcs:enable268 269 $projects = array();270 foreach ( $rows as $row ) {271 $row->project = GP::$project->get( $row->project );272 $project_name = $row->project->name;273 $parent_project_id = $row->project->parent_project_id;274 while ( $parent_project_id ) {275 $parent_project = GP::$project->get( $parent_project_id );276 $parent_project_id = $parent_project->parent_project_id;277 $project_name = substr( htmlspecialchars_decode( $parent_project->name ), 0, 35 ) . ' - ' . $project_name;278 }279 $projects[ $project_name ] = $row;280 }281 282 ksort( $projects );283 284 return $projects;285 153 } 286 154 … … 301 169 return ! empty( $stats->rows() ); 302 170 } 303 304 /**305 * Check if a user is a new translation contributor. A new contributor is a user who has made 10 or fewer translations before event start time.306 *307 * @param Event_Start_Date $event_start The event start date.308 * @param int $user_id The user ID.309 *310 * @return bool True if the user is a new translation contributor, false otherwise.311 */312 public function is_new_translation_contributor( $event_start, $user_id ) {313 global $wpdb, $gp_table_prefix;314 $new_contributor_max_translation_count = 10;315 $event_start_date_time = $event_start->__toString();316 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared317 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery318 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching319 // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange320 $user_translations_count = $wpdb->get_var(321 $wpdb->prepare(322 "323 select count(*) from {$gp_table_prefix}translations where user_id = %d and date_added < %s324 ",325 array(326 $user_id,327 $event_start_date_time,328 )329 )330 );331 332 if ( get_userdata( $user_id ) && ! $user_translations_count ) {333 return true;334 }335 return $user_translations_count <= $new_contributor_max_translation_count;336 }337 171 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/upgrade.php
r13541 r13683 2 2 3 3 namespace Wporg\TranslationEvents; 4 5 use Exception;6 use WP_Query;7 use Wporg\TranslationEvents\Attendee\Attendee;8 use Wporg\TranslationEvents\Stats\Stats_Calculator;9 4 10 5 class Upgrade { … … 28 23 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 29 24 dbDelta( self::get_database_schema_sql() ); 30 31 // Run version-specific upgrades.32 $is_running_tests = 'yes' === getenv( 'WPORG_TRANSLATION_EVENTS_TESTS' );33 if ( $previous_version < 2 && ! $is_running_tests ) {34 try {35 self::v2_import_legacy_attendees();36 } catch ( Exception $e ) {37 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log38 error_log( $e );39 }40 }41 25 42 26 update_option( self::VERSION_OPTION, self::VERSION ); … … 70 54 "; 71 55 } 72 73 /**74 * Previously, event attendance was tracked through user_meta.75 * This function imports this legacy attendance information into the attendees table.76 *77 * Instead of looping through all users, we consider only users who have contributed to an event.78 *79 * @throws Exception80 */81 private static function v2_import_legacy_attendees(): void {82 $query = new WP_Query(83 array(84 'post_type' => Translation_Events::CPT,85 'post_status' => 'publish',86 )87 );88 89 $events = $query->get_posts();90 $stats_calculator = new Stats_Calculator();91 $attendee_repository = Translation_Events::get_attendee_repository();92 foreach ( $events as $event ) {93 $host_attendee = new Attendee( $event->ID, intval( $event->post_author ) );94 $host_attendee->mark_as_host();95 $attendee_repository->insert_attendee( $host_attendee );96 97 foreach ( $stats_calculator->get_contributors( $event->ID ) as $user ) {98 $attendee = $attendee_repository->get_attendee( $event->ID, $user->id );99 if ( ! $attendee ) {100 $attendee = new Attendee( $event->ID, $user->ID );101 $attendee_repository->insert_attendee( $attendee );102 }103 }104 }105 }106 56 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event.php
r13566 r13683 9 9 use WP_User; 10 10 use Wporg\TranslationEvents\Attendee\Attendee; 11 use Wporg\TranslationEvents\Attendee\Attendee_Repository;12 11 use Wporg\TranslationEvents\Event\Event; 13 12 use Wporg\TranslationEvents\Event\Event_End_Date; … … 16 15 use Wporg\TranslationEvents\Stats\Stats_Row; 17 16 18 /** @var Attendee_Repository $attendee_repo */ 19 /** @var Attendee $attendee */ 17 /** @var bool $user_is_attending */ 18 /** @var bool $user_is_contributor */ 19 /** @var Attendee[] $attendees_not_contributing */ 20 /** @var Attendee[] $contributors */ 21 /** @var array $new_contributor_ids */ 20 22 /** @var Event $event */ 21 23 /** @var int $event_id */ … … 53 55 <ul> 54 56 <?php foreach ( $contributors as $contributor ) : ?> 55 <li class="event-contributor" title="<?php echo esc_html( implode( ', ', $contributor-> locales) ); ?>">56 <a href="<?php echo esc_url( get_author_posts_url( $contributor-> ID ) ); ?>" class="avatar"><?php echo get_avatar( $contributor->ID, 48 ); ?></a>57 <a href="<?php echo esc_url( get_author_posts_url( $contributor-> ID ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $contributor->ID) ); ?></a>58 <?php if ( $stats_calculator->is_new_translation_contributor( $event_start, $contributor->ID) ) : ?>57 <li class="event-contributor" title="<?php echo esc_html( implode( ', ', $contributor->contributed_locales() ) ); ?>"> 58 <a href="<?php echo esc_url( get_author_posts_url( $contributor->user_id() ) ); ?>" class="avatar"><?php echo get_avatar( $contributor->user_id(), 48 ); ?></a> 59 <a href="<?php echo esc_url( get_author_posts_url( $contributor->user_id() ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $contributor->user_id() ) ); ?></a> 60 <?php if ( isset( $new_contributor_ids[ $contributor->user_id() ] ) ) : ?> 59 61 <span class="first-time-contributor-tada" title="<?php esc_html_e( 'New Translation Contributor', 'gp-translation-events' ); ?>"></span> 60 62 <?php endif; ?> 61 <?php62 if ( ! $event->end()->is_in_the_past() ) :63 if ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) || $user->ID === $event->author_id() ) :64 $_attendee = $attendee_repo->get_attendee( $event_id, $contributor->ID );65 if ( $_attendee instanceof Attendee ) :66 echo '<form class="add-remove-user-as-host" method="post" action="' . esc_url( gp_url( "/events/host/$event_id/$contributor->ID" ) ) . '">';67 if ( $_attendee->is_host() ) :68 echo '<input type="submit" class="button is-primary remove-as-host" value="Remove as host"/>';69 else :70 echo '<input type="submit" class="button is-secondary convert-to-host" value="Make co-host"/>';71 endif;72 echo '</form>';73 else :74 echo '<span class="event-not-attending">' . esc_html__( 'Not attending', 'gp-translation-events' ) . '</span>';75 endif;76 endif;77 endif;78 ?>79 63 </li> 80 64 <?php endforeach; ?> … … 82 66 </div> 83 67 <?php endif; ?> 84 <?php if ( ! empty( $attendees ) && ( ! $event->end()->is_in_the_past() || ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) || $user->ID === $event->author_id()) ) ) : ?>68 <?php if ( ! empty( $attendees_not_contributing ) && current_user_can( 'edit_translation_event', $event->id() ) ) : ?> 85 69 <div class="event-attendees"> 86 70 <h2> 87 71 <?php 88 72 // translators: %d is the number of attendees. 89 echo esc_html( sprintf( __( 'Attendees (%d)', 'gp-translation-events' ), number_format_i18n( count( $attendees ) ) ) );73 echo esc_html( sprintf( __( 'Attendees (%d)', 'gp-translation-events' ), number_format_i18n( count( $attendees_not_contributing ) ) ) ); 90 74 ?> 91 75 </h2> 92 76 <ul> 93 <?php foreach ( $attendees as $_user) : ?>77 <?php foreach ( $attendees_not_contributing as $_attendee ) : ?> 94 78 <li class="event-attendee"> 95 <a href="<?php echo esc_url( get_author_posts_url( $_ user->ID ) ); ?>" class="avatar"><?php echo get_avatar( $_user->ID, 48 ); ?></a>96 <a href="<?php echo esc_url( get_author_posts_url( $_ user->ID ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $_user->ID) ); ?></a>97 <?php if ( $stats_calculator->is_new_translation_contributor( $event_start, $_user->ID) ) : ?>79 <a href="<?php echo esc_url( get_author_posts_url( $_attendee->user_id() ) ); ?>" class="avatar"><?php echo get_avatar( $_attendee->user_id(), 48 ); ?></a> 80 <a href="<?php echo esc_url( get_author_posts_url( $_attendee->user_id() ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $_attendee->user_id() ) ); ?></a> 81 <?php if ( isset( $new_contributor_ids[ $_attendee->user_id() ] ) ) : ?> 98 82 <span class="first-time-contributor-tada" title="<?php esc_html_e( 'New Translation Contributor', 'gp-translation-events' ); ?>"></span> 99 83 <?php endif; ?> 100 <?php101 if ( ! $event->end()->is_in_the_past() ) :102 if ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) || $user->ID === $event->author_id() ) :103 $_attendee = $attendee_repo->get_attendee( $event_id, $_user->ID );104 if ( $_attendee instanceof Attendee ) :105 echo '<form class="add-remove-user-as-host" method="post" action="' . esc_url( gp_url( "/events/host/$event_id/$_user->ID" ) ) . '">';106 if ( $_attendee->is_host() ) :107 echo '<input type="submit" class="button is-primary remove-as-host" value="Remove as host"/>';108 else :109 echo '<input type="submit" class="button is-secondary convert-to-host" value="Make co-host"/>';110 endif;111 echo '</form>';112 endif;113 endif;114 endif;115 ?>116 84 </li> 117 85 <?php endforeach; ?> … … 125 93 <thead> 126 94 <tr> 127 <th scope="col">Locale</th> 128 <th scope="col">Translations created</th> 129 <th scope="col">Translations reviewed</th> 130 <th scope="col">Contributors</th> 95 <th scope="col"><?php esc_html_e( 'Translations', 'gp-translation-events' ); ?></th> 96 <th scope="col"><?php esc_html_e( 'Created', 'gp-translation-events' ); ?></th> 97 <th scope="col"><?php esc_html_e( 'Waiting', 'gp-translation-events' ); ?></th> 98 <th scope="col"><?php esc_html_e( 'Reviewed', 'gp-translation-events' ); ?></th> 99 <th scope="col"><?php esc_html_e( 'Contributors', 'gp-translation-events' ); ?></th> 131 100 </tr> 132 101 </thead> … … 136 105 <tr> 137 106 <td title="<?php echo esc_html( $_locale ); ?> "><a href="<?php echo esc_url( gp_url_join( gp_url( '/languages' ), $row->language->slug ) ); ?>"><?php echo esc_html( $row->language->english_name ); ?></a></td> 138 <td><?php echo esc_html( $row->created ); ?></td> 107 <td><a href="<?php echo esc_url( Urls::event_translations( $event->id(), $row->language->slug ) ); ?>"><?php echo esc_html( $row->created ); ?></a></td> 108 <td><a href="<?php echo esc_url( Urls::event_translations( $event->id(), $row->language->slug, 'waiting' ) ); ?>"><?php echo esc_html( $row->waiting ); ?></a></td> 139 109 <td><?php echo esc_html( $row->reviewed ); ?></td> 140 110 <td><?php echo esc_html( $row->users ); ?></td> … … 144 114 <td>Total</td> 145 115 <td><?php echo esc_html( $event_stats->totals()->created ); ?></td> 116 <td><?php echo esc_html( $event_stats->totals()->waiting ); ?></td> 146 117 <td><?php echo esc_html( $event_stats->totals()->reviewed ); ?></td> 147 118 <td><?php echo esc_html( $event_stats->totals()->users ); ?></td> … … 155 126 <?php foreach ( $projects as $project_name => $row ) : ?> 156 127 <li class="event-project" title="<?php echo esc_html( str_replace( ',', ', ', $row->locales ) ); ?>"> 157 <a href="<?php echo esc_url( gp_url_project( $row->project ) ); ?>"><?php echo esc_html( $project_name ); ?></a> <small> to158 128 <?php 129 $row_locales = array(); 159 130 foreach ( explode( ',', $row->locales ) as $_locale ) { 160 $_locale = GP_Locales::by_slug( $_locale ); 161 ?> 162 <a href="<?php echo esc_url( gp_url_project_locale( $row->project, $_locale->slug, 'default' ) ); ?>"><?php echo esc_html( $_locale->english_name ); ?></a> 163 <?php 131 $_locale = GP_Locales::by_slug( $_locale ); 132 $row_locales[] = '<a href="' . esc_url( gp_url_project_locale( $row->project, $_locale->slug, 'default' ) ) . '">' . esc_html( $_locale->english_name ) . '</a>'; 164 133 } 165 // translators: %d: Number of contributors. 166 echo esc_html( sprintf( _n( 'by %d contributor', 'by %d contributors', $row->users, 'gp-translation-events' ), $row->users ) ); 134 echo wp_kses_post( 135 wp_sprintf( 136 // translators: 1: Project translated. 2: List of languages. 3: Number of contributors. 137 _n( 138 '%1$s <small>to %2$l by %3$d contributor</small>', 139 '%1$s <small>to %2$l by %3$d contributors</small>', 140 $row->users, 141 'gp-translation-events' 142 ), 143 '<a href="' . esc_url( gp_url_project( $row->project ) ) . '">' . esc_html( $project_name ) . '</a>', 144 $row_locales, 145 $row->users 146 ) 147 ); 167 148 ?> 168 </small>169 149 </li> 170 150 <?php endforeach; ?> … … 175 155 <p class="event-stats-text"> 176 156 <?php 177 $new_contributors = array_filter(178 $contributors,179 function ( $contributor ) use ( $stats_calculator, $event_start ) {180 return $stats_calculator->is_new_translation_contributor( $event_start, $contributor->ID );181 }182 );183 184 157 $new_contributors_text = ''; 185 if ( ! empty( $new_contributor s ) ) {158 if ( ! empty( $new_contributor_ids ) ) { 186 159 $new_contributors_text = sprintf( 187 160 // translators: %d is the number of new contributors. 188 _n( '(%d new contributor 🎉)', '(%d new contributors 🎉)', count( $new_contributor s ), 'gp-translation-events' ),189 count( $new_contributor s )161 _n( '(%d new contributor 🎉)', '(%d new contributors 🎉)', count( $new_contributor_ids ), 'gp-translation-events' ), 162 count( $new_contributor_ids ) 190 163 ); 191 164 } 192 165 193 166 echo wp_kses( 194 sprintf(195 // translators: %1$s: Event title, %2$d: Number of contributors, %3$s: is a parenthesis with potential text "x new contributors", %4$d: Number of languages, %5$ s: List of languages, %6$d: Number of strings translated, %7$d: Number of strings reviewed.196 __( 'At the <strong>%1$s</strong> event, we had %2$d people %3$s who contributed in %4$d languages (%5$ s), translated %6$d strings and reviewed %7$d strings.', 'gp-translation-events' ),167 wp_sprintf( 168 // translators: %1$s: Event title, %2$d: Number of contributors, %3$s: is a parenthesis with potential text "x new contributors", %4$d: Number of languages, %5$l: List of languages, %6$d: Number of strings translated, %7$d: Number of strings reviewed. 169 __( 'At the <strong>%1$s</strong> event, we had %2$d people %3$s who contributed in %4$d languages (%5$l), translated %6$d strings and reviewed %7$d strings.', 'gp-translation-events' ), 197 170 esc_html( $event_title ), 198 171 esc_html( $event_stats->totals()->users ), 199 172 $new_contributors_text, 200 173 count( $event_stats->rows() ), 201 esc_html( 202 implode( 203 ', ', 204 array_map( 205 function ( $row ) { 206 return $row->language->english_name; 207 }, 208 $event_stats->rows() 209 ) 210 ) 174 array_map( 175 function ( $row ) { 176 return $row->language->english_name; 177 }, 178 $event_stats->rows() 211 179 ), 212 180 esc_html( $event_stats->totals()->created ), … … 220 188 <?php 221 189 echo wp_kses( 222 sprintf( 223 // translators: %s the contributors. 224 esc_html__( 'Contributors were %s.', 'gp-translation-events' ), 225 implode( 226 ', ', 227 array_map( 228 function ( $contributor ) use ( $stats_calculator, $event_start ) { 229 $append_tada = ''; 230 if ( $stats_calculator->is_new_translation_contributor( $event_start, $contributor->ID ) ) { 190 wp_sprintf( 191 // translators: %s List of contributors. 192 _n( 193 'Contributor was %l.', 194 'Contributors were %l.', 195 count( $contributors ), 196 'gp-translation-events' 197 ), 198 array_map( 199 function ( $contributor ) { 200 $append_tada = ''; 201 if ( isset( $new_contributor_ids[ $contributor->user_id() ] ) ) { 231 202 $append_tada = ' <span class="new-contributor" title="' . esc_html__( 'New Translation Contributor', 'gp-translation-events' ) . '">🎉</span>'; 232 } 233 return '@' . $contributor->user_login . $append_tada; 234 }, 235 $contributors 236 ) 203 } 204 return '@' . ( new WP_User( $contributor->user_id() ) )->user_login . $append_tada; 205 }, 206 $contributors 237 207 ) 238 208 ), … … 268 238 <div class="event-details-join"> 269 239 <?php if ( $event_end->is_in_the_past() ) : ?> 270 <?php if ( $ attendee instanceof Attendee) : ?>240 <?php if ( $user_is_attending ) : ?> 271 241 <button disabled="disabled" class="button is-primary attend-btn"><?php esc_html_e( 'You attended', 'gp-translation-events' ); ?></button> 272 242 <?php endif; ?> 243 <?php elseif ( $user_is_contributor ) : ?> 244 <?php // Contributors can't un-attend so don't show anything. ?> 273 245 <?php else : ?> 274 <form class="event-details-attend" method="post" action="<?php echo esc_url( gp_url( "/events/attend/$event_id") ); ?>">275 <?php if ( $ attendee instanceof Attendee) : ?>276 <input type="submit" class="button is-secondary attending-btn" value=" You're attending" />246 <form class="event-details-attend" method="post" action="<?php echo esc_url( Urls::event_toggle_attendee( $event_id ) ); ?>"> 247 <?php if ( $user_is_attending ) : ?> 248 <input type="submit" class="button is-secondary attending-btn" value="<?php esc_attr_e( "You're attending", 'gp-translation-events' ); ?>" /> 277 249 <?php else : ?> 278 <input type="submit" class="button is-primary attend-btn" value=" Attend Event"/>250 <input type="submit" class="button is-primary attend-btn" value="<?php esc_attr_e( 'Attend Event', 'gp-translation-events' ); ?>"/> 279 251 <?php endif; ?> 280 252 </form> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-form.php
r13529 r13683 29 29 <form class="translation-event-form" action="" method="post"> 30 30 <?php wp_nonce_field( '_event_nonce', '_event_nonce' ); ?> 31 <?php if ( ! $event_id ) : ?> 32 <details id="quick-add"><summary><?php esc_html_e( 'Upcoming WordCamps', 'gp-translation-events' ); ?></summary><div class="loading"></div></details> 33 <?php endif; ?> 31 34 <input type="hidden" name="action" value="submit_event_ajax"> 32 35 <input type="hidden" id="form-name" name="form_name" value="<?php echo esc_attr( $event_form_name ); ?>"> … … 34 37 <input type="hidden" id="event-form-action" name="event_form_action"> 35 38 <div> 36 <label for="event-title"> Event Title</label>37 <input type="text" id="event-title" name="event_title" value="<?php echo esc_html( $event_title ); ?>" required >39 <label for="event-title"><?php esc_html_e( 'Event Title', 'gp-translation-events' ); ?></label> 40 <input type="text" id="event-title" name="event_title" value="<?php echo esc_html( $event_title ); ?>" required size="42"> 38 41 </div> 39 42 <div id="event-url" class="<?php echo esc_attr( $css_show_url ); ?>"> 40 <label for="event-permalink"> Event URL</label>43 <label for="event-permalink"><?php esc_html_e( 'Event URL', 'gp-translation-events' ); ?></label> 41 44 <a id="event-permalink" class="event-permalink" href="<?php echo esc_url( $event_url ); ?>" target="_blank"><?php echo esc_url( $event_url ); ?></a> 42 45 </div> 43 46 <div> 44 <label for="event-description"> Event Description</label>45 <textarea id="event-description" name="event_description" rows="4" required><?php echo esc_html( $event_description ); ?></textarea>47 <label for="event-description"><?php esc_html_e( 'Event Description', 'gp-translation-events' ); ?></label> 48 <textarea id="event-description" name="event_description" rows="4" cols="40" required><?php echo esc_html( $event_description ); ?></textarea> 46 49 <?php 47 50 echo wp_kses( … … 59 62 ?> 60 63 <div> 61 <label for="event-start"> Start Date</label>64 <label for="event-start"><?php esc_html_e( 'Start Date', 'gp-translation-events' ); ?></label> 62 65 <input type="datetime-local" id="event-start" name="event_start" value="<?php echo esc_attr( $event_start->format( 'Y-m-d H:i' ) ); ?>" required> 63 66 </div> 64 67 <div> 65 <label for="event-end"> End Date</label>68 <label for="event-end"><?php esc_html_e( 'End Date', 'gp-translation-events' ); ?></label> 66 69 <input type="datetime-local" id="event-end" name="event_end" value="<?php echo esc_attr( $event_end->format( 'Y-m-d H:i' ) ); ?>" required> 67 70 </div> 68 71 <div> 69 <label for="event-timezone"> Event Timezone</label>72 <label for="event-timezone"><?php esc_html_e( 'Event Timezone', 'gp-translation-events' ); ?></label> 70 73 <select id="event-timezone" name="event_timezone" required> 71 74 <?php … … 96 99 <button class="button is-primary submit-event" type="submit" data-event-status="publish">Publish Event</button> 97 100 <?php endif; ?> 98 <?php if ( isset( $create_ delete_button ) && $create_delete_button ) : ?>99 <button id=" delete-button" class="button is-destructive delete-event" type="submit" name="submit" value="Delete" style="display: <?php echo esc_attr( $visibility_delete_button ); ?>">Delete Event</button>101 <?php if ( isset( $create_trash_button ) && $create_trash_button ) : ?> 102 <button id="trash-button" class="button is-destructive trash-event" type="submit" name="submit" value="Delete" style="display: <?php echo esc_attr( $visibility_trash_button ); ?>">Delete Event</button> 100 103 <?php endif; ?> 101 104 </div> … … 127 130 </form> 128 131 </div> 132 <?php if ( $event_id ) : ?> 133 <div class="event-edit-right"> 134 <a class="manage-attendees-btn button is-primary" href="<?php echo esc_url( Urls::event_attendees( $event_id ) ); ?>"><?php esc_html_e( 'Manage Attendees', 'gp-translation-events' ); ?></a> 135 </div> 136 <?php endif; ?> 129 137 <div class="clear"></div> 130 138 <?php gp_tmpl_footer(); ?> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-header.php
r13541 r13683 3 3 namespace Wporg\TranslationEvents; 4 4 5 use GP;5 use WP_User; 6 6 use Wporg\TranslationEvents\Attendee\Attendee; 7 7 use Wporg\TranslationEvents\Event\Event; 8 8 9 /** @var WP_User $user */ 10 /** @var Attendee[] $hosts */ 9 11 /** @var Attendee $attendee */ 10 /** @var Event $event */12 /** @var Event $event */ 11 13 /** @var string $event_page_title */ 12 /** @var bool $is_editable_event */13 14 ?> 14 15 … … 22 23 <ul class="event-list-nav"> 23 24 <?php if ( is_user_logged_in() ) : ?> 24 <li><a href="<?php echo esc_url( gp_url( '/events/my-events/' ) ); ?>">My Events</a></li> 25 <?php 26 /** 27 * Filter the ability to create, edit, or delete an event. 28 * 29 * @param bool $can_crud_event Whether the user can create, edit, or delete an event. 30 */ 31 $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) ); 32 if ( $can_crud_event ) : 33 ?> 34 <li><a class="button is-primary" href="<?php echo esc_url( gp_url( '/events/new/' ) ); ?>">Create Event</a></li> 25 <?php if ( current_user_can( 'manage_translation_events' ) ) : ?> 26 <li><a href="<?php echo esc_url( Urls::events_trashed() ); ?>">Deleted Events</a></li> 27 <?php endif; ?> 28 <li><a href="<?php echo esc_url( Urls::my_events() ); ?>">My Events</a></li> 29 <?php if ( current_user_can( 'create_translation_event' ) ) : ?> 30 <li><a class="button is-primary" href="<?php echo esc_url( Urls::event_create() ); ?>">Create Event</a></li> 35 31 <?php endif; ?> 36 32 <?php endif; ?> 37 33 </ul> 38 <?php if ( isset( $event ) && ! isset( $event_form_name ) ) : ?>34 <?php if ( isset( $event ) && ! isset( $event_form_name ) && ! isset( $hide_sub_head ) ) : ?> 39 35 <p class="event-sub-head"> 40 36 <span class="event-host"> 41 37 <?php 42 if ( count( $hosts ) > 0 ) : 43 if ( 1 === count( $hosts ) ) : 44 esc_html_e( 'Host:', 'gp-translation-events' ); 38 if ( isset( $hosts ) ) : 39 if ( count( $hosts ) > 0 ) : 40 if ( 1 === count( $hosts ) ) : 41 esc_html_e( 'Host:', 'gp-translation-events' ); 42 else : 43 esc_html_e( 'Hosts:', 'gp-translation-events' ); 44 endif; 45 45 else : 46 esc_html_e( 'Hosts:', 'gp-translation-events' ); 46 esc_html_e( 'Created by:', 'gp-translation-events' ); 47 ?> 48 <a href="<?php echo esc_attr( get_author_posts_url( $user->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $user->ID ) ); ?></a> 49 <?php 47 50 endif; 48 else :49 esc_html_e( 'Created by:', 'gp-translation-events' );50 51 ?> 51 <a href="<?php echo esc_attr( get_author_posts_url( $user->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $user->ID ) ); ?></a> 52 <?php 53 endif; 54 ?> 55 <?php foreach ( $hosts as $host ) : ?> 56 <a href="<?php echo esc_attr( get_author_posts_url( $host->user_id() ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $host->user_id() ) ); ?></a> 57 <?php if ( end( $hosts ) !== $host ) : ?> 58 , 59 <?php else : ?> 60 . 61 <?php endif; ?> 62 <?php endforeach; ?> 52 <?php foreach ( $hosts as $host ) : ?> 53 <a href="<?php echo esc_attr( get_author_posts_url( $host->user_id() ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $host->user_id() ) ); ?></a> 54 <?php if ( end( $hosts ) !== $host ) : ?> 55 , 56 <?php else : ?> 57 . 58 <?php endif; ?> 59 <?php endforeach; ?> 60 <?php endif; ?> 63 61 </span> 64 <?php $show_edit_button = ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event->id() ) ) && $is_editable_event; ?> 65 <?php if ( $show_edit_button ) : ?> 66 <a class="event-page-edit-link" href="<?php echo esc_url( gp_url( 'events/edit/' . $event->id() ) ); ?>"><span class="dashicons dashicons-edit"></span><?php esc_html_e( 'Edit event', 'gp-translation-events' ); ?></a> 62 <?php if ( current_user_can( 'edit_translation_event', $event->id() ) ) : ?> 63 <a class="event-page-edit-link" href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>"><span class="dashicons dashicons-edit"></span><?php esc_html_e( 'Edit event', 'gp-translation-events' ); ?></a> 64 <?php elseif ( current_user_can( 'edit_translation_event_attendees', $event->id() ) ) : ?> 65 <a class="event-page-attendees-link" href="<?php echo esc_url( Urls::event_attendees( $event->id() ) ); ?>"><?php esc_html_e( 'Manage attendees', 'gp-translation-events' ); ?></a> 67 66 <?php endif ?> 68 67 </p> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list.php
r13529 r13683 30 30 <h2><?php esc_html_e( 'Current events', 'gp-translation-events' ); ?></h2> 31 31 <ul class="event-list"> 32 <?php 33 foreach ( $current_events_query->events as $event ) : 34 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 35 ?> 32 <?php foreach ( $current_events_query->events as $event ) : ?> 36 33 <li class="event-list-item"> 37 <a href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>34 <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 38 35 <span class="event-list-date">ends <?php $event->end()->print_relative_time_html(); ?></time></span> 39 36 <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?> 40 37 </li> 41 <?php 42 endforeach; 43 ?> 38 <?php endforeach; ?> 44 39 </ul> 45 40 … … 64 59 <h2><?php esc_html_e( 'Upcoming events', 'gp-translation-events' ); ?></h2> 65 60 <ul class="event-list"> 66 <?php 67 foreach ( $upcoming_events_query->events as $event ) : 68 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 69 ?> 61 <?php foreach ( $upcoming_events_query->events as $event ) : ?> 70 62 <li class="event-list-item"> 71 <a href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>63 <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 72 64 <span class="event-list-date">starts <?php $event->start()->print_relative_time_html(); ?></span> 73 65 <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?> 74 66 </li> 75 <?php 76 endforeach; 77 ?> 67 <?php endforeach; ?> 78 68 </ul> 79 69 … … 97 87 <h2><?php esc_html_e( 'Past events', 'gp-translation-events' ); ?></h2> 98 88 <ul class="event-list"> 99 <?php 100 foreach ( $past_events_query->events as $event ) : 101 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 102 ?> 89 <?php foreach ( $past_events_query->events as $event ) : ?> 103 90 <li class="event-list-item"> 104 <a href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>91 <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 105 92 <span class="event-list-date">ended <?php $event->end()->print_relative_time_html( 'F j, Y H:i T' ); ?></span> 106 93 <?php esc_html( get_the_excerpt( $event->id() ) ); ?> 107 94 </li> 108 <?php 109 endforeach; 110 ?> 95 <?php endforeach; ?> 111 96 </ul> 112 97 … … 139 124 <?php else : ?> 140 125 <ul class="event-attending-list"> 141 <?php 142 foreach ( $user_attending_events_query->events as $event ) : 143 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 144 ?> 126 <?php foreach ( $user_attending_events_query->events as $event ) : ?> 145 127 <li class="event-list-item"> 146 <a href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>128 <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 147 129 <?php if ( $event->start() === $event->end() ) : ?> 148 130 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html( 'F j, Y H:i T' ); ?></span> … … 151 133 <?php endif; ?> 152 134 </li> 153 <?php 154 endforeach; 155 ?> 135 <?php endforeach; ?> 156 136 </ul> 157 137 <?php -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-my-events.php
r13541 r13683 26 26 <?php 27 27 foreach ( $events_i_host_query->events as $event ) : 28 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 29 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 30 $event_url = gp_url( wp_make_link_relative( $permalink ) ); 31 $event_edit_url = gp_url( 'events/edit/' . $event->id() ); 32 $stats_calculator = new Stats_Calculator(); 33 $has_stats = $stats_calculator->event_has_stats( $event->id() ); 28 $stats_calculator = new Stats_Calculator(); 29 $has_stats = $stats_calculator->event_has_stats( $event->id() ); 34 30 ?> 35 31 <li class="event-list-item"> 36 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>32 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 37 33 <?php if ( ! $event->end()->is_in_the_past() && ! $has_stats ) : ?> 38 <a href="<?php echo esc_url( $event_edit_url); ?>" class="button is-small action edit">Edit</a>34 <a href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>" class="button is-small action edit">Edit</a> 39 35 <?php endif; ?> 40 36 <?php if ( 'draft' === $event->status() ) : ?> … … 73 69 <?php 74 70 foreach ( $events_i_created_query->events as $event ) : 75 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 76 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 77 $event_url = gp_url( wp_make_link_relative( $permalink ) ); 78 $event_edit_url = gp_url( 'events/edit/' . $event->id() ); 79 $stats_calculator = new Stats_Calculator(); 80 $has_stats = $stats_calculator->event_has_stats( $event->id() ); 71 $stats_calculator = new Stats_Calculator(); 72 $has_stats = $stats_calculator->event_has_stats( $event->id() ); 81 73 ?> 82 74 <li class="event-list-item"> 83 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>75 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 84 76 <?php if ( ! $event->end()->is_in_the_past() && ! $has_stats ) : ?> 85 <a href="<?php echo esc_url( $event_edit_url); ?>" class="button is-small action edit">Edit</a>77 <a href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>" class="button is-small action edit">Edit</a> 86 78 <?php endif; ?> 87 79 <?php if ( 'draft' === $event->status() ) : ?> … … 118 110 <?php if ( ! empty( $events_i_attended_query->events ) ) : ?> 119 111 <ul> 120 <?php 121 foreach ( $events_i_attended_query->events as $event ) : 122 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 123 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 124 $event_url = gp_url( wp_make_link_relative( $permalink ) ); 125 ?> 112 <?php foreach ( $events_i_attended_query->events as $event ) : ?> 126 113 <li class="event-list-item"> 127 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url); ?>"><?php echo esc_html( $event->title() ); ?></a>114 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a> 128 115 <?php if ( $event->start() === $event->end() ) : ?> 129 116 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?></span> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/helper-functions.php
r13268 r13683 1 1 <?php 2 3 use Wporg\TranslationEvents\Urls; 4 2 5 /** 3 6 * Get event breadcrumb. … … 9 12 function gp_breadcrumb_translation_events( $extra_items = array() ) { 10 13 $breadcrumb = array( 11 empty( $extra_items ) ? __( 'Events', 'gp-translation-events' ) : gp_link_get( gp_url( '/events'), __( 'Events', 'gp-translation-events' ) ),14 empty( $extra_items ) ? __( 'Events', 'gp-translation-events' ) : gp_link_get( Urls::events_home(), __( 'Events', 'gp-translation-events' ) ), 12 15 ); 13 16 if ( ! empty( $extra_items ) ) { -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php
r13541 r13683 23 23 use Exception; 24 24 use GP; 25 use GP_Locales; 25 26 use WP_Post; 26 27 use WP_Query; 27 28 use Wporg\TranslationEvents\Attendee\Attendee; 28 29 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 30 use Wporg\TranslationEvents\Event\Event_Capabilities; 29 31 use Wporg\TranslationEvents\Event\Event_Form_Handler; 30 32 use Wporg\TranslationEvents\Event\Event_Repository_Cached; 31 33 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 34 use Wporg\TranslationEvents\Notifications\Notifications_Send; 35 use Wporg\TranslationEvents\Stats\Stats_Calculator; 32 36 use Wporg\TranslationEvents\Stats\Stats_Listener; 33 37 34 38 class Translation_Events { 35 39 public const CPT = 'translation_event'; 40 41 private Event_Capabilities $event_capabilities; 36 42 37 43 public static function get_instance(): Translation_Events { … … 65 71 add_action( 'wp_enqueue_scripts', array( $this, 'register_translation_event_js' ) ); 66 72 add_action( 'init', array( $this, 'register_event_post_type' ) ); 73 add_action( 'init', array( $this, 'send_notifications' ) ); 67 74 add_action( 'add_meta_boxes', array( $this, 'event_meta_boxes' ) ); 68 75 add_action( 'save_post', array( $this, 'save_event_meta_boxes' ) ); … … 72 79 add_action( 'gp_init', array( $this, 'gp_init' ) ); 73 80 add_action( 'gp_before_translation_table', array( $this, 'add_active_events_current_user' ) ); 81 add_filter( 'wp_post_revision_meta_keys', array( $this, 'wp_post_revision_meta_keys' ) ); 82 add_filter( 'pre_wp_unique_post_slug', array( $this, 'pre_wp_unique_post_slug' ), 10, 6 ); 74 83 75 84 if ( is_admin() ) { 76 85 Upgrade::upgrade_if_needed(); 77 86 } 87 88 $this->event_capabilities = new Event_Capabilities( 89 self::get_event_repository(), 90 self::get_attendee_repository(), 91 new Stats_Calculator() 92 ); 93 $this->event_capabilities->register_hooks(); 78 94 } 79 95 80 96 public function gp_init() { 97 $locale = '(' . implode( '|', wp_list_pluck( GP_Locales::locales(), 'slug' ) ) . ')'; 98 $slug = '((?:2[0-9]{3}/)?[a-z0-9_-]+)'; 99 $status = '(waiting)'; 100 $id = '(\d+)'; 101 81 102 GP::$router->add( '/events?', array( 'Wporg\TranslationEvents\Routes\Event\List_Route', 'handle' ) ); 103 GP::$router->add( '/events/trashed?', array( 'Wporg\TranslationEvents\Routes\Event\List_Trashed_Route', 'handle' ) ); 82 104 GP::$router->add( '/events/new', array( 'Wporg\TranslationEvents\Routes\Event\Create_Route', 'handle' ) ); 83 GP::$router->add( '/events/edit/(\d+)', array( 'Wporg\TranslationEvents\Routes\Event\Edit_Route', 'handle' ) ); 84 GP::$router->add( '/events/attend/(\d+)', array( 'Wporg\TranslationEvents\Routes\User\Attend_Event_Route', 'handle' ), 'post' ); 85 GP::$router->add( '/events/host/(\d+)/(\d+)', array( 'Wporg\TranslationEvents\Routes\User\Host_Event_Route', 'handle' ), 'post' ); 105 GP::$router->add( "/events/edit/$id", array( 'Wporg\TranslationEvents\Routes\Event\Edit_Route', 'handle' ) ); 106 GP::$router->add( "/events/trash/$id", array( 'Wporg\TranslationEvents\Routes\Event\Trash_Route', 'handle' ) ); 107 GP::$router->add( "/events/delete/$id", array( 'Wporg\TranslationEvents\Routes\Event\Delete_Route', 'handle' ) ); 108 GP::$router->add( "/events/attend/$id", array( 'Wporg\TranslationEvents\Routes\User\Attend_Event_Route', 'handle' ), 'post' ); 109 GP::$router->add( "/events/host/$id/$id", array( 'Wporg\TranslationEvents\Routes\User\Host_Event_Route', 'handle' ), 'post' ); 86 110 GP::$router->add( '/events/my-events', array( 'Wporg\TranslationEvents\Routes\User\My_Events_Route', 'handle' ) ); 87 GP::$router->add( '/events/([a-z0-9_-]+)', array( 'Wporg\TranslationEvents\Routes\Event\Details_Route', 'handle' ) ); 111 GP::$router->add( "/events/$slug/translations/$locale/$status", array( 'Wporg\TranslationEvents\Routes\Event\Translations_Route', 'handle' ) ); 112 GP::$router->add( "/events/$slug/translations/$locale", array( 'Wporg\TranslationEvents\Routes\Event\Translations_Route', 'handle' ) ); 113 GP::$router->add( "/events/$slug", array( 'Wporg\TranslationEvents\Routes\Event\Details_Route', 'handle' ) ); 114 GP::$router->add( "/events/$slug/attendees", array( 'Wporg\TranslationEvents\Routes\Attendee\List_Route', 'handle' ) ); 88 115 89 116 $stats_listener = new Stats_Listener( … … 113 140 114 141 $args = array( 115 'labels' => $labels, 116 'public' => true, 117 'has_archive' => true, 118 'menu_icon' => 'dashicons-calendar', 119 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions' ), 120 'rewrite' => array( 'slug' => 'events' ), 121 'show_ui' => false, 142 'labels' => $labels, 143 'public' => true, 144 'has_archive' => true, 145 'hierarchical' => true, 146 'menu_icon' => 'dashicons-calendar', 147 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions' ), 148 'rewrite' => array( 'slug' => 'events' ), 149 'show_ui' => false, 122 150 ); 123 151 … … 163 191 } 164 192 } 165 166 193 $fields = array( 'event_start', 'event_end' ); 167 194 foreach ( $fields as $field ) { … … 219 246 220 247 if ( null === $attendee ) { 221 $attendee = new Attendee( $event_id, $user_id ); 222 $attendee->mark_as_host(); 248 $attendee = new Attendee( $event_id, $user_id, true ); 223 249 $attendee_repository->insert_attendee( $attendee ); 224 250 } … … 237 263 return $items; 238 264 } 239 $new[ esc_url( gp_url( '/events/') ) ] = esc_html__( 'Events', 'gp-translation-events' );265 $new[ esc_url( Urls::events_home() ) ] = esc_html__( 'Events', 'gp-translation-events' ); 240 266 return array_merge( $items, $new ); 241 267 } … … 254 280 public function generate_event_slug( array $data, array $postarr ): array { 255 281 if ( self::CPT === $data['post_type'] ) { 282 if ( isset( $data['post_name'] ) && preg_match( '/^2[0-9]+$/', $data['post_name'] ) ) { 283 return $data; 284 } 256 285 if ( 'draft' === $data['post_status'] ) { 257 286 $data['post_name'] = sanitize_title( $data['post_title'] ); … … 278 307 */ 279 308 public function add_active_events_current_user(): void { 280 $attendee_repository = new Attendee_Repository(); 281 $user_attending_event_ids = $attendee_repository->get_events_for_user( get_current_user_id() ); 282 if ( empty( $user_attending_event_ids ) ) { 283 return; 284 } 285 286 $current_datetime_utc = ( new DateTime( 'now', new DateTimeZone( 'UTC' ) ) )->format( 'Y-m-d H:i:s' ); 287 $user_attending_events_args = array( 288 'post_type' => self::CPT, 289 'post__in' => $user_attending_event_ids, 290 'post_status' => 'publish', 291 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 292 'meta_query' => array( 293 array( 294 'key' => '_event_start', 295 'value' => $current_datetime_utc, 296 'compare' => '<=', 297 'type' => 'DATETIME', 298 ), 299 array( 300 'key' => '_event_end', 301 'value' => $current_datetime_utc, 302 'compare' => '>=', 303 'type' => 'DATETIME', 304 ), 305 ), 306 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 307 'meta_key' => '_event_start', 308 'orderby' => 'meta_value', 309 'order' => 'ASC', 310 ); 311 312 $user_attending_events_query = new WP_Query( $user_attending_events_args ); 313 $number_of_events = $user_attending_events_query->post_count; 309 $event_repository = self::get_event_repository(); 310 $user_attending_events_query = $event_repository->get_current_events_for_user( get_current_user_id() ); 311 $events = $user_attending_events_query->events; 312 313 $number_of_events = count( $events ); 314 314 if ( 0 === $number_of_events ) { 315 315 return; … … 320 320 $content .= sprintf( _n( 'Contributing to %d event:', 'Contributing to %d events:', $number_of_events, 'gp-translation-events' ), $number_of_events ); 321 321 $content .= ' '; 322 323 foreach ( array_splice( $events, 0, 2 ) as $event ) { 324 $content .= '<span class="active-events-before-translation-table"><a href="' . Urls::event_details( $event->id() ) . '" target="_blank">' . esc_html( $event->title() ) . '</a></span>'; 325 } 326 322 327 if ( $number_of_events > 3 ) { 323 $counter = 0;324 while ( $user_attending_events_query->have_posts() && $counter < 2 ) {325 $user_attending_events_query->the_post();326 $url = esc_url( gp_url( '/events/' . get_post_field( 'post_name', get_post() ) ) );327 $content .= '<span class="active-events-before-translation-table"><a href="' . $url . '" target="_blank">' . get_the_title() . '</a></span>';328 ++$counter;329 }330 331 328 $remaining_events = $number_of_events - 2; 332 $url = esc_url( gp_url( '/events/' ) );333 329 /* translators: %d: Number of remaining events */ 334 $content .= '<span class="remaining-events"><a href="' . $url . '" target="_blank">' . sprintf( esc_html__( ' and %d more events.', 'gp-translation-events' ), $remaining_events ) . '</a></span>'; 335 336 } else { 337 while ( $user_attending_events_query->have_posts() ) { 338 $user_attending_events_query->the_post(); 339 $url = esc_url( gp_url( '/events/' . get_post_field( 'post_name', get_post() ) ) ); 340 $content .= '<span class="active-events-before-translation-table"><a href="' . $url . '" target="_blank">' . get_the_title() . '</a></span>'; 341 } 342 } 330 $content .= '<span class="remaining-events"><a href="' . esc_url( Urls::events_home() ) . '" target="_blank">' . sprintf( esc_html__( ' and %d more events.', 'gp-translation-events' ), $remaining_events ) . '</a></span>'; 331 } 332 343 333 $content .= '</div>'; 344 334 … … 360 350 ); 361 351 } 352 353 /** 354 * Send notifications for the events. 355 */ 356 public function send_notifications() { 357 new Notifications_Send( self::get_event_repository(), self::get_attendee_repository() ); 358 } 359 360 /** 361 * Add the event meta keys to the list of meta keys to keep in post revisions. 362 * 363 * @param array $keys The list of meta keys to keep in post revisions. 364 * 365 * @return array The modified list of meta keys to keep in post revisions. 366 */ 367 public function wp_post_revision_meta_keys( array $keys ): array { 368 $meta_keys_to_keep = array( '_event_start', '_event_end', '_event_timezone', '_hosts' ); 369 return array_merge( $keys, $meta_keys_to_keep ); 370 } 371 372 public function pre_wp_unique_post_slug( $override_slug, string $slug, int $post_id, string $post_status, string $post_type, int $post_parent ) { 373 if ( self::CPT !== $post_type || $post_parent ) { 374 return $override_slug; 375 } 376 // Normally the slug is not allowed to be a year, this overrides it since we have a CPT and translate.wordpress.org doesn't have blog posts. 377 if ( preg_match( '/^2[0-9]{3}$/', $slug ) ) { 378 return $slug; 379 } 380 return $override_slug; 381 } 362 382 } 363 383 Translation_Events::get_instance();
Note: See TracChangeset
for help on using the changeset viewer.