Changeset 13529
- Timestamp:
- 04/15/2024 01:37:55 PM (21 months ago)
- Location:
- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events
- Files:
-
- 14 added
- 2 deleted
- 16 edited
-
assets/css/translation-events.css (modified) (19 diffs)
-
assets/js/translation-events.js (modified) (6 diffs)
-
autoload.php (added)
-
includes/active-events-cache.php (deleted)
-
includes/attendee (added)
-
includes/attendee/attendee-repository.php (added)
-
includes/attendee/attendee.php (added)
-
includes/event (added)
-
includes/event-text-snippet.php (added)
-
includes/event.php (deleted)
-
includes/event/event-date.php (added)
-
includes/event/event-form-handler.php (added)
-
includes/event/event-repository-cached.php (added)
-
includes/event/event-repository-interface.php (added)
-
includes/event/event-repository.php (added)
-
includes/event/event.php (added)
-
includes/routes/event/create.php (modified) (3 diffs)
-
includes/routes/event/details.php (modified) (4 diffs)
-
includes/routes/event/edit.php (modified) (3 diffs)
-
includes/routes/event/list.php (modified) (3 diffs)
-
includes/routes/user/attend-event.php (modified) (3 diffs)
-
includes/routes/user/host-event.php (added)
-
includes/routes/user/my-events.php (modified) (5 diffs)
-
includes/stats-calculator.php (modified) (8 diffs)
-
includes/stats-listener.php (modified) (5 diffs)
-
includes/upgrade.php (added)
-
templates/event.php (modified) (3 diffs)
-
templates/events-form.php (modified) (2 diffs)
-
templates/events-header.php (modified) (2 diffs)
-
templates/events-list.php (modified) (9 diffs)
-
templates/events-my-events.php (modified) (4 diffs)
-
wporg-gp-translation-events.php (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css
r13299 r13529 4 4 vertical-align: top; 5 5 } 6 6 7 .translation-event-form #event-title, 7 8 .translation-event-form #event-description { 8 9 width: 30%; 9 10 } 11 10 12 .translation-event-form input[type="text"], 11 13 .translation-event-form input[type="date"] { 12 14 width: 13%; 13 15 } 16 14 17 .translation-event-form div { 15 18 margin-top: 1em; 16 19 } 20 17 21 .translation-event-form #submit-event { 18 22 margin-left: 10%; … … 21 25 display: block; 22 26 } 23 .event-page-title { 24 border-bottom: #d9d8d8 thin solid; 25 padding-bottom: .5em; 26 } 27 27 28 .event-details-page { 28 29 border-bottom: #cfd4d4 thin solid; … … 30 31 margin: 0 auto; 31 32 } 33 32 34 .event-details-left { 33 35 width: 77%; … … 37 39 padding-right: 1em; 38 40 } 41 39 42 .event-details-right { 40 43 width: 22%; … … 42 45 padding-top: 1em; 43 46 } 47 44 48 .event-details-stats { 45 49 clear: both; 46 margin: 1em; 50 margin: 0; 51 } 52 53 .event-details-stats table { 54 margin: 1rem; 47 55 } 48 56 .event-details-stats table { … … 50 58 table-layout: fixed; 51 59 } 52 .event-details-stats table th, .event-details-stats table td { 60 61 .event-details-stats table th, 62 .event-details-stats table td { 53 63 padding: 1em; 54 64 text-align: center; 55 65 } 66 56 67 .event-details-stats table td:first-child { 57 68 text-align: left; 58 69 word-break: break-all; 59 70 } 71 60 72 .event-details-stats table tr { 61 73 border-bottom: thin solid #f0f0f0; 62 74 } 75 63 76 .event-details-stats table th { 64 77 background-color: #e9e9e9; … … 66 79 word-break: break-all; 67 80 } 81 68 82 .event-details-stats-totals td { 69 83 font-weight: bold; 70 84 } 71 .event-contributors li { 85 86 .event-contributors li, .event-attendees li { 72 87 display: inline-block; 73 88 list-style-type: none; … … 76 91 width: 15em; 77 92 } 78 .event-contributors li .avatar { 93 94 .event-contributors li .avatar, .event-attendees li .avatar { 79 95 border-radius: 50%; 80 96 vertical-align: middle; 81 97 } 98 99 .event-attendees h2 { 100 margin-bottom: 0; 101 } 102 103 span.event-creator, span.event-you { 104 display: block; 105 margin-left: 55px; 106 margin-top: 1em; 107 font-size: smaller; 108 } 82 109 .hide-event-url { 83 110 display: none; 84 111 } 112 85 113 .translation-event-form button.save-draft { 86 114 background: #fff; 87 115 color: var(--gp-color-btn-primary-bg); 88 116 } 117 89 118 #event-url span { 90 119 width: 10%; 91 120 display: inline-block; 92 121 } 122 93 123 span.event-list-date { 94 124 display: block; … … 97 127 font-weight: normal; 98 128 } 129 99 130 span.event-list-date.events-i-am-attending { 100 131 font-size: .8em; 101 132 font-weight: normal; 102 133 } 103 .event-list-item a{ 134 135 .event-list-item a { 104 136 font-weight: bold; 105 137 font-size: 1.2em; 106 138 } 139 107 140 li.event-list-item { 108 141 list-style-type: none; 109 } 142 margin-bottom: .5em; 143 } 144 110 145 li.event-list-item p { 111 146 margin-top: 0; … … 113 148 font-size: .95em; 114 149 } 150 115 151 h2.event_page_title { 116 152 border-bottom: #e0e0e0 thin solid; … … 118 154 font-weight: bold; 119 155 } 156 120 157 .event-list-top-bar { 121 display: block;122 158 width: 80%; 123 159 margin: 0 auto; 124 text-align: right; 125 } 160 display: grid; 161 grid-template-columns: 1fr auto; 162 align-items: center; 163 border-bottom: #d9d8d8 thin solid; 164 padding-bottom: .5em; 165 } 166 126 167 .event-list-nav li { 127 168 display: inline; 128 169 margin: 0 0.1rem; 129 170 } 171 130 172 .event-list-nav li a { 131 173 text-decoration: none; 132 174 font-weight: bold; 133 175 } 176 134 177 .event-list-nav li:not(:first-child):not(:last-child):before { 135 178 content: " | "; 136 179 } 180 137 181 .event-left-col { 138 182 width: 75%; … … 140 184 border-right: var(--gp-color-secondary-400) thin solid; 141 185 } 186 142 187 .event-right-col { 143 188 width: 25%; … … 145 190 padding-left: 1em; 146 191 } 192 147 193 .event-attending-list { 148 194 list-style-type: none; 149 195 padding: 0; 150 196 } 197 151 198 .event-attending-list li { 152 199 margin-bottom: 1em; 153 200 } 201 154 202 .event-list-nav li a.button { 155 203 background-color: var(--gp-color-btn-primary-bg) !important; 156 204 color: #fff !important; 157 205 } 206 158 207 a.event-link-draft { 159 color:#80807f; 160 } 161 span.event-label-draft, span.event-details-join-expired, span.active-events-before-translation-table { 208 color: #80807f; 209 } 210 211 span.event-label-draft, 212 span.event-details-join-expired, 213 span.active-events-before-translation-table { 162 214 font-weight: 500; 163 color: var( --gp-color-bubble-inactive-project-text);164 border: 1px solid var( --gp-color-bubble-inactive-project-text);215 color: var(--gp-color-bubble-inactive-project-text); 216 border: 1px solid var(--gp-color-bubble-inactive-project-text); 165 217 font-size: .7em; 166 218 margin-right: 0.3em; … … 171 223 text-transform: capitalize; 172 224 } 225 173 226 span.active-events-before-translation-table a { 174 color: var( --gp-color-bubble-inactive-project-text ); 175 text-decoration: none; 176 } 227 color: var(--gp-color-bubble-inactive-project-text); 228 text-decoration: none; 229 } 230 177 231 .active-events-before-translation-table { 178 232 width: 100%; 179 border: 1px solid var( --gp-color-border-default);180 background: var( --gp-color-status-waiting-subtle);233 border: 1px solid var(--gp-color-border-default); 234 background: var(--gp-color-status-waiting-subtle); 181 235 margin: 1rem 0; 182 236 padding: 0.3rem 0.8rem; 183 237 } 238 184 239 .event-page-wrapper { 185 240 margin: 0 auto; 186 241 width: 80%; 187 242 } 243 188 244 .event-list { 189 245 margin: 0; 190 246 padding: 0; 191 247 } 192 .event-details-head { 193 border-bottom: var(--gp-color-secondary-400) thin solid; 194 } 248 249 input[type="submit"].remove-as-host, input[type="submit"].convert-to-host { 250 text-align: center; 251 display: inline; 252 margin-top: 1em; 253 font-weight: bold; 254 } 255 195 256 input[type="submit"].attend-btn { 196 257 width: 100%; … … 200 261 font-weight: bold; 201 262 } 202 input[type="submit"].attending-btn, a.button.is-primary.attend-btn { 263 264 input[type="submit"].attending-btn, 265 a.button.is-primary.attend-btn { 203 266 width: 100%; 204 267 text-align: center; … … 206 269 margin-top: 1em; 207 270 } 271 208 272 .event-details-date { 209 273 background: #f4f4f4; 210 274 color: #606161; 211 padding: .5em;275 padding: 0.1em .5em; 212 276 font-size: .9em; 213 277 line-height: 1.8em; 214 278 font-weight: 500; 215 } 279 margin-bottom: 1em; 280 } 281 216 282 .event-page-edit-link { 217 283 float: right; 218 284 text-decoration: none; 219 } 285 justify-self: end; 286 grid-column: 2; 287 } 288 220 289 .event-stats-summary { 221 290 margin-top: 1em; 222 291 } 292 223 293 .event-stats-summary summary { 224 294 background: #f8f8f8; … … 229 299 font-size: .9em; 230 300 } 301 231 302 .event-stats-summary p.event-stats-text { 232 303 margin: 0; … … 236 307 border-top: thin solid #e0e0e0; 237 308 } 238 time.event-utc-time { 239 display: block; 309 310 .event-details-date time { 240 311 font-size: .95em; 241 } 242 .event-utc-time:first-of-type{ 312 font-weight: normal; 313 } 314 315 .event-details-date time:last-child { 316 margin-bottom: 0; 317 } 318 319 .event-details-date time.full-time { 320 display: block; 243 321 border-bottom: #cdcdcd thin solid; 244 322 padding-bottom: 0.5em; 245 323 margin-bottom: 0.5em; 246 324 } 325 247 326 span.event-details-date-label { 248 327 font-weight: bold; 249 328 color: #5a5a5a; 250 329 display: block; 330 } 331 332 .event-host { 333 display: flex; 334 grid-column: 1; 335 } 336 337 p.event-sub-head { 338 grid-column: 1 / -1; 339 margin-top: 0; 340 } 341 342 a.event-page-edit-link { 343 font-size: .9em; 344 text-decoration: none; 345 } 346 347 a.event-page-edit-link:hover { 348 border-bottom: var(--gp-color-btn-primary-bg) thin solid; 349 text-decoration: none; 350 } 351 ul.text-snippets { 352 padding: 0; 353 margin-left: 160px; 354 } 355 .first-time-contributor-tada::after { 356 content: ' 🎉'; 251 357 } 252 358 253 359 /* show the event-details-right below instead of on the right on mobile */ 254 360 @media (max-width: 768px) { 255 .event-page-wrapper, .event-details-right, .event-details-left { 361 362 .event-page-wrapper, 363 .event-details-right, 364 .event-details-left { 256 365 width: 100%; 257 366 float: none; 258 367 } 368 259 369 .event-details-left { 260 370 border-right: none; … … 262 372 padding: 0; 263 373 } 264 265 .event-details-stats { 266 margin: 0; 267 } 268 } 374 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js
r13268 r13529 10 10 validateEventDates(); 11 11 convertToUserLocalTime(); 12 setInterval( convertToUserLocalTime, 10000 ); 12 13 13 14 $( '.submit-event' ).on( … … 28 29 } 29 30 ); 31 32 $( '.text-snippet' ).on( 33 'click', 34 function ( e ) { 35 e.preventDefault(); 36 var textArea = $( this ).closest( 'div' ).find( 'textarea' ); 37 var textAreaContent = textArea.val(); 38 textArea.val( textAreaContent + $( this ).data( 'snippet' ) ); 39 } 40 ); 30 41 } 31 42 ); … … 38 49 */ 39 50 function handleSubmit( eventStatus, isDraft ) { 51 const $form = $( '.translation-event-form' ); 52 if ( ! $form[0].reportValidity() ) { 53 return; 54 } 55 56 if ( '' === $( '#event-title' ).val() ) { 57 $gp.notices.error( 'Event title must be set.' ); 58 return; 59 } 40 60 if ( '' === $( '#event-start' ).val() ) { 41 61 $gp.notices.error( 'Event start date and time must be set.' ); … … 57 77 } 58 78 $( '#event-form-action' ).val( eventStatus ); 59 const $form = $( '.translation-event-form' );60 79 const $is_creation = $( '#form-name' ).val() === 'create_event'; 61 80 … … 149 168 timeElements.forEach( 150 169 function ( timeEl ) { 151 const eventDateObj = new Date( timeEl.getAttribute( 'datetime' ) ); 170 const datetime = timeEl.getAttribute( 'datetime' ); 171 if ( ! datetime ) { 172 return; 173 } 174 const eventDateObj = new Date( datetime ); 175 timeEl.title = eventDateObj.toUTCString(); 176 152 177 const userTimezoneOffset = new Date().getTimezoneOffset(); 153 178 const userTimezoneOffsetMs = userTimezoneOffset * 60 * 1000; 154 179 const userLocalDateTime = new Date( eventDateObj.getTime() - userTimezoneOffsetMs ); 155 180 156 const options = { 181 if ( timeEl.classList.contains( 'relative' ) ) { 182 // Display the relative time. 183 const now = new Date(); 184 let diff = userLocalDateTime - now; 185 let in_text = 'in '; 186 let ago_text = ''; 187 if ( diff < 0 ) { 188 if ( timeEl.classList.contains( 'future' ) ) { 189 // If an event transitions from future to past, reload the page to move it from active to past events and vice versa. 190 location.reload(); 191 } 192 in_text = ''; 193 ago_text = ' ago'; 194 diff = - diff; 195 } 196 197 const seconds = Math.floor( diff / 1000 ); 198 const minutes = Math.floor( seconds / 60 ); 199 const hours = Math.floor( minutes / 60 ); 200 const days = Math.floor( hours / 24 ); 201 const weeks = Math.floor( days / 7 ); 202 const months = Math.floor( days / 30 ); 203 const years = Math.floor( days / 365.25 ); 204 let relativeTime = ''; 205 if ( years > 1 ) { 206 if ( ! timeEl.classList.contains( 'hide-if-too-far' ) ) { 207 relativeTime = years + ' year' + ( years > 1 ? 's' : '' ); 208 } else { 209 in_text = ''; 210 } 211 } else if ( months > 1 ) { 212 if ( ! timeEl.classList.contains( 'hide-if-too-far' ) ) { 213 relativeTime = months + ' month' + ( months > 1 ? 's' : '' ); 214 } else { 215 in_text = ''; 216 } 217 } else if ( weeks > 1 ) { 218 if ( ! timeEl.classList.contains( 'hide-if-too-far' ) || weeks < 3 ) { 219 relativeTime = weeks + ' week' + ( weeks > 1 ? 's' : '' ); 220 } else { 221 in_text = ''; 222 } 223 } else if ( days > 0 ) { 224 relativeTime = days + ' day' + ( days > 1 ? 's' : '' ); 225 } else if ( hours > 0 ) { 226 relativeTime = hours + ' hour' + ( hours > 1 ? 's' : '' ); 227 } else if ( minutes > 0 ) { 228 relativeTime = minutes + ' minute' + ( minutes > 1 ? 's' : '' ); 229 } else { 230 relativeTime = 'less than a minute'; 231 } 232 timeEl.textContent = in_text + relativeTime + ago_text; 233 return; 234 } 235 236 const options = { 157 237 weekday: 'short', 158 238 year: 'numeric', … … 163 243 timeZoneName: 'short' 164 244 }; 245 if ( timeEl.dataset.format ) { 246 if ( timeEl.dataset.format.includes( 'l' ) ) { 247 options.weekday = 'long'; 248 } else if ( ! timeEl.dataset.format.includes( 'D' ) ) { 249 delete options.weekday; 250 } 251 if ( timeEl.dataset.format.includes( 'F' ) ) { 252 options.month = 'long'; 253 } else if ( timeEl.dataset.format.includes( 'm' ) || timeEl.dataset.format.includes( 'n' ) ) { 254 options.month = 'numeric'; 255 } 256 } 165 257 timeEl.textContent = userLocalDateTime.toLocaleTimeString( navigator.language, options ); 166 258 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/create.php
r13298 r13529 3 3 namespace Wporg\TranslationEvents\Routes\Event; 4 4 5 use Wporg\TranslationEvents\Event\Event_End_Date; 6 use Wporg\TranslationEvents\Event\Event_Start_Date; 5 7 use Wporg\TranslationEvents\Routes\Route; 6 8 … … 15 17 exit; 16 18 } 17 $event_ form_title = 'Create Event';19 $event_page_title = 'Create Event'; 18 20 $event_form_name = 'create_event'; 19 21 $css_show_url = 'hide-event-url'; … … 21 23 $event_title = ''; 22 24 $event_description = ''; 23 $event_timezone = '';24 $event_start = '';25 $event_end = '';26 25 $event_url = ''; 27 26 $create_delete_button = true; 28 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' ) ); 29 31 30 32 $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
r13298 r13529 5 5 use Exception; 6 6 use GP; 7 use Wporg\TranslationEvents\Attendee\Attendee; 8 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 9 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 7 10 use Wporg\TranslationEvents\Routes\Route; 8 11 use Wporg\TranslationEvents\Stats_Calculator; … … 13 16 */ 14 17 class Details_Route extends Route { 18 private Event_Repository_Interface $event_repository; 19 private Attendee_Repository $attendee_repository; 20 21 public function __construct() { 22 parent::__construct(); 23 $this->event_repository = Translation_Events::get_event_repository(); 24 $this->attendee_repository = Translation_Events::get_attendee_repository(); 25 } 26 15 27 public function handle( string $event_slug ): void { 16 28 $user = wp_get_current_user(); 17 29 $event = get_page_by_path( $event_slug, OBJECT, Translation_Events::CPT ); 30 if ( ! $event ) { 31 $this->die_with_404(); 32 } 33 $event = $this->event_repository->get_event( $event->ID ); 18 34 if ( ! $event ) { 19 35 $this->die_with_404(); … … 26 42 */ 27 43 $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) ); 28 if ( 'publish' !== $event-> post_status&& ! $can_crud_event ) {44 if ( 'publish' !== $event->status() && ! $can_crud_event ) { 29 45 $this->die_with_error( esc_html__( 'You are not authorized to view this page.', 'gp-translation-events' ), 403 ); 30 46 } 31 47 32 $event_id = $event->ID; 33 $event_title = $event->post_title; 34 $event_description = $event->post_content; 35 $event_start = get_post_meta( $event->ID, '_event_start', true ) ?: ''; 36 $event_end = get_post_meta( $event->ID, '_event_end', true ) ?: ''; 37 $attending_event_ids = get_user_meta( $user->ID, Translation_Events::USER_META_KEY_ATTENDING, true ) ?: array(); 38 $user_is_attending = isset( $attending_event_ids[ $event_id ] ); 48 $event_id = $event->id(); 49 $event_title = $event->title(); 50 $event_description = $event->description(); 51 $event_start = $event->start(); 52 $event_end = $event->end(); 53 54 $attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID ); 55 $user_is_attending = $attendee instanceof Attendee; 39 56 40 57 $stats_calculator = new Stats_Calculator(); 41 58 try { 42 $event_stats = $stats_calculator->for_event( $event ); 43 $contributors = $stats_calculator->get_contributors( $event ); 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() ); 44 65 } catch ( Exception $e ) { 45 66 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log … … 48 69 } 49 70 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 50 76 $this->tmpl( 'event', get_defined_vars() ); 51 77 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/edit.php
r13298 r13529 3 3 namespace Wporg\TranslationEvents\Routes\Event; 4 4 5 use DateTime;6 use DateTimeZone;7 use Exception;5 use Wporg\TranslationEvents\Attendee\Attendee; 6 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 7 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 8 8 use Wporg\TranslationEvents\Routes\Route; 9 9 use Wporg\TranslationEvents\Stats_Calculator; … … 14 14 */ 15 15 class Edit_Route extends Route { 16 private Event_Repository_Interface $event_repository; 17 private Attendee_Repository $attendee_repository; 18 19 public function __construct() { 20 parent::__construct(); 21 $this->event_repository = Translation_Events::get_event_repository(); 22 $this->attendee_repository = Translation_Events::get_attendee_repository(); 23 } 24 16 25 public function handle( int $event_id ): void { 17 26 global $wp; … … 20 29 exit; 21 30 } 22 $event = get_post( $event_id ); 23 if ( ! $event || Translation_Events::CPT !== $event->post_type || ! ( current_user_can( 'edit_post', $event->ID ) || intval( $event->post_author ) === get_current_user_id() ) ) { 31 $event = $this->event_repository->get_event( $event_id ); 32 $attendee = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() ); 33 34 if ( ! $event || ! ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event->id() ) || $event->author_id() === get_current_user_id() ) ) { 24 35 $this->die_with_error( esc_html__( 'Event does not exist, or you do not have permission to edit it.', 'gp-translation-events' ), 403 ); 25 36 } 26 if ( 'trash' === $event-> post_status) {37 if ( 'trash' === $event->status() ) { 27 38 $this->die_with_error( esc_html__( 'You cannot edit a trashed event', 'gp-translation-events' ), 403 ); 28 39 } 29 40 30 41 include ABSPATH . 'wp-admin/includes/post.php'; 31 $event_ form_title = 'Edit Event';42 $event_page_title = 'Edit Event'; 32 43 $event_form_name = 'edit_event'; 33 44 $css_show_url = ''; 34 $event_title = $event-> post_title;35 $event_description = $event-> post_content;36 $event_status = $event-> post_status;37 list( $permalink, $post_name ) = get_sample_permalink( $event _id);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() ); 38 49 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 39 50 $event_url = get_site_url() . gp_url( wp_make_link_relative( $permalink ) ); 40 $event_timezone = get_post_meta( $event_id, '_event_timezone', true ) ?: ''; 51 $event_timezone = $event->timezone(); 52 $event_start = $event->start(); 53 $event_end = $event->end(); 41 54 $create_delete_button = false; 42 55 $visibility_delete_button = 'inline-flex'; 43 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 44 61 $stats_calculator = new Stats_Calculator(); 45 if ( ! $stats_calculator->event_has_stats( $event ) ) { 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() ) ) { 46 68 $current_user = wp_get_current_user(); 47 if ( $current_user->ID === $event->post_author || current_user_can( 'manage_options') ) {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() ) { 48 70 $create_delete_button = true; 49 71 } 50 72 } 51 73 52 try {53 $event_start = self::convertToTimezone( get_post_meta( $event_id, '_event_start', true ), $event_timezone );54 $event_end = self::convertToTimezone( get_post_meta( $event_id, '_event_end', true ), $event_timezone );55 } catch ( Exception $e ) {56 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log57 error_log( $e );58 $this->die_with_error( esc_html__( 'Something is wrong.', 'gp-translation-events' ) );59 }60 61 74 $this->tmpl( 'events-form', get_defined_vars() ); 62 75 } 63 64 /**65 * Convert date time stored in UTC to a date time in a time zone.66 *67 * @param string $date_time The date time in UTC.68 * @param string $time_zone The time zone.69 *70 * @return string The date time in the time zone.71 * @throws Exception When date is invalid.72 */73 private static function convertToTimezone( string $date_time, string $time_zone ): string {74 return ( new DateTime( $date_time, new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $time_zone ) )->format( 'Y-m-d H:i:s' );75 }76 76 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/list.php
r13298 r13529 7 7 use Exception; 8 8 use WP_Query; 9 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 9 10 use Wporg\TranslationEvents\Routes\Route; 10 11 use Wporg\TranslationEvents\Translation_Events; … … 14 15 */ 15 16 class List_Route extends Route { 17 private Event_Repository_Interface $event_repository; 18 19 public function __construct() { 20 parent::__construct(); 21 $this->event_repository = Translation_Events::get_event_repository(); 22 } 23 16 24 public function handle(): void { 17 25 $current_datetime_utc = null; … … 56 64 // phpcs:enable 57 65 58 $current_events_args = array( 59 'post_type' => Translation_Events::CPT, 60 'posts_per_page' => 10, 61 'paged' => $_current_events_paged, 62 'post_status' => 'publish', 63 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 64 'meta_query' => array( 65 array( 66 'key' => '_event_start', 67 'value' => $current_datetime_utc, 68 'compare' => '<=', 69 'type' => 'DATETIME', 70 ), 71 array( 72 'key' => '_event_end', 73 'value' => $current_datetime_utc, 74 'compare' => '>=', 75 'type' => 'DATETIME', 76 ), 77 ), 78 'orderby' => 'meta_value', 79 'order' => 'ASC', 80 ); 81 $current_events_query = new WP_Query( $current_events_args ); 82 83 $upcoming_events_args = array( 84 'post_type' => Translation_Events::CPT, 85 'posts_per_page' => 10, 86 'paged' => $_upcoming_events_paged, 87 'post_status' => 'publish', 88 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 89 'meta_query' => array( 90 array( 91 'key' => '_event_start', 92 'value' => $current_datetime_utc, 93 'compare' => '>=', 94 'type' => 'DATETIME', 95 ), 96 ), 97 'orderby' => 'meta_value', 98 'order' => 'ASC', 99 ); 100 $upcoming_events_query = new WP_Query( $upcoming_events_args ); 101 102 $past_events_args = array( 103 'post_type' => Translation_Events::CPT, 104 'posts_per_page' => 10, 105 'paged' => $_past_events_paged, 106 'post_status' => 'publish', 107 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 108 'meta_query' => array( 109 array( 110 'key' => '_event_end', 111 'value' => $current_datetime_utc, 112 'compare' => '<', 113 'type' => 'DATETIME', 114 ), 115 ), 116 'orderby' => 'meta_value', 117 'order' => 'ASC', 118 ); 119 $past_events_query = new WP_Query( $past_events_args ); 120 121 $user_attending_events = get_user_meta( get_current_user_id(), Translation_Events::USER_META_KEY_ATTENDING, true ) ?: array( 0 ); 122 $user_attending_events_args = array( 123 'post_type' => Translation_Events::CPT, 124 'post__in' => array_keys( $user_attending_events ), 125 'posts_per_page' => 10, 126 'paged' => $_user_attending_events_paged, 127 'post_status' => 'publish', 128 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 129 'meta_query' => array( 130 array( 131 'key' => '_event_end', 132 'value' => $current_datetime_utc, 133 'compare' => '>', 134 'type' => 'DATETIME', 135 ), 136 ), 137 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 138 'meta_key' => '_event_start', 139 'orderby' => 'meta_value', 140 'order' => 'ASC', 141 ); 142 $user_attending_events_query = new WP_Query( $user_attending_events_args ); 66 $current_events_query = $this->event_repository->get_current_events( $_current_events_paged, 10 ); 67 $upcoming_events_query = $this->event_repository->get_upcoming_events( $_upcoming_events_paged, 10 ); 68 $past_events_query = $this->event_repository->get_past_events( $_past_events_paged, 10 ); 69 $user_attending_events_query = $this->event_repository->get_current_and_upcoming_events_for_user( get_current_user_id(), $_user_attending_events_paged, 10 ); 143 70 144 71 $this->tmpl( 'events-list', get_defined_vars() ); -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/attend-event.php
r13298 r13529 3 3 namespace Wporg\TranslationEvents\Routes\User; 4 4 5 use Wporg\TranslationEvents\Attendee\Attendee; 6 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 7 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 5 8 use Wporg\TranslationEvents\Routes\Route; 6 9 use Wporg\TranslationEvents\Translation_Events; … … 12 15 */ 13 16 class Attend_Event_Route extends Route { 17 private Event_Repository_Interface $event_repository; 18 private Attendee_Repository $attendee_repository; 19 20 public function __construct() { 21 parent::__construct(); 22 $this->event_repository = Translation_Events::get_event_repository(); 23 $this->attendee_repository = Translation_Events::get_attendee_repository(); 24 } 25 14 26 public function handle( int $event_id ): void { 15 27 $user = wp_get_current_user(); … … 18 30 } 19 31 20 $event = get_post( $event_id ); 21 32 $event = $this->event_repository->get_event( $event_id ); 22 33 if ( ! $event ) { 23 34 $this->die_with_404(); 24 35 } 25 36 26 $event_ids = get_user_meta( $user->ID, Translation_Events::USER_META_KEY_ATTENDING, true ) ?? array(); 27 if ( ! $event_ids ) { 28 $event_ids = array(); 37 $attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID ); 38 if ( $attendee instanceof Attendee && $attendee->is_host() && ( 1 === count( $this->attendee_repository->get_hosts( $event_id ) ) ) ) { 39 $this->die_with_error( esc_html__( 'The event needs a host. Add a new host before stopping to attend the event.', 'gp-translation-events' ), 403 ); 40 } 41 if ( $attendee instanceof Attendee ) { 42 $this->attendee_repository->remove_attendee( $event->id(), $user->ID ); 43 } else { 44 $attendee = new Attendee( $event->id(), $user->ID ); 45 $this->attendee_repository->insert_attendee( $attendee ); 29 46 } 30 47 31 if ( ! isset( $event_ids[ $event_id ] ) ) { 32 // Not yet attending, mark as attending. 33 $event_ids[ $event_id ] = true; 34 } else { 35 // Currently attending, mark as not attending. 36 unset( $event_ids[ $event_id ] ); 37 } 38 39 update_user_meta( $user->ID, Translation_Events::USER_META_KEY_ATTENDING, $event_ids ); 40 41 wp_safe_redirect( gp_url( "/events/$event->post_name" ) ); 48 wp_safe_redirect( gp_url( "/events/{$event->slug()}" ) ); 42 49 exit; 43 50 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/my-events.php
r13298 r13529 3 3 namespace Wporg\TranslationEvents\Routes\User; 4 4 5 use DateTime; 6 use DateTimeZone; 7 use WP_Query; 5 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 8 6 use Wporg\TranslationEvents\Routes\Route; 9 7 use Wporg\TranslationEvents\Translation_Events; … … 13 11 */ 14 12 class My_Events_Route extends Route { 13 private Event_Repository_Interface $event_repository; 14 15 public function __construct() { 16 parent::__construct(); 17 $this->event_repository = Translation_Events::get_event_repository(); 18 } 19 15 20 public function handle(): void { 16 21 global $wp; … … 22 27 23 28 $_events_i_created_paged = 1; 29 $_events_i_hosted_paged = 1; 24 30 $_events_i_attended_paged = 1; 25 31 … … 29 35 if ( is_numeric( $value ) ) { 30 36 $_events_i_created_paged = (int) $value; 37 } 38 } 39 if ( isset( $_GET['events_i_hosted_paged'] ) ) { 40 $value = sanitize_text_field( wp_unslash( $_GET['events_i_hosted_paged'] ) ); 41 if ( is_numeric( $value ) ) { 42 $_events_i_hosted_paged = (int) $value; 31 43 } 32 44 } … … 39 51 // phpcs:enable 40 52 41 $user_id = get_current_user_id(); 42 $events = get_user_meta( $user_id, Translation_Events::USER_META_KEY_ATTENDING, true ) ?: array(); 43 $events = array_keys( $events ); 44 $current_datetime_utc = ( new DateTime( 'now', new DateTimeZone( 'UTC' ) ) )->format( 'Y-m-d H:i:s' ); 45 $args = array( 46 'post_type' => Translation_Events::CPT, 47 'posts_per_page' => 10, 48 'events_i_created_paged' => $_events_i_created_paged, 49 'paged' => $_events_i_created_paged, 50 'post_status' => array( 'publish', 'draft' ), 51 'author' => $user_id, 52 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 53 'meta_key' => '_event_start', 54 'orderby' => 'meta_value', 55 'order' => 'DESC', 56 ); 57 $events_i_created_query = new WP_Query( $args ); 58 59 $args = array( 60 'post_type' => Translation_Events::CPT, 61 'posts_per_page' => 10, 62 'events_i_attended_paged' => $_events_i_attended_paged, 63 'paged' => $_events_i_attended_paged, 64 'post_status' => 'publish', 65 'post__in' => $events, 66 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 67 'meta_query' => array( 68 array( 69 'key' => '_event_end', 70 'value' => $current_datetime_utc, 71 'compare' => '<', 72 'type' => 'DATETIME', 73 ), 74 ), 75 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 76 'meta_key' => '_event_end', 77 'orderby' => 'meta_value', 78 'order' => 'DESC', 79 ); 80 $events_i_attended_query = new WP_Query( $args ); 53 $events_i_created_query = $this->event_repository->get_events_created_by_user( get_current_user_id(), $_events_i_created_paged, 10 ); 54 $events_i_host_query = $this->event_repository->get_events_hosted_by_user( get_current_user_id(), $_events_i_hosted_paged, 10 ); 55 $events_i_attended_query = $this->event_repository->get_past_events_for_user( get_current_user_id(), $_events_i_attended_paged, 10 ); 81 56 82 57 $this->tmpl( 'events-my-events', get_defined_vars() ); -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats-calculator.php
r13298 r13529 6 6 use WP_Post; 7 7 use WP_User; 8 use GP; 8 9 use GP_Locale; 9 10 use GP_Locales; 11 use DateTimeImmutable; 12 use DateTimeZone; 10 13 11 14 class Stats_Row { … … 86 89 * @throws Exception When stats calculation failed. 87 90 */ 88 public function for_event( WP_Post $event): Event_Stats {91 public function for_event( int $event_id ): Event_Stats { 89 92 $stats = new Event_Stats(); 90 93 global $wpdb, $gp_table_prefix; … … 99 102 " 100 103 select locale, 101 sum(action = 'create') as created,102 count(*) as total,103 count(distinct user_id) as users104 sum(action = 'create') as created, 105 count(*) as total, 106 count(distinct user_id) as users 104 107 from {$gp_table_prefix}event_actions 105 108 where event_id = %d … … 107 110 ", 108 111 array( 109 $event ->ID,112 $event_id, 110 113 ) 111 114 ) … … 148 151 * Get contributors for an event. 149 152 */ 150 public function get_contributors( WP_Post $event): array {153 public function get_contributors( int $event_id ): array { 151 154 global $wpdb, $gp_table_prefix; 152 155 … … 165 168 ", 166 169 array( 167 $event ->ID,170 $event_id, 168 171 ) 169 172 ) … … 189 192 190 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.InterpolatedNotPrepared 200 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery 201 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching 202 $all_attendees_ids = $wpdb->get_col( 203 $wpdb->prepare( 204 " 205 select distinct user_id 206 from {$gp_table_prefix}event_attendees 207 where event_id = %d 208 ", 209 array( 210 $event_id, 211 ) 212 ), 213 ); 214 215 $contributing_ids = $wpdb->get_col( 216 $wpdb->prepare( 217 " 218 select distinct user_id 219 from {$gp_table_prefix}event_actions 220 where event_id = %d 221 ", 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.InterpolatedNotPrepared 245 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery 246 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching 247 // phpcs thinks we're doing a schema change but we aren't. 248 // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange 249 $rows = $wpdb->get_results( 250 $wpdb->prepare( 251 " 252 select 253 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 users 258 from {$gp_table_prefix}event_actions e, {$gp_table_prefix}originals o 259 where e.event_id = %d and e.original_id = o.id 260 group by o.project_id 261 ", 262 array( 263 $event_id, 264 ) 265 ) 266 ); 267 // phpcs:enable 268 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 } 286 287 /** 191 288 * Check if an event has stats. 192 289 * 193 * @param WP_Post $event The event to check.290 * @param int $event_id The id of the event to check. 194 291 * 195 292 * @return bool True if the event has stats, false otherwise. 196 293 */ 197 public function event_has_stats( WP_Post $event): bool {294 public function event_has_stats( int $event_id ): bool { 198 295 try { 199 $stats = $this->for_event( $event );296 $stats = $this->for_event( $event_id ); 200 297 } catch ( Exception $e ) { 201 298 return false; … … 204 301 return ! empty( $stats->rows() ); 205 302 } 303 304 /** 305 * Check if a user is a first time contributor. 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 first time contributor, false otherwise. 311 */ 312 public function is_first_time_contributor( $event_start, $user_id ) { 313 global $wpdb, $gp_table_prefix; 314 315 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared 316 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery 317 // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching 318 // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange 319 $users_first_translation_date = $wpdb->get_var( 320 $wpdb->prepare( 321 " 322 select min(date_added) from {$gp_table_prefix}translations where user_id = %d 323 ", 324 array( 325 $user_id, 326 ) 327 ) 328 ); 329 330 if ( get_userdata( $user_id ) && ! $users_first_translation_date ) { 331 return true; 332 } 333 $event_start_date_time = new DateTimeImmutable( $event_start->__toString(), new DateTimeZone( 'UTC' ) ); 334 $first_translation_date = new DateTimeImmutable( $users_first_translation_date, new DateTimeZone( 'UTC' ) ); 335 // A first time contributor is someone whose first translation was made not earlier than 24 hours before the event. 336 $event_start_date_time = $event_start_date_time->modify( '-1 day' ); 337 return $event_start_date_time <= $first_translation_date; 338 } 206 339 } -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats-listener.php
r13298 r13529 8 8 use GP_Translation; 9 9 use GP_Translation_Set; 10 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 11 use Wporg\TranslationEvents\Event\Event; 12 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 10 13 11 14 class Stats_Listener { … … 15 18 const ACTION_REQUEST_CHANGES = 'request_changes'; 16 19 17 private Active_Events_Cache $active_events_cache; 20 private Attendee_Repository $attendee_repository; 21 private Event_Repository_Interface $event_repository; 18 22 19 public function __construct( Active_Events_Cache $active_events_cache ) { 20 $this->active_events_cache = $active_events_cache; 23 public function __construct( 24 Event_Repository_Interface $event_repository, 25 Attendee_Repository $attendee_repository 26 ) { 27 $this->event_repository = $event_repository; 28 $this->attendee_repository = $attendee_repository; 21 29 } 22 30 … … 70 78 try { 71 79 // Get events that are active when the action happened, for which the user is registered for. 72 $active_events = $this-> get_active_events( $happened_at);73 $events = $this->select_events_user_is_registered_for( $active_events , $user_id );80 $active_events = $this->event_repository->get_current_events(); 81 $events = $this->select_events_user_is_registered_for( $active_events->events, $user_id ); 74 82 75 83 // phpcs:ignore Generic.Commenting.DocComment.MissingShort … … 108 116 109 117 /** 110 * Get active events at a given time.111 *112 * @return Event[]113 * @throws Exception When it fails to get active events.114 */115 private function get_active_events( DateTimeImmutable $at ): array {116 $events = $this->active_events_cache->get();117 if ( null === $events ) {118 $cache_duration = Active_Events_Cache::CACHE_DURATION;119 $boundary_start = $at;120 $boundary_end = $at->modify( "+$cache_duration seconds" );121 122 // Get events for which start is before $boundary_end AND end is after $boundary_start.123 $event_ids = get_posts(124 array(125 'post_type' => Translation_Events::CPT,126 'post_status' => 'publish',127 'posts_per_page' => - 1,128 'fields' => 'ids',129 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query130 array(131 'key' => '_event_start',132 'value' => $boundary_end->format( 'Y-m-d H:i:s' ),133 'compare' => '<',134 'type' => 'DATETIME',135 ),136 array(137 'key' => '_event_end',138 'value' => $boundary_start->format( 'Y-m-d H:i:s' ),139 'compare' => '>',140 'type' => 'DATETIME',141 ),142 ),143 ),144 );145 146 $events = array();147 foreach ( $event_ids as $event_id ) {148 $meta = get_post_meta( $event_id );149 $events[] = Event::from_post_meta( $event_id, $meta );150 }151 152 $this->active_events_cache->cache( $events );153 }154 155 // Filter out events that aren't actually active at $at.156 return array_filter(157 $events,158 function ( $event ) use ( $at ) {159 return $event->start() <= $at && $at <= $event->end();160 }161 );162 }163 164 /**165 118 * Filter an array of events so that it only includes events the given user is attending. 166 119 * … … 172 125 // phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found 173 126 private function select_events_user_is_registered_for( array $events, int $user_id ): array { 174 $attending_event_ids = get_user_meta( $user_id, Translation_Events::USER_META_KEY_ATTENDING, true);127 $attending_event_ids = $this->attendee_repository->get_events_for_user( $user_id ); 175 128 return array_filter( 176 129 $events, 177 130 function ( Event $event ) use ( $attending_event_ids ) { 178 return i sset( $attending_event_ids[ $event->id() ]);131 return in_array( $event->id(), $attending_event_ids, true ); 179 132 } 180 133 ); -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event.php
r13298 r13529 6 6 namespace Wporg\TranslationEvents; 7 7 8 use WP_Post; 9 10 /** @var WP_Post $event */ 8 use WP_User; 9 use Wporg\TranslationEvents\Attendee\Attendee; 10 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 11 use Wporg\TranslationEvents\Event\Event; 12 use Wporg\TranslationEvents\Event\Event_End_Date; 13 use Wporg\TranslationEvents\Event\Event_Start_Date; 14 15 /** @var Attendee_Repository $attendee_repo */ 16 /** @var Attendee $attendee */ 17 /** @var Event $event */ 11 18 /** @var int $event_id */ 12 19 /** @var string $event_title */ 13 20 /** @var string $event_description */ 14 /** @var string $event_start */ 15 /** @var string $event_end */ 16 /** @var bool $user_is_attending */ 21 /** @var Event_Start_Date $event_start */ 22 /** @var Event_End_Date $event_end */ 17 23 /** @var Event_Stats $event_stats */ 24 /** @var array $projects */ 25 /** @var WP_User $user */ 18 26 19 27 /* translators: %s: Event title. */ … … 21 29 gp_breadcrumb_translation_events( array( esc_html( $event_title ) ) ); 22 30 gp_tmpl_header(); 31 $event_page_title = $event_title; 23 32 gp_tmpl_load( 'events-header', get_defined_vars(), __DIR__ ); 24 33 ?> 25 34 26 35 <div class="event-page-wrapper"> 27 <div class="event-details-head">28 <h1>29 <?php echo esc_html( $event_title ); ?>30 <?php if ( 'draft' === $event->post_status ) : ?>31 <span class="event-label-draft"><?php echo esc_html( $event->post_status ); ?></span>32 <?php endif; ?>33 </h1>34 <p>35 Host: <a href="<?php echo esc_attr( get_author_posts_url( $event->post_author ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $event->post_author ) ); ?></a>36 <?php if ( current_user_can( 'edit_post', $event_id ) ) : ?>37 <a class="event-page-edit-link button" href="<?php echo esc_url( gp_url( 'events/edit/' . $event_id ) ); ?>"><span class="dashicons dashicons-edit"></span>Edit event</a>38 <?php endif ?>39 </p>40 </div>41 36 <div class="event-details-left"> 42 37 <div class="event-page-content"> … … 45 40 ?> 46 41 </div> 42 <?php if ( ! empty( $contributors ) ) : ?> 43 <div class="event-contributors"> 44 <h2><?php esc_html_e( 'Contributors', 'gp-translation-events' ); ?></h2> 45 <ul> 46 <?php foreach ( $contributors as $contributor ) : ?> 47 <li class="event-contributor" title="<?php echo esc_html( implode( ', ', $contributor->locales ) ); ?>"> 48 <a href="<?php echo esc_url( get_author_posts_url( $contributor->ID ) ); ?>"><?php echo get_avatar( $contributor->ID, 48 ); ?></a> 49 <a href="<?php echo esc_url( get_author_posts_url( $contributor->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $contributor->ID ) ); ?></a> 50 <?php if ( $stats_calculator->is_first_time_contributor( $event_start, $contributor->ID ) ) : ?> 51 <span class="first-time-contributor-tada"></span> 52 <?php endif; ?> 53 <?php 54 if ( ! $event->end()->is_in_the_past() ) : 55 if ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) ) : 56 if ( $user->ID !== $contributor->ID ) : 57 $_attendee = $attendee_repo->get_attendee( $event_id, $contributor->ID ); 58 if ( $_attendee instanceof Attendee ) : 59 echo '<form class="add-remove-user-as-host" method="post" action="' . esc_url( gp_url( "/events/host/$event_id/$contributor->ID" ) ) . '">'; 60 if ( $_attendee->is_host() ) : 61 if ( 1 === count( $attendee_repo->get_hosts( $event_id ) ) ) : 62 echo '<input type="submit" class="button is-primary remove-as-host" disabled value="Remove as host"/>'; 63 else : 64 echo '<input type="submit" class="button is-primary remove-as-host" value="Remove as host"/>'; 65 endif; 66 else : 67 echo '<input type="submit" class="button is-secondary convert-to-host" value="Make co-host"/>'; 68 endif; 69 echo '</form>'; 70 endif; 71 elseif ( ( $attendee instanceof Attendee && $attendee->is_host() ) ) : 72 echo '<span class="event-you">' . esc_html__( 'You (host)', 'gp-translation-events' ) . '</span>'; 73 else : 74 echo '<span class="event-you">' . esc_html__( 'You (event creator)', 'gp-translation-events' ) . '</span>'; 75 endif; 76 endif; 77 endif; 78 ?> 79 </li> 80 <?php endforeach; ?> 81 </ul> 82 </div> 83 <?php endif; ?> 84 <?php if ( ! empty( $attendees ) && ( ! $event->end()->is_in_the_past() || ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) ) ) ) : ?> 85 <div class="event-attendees"> 86 <h2><?php esc_html_e( 'Attendees', 'gp-translation-events' ); ?></h2> 87 <ul> 88 <?php foreach ( $attendees as $_user ) : ?> 89 <li class="event-attendee"> 90 <a href="<?php echo esc_url( get_author_posts_url( $_user->ID ) ); ?>"><?php echo get_avatar( $_user->ID, 48 ); ?></a> 91 <a href="<?php echo esc_url( get_author_posts_url( $_user->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $_user->ID ) ); ?></a> 92 <?php if ( $stats_calculator->is_first_time_contributor( $event_start, $_user->ID ) ) : ?> 93 <span class="first-time-contributor-tada"></span> 94 <?php endif; ?> 95 <?php 96 if ( ! $event->end()->is_in_the_past() ) : 97 if ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) ) : 98 if ( $user->ID !== $_user->ID ) : 99 $_attendee = $attendee_repo->get_attendee( $event_id, $_user->ID ); 100 if ( $_attendee instanceof Attendee ) : 101 echo '<form class="add-remove-user-as-host" method="post" action="' . esc_url( gp_url( "/events/host/$event_id/$_user->ID" ) ) . '">'; 102 if ( $_attendee->is_host() ) : 103 if ( 1 === count( $attendee_repo->get_hosts( $event_id ) ) ) : 104 echo '<input type="submit" class="button is-primary remove-as-host" disabled value="Remove as host"/>'; 105 else : 106 echo '<input type="submit" class="button is-primary remove-as-host" value="Remove as host"/>'; 107 endif; 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 elseif ( ( $attendee instanceof Attendee && $attendee->is_host() ) ) : 114 echo '<span class="event-you">' . esc_html__( 'You (host)', 'gp-translation-events' ) . '</span>'; 115 else : 116 echo '<span class="event-you">' . esc_html__( 'You (event creator)', 'gp-translation-events' ) . '</span>'; 117 endif; 118 endif; 119 endif; 120 ?> 121 </li> 122 <?php endforeach; ?> 123 </ul> 124 </div> 125 <?php endif; ?> 47 126 <?php if ( ! empty( $event_stats->rows() ) ) : ?> 48 <div class="event-details-stats"> 49 <h2><?php esc_html_e( 'Stats', 'gp-translation-events' ); ?></h2> 50 <table> 51 <thead> 52 <tr> 53 <th scope="col">Locale</th> 54 <th scope="col">Translations created</th> 55 <th scope="col">Translations reviewed</th> 56 <th scope="col">Contributors</th> 57 </tr> 58 </thead> 59 <tbody> 60 <?php /** @var $row Stats_Row */ ?> 61 <?php foreach ( $event_stats->rows() as $_locale => $row ) : ?> 62 <tr> 63 <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> 64 <td><?php echo esc_html( $row->created ); ?></td> 65 <td><?php echo esc_html( $row->reviewed ); ?></td> 66 <td><?php echo esc_html( $row->users ); ?></td> 67 </tr> 68 <?php endforeach ?> 69 <tr class="event-details-stats-totals"> 70 <td>Total</td> 71 <td><?php echo esc_html( $event_stats->totals()->created ); ?></td> 72 <td><?php echo esc_html( $event_stats->totals()->reviewed ); ?></td> 73 <td><?php echo esc_html( $event_stats->totals()->users ); ?></td> 74 </tr> 75 </tbody> 76 </table> 77 </div> 78 <div class="event-contributors"> 79 <h2><?php esc_html_e( 'Contributors', 'gp-translation-events' ); ?></h2> 80 <ul> 81 <?php foreach ( $contributors as $contributor ) : ?> 82 <li class="event-contributor" title="<?php echo esc_html( implode( ', ', $contributor->locales ) ); ?>" 83 <a href="<?php echo esc_url( get_author_posts_url( $contributor->ID ) ); ?>"><?php echo get_avatar( $contributor->ID, 48 ); ?></a> 84 <a href="<?php echo esc_url( get_author_posts_url( $contributor->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $contributor->ID ) ); ?></a> 85 </li> 86 <?php endforeach; ?> 87 </ul> 88 </div> 89 <details class="event-stats-summary"> 90 <summary>View stats summary in text </summary> 91 <p class="event-stats-text"> 92 <?php 93 echo wp_kses( 94 sprintf( 95 // translators: %1$s: Event title, %2$d: Number of contributors, %3$d: Number of languages, %4$s: List of languages, %5$d: Number of strings translated, %6$d: Number of strings reviewed. 96 __( 'At the <strong>%1$s</strong> event, %2$d people contributed in %3$d languages (%4$s), translated %5$d strings and reviewed %6$d strings.', 'gp-translation-events' ), 97 esc_html( $event_title ), 98 esc_html( $event_stats->totals()->users ), 99 count( $event_stats->rows() ), 100 esc_html( 101 implode( 102 ', ', 103 array_map( 104 function ( $row ) { 105 return $row->language->english_name; 106 }, 107 $event_stats->rows() 127 <div class="event-details-stats"> 128 <h2><?php esc_html_e( 'Stats', 'gp-translation-events' ); ?></h2> 129 <table> 130 <thead> 131 <tr> 132 <th scope="col">Locale</th> 133 <th scope="col">Translations created</th> 134 <th scope="col">Translations reviewed</th> 135 <th scope="col">Contributors</th> 136 </tr> 137 </thead> 138 <tbody> 139 <?php /** @var $row Stats_Row */ ?> 140 <?php foreach ( $event_stats->rows() as $_locale => $row ) : ?> 141 <tr> 142 <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> 143 <td><?php echo esc_html( $row->created ); ?></td> 144 <td><?php echo esc_html( $row->reviewed ); ?></td> 145 <td><?php echo esc_html( $row->users ); ?></td> 146 </tr> 147 <?php endforeach ?> 148 <tr class="event-details-stats-totals"> 149 <td>Total</td> 150 <td><?php echo esc_html( $event_stats->totals()->created ); ?></td> 151 <td><?php echo esc_html( $event_stats->totals()->reviewed ); ?></td> 152 <td><?php echo esc_html( $event_stats->totals()->users ); ?></td> 153 </tr> 154 </tbody> 155 </table> 156 </div> 157 <div class="event-projects"> 158 <h2><?php esc_html_e( 'Projects', 'gp-translation-events' ); ?></h2> 159 <ul> 160 <?php foreach ( $projects as $project_name => $row ) : ?> 161 <li class="event-project" title="<?php echo esc_html( str_replace( ',', ', ', $row->locales ) ); ?>"> 162 <a href="<?php echo esc_url( gp_url_project( $row->project ) ); ?>"><?php echo esc_html( $project_name ); ?></a> <small> to 163 <?php 164 foreach ( explode( ',', $row->locales ) as $_locale ) { 165 $_locale = \GP_Locales::by_slug( $_locale ); 166 ?> 167 <a href="<?php echo esc_url( gp_url_project_locale( $row->project, $_locale, 'default' ) ); ?>"><?php echo esc_html( $_locale->english_name ); ?></a> 168 <?php 169 } 170 // translators: %d: Number of contributors. 171 echo esc_html( sprintf( _n( 'by %d contributor', 'by %d contributors', $row->users, 'gp-translation-events' ), $row->users ) ); 172 ?> 173 </small> 174 </li> 175 <?php endforeach; ?> 176 </ul> 177 </div> 178 <details class="event-stats-summary"> 179 <summary>View stats summary in text </summary> 180 <p class="event-stats-text"> 181 <?php 182 echo wp_kses( 183 sprintf( 184 // translators: %1$s: Event title, %2$d: Number of contributors, %3$d: Number of languages, %4$s: List of languages, %5$d: Number of strings translated, %6$d: Number of strings reviewed. 185 __( 'At the <strong>%1$s</strong> event, %2$d people contributed in %3$d languages (%4$s), translated %5$d strings and reviewed %6$d strings.', 'gp-translation-events' ), 186 esc_html( $event_title ), 187 esc_html( $event_stats->totals()->users ), 188 count( $event_stats->rows() ), 189 esc_html( 190 implode( 191 ', ', 192 array_map( 193 function ( $row ) { 194 return $row->language->english_name; 195 }, 196 $event_stats->rows() 197 ) 198 ) 199 ), 200 esc_html( $event_stats->totals()->created ), 201 esc_html( $event_stats->totals()->reviewed ) 202 ), 203 array( 204 'strong' => array(), 205 ) 206 ); 207 ?> 208 <?php 209 echo wp_kses( 210 sprintf( 211 // translators: %s the contributors. 212 esc_html__( 'Contributors were %s.', 'gp-translation-events' ), 213 implode( 214 ', ', 215 array_map( 216 function ( $contributor ) use ( $stats_calculator, $event_start ) { 217 $append_tada = $stats_calculator->is_first_time_contributor( $event_start, $contributor->ID ) ? '<span class="first-time-contributor-tada"></span>' : ''; 218 return '@' . $contributor->user_login . $append_tada; 219 }, 220 $contributors 221 ) 108 222 ) 223 ), 224 array( 225 'span' => array( 226 'class' => array(), 227 ), 109 228 ) 110 ), 111 esc_html( $event_stats->totals()->created ), 112 esc_html( $event_stats->totals()->reviewed ) 113 ), 114 array( 115 'strong' => array(), 116 ) 117 ); 118 ?> 119 <?php 120 echo esc_html( 121 sprintf( 122 // translators: %s the contributors. 123 __( 'Contributors were %s.', 'gp-translation-events' ), 124 esc_html( 125 implode( 126 ', ', 127 array_map( 128 function ( $contributor ) { 129 return '@' . $contributor->user_login; 130 }, 131 $contributors 132 ) 133 ) 134 ) 135 ) 136 ); 137 ?> 229 ); 230 ?> 138 231 </p> 139 232 </details> 140 141 <?php endif; ?> 233 <?php endif; ?> 142 234 </div> 143 235 <div class="event-details-right"> 144 236 <div class="event-details-date"> 145 237 <p> 146 <span class="event-details-date-label">Starts:</span> <time class="event-utc-time" datetime="<?php echo esc_attr( $event_start ); ?>"></time> 147 <span class="event-details-date-label">Ends:</span><time class="event-utc-time" datetime="<?php echo esc_attr( $event_end ); ?>"></time> 238 <span class="event-details-date-label"> 239 <?php echo esc_html( $event_start->is_in_the_past() ? __( 'Started', 'gp-translation-events' ) : __( 'Starts', 'gp-translation-events' ) ); ?>: 240 <?php $event_start->print_relative_time_html(); ?> 241 </span> 242 <?php $event_start->print_time_html(); ?> 243 <span class="event-details-date-label"> 244 <?php echo esc_html( $event_end->is_in_the_past() ? __( 'Ended', 'gp-translation-events' ) : __( 'Ends', 'gp-translation-events' ) ); ?>: 245 <?php $event_end->print_relative_time_html(); ?> 246 247 </span> 248 <?php $event_end->print_time_html(); ?> 148 249 </p> 149 250 </div> 150 251 <?php if ( is_user_logged_in() ) : ?> 151 252 <div class="event-details-join"> 152 <?php 153 $current_time = gmdate( 'Y-m-d H:i:s' ); 154 if ( strtotime( $current_time ) > strtotime( $event_end ) ) : 155 ?> 156 <?php if ( $user_is_attending ) : ?> 157 <span class="event-details-join-expired"><?php esc_html_e( 'You attended', 'gp-translation-events' ); ?></span> 158 <?php endif ?> 253 <?php if ( $event_end->is_in_the_past() ) : ?> 254 <?php if ( $attendee instanceof Attendee ) : ?> 255 <button disabled="disabled" class="button is-primary attend-btn"><?php esc_html_e( 'You attended', 'gp-translation-events' ); ?></button> 256 <?php endif; ?> 159 257 <?php else : ?> 160 258 <form class="event-details-attend" method="post" action="<?php echo esc_url( gp_url( "/events/attend/$event_id" ) ); ?>"> 161 <?php if ( ! $user_is_attending ) : ?> 259 <?php if ( $attendee instanceof Attendee ) : ?> 260 <?php if ( $attendee->is_host() && ( 1 === count( $attendee_repo->get_hosts( $event_id ) ) ) ) : ?> 261 <input type="submit" class="button is-secondary attending-btn" disabled value="You're attending" /> 262 <?php else : ?> 263 <input type="submit" class="button is-secondary attending-btn" value="You're attending" /> 264 <?php endif; ?> 265 <?php else : ?> 162 266 <input type="submit" class="button is-primary attend-btn" value="Attend Event"/> 163 <?php else : ?> 164 <input type="submit" class="button is-secondary attending-btn" value="You're attending"/> 165 <?php endif ?> 267 <?php endif; ?> 166 268 </form> 167 <?php endif ?>269 <?php endif; ?> 168 270 </div> 169 271 <?php else : ?> 170 272 <div class="event-details-join"> 171 273 <p> 172 <?php global $wp; ?> 173 <a href="<?php echo esc_url( wp_login_url( home_url( $wp->request ) ) ); ?>" class="button is-primary attend-btn"><?php esc_html_e( 'Login to attend', 'gp-translation-events' ); ?></a> 274 <?php if ( ! $event_end->is_in_the_past() ) : ?> 275 <a href="<?php echo esc_url( wp_login_url() ); ?>" class="button is-primary attend-btn"><?php esc_html_e( 'Login to attend', 'gp-translation-events' ); ?></a> 276 <?php else : ?> 277 <button disabled="disabled" class="button is-primary attend-btn"><?php esc_html_e( 'Event is over', 'gp-translation-events' ); ?></button> 278 <?php endif; ?> 174 279 </p> 175 280 </div> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-form.php
r13268 r13529 6 6 namespace Wporg\TranslationEvents; 7 7 8 /** @var string $event_form_title */ 8 use DateTimeZone; 9 use Wporg\TranslationEvents\Event\Event_End_Date; 10 use Wporg\TranslationEvents\Event\Event_Start_Date; 11 12 /** @var string $event_page_title */ 9 13 /** @var string $event_form_name */ 10 /** @var int $event_id */14 /** @var int $event_id */ 11 15 /** @var string $event_title */ 12 16 /** @var string $event_description */ 13 /** @var string$event_start */14 /** @var string$event_end */15 /** @var string$event_timezone */17 /** @var Event_Start_Date $event_start */ 18 /** @var Event_End_Date $event_end */ 19 /** @var DateTimeZone|null $event_timezone */ 16 20 /** @var string $event_url */ 17 21 /** @var string $css_show_url */ 18 22 19 gp_title( __( 'Translation Events' ) . ' - ' . esc_html( $event_ form_title . ' - ' . $event_title ) );20 gp_breadcrumb_translation_events( array( esc_html( $event_ form_title ) ) );23 gp_title( __( 'Translation Events' ) . ' - ' . esc_html( $event_page_title . ' - ' . $event_title ) ); 24 gp_breadcrumb_translation_events( array( esc_html( $event_page_title ) ) ); 21 25 gp_tmpl_header(); 22 26 gp_tmpl_load( 'events-header', get_defined_vars(), __DIR__ ); 23 27 ?> 24 28 <div class="event-page-wrapper"> 25 <h2 class="event-page-title"><?php echo esc_html( $event_form_title ); ?></h2>26 29 <form class="translation-event-form" action="" method="post"> 27 30 <?php wp_nonce_field( '_event_nonce', '_event_nonce' ); ?> … … 41 44 <label for="event-description">Event Description</label> 42 45 <textarea id="event-description" name="event_description" rows="4" required><?php echo esc_html( $event_description ); ?></textarea> 43 </div> 44 <div> 46 <?php 47 echo wp_kses( 48 Event_Text_Snippet::get_snippet_links(), 49 array( 50 'a' => array( 51 'href' => array(), 52 'data-snippet' => array(), 53 'class' => array(), 54 ), 55 'ul' => array( 'class' => array() ), 56 'li' => array(), 57 ) 58 ); 59 ?> 60 <div> 45 61 <label for="event-start">Start Date</label> 46 <input type="datetime-local" id="event-start" name="event_start" value="<?php echo esc_attr( $event_start ); ?>" required>62 <input type="datetime-local" id="event-start" name="event_start" value="<?php echo esc_attr( $event_start->format( 'Y-m-d H:i' ) ); ?>" required> 47 63 </div> 48 64 <div> 49 65 <label for="event-end">End Date</label> 50 <input type="datetime-local" id="event-end" name="event_end" value="<?php echo esc_attr( $event_end ); ?>" required>66 <input type="datetime-local" id="event-end" name="event_end" value="<?php echo esc_attr( $event_end->format( 'Y-m-d H:i' ) ); ?>" required> 51 67 </div> 52 68 <div> 53 69 <label for="event-timezone">Event Timezone</label> 54 <select id="event-timezone" name="event_timezone" required>70 <select id="event-timezone" name="event_timezone" required> 55 71 <?php 56 72 echo wp_kses( 57 wp_timezone_choice( $event_timezone , get_user_locale() ),73 wp_timezone_choice( $event_timezone ? $event_timezone->getName() : null, get_user_locale() ), 58 74 array( 59 75 'optgroup' => array( 'label' => array() ), -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-header.php
r13268 r13529 1 1 <?php 2 2 3 namespace Wporg\TranslationEvents; 3 4 4 5 use GP; 6 use Wporg\TranslationEvents\Attendee\Attendee; 7 use Wporg\TranslationEvents\Event\Event; 8 9 /** @var Attendee $attendee */ 10 /** @var Event $event */ 11 /** @var string $event_page_title */ 12 /** @var bool $is_editable_event */ 5 13 ?> 6 14 7 15 <div class="event-list-top-bar"> 16 <h2 class="event-page-title"> 17 <?php echo esc_html( $event_page_title ); ?> 18 <?php if ( isset( $event ) && 'draft' === $event->status() ) : ?> 19 <span class="event-label-draft"><?php echo esc_html( $event->status() ); ?></span> 20 <?php endif; ?> 21 </h2> 8 22 <ul class="event-list-nav"> 9 23 <?php if ( is_user_logged_in() ) : ?> … … 22 36 <?php endif; ?> 23 37 </ul> 38 <?php if ( isset( $event ) && ! isset( $event_form_name ) ) : ?> 39 <p class="event-sub-head"> 40 <span class="event-host"> 41 <?php 42 if ( 1 === count( $hosts ) ) : 43 esc_html_e( 'Host:', 'gp-translation-events' ); 44 else : 45 esc_html_e( 'Hosts:', 'gp-translation-events' ); 46 endif; 47 ?> 48 <?php foreach ( $hosts as $host ) : ?> 49 <?php $user = get_userdata( $host->user_id() ); ?> 50 <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> 51 <?php if ( end( $hosts ) !== $host ) : ?> 52 , 53 <?php endif; ?> 54 <?php endforeach; ?> 55 .</span> 56 <?php $show_edit_button = ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event->id() ) ) && $is_editable_event; ?> 57 <?php if ( $show_edit_button ) : ?> 58 <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> 59 <?php endif ?> 60 </p> 61 <?php endif; ?> 62 24 63 </div> -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list.php
r13268 r13529 8 8 use DateTime; 9 9 use WP_Query; 10 use Wporg\TranslationEvents\Event\Event; 11 use Wporg\TranslationEvents\Event\Events_Query_Result; 10 12 11 /** @var WP_Query $current_events_query */ 12 /** @var WP_Query $upcoming_events_query */ 13 /** @var WP_Query $past_events_query */ 13 /** @var Events_Query_Result $current_events_query */ 14 /** @var Events_Query_Result $upcoming_events_query */ 15 /** @var Events_Query_Result $past_events_query */ 16 /** @var Events_Query_Result $user_attending_events_query */ 14 17 15 18 gp_title( __( 'Translation Events', 'gp-translation-events' ) ); 16 19 gp_breadcrumb_translation_events(); 17 20 gp_tmpl_header(); 21 $event_page_title = __( 'Translation Events', 'gp-translation-events' ); 18 22 gp_tmpl_load( 'events-header', get_defined_vars(), __DIR__ ); 19 23 ?> 20 24 21 25 <div class="event-page-wrapper"> 22 <h1 class="event_page_title"><?php esc_html_e( 'Translation Events', 'gp-translation-events' ); ?></h1>23 26 <div class="event-left-col"> 24 27 <?php 25 if ( $current_events_query->have_posts() ) :28 if ( ! empty( $current_events_query->events ) ) : 26 29 ?> 27 30 <h2><?php esc_html_e( 'Current events', 'gp-translation-events' ); ?></h2> 28 31 <ul class="event-list"> 29 32 <?php 30 while ( $current_events_query->have_posts() ) : 31 $current_events_query->the_post(); 32 $event_end = Event::get_end_date_text( get_post_meta( get_the_ID(), '_event_end', true ) ); 33 $event_url = gp_url( wp_make_link_relative( get_the_permalink() ) ); 33 foreach ( $current_events_query->events as $event ) : 34 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 34 35 ?> 35 36 <li class="event-list-item"> 36 <a href="<?php echo esc_url( $event_url ); ?>"><?php the_title(); ?></a>37 <span class="event-list-date"> <?php echo esc_html( $event_end ); ?></span>38 <?php the_excerpt(); ?>37 <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 38 <span class="event-list-date">ends <?php $event->end()->print_relative_time_html(); ?></time></span> 39 <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?> 39 40 </li> 40 41 <?php 41 end while;42 endforeach; 42 43 ?> 43 44 </ul> … … 47 48 paginate_links( 48 49 array( 49 'total' => $current_events_query-> max_num_pages,50 'current' => max( 1, $current_events_query->query_vars['paged'] ),50 'total' => $current_events_query->page_count, 51 'current' => $current_events_query->current_page, 51 52 'format' => '?current_events_paged=%#%', 52 53 'prev_text' => '« Previous', … … 58 59 wp_reset_postdata(); 59 60 endif; 60 if ( $upcoming_events_query->have_posts() ) : 61 62 if ( ! empty( $upcoming_events_query->events ) ) : 61 63 ?> 62 64 <h2><?php esc_html_e( 'Upcoming events', 'gp-translation-events' ); ?></h2> 63 65 <ul class="event-list"> 64 66 <?php 65 while ( $upcoming_events_query->have_posts() ) : 66 $upcoming_events_query->the_post(); 67 $event_start = ( new DateTime( get_post_meta( get_the_ID(), '_event_start', true ) ) )->format( 'l, F j, Y' ); 67 foreach ( $upcoming_events_query->events as $event ) : 68 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 68 69 ?> 69 70 <li class="event-list-item"> 70 <a href="<?php echo esc_url( gp_url( wp_make_link_relative( get_the_permalink() ) ) ); ?>"><?php the_title(); ?></a>71 <span class="event-list-date"> <?php echo esc_html( $event_start); ?></span>72 <?php the_excerpt(); ?>71 <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 72 <span class="event-list-date">starts <?php $event->start()->print_relative_time_html(); ?></span> 73 <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?> 73 74 </li> 74 75 <?php 75 end while;76 endforeach; 76 77 ?> 77 78 </ul> … … 81 82 paginate_links( 82 83 array( 83 'total' => $upcoming_events_query-> max_num_pages,84 'current' => max( 1, $upcoming_events_query->query_vars['paged'] ),84 'total' => $upcoming_events_query->page_count, 85 'current' => $upcoming_events_query->current_page, 85 86 'format' => '?upcoming_events_paged=%#%', 86 87 'prev_text' => '« Previous', … … 92 93 wp_reset_postdata(); 93 94 endif; 94 if ( $past_events_query->have_posts() ) :95 if ( ! empty( $past_events_query->events ) ) : 95 96 ?> 96 97 <h2><?php esc_html_e( 'Past events', 'gp-translation-events' ); ?></h2> 97 98 <ul class="event-list"> 98 99 <?php 99 while ( $past_events_query->have_posts() ) : 100 $past_events_query->the_post(); 101 $event_start = ( new DateTime( get_post_meta( get_the_ID(), '_event_start', true ) ) )->format( 'M j, Y' ); 102 $event_end = ( new DateTime( get_post_meta( get_the_ID(), '_event_end', true ) ) )->format( 'M j, Y' ); 100 foreach ( $past_events_query->events as $event ) : 101 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 103 102 ?> 104 103 <li class="event-list-item"> 105 <a href="<?php echo esc_url( gp_url( wp_make_link_relative( get_the_permalink() ) ) ); ?>"><?php the_title(); ?></a> 106 <?php if ( $event_start === $event_end ) : ?> 107 <span class="event-list-date"><?php echo esc_html( $event_start ); ?></span> 108 <?php else : ?> 109 <span class="event-list-date"><?php echo esc_html( $event_start ); ?> - <?php echo esc_html( $event_end ); ?></span> 110 <?php endif; ?> 111 <?php the_excerpt(); ?> 104 <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 105 <span class="event-list-date">ended <?php $event->end()->print_relative_time_html( 'F j, Y H:i T' ); ?></span> 106 <?php esc_html( get_the_excerpt( $event->id() ) ); ?> 112 107 </li> 113 108 <?php 114 end while;109 endforeach; 115 110 ?> 116 111 </ul> … … 120 115 paginate_links( 121 116 array( 122 'total' => $past_events_query-> max_num_pages,123 'current' => max( 1, $past_events_query->query_vars['paged'] ),117 'total' => $past_events_query->page_count, 118 'current' => $past_events_query->current_page, 124 119 'format' => '?past_events_paged=%#%', 125 120 'prev_text' => '« Previous', … … 132 127 endif; 133 128 134 if ( 0 === $current_events_query->post_count && 0 === $upcoming_events_query->post_count && 0 === $past_events_query->post_count) :129 if ( empty( $current_events_query->events ) && empty( $upcoming_events_query->events ) && empty( $past_events_query->post_count ) ) : 135 130 esc_html_e( 'No events found.', 'gp-translation-events' ); 136 131 endif; … … 139 134 <?php if ( is_user_logged_in() ) : ?> 140 135 <div class="event-right-col"> 141 <h 3 class="">Events I'm Attending</h3>142 <?php if ( ! $user_attending_events_query->have_posts() ) : ?>136 <h2>Events I'm Attending</h2> 137 <?php if ( empty( $user_attending_events_query->events ) ) : ?> 143 138 <p>You don't have any events to attend.</p> 144 139 <?php else : ?> 145 140 <ul class="event-attending-list"> 146 141 <?php 147 while ( $user_attending_events_query->have_posts() ) : 148 $user_attending_events_query->the_post(); 149 $event_start = ( new DateTime( get_post_meta( get_the_ID(), '_event_start', true ) ) )->format( 'M j, Y' ); 150 $event_end = ( new DateTime( get_post_meta( get_the_ID(), '_event_end', true ) ) )->format( 'M j, Y' ); 142 foreach ( $user_attending_events_query->events as $event ) : 143 $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ); 151 144 ?> 152 145 <li class="event-list-item"> 153 <a href="<?php echo esc_url( gp_url( wp_make_link_relative( get_the_permalink() ) ) ); ?>"><?php the_title(); ?></a>154 <?php if ( $event _start === $event_end) : ?>155 <span class="event-list-date events-i-am-attending"><?php echo esc_html( $event_start); ?></span>146 <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 147 <?php if ( $event->start() === $event->end() ) : ?> 148 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html( 'F j, Y H:i T' ); ?></span> 156 149 <?php else : ?> 157 <span class="event-list-date events-i-am-attending"><?php echo esc_html( $event_start ); ?> - <?php echo esc_html( $event_end); ?></span>150 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html( 'F j, Y H:i T' ); ?> - <?php $event->end()->print_time_html( 'F j, Y H:i T' ); ?></span> 158 151 <?php endif; ?> 159 152 </li> 160 153 <?php 161 end while;154 endforeach; 162 155 ?> 163 156 </ul> … … 166 159 paginate_links( 167 160 array( 168 'total' => $user_attending_events_query-> max_num_pages,169 'current' => max( 1, $user_attending_events_query->query_vars['paged'] ),161 'total' => $user_attending_events_query->page_count, 162 'current' => $user_attending_events_query->current_page, 170 163 'format' => '?user_attending_events_paged=%#%', 171 164 'prev_text' => '« Previous', -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-my-events.php
r13268 r13529 6 6 namespace Wporg\TranslationEvents; 7 7 8 use DateTime; 9 use WP_Query; 8 use Wporg\TranslationEvents\Event\Events_Query_Result; 10 9 11 /** @var WP_Query $events_i_created_query */ 12 /** @var WP_Query $events_i_attended_query */ 10 /** @var Events_Query_Result $events_i_created_query */ 11 /** @var Events_Query_Result $events_i_host_query */ 12 /** @var Events_Query_Result $events_i_attended_query */ 13 13 14 14 gp_title( esc_html__( 'Translation Events', 'gp-translation-events' ) . ' - ' . esc_html__( 'My Events', 'gp-translation-events' ) ); 15 15 gp_breadcrumb_translation_events( array( esc_html__( 'My Events', 'gp-translation-events' ) ) ); 16 16 gp_tmpl_header(); 17 $event_page_title = __( 'My Events', 'gp-translation-events' ); 17 18 gp_tmpl_load( 'events-header', get_defined_vars(), __DIR__ ); 18 19 ?> 19 20 20 21 <div class="event-page-wrapper"> 21 <h1 class="event_page_title"><?php esc_html_e( 'My Events', 'gp-translation-events' ); ?> </h1> 22 <h2 class="event_page_title"><?php esc_html_e( 'Events I have created', 'gp-translation-events' ); ?> </h2> 23 <?php if ( $events_i_created_query->have_posts() ) : ?> 22 <?php if ( ! empty( $events_i_host_query->events ) ) : ?> 23 <h2><?php esc_html_e( 'Events I host', 'gp-translation-events' ); ?> </h2> 24 24 <ul> 25 25 <?php 26 while ( $events_i_created_query->have_posts() ) : 27 $events_i_created_query->the_post(); 28 $event_id = get_the_ID(); 29 $event_start = get_post_meta( $event_id, '_event_start', true ); 30 list( $permalink, $post_name ) = get_sample_permalink( $event_id ); 26 foreach ( $events_i_host_query->events as $event ) : 27 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 31 28 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 32 29 $event_url = gp_url( wp_make_link_relative( $permalink ) ); 33 $event_edit_url = gp_url( 'events/edit/' . $event_id ); 34 $event_status = get_post_status( $event_id ); 35 $event_start = ( new DateTime( get_post_meta( get_the_ID(), '_event_start', true ) ) )->format( 'M j, Y' ); 36 $event_end = ( new DateTime( get_post_meta( get_the_ID(), '_event_end', true ) ) )->format( 'M j, Y' ); 30 $event_edit_url = gp_url( 'events/edit/' . $event->id() ); 31 $stats_calculator = new Stats_Calculator(); 32 $has_stats = $stats_calculator->event_has_stats( $event->id() ); 37 33 ?> 38 34 <li class="event-list-item"> 39 <a class="event-link-<?php echo esc_attr( $event_status ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php the_title(); ?></a> 40 <a href="<?php echo esc_url( $event_edit_url ); ?>" class="button is-small action edit">Edit</a> 41 <?php if ( 'draft' === $event_status ) : ?> 42 <span class="event-label-<?php echo esc_attr( $event_status ); ?>"><?php echo esc_html( $event_status ); ?></span> 35 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 36 <?php if ( ! $event->end()->is_in_the_past() && ! $has_stats ) : ?> 37 <a href="<?php echo esc_url( $event_edit_url ); ?>" class="button is-small action edit">Edit</a> 43 38 <?php endif; ?> 44 <?php if ( $event_start === $event_end ) : ?> 45 <span class="event-list-date events-i-am-attending"><?php echo esc_html( $event_start ); ?></span> 39 <?php if ( 'draft' === $event->status() ) : ?> 40 <span class="event-label-<?php echo esc_attr( $event->status() ); ?>"><?php echo esc_html( $event->status() ); ?></span> 41 <?php endif; ?> 42 <?php if ( $event->start()->format( 'Y-m-d' ) === $event->end()->format( 'Y-m-d' ) ) : ?> 43 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?></span> 46 44 <?php else : ?> 47 <span class="event-list-date events-i-am-attending"><?php echo esc_html( $event_start ); ?> - <?php echo esc_html( $event_end); ?></span>45 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?> - <?php $event->end()->print_time_html(); ?></span> 48 46 <?php endif; ?> 49 <p><?php the_excerpt(); ?></p>47 <p><?php echo esc_html( get_the_excerpt( $event->id() ) ); ?></p> 50 48 </li> 51 <?php end while; ?>49 <?php endforeach; ?> 52 50 </ul> 53 51 … … 56 54 paginate_links( 57 55 array( 58 'total' => $events_i_created_query->max_num_pages, 59 'current' => max( 1, $events_i_created_query->query_vars['events_i_created_paged'] ), 56 'total' => $events_i_host_query->page_count, 57 'current' => $events_i_host_query->current_page, 58 'format' => '?events_i_hosted_paged=%#%', 59 'prev_text' => '« Previous', 60 'next_text' => 'Next »', 61 ) 62 ) ?? '' 63 ); 64 65 wp_reset_postdata(); 66 endif; 67 ?> 68 69 <?php if ( ! empty( $events_i_created_query->events ) ) : ?> 70 <h2><?php esc_html_e( 'Events I have created', 'gp-translation-events' ); ?> </h2> 71 <ul> 72 <?php 73 foreach ( $events_i_created_query->events as $event ) : 74 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 75 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 76 $event_url = gp_url( wp_make_link_relative( $permalink ) ); 77 $event_edit_url = gp_url( 'events/edit/' . $event->id() ); 78 $stats_calculator = new Stats_Calculator(); 79 $has_stats = $stats_calculator->event_has_stats( $event->id() ); 80 ?> 81 <li class="event-list-item"> 82 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 83 <?php if ( ! $event->end()->is_in_the_past() && ! $has_stats ) : ?> 84 <a href="<?php echo esc_url( $event_edit_url ); ?>" class="button is-small action edit">Edit</a> 85 <?php endif; ?> 86 <?php if ( 'draft' === $event->status() ) : ?> 87 <span class="event-label-<?php echo esc_attr( $event->status() ); ?>"><?php echo esc_html( $event->status() ); ?></span> 88 <?php endif; ?> 89 <?php if ( $event->start()->format( 'Y-m-d' ) === $event->end()->format( 'Y-m-d' ) ) : ?> 90 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?></span> 91 <?php else : ?> 92 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?> - <?php $event->end()->print_time_html(); ?></span> 93 <?php endif; ?> 94 <p><?php echo esc_html( get_the_excerpt( $event->id() ) ); ?></p> 95 </li> 96 <?php endforeach; ?> 97 </ul> 98 99 <?php 100 echo wp_kses_post( 101 paginate_links( 102 array( 103 'total' => $events_i_created_query->page_count, 104 'current' => $events_i_created_query->current_page, 60 105 'format' => '?events_i_created_paged=%#%', 61 106 'prev_text' => '« Previous', … … 66 111 67 112 wp_reset_postdata(); 68 else :69 echo 'No events found.';70 113 endif; 71 114 ?> 72 115 73 <h2 class="event_page_title"><?php esc_html_e( 'Events I attended', 'gp-translation-events' ); ?> </h2>74 <?php if ( $events_i_attended_query->have_posts() ) : ?>116 <h2><?php esc_html_e( 'Events I attended', 'gp-translation-events' ); ?> </h2> 117 <?php if ( ! empty( $events_i_attended_query->events ) ) : ?> 75 118 <ul> 76 119 <?php 77 while ( $events_i_attended_query->have_posts() ) : 78 $events_i_attended_query->the_post(); 79 $event_id = get_the_ID(); 80 $event_start = get_post_meta( $event_id, '_event_start', true ); 81 list( $permalink, $post_name ) = get_sample_permalink( $event_id ); 120 foreach ( $events_i_attended_query->events as $event ) : 121 list( $permalink, $post_name ) = get_sample_permalink( $event->id() ); 82 122 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 83 123 $event_url = gp_url( wp_make_link_relative( $permalink ) ); 84 $event_edit_url = gp_url( 'events/edit/' . $event_id );85 $event_status = get_post_status( $event_id );86 $event_start = ( new DateTime( get_post_meta( get_the_ID(), '_event_start', true ) ) )->format( 'M j, Y' );87 $event_end = ( new DateTime( get_post_meta( get_the_ID(), '_event_end', true ) ) )->format( 'M j, Y' );88 124 ?> 89 125 <li class="event-list-item"> 90 <a class="event-link-<?php echo esc_attr( $event _status ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php the_title(); ?></a>91 <?php if ( $event _start === $event_end) : ?>92 <span class="event-list-date events-i-am-attending"><?php echo esc_html( $event_start); ?></span>126 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a> 127 <?php if ( $event->start() === $event->end() ) : ?> 128 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?></span> 93 129 <?php else : ?> 94 <span class="event-list-date events-i-am-attending"><?php echo esc_html( $event_start ); ?> - <?php echo esc_html( $event_end); ?></span>130 <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?> - <?php $event->end()->print_time_html(); ?></span> 95 131 <?php endif; ?> 96 <p><?php the_excerpt(); ?></p>132 <p><?php echo esc_html( get_the_excerpt( $event->id() ) ); ?></p> 97 133 </li> 98 <?php end while; ?>134 <?php endforeach; ?> 99 135 </ul> 100 136 … … 103 139 paginate_links( 104 140 array( 105 'total' => $events_i_attended_query-> max_num_pages,106 'current' => max( 1, $events_i_attended_query->query_vars['events_i_attended_paged'] ),141 'total' => $events_i_attended_query->page_count, 142 'current' => $events_i_attended_query->current_page, 107 143 'format' => '?events_i_attended_paged=%#%', 108 144 'prev_text' => '« Previous', -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php
r13298 r13529 25 25 use WP_Post; 26 26 use WP_Query; 27 use Wporg\TranslationEvents\Attendee\Attendee; 28 use Wporg\TranslationEvents\Attendee\Attendee_Repository; 29 use Wporg\TranslationEvents\Event\Event_Form_Handler; 30 use Wporg\TranslationEvents\Event\Event_Repository_Cached; 31 use Wporg\TranslationEvents\Event\Event_Repository_Interface; 27 32 28 33 class Translation_Events { 29 public const CPT = 'translation_event'; 30 public const USER_META_KEY_ATTENDING = 'translation-events-attending'; 31 32 public static function get_instance() { 34 public const CPT = 'translation_event'; 35 36 public static function get_instance(): Translation_Events { 33 37 static $instance = null; 34 38 if ( null === $instance ) { 39 require_once __DIR__ . '/autoload.php'; 35 40 $instance = new self(); 36 41 } … … 38 43 } 39 44 45 public static function get_event_repository(): Event_Repository_Interface { 46 static $event_repository = null; 47 if ( null === $event_repository ) { 48 $event_repository = new Event_Repository_Cached( self::get_attendee_repository() ); 49 } 50 return $event_repository; 51 } 52 53 public static function get_attendee_repository(): Attendee_Repository { 54 static $attendee_repository = null; 55 if ( null === $attendee_repository ) { 56 $attendee_repository = new Attendee_Repository(); 57 } 58 return $attendee_repository; 59 } 60 40 61 public function __construct() { 41 \add_action( 'wp_ajax_submit_event_ajax', array( $this, 'submit_event_ajax' ) ); 42 \add_action( 'wp_ajax_nopriv_submit_event_ajax', array( $this, 'submit_event_ajax' ) ); 43 \add_action( 'wp_enqueue_scripts', array( $this, 'register_translation_event_js' ) ); 44 \add_action( 'init', array( $this, 'register_event_post_type' ) ); 45 \add_action( 'add_meta_boxes', array( $this, 'event_meta_boxes' ) ); 46 \add_action( 'save_post', array( $this, 'save_event_meta_boxes' ) ); 47 \add_action( 'transition_post_status', array( $this, 'event_status_transition' ), 10, 3 ); 48 \add_filter( 'gp_nav_menu_items', array( $this, 'gp_event_nav_menu_items' ), 10, 2 ); 49 \add_filter( 'wp_insert_post_data', array( $this, 'generate_event_slug' ), 10, 2 ); 50 \add_action( 'gp_init', array( $this, 'gp_init' ) ); 51 \add_action( 'gp_before_translation_table', array( $this, 'add_active_events_current_user' ) ); 52 \register_activation_hook( __FILE__, array( $this, 'activate' ) ); 62 add_action( 'wp_ajax_submit_event_ajax', array( $this, 'submit_event_ajax' ) ); 63 add_action( 'wp_ajax_nopriv_submit_event_ajax', array( $this, 'submit_event_ajax' ) ); 64 add_action( 'wp_enqueue_scripts', array( $this, 'register_translation_event_js' ) ); 65 add_action( 'init', array( $this, 'register_event_post_type' ) ); 66 add_action( 'add_meta_boxes', array( $this, 'event_meta_boxes' ) ); 67 add_action( 'save_post', array( $this, 'save_event_meta_boxes' ) ); 68 add_action( 'transition_post_status', array( $this, 'event_status_transition' ), 10, 3 ); 69 add_filter( 'gp_nav_menu_items', array( $this, 'gp_event_nav_menu_items' ), 10, 2 ); 70 add_filter( 'wp_insert_post_data', array( $this, 'generate_event_slug' ), 10, 2 ); 71 add_action( 'gp_init', array( $this, 'gp_init' ) ); 72 add_action( 'gp_before_translation_table', array( $this, 'add_active_events_current_user' ) ); 73 74 if ( is_admin() ) { 75 Upgrade::upgrade_if_needed(); 76 } 53 77 } 54 78 55 79 public function gp_init() { 56 require_once __DIR__ . '/templates/helper-functions.php';57 require_once __DIR__ . '/includes/active-events-cache.php';58 require_once __DIR__ . '/includes/event.php';59 require_once __DIR__ . '/includes/routes/route.php';60 require_once __DIR__ . '/includes/routes/event/create.php';61 require_once __DIR__ . '/includes/routes/event/details.php';62 require_once __DIR__ . '/includes/routes/event/edit.php';63 require_once __DIR__ . '/includes/routes/event/list.php';64 require_once __DIR__ . '/includes/routes/user/attend-event.php';65 require_once __DIR__ . '/includes/routes/user/my-events.php';66 require_once __DIR__ . '/includes/stats-calculator.php';67 require_once __DIR__ . '/includes/stats-listener.php';68 69 80 GP::$router->add( '/events?', array( 'Wporg\TranslationEvents\Routes\Event\List_Route', 'handle' ) ); 70 81 GP::$router->add( '/events/new', array( 'Wporg\TranslationEvents\Routes\Event\Create_Route', 'handle' ) ); 71 82 GP::$router->add( '/events/edit/(\d+)', array( 'Wporg\TranslationEvents\Routes\Event\Edit_Route', 'handle' ) ); 72 83 GP::$router->add( '/events/attend/(\d+)', array( 'Wporg\TranslationEvents\Routes\User\Attend_Event_Route', 'handle' ), 'post' ); 84 GP::$router->add( '/events/host/(\d+)/(\d+)', array( 'Wporg\TranslationEvents\Routes\User\Host_Event_Route', 'handle' ), 'post' ); 73 85 GP::$router->add( '/events/my-events', array( 'Wporg\TranslationEvents\Routes\User\My_Events_Route', 'handle' ) ); 74 86 GP::$router->add( '/events/([a-z0-9_-]+)', array( 'Wporg\TranslationEvents\Routes\Event\Details_Route', 'handle' ) ); 75 87 76 $active_events_cache = new Active_Events_Cache(); 77 $stats_listener = new Stats_Listener( $active_events_cache ); 88 $stats_listener = new Stats_Listener( 89 self::get_event_repository(), 90 self::get_attendee_repository(), 91 ); 78 92 $stats_listener->start(); 79 }80 81 public function activate() {82 global $gp_table_prefix;83 $create_table = "84 CREATE TABLE `{$gp_table_prefix}event_actions` (85 `translate_event_actions_id` int(11) unsigned NOT NULL AUTO_INCREMENT,86 `event_id` int(10) NOT NULL COMMENT 'Post_ID of the translation_event post in the wp_posts table',87 `original_id` int(10) NOT NULL COMMENT 'ID of the translation',88 `user_id` int(10) NOT NULL COMMENT 'ID of the user who made the action',89 `action` enum('approve','create','reject','request_changes') NOT NULL COMMENT 'The action that the user made (create, reject, etc)',90 `locale` varchar(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL COMMENT 'Locale of the translation',91 `happened_at` datetime NOT NULL COMMENT 'When the action happened, in UTC',92 PRIMARY KEY (`translate_event_actions_id`),93 UNIQUE KEY `event_per_translated_original_per_user` (`event_id`,`locale`,`original_id`,`user_id`)94 ) COMMENT='Tracks translation actions that happened during a translation event'";95 require_once ABSPATH . 'wp-admin/includes/upgrade.php';96 dbDelta( $create_table );97 93 } 98 94 … … 131 127 */ 132 128 public function event_meta_boxes() { 133 \add_meta_box( 'event_dates', 'Event Dates', array( $this, 'event_dates_meta_box' ), self::CPT, 'normal', 'high' );129 add_meta_box( 'event_dates', 'Event Dates', array( $this, 'event_dates_meta_box' ), self::CPT, 'normal', 'high' ); 134 130 } 135 131 … … 176 172 177 173 /** 178 * Validate the event dates.179 *180 * @param string $event_start The event start date.181 * @param string $event_end The event end date.182 * @return bool Whether the event dates are valid.183 * @throws Exception When dates are invalid.184 */185 public function validate_event_dates( string $event_start, string $event_end ): bool {186 if ( ! $event_start || ! $event_end ) {187 return false;188 }189 $event_start = new DateTime( $event_start );190 $event_end = new DateTime( $event_end );191 if ( $event_start < $event_end ) {192 return true;193 }194 return false;195 }196 197 /**198 174 * Handle the event form submission for the creation, editing, and deletion of events. This function is called via AJAX. 199 175 */ 200 176 public function submit_event_ajax() { 201 if ( ! is_user_logged_in() ) { 202 wp_send_json_error( esc_html__( 'The user must be logged in.', 'gp-translation-events' ), 403 ); 203 } 204 $action = isset( $_POST['form_name'] ) ? sanitize_text_field( wp_unslash( $_POST['form_name'] ) ) : ''; 205 $event_id = null; 206 $event = null; 207 $response_message = ''; 208 $form_actions = array( 'draft', 'publish', 'delete' ); 209 $is_nonce_valid = false; 210 $nonce_name = '_event_nonce'; 211 if ( ! in_array( $action, array( 'create_event', 'edit_event', 'delete_event' ), true ) ) { 212 wp_send_json_error( esc_html__( 'Invalid form name.', 'gp-translation-events' ), 403 ); 213 } 214 /** 215 * Filter the ability to create, edit, or delete an event. 216 * 217 * @param bool $can_crud_event Whether the user can create, edit, or delete an event. 218 */ 219 $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) ); 220 if ( 'create_event' === $action && ( ! $can_crud_event ) ) { 221 wp_send_json_error( esc_html__( 'The user does not have permission to create an event.', 'gp-translation-events' ), 403 ); 222 } 223 if ( 'edit_event' === $action ) { 224 $event_id = isset( $_POST['event_id'] ) ? sanitize_text_field( wp_unslash( $_POST['event_id'] ) ) : ''; 225 $event = get_post( $event_id ); 226 if ( ! ( $can_crud_event || current_user_can( 'edit_post', $event_id ) || intval( $event->post_author ) === get_current_user_id() ) ) { 227 wp_send_json_error( esc_html__( 'The user does not have permission to edit or delete the event.', 'gp-translation-events' ), 403 ); 228 } 229 } 230 if ( 'delete_event' === $action ) { 231 $event_id = isset( $_POST['event_id'] ) ? sanitize_text_field( wp_unslash( $_POST['event_id'] ) ) : ''; 232 $event = get_post( $event_id ); 233 if ( ! ( $can_crud_event || current_user_can( 'delete_post', $event->ID ) || get_current_user_id() === $event->post_author ) ) { 234 wp_send_json_error( esc_html__( 'You do not have permission to delete this event.', 'gp-translation-events' ), 403 ); 235 } 236 } 237 if ( isset( $_POST[ $nonce_name ] ) ) { 238 $nonce_value = sanitize_text_field( wp_unslash( $_POST[ $nonce_name ] ) ); 239 if ( wp_verify_nonce( $nonce_value, $nonce_name ) ) { 240 $is_nonce_valid = true; 241 } 242 } 243 if ( ! $is_nonce_valid ) { 244 wp_send_json_error( esc_html__( 'Nonce verification failed.', 'gp-translation-events' ), 403 ); 245 } 246 // This is a list of slugs that are not allowed, as they conflict with the event URLs. 247 $invalid_slugs = array( 'new', 'edit', 'attend', 'my-events' ); 248 $title = isset( $_POST['event_title'] ) ? sanitize_text_field( wp_unslash( $_POST['event_title'] ) ) : ''; 249 // This will be sanitized by santitize_post which is called in wp_insert_post. 250 $description = isset( $_POST['event_description'] ) ? force_balance_tags( wp_unslash( $_POST['event_description'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 251 $event_start = isset( $_POST['event_start'] ) ? sanitize_text_field( wp_unslash( $_POST['event_start'] ) ) : ''; 252 $event_end = isset( $_POST['event_end'] ) ? sanitize_text_field( wp_unslash( $_POST['event_end'] ) ) : ''; 253 $event_timezone = isset( $_POST['event_timezone'] ) ? sanitize_text_field( wp_unslash( $_POST['event_timezone'] ) ) : ''; 254 if ( isset( $title ) && in_array( sanitize_title( $title ), $invalid_slugs, true ) ) { 255 wp_send_json_error( esc_html__( 'Invalid slug.', 'gp-translation-events' ), 422 ); 256 } 257 258 $is_valid_event_date = false; 259 try { 260 $is_valid_event_date = $this->validate_event_dates( $event_start, $event_end ); 261 } catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch 262 // Deliberately ignored, handled below. 263 } 264 if ( ! $is_valid_event_date ) { 265 wp_send_json_error( esc_html__( 'Invalid event dates.', 'gp-translation-events' ), 422 ); 266 } 267 268 $event_status = ''; 269 if ( isset( $_POST['event_form_action'] ) && in_array( $_POST['event_form_action'], $form_actions, true ) ) { 270 $event_status = sanitize_text_field( wp_unslash( $_POST['event_form_action'] ) ); 271 } 272 273 if ( ! isset( $_POST['form_name'] ) ) { 274 wp_send_json_error( esc_html__( 'Form name must be set.', 'gp-translation-events' ), 422 ); 275 } 276 277 if ( 'create_event' === $action ) { 278 $event_id = wp_insert_post( 279 array( 280 'post_type' => self::CPT, 281 'post_title' => $title, 282 'post_content' => $description, 283 'post_status' => $event_status, 284 ) 285 ); 286 $response_message = esc_html__( 'Event created successfully!', 'gp-translation-events' ); 287 } 288 if ( 'edit_event' === $action ) { 289 if ( ! isset( $_POST['event_id'] ) ) { 290 wp_send_json_error( esc_html__( 'Event id is required.', 'gp-translation-events' ), 422 ); 291 } 292 $event_id = sanitize_text_field( wp_unslash( $_POST['event_id'] ) ); 293 $event = get_post( $event_id ); 294 if ( ! $event || self::CPT !== $event->post_type || ! ( current_user_can( 'edit_post', $event->ID ) || intval( $event->post_author ) === get_current_user_id() ) ) { 295 wp_send_json_error( esc_html__( 'Event does not exist.', 'gp-translation-events' ), 404 ); 296 } 297 wp_update_post( 298 array( 299 'ID' => $event_id, 300 'post_title' => $title, 301 'post_content' => $description, 302 'post_status' => $event_status, 303 ) 304 ); 305 $response_message = esc_html__( 'Event updated successfully!', 'gp-translation-events' ); 306 } 307 if ( 'delete_event' === $action ) { 308 $event_id = sanitize_text_field( wp_unslash( $_POST['event_id'] ) ); 309 $event = get_post( $event_id ); 310 if ( ! $event || self::CPT !== $event->post_type ) { 311 wp_send_json_error( esc_html__( 'Event does not exist.', 'gp-translation-events' ), 404 ); 312 } 313 if ( ! ( current_user_can( 'delete_post', $event->ID ) || get_current_user_id() === $event->post_author ) ) { 314 wp_send_json_error( 'You do not have permission to delete this event' ); 315 } 316 $stats_calculator = new Stats_Calculator(); 317 try { 318 $event_stats = $stats_calculator->for_event( $event ); 319 } catch ( Exception $e ) { 320 wp_send_json_error( esc_html__( 'Failed to calculate event stats.', 'gp-translation-events' ), 500 ); 321 } 322 if ( ! empty( $event_stats->rows() ) ) { 323 wp_send_json_error( esc_html__( 'Event has translations and cannot be deleted.', 'gp-translation-events' ), 422 ); 324 } 325 wp_trash_post( $event_id ); 326 $response_message = esc_html__( 'Event deleted successfully!', 'gp-translation-events' ); 327 } 328 if ( ! $event_id ) { 329 wp_send_json_error( esc_html__( 'Event could not be created or updated.', 'gp-translation-events' ), 422 ); 330 } 331 if ( 'delete_event' !== $_POST['form_name'] ) { 332 try { 333 update_post_meta( $event_id, '_event_start', $this->convert_to_utc( $event_start, $event_timezone ) ); 334 update_post_meta( $event_id, '_event_end', $this->convert_to_utc( $event_end, $event_timezone ) ); 335 } catch ( Exception $e ) { 336 wp_send_json_error( esc_html__( 'Invalid start or end', 'gp-translation-events' ), 422 ); 337 } 338 339 update_post_meta( $event_id, '_event_timezone', $event_timezone ); 340 } 341 try { 342 Active_Events_Cache::invalidate(); 343 } catch ( Exception $e ) { 344 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 345 error_log( $e ); 346 } 347 348 list( $permalink, $post_name ) = get_sample_permalink( $event_id ); 349 $permalink = str_replace( '%pagename%', $post_name, $permalink ); 350 wp_send_json_success( 351 array( 352 'message' => $response_message, 353 'eventId' => $event_id, 354 'eventUrl' => str_replace( '%pagename%', $post_name, $permalink ), 355 'eventStatus' => $event_status, 356 'eventEditUrl' => esc_url( gp_url( '/events/edit/' . $event_id ) ), 357 'eventDeleteUrl' => esc_url( gp_url( '/events/my-events/' ) ), 358 ) 359 ); 360 } 361 362 363 364 365 /** 366 * Convert a date time in a time zone to UTC. 367 * 368 * @param string $date_time The date time in the time zone. 369 * @param string $time_zone The time zone. 370 * @return string The date time in UTC. 371 * @throws Exception When dates are invalid. 372 */ 373 public function convert_to_utc( string $date_time, string $time_zone ): string { 374 $date_time = new DateTime( $date_time, new DateTimeZone( $time_zone ) ); 375 $date_time->setTimezone( new DateTimeZone( 'UTC' ) ); 376 return $date_time->format( 'Y-m-d H:i:s' ); 177 $form_handler = new Event_Form_Handler( self::get_event_repository(), self::get_attendee_repository() ); 178 // Nonce verification is done by the form handler. 179 // phpcs:ignore WordPress.Security.NonceVerification.Missing 180 $form_handler->handle( $_POST ); 377 181 } 378 182 … … 400 204 * @param string $old_status The old post status. 401 205 * @param WP_Post $post The post object. 206 * 207 * @throws Exception 402 208 */ 403 209 public function event_status_transition( string $new_status, string $old_status, WP_Post $post ): void { … … 406 212 } 407 213 if ( 'publish' === $new_status && ( 'new' === $old_status || 'draft' === $old_status ) ) { 408 $current_user_id = get_current_user_id(); 409 $user_attending_events = get_user_meta( $current_user_id, self::USER_META_KEY_ATTENDING, true ) ?: array(); 410 $is_user_attending_event = in_array( $post->ID, $user_attending_events, true ); 411 if ( ! $is_user_attending_event ) { 412 $new_user_attending_events = $user_attending_events; 413 $new_user_attending_events[ $post->ID ] = true; 414 update_user_meta( $current_user_id, self::USER_META_KEY_ATTENDING, $new_user_attending_events, $user_attending_events ); 214 $event_id = $post->ID; 215 $user_id = $post->post_author; 216 $attendee_repository = self::get_attendee_repository(); 217 $attendee = $attendee_repository->get_attendee( $event_id, $user_id ); 218 219 if ( null === $attendee ) { 220 $attendee = new Attendee( $event_id, $user_id ); 221 $attendee->mark_as_host(); 222 $attendee_repository->insert_attendee( $attendee ); 415 223 } 416 224 } … … 466 274 * Add the active events for the current user before the translation table. 467 275 * 468 * @ return void276 * @throws Exception 469 277 */ 470 278 public function add_active_events_current_user(): void { 471 $user_attending_events = get_user_meta( get_current_user_id(), self::USER_META_KEY_ATTENDING, true ) ?: array(); 472 if ( empty( $user_attending_events ) ) { 279 $attendee_repository = new Attendee_Repository(); 280 $user_attending_event_ids = $attendee_repository->get_events_for_user( get_current_user_id() ); 281 if ( empty( $user_attending_event_ids ) ) { 473 282 return; 474 283 } … … 477 286 $user_attending_events_args = array( 478 287 'post_type' => self::CPT, 479 'post__in' => array_keys( $user_attending_events ),288 'post__in' => $user_attending_event_ids, 480 289 'post_status' => 'publish', 481 290 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query … … 499 308 'order' => 'ASC', 500 309 ); 310 501 311 $user_attending_events_query = new WP_Query( $user_attending_events_args ); 502 312 $number_of_events = $user_attending_events_query->post_count;
Note: See TracChangeset
for help on using the changeset viewer.