Ticket #2733: 2733.diff
File 2733.diff, 36.0 KB (added by , 6 years ago) |
---|
-
wordcamp.org/public_html/wp-content/plugins/wc-post-types/css/shortcodes.css
diff --git wordcamp.org/public_html/wp-content/plugins/wc-post-types/css/shortcodes.css wordcamp.org/public_html/wp-content/plugins/wc-post-types/css/shortcodes.css index 54540de4..c6000cdf 100644
1 1 /* 2 2 * [schedule] 3 3 */ 4 .wcb-favourite-session { 5 background: #e0f8ff; 6 } 7 8 .wcpt-schedule td { 9 vertical-align: top; 10 } 11 12 .wcpt-schedule div.wcb-session-favourite-icon { 13 float: right; 14 width: 35px; 15 text-align: center; 16 } 17 18 .wcpt-schedule .dashicons { 19 position: relative; 20 box-sizing: content-box; 21 width: 25px; 22 height: 25px; 23 overflow: hidden; 24 white-space: nowrap; 25 font-size: 16px; 26 line-height: 1; 27 cursor: pointer; 28 } 29 30 .wcpt-schedule .dashicons:before { 31 margin-right: 0px; 32 } 33 34 .wcpt-schedule .dashicons:after { 35 display: block; 36 font-size: 9px; 37 color: #999; 38 text-align: right; 39 } 40 41 42 div.wcb-session-favourite-icon a.fav-session-button { 43 color: #e8e8e8; 44 } 45 46 47 div.wcb-session-favourite-icon a.fav-session-button:hover { 48 text-decoration: none; 49 color: #fff689; 50 } 51 52 53 td.wcb-favourite-session a.fav-session-button { 54 color: #fff689; 55 } 56 57 .fav-session-email-wait-spinner { 58 display: none; 59 border: 2px solid #f3f3f3; 60 border-radius: 50%; 61 border-top: 2px solid #777; 62 width: 16px; 63 height: 16px; 64 margin: 10px auto; 65 -webkit-animation: spin 2s linear infinite; 66 animation: spin 2s linear infinite; 67 } 68 69 @-webkit-keyframes spin { 70 0% { -webkit-transform: rotate(0deg); } 71 100% { -webkit-transform: rotate(360deg); } 72 } 73 74 @keyframes spin { 75 0% { transform: rotate(0deg); } 76 100% { transform: rotate(360deg); } 77 } 78 79 80 /* 81 * CSS slide for email form for favourite sessions 82 */ 83 84 .fav-session-email-form-hide { 85 overflow: hidden; 86 max-height: 0; 87 padding-top: 0; 88 padding-bottom: 0; 89 margin-top: 0; 90 margin-bottom: 0; 91 -moz-transition-duration: 0.5s; 92 -webkit-transition-duration: 0.5s; 93 -o-transition-duration: 0.5s; 94 transition-duration: 0.5s; 95 -moz-transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 96 -webkit-transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 97 -o-transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 98 transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 99 } 100 .fav-session-email-form-show { 101 -moz-transition-duration: 0.5s; 102 -webkit-transition-duration: 0.5s; 103 -o-transition-duration: 0.5s; 104 transition-duration: 0.5s; 105 -moz-transition-timing-function: ease-in; 106 -webkit-transition-timing-function: ease-in; 107 -o-transition-timing-function: ease-in; 108 transition-timing-function: ease-in; 109 max-height: 1000px; 110 overflow: hidden; 111 } 112 113 114 .show-email-form { 115 display: none; 116 position: fixed; 117 bottom: 20px; 118 right: 100px; 119 padding: 5px 8px 1px 7px; 120 border: 1px solid #a1a1a1; 121 background: #dddddd; 122 border-radius: 6px; 123 color: #a1a1a1; 124 } 125 126 .email-form { 127 position: fixed; 128 bottom: 50px; 129 right: 100px; 130 width: 200px; 131 background: #dcdcdc; 132 font-size: 12px; 133 } 134 135 #fav-session-email-form { 136 margin: 10px; 137 } 138 139 .fav-session-email-result { 140 display: none; 141 margin: 10px; 142 } 143 144 .fav-sessions-form { 145 display: none; 146 } 147 148 .entry-content a.show-email-form { 149 color: #a1a1a1; 150 } 151 152 .entry-content a.show-email-form:hover { 153 color: #555; 154 text-decoration: none; 155 } 156 157 .show-email-form .dashicons-star-filled { 158 font-size: 14px; 159 padding-top: 2px; 160 width: 14px; 161 } 162 4 163 @media screen and ( max-width: 700px ) { 5 164 .wcpt-schedule { 6 165 border: none; … … 72 231 span.wcpt-session-speakers a { 73 232 color: #21759b; 74 233 } 234 235 .show-email-form, 236 .email-form { 237 right: 20px; 238 } 239 75 240 } 76 241 242 243 77 244 /* 78 245 * [sessions] 79 246 * [speakers] -
new file wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/favorite-schedule-shortcode.php
diff --git wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/favorite-schedule-shortcode.php wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/favorite-schedule-shortcode.php new file mode 100644 index 00000000..5ab69277
- + 1 <?php 2 /** 3 * [schedule] shortcode building blocks and favourite session picker support. 4 * 5 * @package wc-post-types 6 * @subpackage wc-post-types/inc 7 */ 8 9 // If this file is called directly, abort. 10 defined( 'WPINC' ) || die(); 11 12 /** 13 * Retrun an associative array of term_id -> term object mapping for all selected tracks. 14 * 15 * In case of 'all' is used as a value for $selected_tracks, information for all available tracks 16 * gets returned. 17 * 18 * @param string $selected_tracks Comma-separated list of tracks to display or 'all'. 19 * @return array Associative array of terms with term_id as the key. 20 */ 21 function get_schedule_tracks( $selected_tracks ) { 22 $tracks = array(); 23 if ( 'all' === $selected_tracks ) { 24 // Include all tracks. 25 $tracks = get_terms( 'wcb_track' ); 26 27 } else { 28 // Loop through given tracks and look for terms. 29 $terms = array_map( 'trim', explode( ',', $selected_tracks ) ); 30 foreach ( $terms as $term_slug ) { 31 $term = get_term_by( 'slug', $term_slug, 'wcb_track' ); 32 if ( $term ) { 33 $tracks[ $term->term_id ] = $term; 34 } 35 } 36 } 37 return $tracks; 38 } 39 40 /** 41 * Return a time-sorted associative array mapping timestamp -> track_id -> session id. 42 * 43 * @param string $schedule_date Date for which the sessions should be retrieved. 44 * @param bool $tracks_explicitly_specified True if tracks were explicitly specified in the shortcode, 45 * false otherwise. 46 * @param array $tracks Array of terms for tracks from get_schedule_tracks(). 47 * @return array Associative array of session ids by time and track. 48 */ 49 function get_schedule_sessions( $schedule_date, $tracks_explicitly_specified, $tracks ) { 50 $query_args = array( 51 'post_type' => 'wcb_session', 52 'posts_per_page' => -1, 53 'meta_query' => array( 54 'relation' => 'AND', 55 array( 56 'key' => '_wcpt_session_time', 57 'compare' => 'EXISTS', 58 ), 59 ), 60 ); 61 62 if ( $schedule_date && strtotime( $schedule_date ) ) { 63 $query_args['meta_query'][] = array( 64 'key' => '_wcpt_session_time', 65 'value' => array( 66 strtotime( $schedule_date ), 67 strtotime( $schedule_date . ' +1 day' ), 68 ), 69 'compare' => 'BETWEEN', 70 'type' => 'NUMERIC', 71 ); 72 } 73 74 if ( $tracks_explicitly_specified ) { 75 // If tracks were provided, restrict the lookup in WP_Query. 76 if ( ! empty( $tracks ) ) { 77 $query_args['tax_query'][] = array( 78 'taxonomy' => 'wcb_track', 79 'field' => 'id', 80 'terms' => array_values( wp_list_pluck( $tracks, 'term_id' ) ), 81 ); 82 } 83 } 84 85 // Loop through all sessions and assign them into the formatted 86 // $sessions array: $sessions[ $time ][ $track ] = $session_id 87 // Use 0 as the track ID if no tracks exist. 88 $sessions = array(); 89 $sessions_query = new WP_Query( $query_args ); 90 foreach ( $sessions_query->posts as $session ) { 91 $time = absint( get_post_meta( $session->ID, '_wcpt_session_time', true ) ); 92 $terms = get_the_terms( $session->ID, 'wcb_track' ); 93 94 if ( ! isset( $sessions[ $time ] ) ) { 95 $sessions[ $time ] = array(); 96 } 97 98 if ( empty( $terms ) ) { 99 $sessions[ $time ][0] = $session->ID; 100 } else { 101 foreach ( $terms as $track ) { 102 $sessions[ $time ][ $track->term_id ] = $session->ID; 103 } 104 } 105 } 106 107 // Sort all sessions by their key (timestamp). 108 ksort( $sessions ); 109 return $sessions; 110 } 111 112 /** 113 * Return an array of columns identified by term ids to be used for schedule table. 114 * 115 * @param array $tracks Array of terms for tracks from get_schedule_tracks(). 116 * @param array $sessions Array of sessions from get_schedule_sessions(). 117 * @param array $tracks_explicitly_specified True if tracks were explicitly specified in the shortcode, 118 * false otherwise. 119 * @return array Array of columns identified by term ids. 120 */ 121 function get_schedule_columns( $tracks, $sessions, $tracks_explicitly_specified ) { 122 $columns = array(); 123 124 // Use tracks to form the columns. 125 if ( $tracks ) { 126 foreach ( $tracks as $track ) { 127 $columns[ $track->term_id ] = $track->term_id; 128 } 129 } else { 130 $columns[0] = 0; 131 } 132 133 // Remove empty columns unless tracks have been explicitly specified. 134 if ( ! $tracks_explicitly_specified ) { 135 $used_terms = array(); 136 137 foreach ( $sessions as $time => $entry ) { 138 if ( is_array( $entry ) ) { 139 foreach ( $entry as $term_id => $session_id ) { 140 $used_terms[ $term_id ] = $term_id; 141 } 142 } 143 } 144 $columns = array_intersect( $columns, $used_terms ); 145 unset( $used_terms ); 146 } 147 return $columns; 148 } 149 150 /** 151 * Update and preprocess input attributes for [schedule] shortcode. 152 * 153 * @param array $attr Array of attributes from shortcode. 154 * @return array Array of attributes, after preprocessing. 155 */ 156 function preprocess_schedule_attributes( $attr ) { 157 $attr = shortcode_atts( 158 array( 159 'date' => null, 160 'tracks' => 'all', 161 'speaker_link' => 'anchor', // anchor|wporg|permalink|none 162 'session_link' => 'permalink', // permalink|anchor|none 163 ), $attr 164 ); 165 166 foreach ( array( 'tracks', 'speaker_link', 'session_link' ) as $key_for_case_sensitive_value ) { 167 $attr[ $key_for_case_sensitive_value ] = strtolower( $attr[ $key_for_case_sensitive_value ] ); 168 } 169 170 if ( ! in_array( $attr['speaker_link'], array( 'anchor', 'wporg', 'permalink', 'none' ), true ) ) { 171 $attr['speaker_link'] = 'anchor'; 172 } 173 174 if ( ! in_array( $attr['session_link'], array( 'permalink', 'anchor', 'none' ), true ) ) { 175 $attr['session_link'] = 'permalink'; 176 } 177 178 return $attr; 179 } 180 181 /** 182 * Return plain text list of sessions marked as favourite sessions. 183 * 184 * Format of each list item: 185 * Time of session | Session title [by Speaker] | Track name(s). 186 * 187 * @param array $sessions_rev Array of sessions with reversed subarray track_id->session_id. 188 * @param array $fav_sessions_lookup Mapping session _id -> 1 for favourite sessions. 189 * @return string List of sessions. 190 */ 191 function generate_plaintext_fav_sessions( $sessions_rev, $fav_sessions_lookup ) { 192 193 $sessions_text = ''; 194 195 // timestamp -> session_id -> track_id. 196 foreach ( $sessions_rev as $timestamp => $sessions_at_time ) { 197 foreach ( $sessions_at_time as $session_id => $track_ids ) { 198 // Skip sessions which are not marked favourite. 199 if ( ! isset( $fav_sessions_lookup[ $session_id ] ) ) { 200 continue; 201 } 202 203 $session = get_post( $session_id ); 204 $session_title = apply_filters( 'the_title', $session->post_title ); 205 $session_tracks = get_the_terms( $session_id, 'wcb_track' ); 206 $session_track_titles = is_array( $session_tracks ) ? implode( ', ', wp_list_pluck( $session_tracks, 'name' ) ) : ''; 207 208 $speakers = array(); 209 $speakers_ids = array_map( 'absint', (array) get_post_meta( $session_id, '_wcpt_speaker_id' ) ); 210 if ( ! empty( $speakers_ids ) ) { 211 $speakers = get_posts( array( 212 'post_type' => 'wcb_speaker', 213 'posts_per_page' => -1, 214 'post__in' => $speakers_ids, 215 ) ); 216 } 217 218 $speakers_names = array(); 219 foreach ( $speakers as $speaker ) { 220 $speaker_name = apply_filters( 'the_title', $speaker->post_title ); 221 $speakers_names[] = $speaker_name; 222 } 223 224 // Line format: Time of session | Session title [by Speaker] | Track name(s). 225 $sessions_text .= date( get_option( 'time_format' ), $timestamp ); 226 $sessions_text .= ' | '; 227 $sessions_text .= $session_title; 228 if ( count( $speakers_names ) > 0 ) { 229 $sessions_text .= _x( ' by ', 'Speaker for the session', 'wordcamporg' ) . implode( ', ', $speakers_names ); 230 } 231 $sessions_text .= ' | '; 232 $sessions_text .= $session_track_titles; 233 $sessions_text .= "\n"; 234 } 235 } 236 return $sessions_text; 237 238 } 239 240 /** 241 * Return array of dates for which there are sessions in the provided array. 242 * 243 * @param array $sessions Array of sessions from get_schedule_sessions(). 244 * @param string $date_format Date format string (same format as php). 245 * @return array Array of dates for WordCamp formatted according to date_format string. 246 */ 247 function get_sessions_dates( $sessions, $date_format ) { 248 $session_timestamps = array_keys( $sessions ); 249 250 $session_dates = array_map( 251 function( $timestamp ) use ( $date_format ) { 252 return date( $date_format, $timestamp ); 253 }, 254 $session_timestamps 255 ); 256 257 return array_unique( $session_dates ); 258 } 259 260 /** 261 * Return true if any of the sessions from $session_rev is in $fav_session_ids, 262 * false otherwise. 263 * 264 * @param array $fav_session_ids Array with favourite sessions as keys. 265 * @param string $sessions_rev Array of sessions from flip_sessions_subarrays(). 266 * @return bool true if there is any intersection, false otherwise. 267 */ 268 function includes_fav_session( $fav_session_ids, $sessions_rev ) { 269 foreach ( $sessions_rev as $timestamp => $session_id_subarray ) { 270 foreach ( $session_id_subarray as $session_id => $_ ) { 271 if ( isset( $fav_session_ids[ $session_id ] ) ) { 272 return true; 273 } 274 } 275 } 276 return false; 277 } 278 279 /** 280 * Return array of sessions with reverted subarrays, i.e. transformed from 281 * timestamp -> track_id -> session_id into 282 * timestamp -> session_id -> [track_id1, track_id2, ...] 283 * 284 * @param array $sessions An array of sessions from get_schedule_sessions(). 285 * @return array Array with format timestamp -> session_id -> [track_id1, track_id2, ...]. 286 */ 287 function flip_sessions_subarrays( $sessions ) { 288 $sessions_reversed = array(); 289 foreach ( $sessions as $timestamp => $sessions_at_time ) { 290 foreach ( $sessions_at_time as $track_id => $session_id ) { 291 $sessions_reversed[ $timestamp ][ $session_id ][] = $track_id; 292 } 293 } 294 return $sessions_reversed; 295 } 296 297 /** 298 * Return plain text email message body for sharing favourite sessions email. 299 * 300 * @param string $wordcamp_name WordCamp name to be used in the email. 301 * @param array $fav_sessions_lookup Mapping session _id -> 1 for favourite sessions. 302 * @return string Plain text body of the email. 303 */ 304 function generate_email_body( $wordcamp_name, $fav_sessions_lookup ) { 305 $date_format = get_option( 'date_format' ); 306 $tracks = get_schedule_tracks( 'all' ); 307 $tracks_explicitly_specified = false; // include all tracks in the email. 308 $sessions = get_schedule_sessions( null, $tracks_explicitly_specified, $tracks ); 309 $sessions_dates = get_sessions_dates( $sessions, $date_format ); 310 311 // Convert timestamp -> track_id -> session_id to timestamp -> session_id -> [track_id1, ...]. 312 $sessions_reversed = flip_sessions_subarrays( $sessions ); 313 314 $email_message = $wordcamp_name . "\n\n"; 315 316 // Create list of sessions for each day. 317 foreach ( $sessions_dates as $current_day ) { 318 // Filter only the sessions for the 'current' day. 319 $sessions_for_current_day = array_filter( 320 $sessions_reversed, 321 function( $date_ ) use ( $current_day, $date_format ) { 322 return date( $date_format, $date_ ) === $current_day; 323 }, 324 ARRAY_FILTER_USE_KEY 325 ); 326 $email_message .= $current_day . "\n"; 327 // Skip days when there's no session marked as favourite. 328 if ( ! includes_fav_session( $fav_sessions_lookup, $sessions_for_current_day ) ) { 329 $email_message .= "\n"; 330 continue; 331 } 332 333 $email_message .= generate_plaintext_fav_sessions( $sessions_for_current_day, $fav_sessions_lookup ); 334 $email_message .= "\n\n"; 335 } 336 return $email_message; 337 } 338 339 /** 340 * Return true if the email favourite sessions feature should be disabled, 341 * false otherwise. 342 * 343 * Kill switch for sharing schedule over email -- both for REST API endpoint and UI 344 * in [schedule] shortcode. 345 * Could be linked to an option in the admin GUI later? 346 * 347 * @return bool true if email functionality should be disabled, false otherwise. 348 */ 349 function email_fav_sessions_disabled() { 350 return false; 351 } 352 353 /** 354 * Send favourite sessions email to address specified in the REST request. 355 * 356 * REST API handler for 'wc-post-types/v1/email-fav-sessions' endpoint. 357 * 358 * @param WP_REST_Request $request REST API Request object. 359 * @return WP_REST_Response REST Response object. 360 */ 361 function send_favourite_sessions_email( WP_REST_Request $request ) { 362 if ( email_fav_sessions_disabled() ) { 363 return new WP_REST_Response( 364 array( 365 'message' => esc_html__( 'Email functionality disabled.', 'wordcamporg' ), 366 ), 200 367 ); 368 } 369 370 $params = $request->get_params(); 371 // Input sanitized by REST controller. 372 $email_address = $params['email-address']; 373 $fav_sessions = $params['session-list']; 374 375 // Don't send the email if no sessions were marked as favourite. 376 if ( count( explode( ',', $fav_sessions ) ) === 0 ) { 377 return new WP_Error( 378 'fav_sessions_no_sessions', 379 esc_html__( 'No sessions selected.', 'wordcamporg' ), 380 array( 381 'status' => 400, 382 ) 383 ); 384 } 385 386 $fav_sessions_lookup = array_fill_keys( explode( ',', $fav_sessions ), 1 ); 387 388 $wordcamp_name = get_wordcamp_name(); 389 390 $headers[] = 'From: ' . $wordcamp_name . ' <dontreply@wordcamp.org>'; 391 $headers[] = 'Content-Type: text/plain; charset=' . get_bloginfo( 'charset' ); 392 393 $subject = sprintf( __( 'My favourite sessions for %s' , 'wordcamporg' ), $wordcamp_name ); 394 $message = generate_email_body( $wordcamp_name, $fav_sessions_lookup ); 395 396 if ( wp_mail( $email_address, $subject, $message, $headers ) ) { 397 return new WP_REST_Response( 398 array( 399 'message' => esc_html__( 'Email sent successfully to ', 'wordcamporg' ) . " $email_address.", 400 ), 200 401 ); 402 } 403 404 // Email was not sent successfully. 405 return new WP_Error( 406 'fav_sessions_email_failed', 407 esc_html__( 'Favourite sessions email failed.', 'wordcamporg' ), 408 array( 409 'status' => 500, 410 ) 411 ); 412 } -
wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/rest-api.php
diff --git wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/rest-api.php wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/rest-api.php index 107c776a..bd59b9aa 100644
6 6 */ 7 7 8 8 namespace WordCamp\Post_Types\REST_API; 9 use WP_Rest_Server; 10 9 11 defined( 'WPINC' ) || die(); 10 12 13 require_once( 'favorite-schedule-shortcode.php' ); 14 11 15 /** 12 16 * Add non-sensitive meta fields to the speaker/session REST API endpoints 13 17 * … … function register_additional_rest_fields() { 111 115 } // End if(). 112 116 } 113 117 118 114 119 add_action( 'rest_api_init', __NAMESPACE__ . '\register_additional_rest_fields' ); 115 120 121 122 /** 123 * Register route for sending schedule of favourite sessions via e-mail. 124 * 125 * This can be disabled in email_fav_sessions_disabled() from favorite-schedule-shortcode.php. 126 * 127 * @return void 128 */ 129 function register_fav_sessions_email(){ 130 register_rest_route( 131 'wc-post-types/v1', // REST namespace + API version 132 '/email-fav-sessions/', // URL slug 133 array( 134 'methods' => WP_REST_Server::CREATABLE, 135 'callback' => 'send_favourite_sessions_email', 136 'args' => array( 137 'email-address' => array( 138 'required' => true, 139 'validate_callback' => function( $value, $request, $param ) { 140 return is_email( $value ); 141 }, 142 'sanitize_callback' => function( $value, $request, $param ) { 143 return sanitize_email( $value ); 144 }, 145 ), 146 'session-list' => array( 147 'required' => true, 148 'validate_callback' => function( $value, $request, $param ) { 149 $session_ids = explode( ',', $value ); 150 $session_count = count( $session_ids ); 151 for ( $i = 0; $i < $session_count; $i++ ) { 152 if ( ! is_numeric( $session_ids[ $i ] ) ) { 153 return false; 154 } 155 } 156 return true; 157 }, 158 'sanitize_callback' => function( $value, $request, $param ) { 159 $session_ids = explode( ',', $value ); 160 return implode( ',', array_filter( $session_ids, 'is_numeric' ) ); 161 }, 162 ), 163 ) 164 ) 165 ); 166 } 167 add_action( 'rest_api_init', __NAMESPACE__ . '\register_fav_sessions_email' ); 168 116 169 /** 117 170 * Link all sessions to the speaker in the `speakers` API endpoint 118 171 * -
new file wordcamp.org/public_html/wp-content/plugins/wc-post-types/js/favourite-sessions.js
diff --git wordcamp.org/public_html/wp-content/plugins/wc-post-types/js/favourite-sessions.js wordcamp.org/public_html/wp-content/plugins/wc-post-types/js/favourite-sessions.js new file mode 100644 index 00000000..c763c746
- + 1 jQuery( document ).ready( function ($) { 2 var FavSessions = { 3 favSessKey: 'favourite_sessions', 4 5 get: function () { 6 var favSessions = JSON.parse( localStorage.getItem( this.favSessKey ) ); 7 if ( ! favSessions ) { 8 favSessions = {}; 9 } 10 return favSessions; 11 }, 12 13 toggleSession: function ( sessionId ) { 14 var favSessions = this.get(); 15 if ( favSessions.hasOwnProperty( sessionId ) ) { 16 delete favSessions[sessionId]; 17 } else { 18 favSessions[sessionId] = true; 19 } 20 localStorage.setItem( this.favSessKey, JSON.stringify( favSessions ) ); 21 }, 22 }; 23 24 function switchCellAppearance( sessionId ) { 25 // (Un)highlight schedule table cell in case a session is (un)marked as favourite. 26 var sessionSelector = "[data-session-id='" + sessionId + "']"; 27 var tdElements = document.querySelectorAll( sessionSelector ); 28 for ( var i = 0; i < tdElements.length; i++ ) { 29 tdElements[i].classList.toggle( 'wcb-favourite-session' ); 30 } 31 } 32 33 function switchEmailFavButton() { 34 var favSessions = FavSessions.get(); 35 // Display email form only if there are any selected sessions. 36 if ( Object.keys( favSessions ).length > 0 ) { 37 $( '.show-email-form' ).show(); 38 } else { 39 $( '.show-email-form' ).hide(); 40 } 41 42 } 43 44 function switchSessionFavourite( sessionId ) { 45 // Update localStorage. 46 FavSessions.toggleSession( sessionId ); 47 // Change table cell appearance. 48 switchCellAppearance( sessionId ); 49 // Display/hide email button if necessary. 50 switchEmailFavButton(); 51 } 52 53 function initFavouriteSessions() { 54 var favSessions = FavSessions.get(); 55 // No favourite session--nothing to do. 56 if (favSessions === {}) { 57 return; 58 } 59 60 // Highlight favourite sessions in table. 61 var sessionIds = Object.keys( favSessions ); 62 for ( var i = 0; i < sessionIds.length; i++ ) { 63 var sessionId = sessionIds[i]; 64 if (favSessions[sessionId] === true) { 65 switchCellAppearance( sessionId ); 66 } 67 } 68 // Display/hide email button if necessary. 69 switchEmailFavButton(); 70 } 71 72 function hideSpinnerShowResult( message ) { 73 var fadeInDelay = 300; 74 $( '.fav-session-email-wait-spinner' ).fadeOut( fadeInDelay ); 75 76 setTimeout(function () { 77 $( '.fav-session-email-result' ).html( message ); 78 $( '.fav-session-email-result' ).fadeIn(); 79 }, fadeInDelay); 80 } 81 82 function hideFormShowSpinner() { 83 var fadeInDelay = 300; 84 $( '#fav-session-email-form' ).fadeOut( fadeInDelay ); 85 setTimeout( function () { 86 $( '.fav-session-email-wait-spinner' ).fadeIn(); 87 }, fadeInDelay); 88 } 89 90 $( '.show-email-form' ).click( function ( event ) { 91 event.preventDefault(); 92 // Slide the slider. 93 $( '.email-form' ).toggleClass( 'fav-session-email-form-hide' ).toggleClass( 'fav-session-email-form-show' ); 94 95 // After the animnation finishes, activate the form again and hide the previous result. 96 setTimeout( function () { 97 // Clear previous email result. 98 $( '.fav-session-email-result' ).html( '' ); 99 // Show form div & clear email address. 100 $( '#fav-session-email-form' ).show(); 101 $( '#fav-sessions-email-address' ).val( '' ); 102 103 }, 500); 104 105 return false; 106 }); 107 108 $( '.fav-session-button' ).click( function ( event ) { 109 event.preventDefault(); 110 var elem = $( this ); 111 var sessionId = elem.parent().parent().data( 'session-id' ); 112 switchSessionFavourite( sessionId ); 113 return false; 114 }); 115 116 $( '#fav-sessions-form' ).on( 'submit', function ( event ) { 117 event.preventDefault(); 118 hideFormShowSpinner(); 119 var favSessions = FavSessions.get(); 120 favSessions = Object.keys( favSessions ).toString(); 121 122 // Get email from the input. 123 var emailAddress = ''; 124 if ( $( '#fav-sessions-email-address' ) ) { 125 emailAddress = $( '#fav-sessions-email-address' ).val(); 126 } else { 127 return; 128 } 129 130 // Compile data object. 131 // The nonce is sent in a request header - see beforeSend in $.ajax. 132 var data = { 133 'email-address': emailAddress, 134 'session-list': favSessions, 135 }; 136 137 $.ajax({ 138 method: 'POST', 139 url: favSessionsPhpObject.root + 'wc-post-types/v1/email-fav-sessions', 140 data: data, 141 beforeSend: function ( xhr ) { 142 // Set the nonce header provided by php script. 143 xhr.setRequestHeader( 'X-WP-Nonce', favSessionsPhpObject.api_nonce ); 144 }, 145 success: function ( response ) { 146 hideSpinnerShowResult( response.message ); 147 }, 148 fail: function ( response ) { 149 hideSpinnerShowResult( response.message ); 150 }, 151 error: function(jqXHR, textStatus, errorThrown) { 152 if ( textStatus === 'timeout' ) { 153 hideSpinnerShowResult( favSessionsPhpObject.i18n.reqTimeOut ); 154 } else { 155 hideSpinnerShowResult( favSessionsPhpObject.i18n.otherError ); 156 } 157 }, 158 timeout: 5000, 159 }); 160 }); 161 162 initFavouriteSessions(); 163 }); -
wordcamp.org/public_html/wp-content/plugins/wc-post-types/wc-post-types.php
diff --git wordcamp.org/public_html/wp-content/plugins/wc-post-types/wc-post-types.php wordcamp.org/public_html/wp-content/plugins/wc-post-types/wc-post-types.php index e99df783..bbe3601e 100644
5 5 */ 6 6 7 7 require( 'inc/back-compat.php' ); 8 require_once('inc/favorite-schedule-shortcode.php'); 8 9 9 10 class WordCamp_Post_Types_Plugin { 10 11 protected $wcpt_permalinks; … … class WordCamp_Post_Types_Plugin { 267 268 268 269 function wp_enqueue_scripts() { 269 270 wp_enqueue_style( 'wcb_shortcodes', plugins_url( 'css/shortcodes.css', __FILE__ ), array(), 2 ); 271 272 // Style and script for [schedule] shortcode. 273 wp_register_script( 274 'favourite-sessions', 275 plugin_dir_url( __FILE__ ) . 'js/favourite-sessions.js', 276 array( 'jquery' ), 277 filemtime( plugin_dir_path( __FILE__ ) . 'js/favourite-sessions.js' ), 278 true 279 ); 270 280 } 271 281 272 282 /** … … class WordCamp_Post_Types_Plugin { 502 512 } 503 513 504 514 /** 505 * The [schedule] shortcode callback (experimental)515 * Return HTML code for email form used to send/share favourite sessions over email. 506 516 * 507 * @todo implement date arg508 * @todo implement anchor for session_link509 * @todo maybe simplify $attr['custom']510 * @ todo cleanup517 * Both form and button/link to show/hide the form can be styled using classes email-form 518 * and show-email-form, respectively. 519 * 520 * @return string HTML code that represents the form to send emails and a link to show and hide it. 511 521 */ 512 function shortcode_schedule( $attr, $content ) { 513 $attr = shortcode_atts( array( 514 'date' => null, 515 'tracks' => 'all', 516 'speaker_link' => 'anchor', // anchor|wporg|permalink|none 517 'session_link' => 'permalink', // permalink|anchor|none 518 ), $attr ); 519 520 foreach ( array( 'tracks', 'speaker_link', 'session_link' ) as $key_for_case_sensitive_value ) { 521 $attr[ $key_for_case_sensitive_value ] = strtolower( $attr[ $key_for_case_sensitive_value ] ); 522 function fav_session_email_form() { 523 static $email_form_count = 0; 524 // Skip email form if it is disabled or it was already added to document. 525 if ( email_fav_sessions_disabled() || $email_form_count !== 0 ) { 526 return ''; 522 527 } 528 $send_email_label = esc_html__( 'Send me my favourite sessions:', 'wordcamporg' ); 529 $send_email_button = esc_attr__( 'Send', 'wordcamporg' ); 530 $email_form = <<<EOS 531 <div class="email-form fav-session-email-form-hide" style=""> 532 <div id="fav-session-email-form"> 533 $send_email_label 534 <form id="fav-sessions-form"> 535 <input type="text" name="email_address" id="fav-sessions-email-address" placeholder="my@email.com" /> 536 <input type="submit" value="$send_email_button" /> 537 </form> 538 </div> 539 <div class="fav-session-email-wait-spinner"></div> 540 <div class="fav-session-email-result"></div> 541 </div> 542 <a class="show-email-form" href="javascript:"> 543 <span class="dashicons dashicons-star-filled"></span> 544 <span class="dashicons dashicons-email-alt"></span> 545 </a> 546 EOS; 547 548 $email_form_count++; 549 return $email_form; 550 } 523 551 524 if ( ! in_array( $attr['speaker_link'], array( 'anchor', 'wporg', 'permalink', 'none' ) ) ) 525 $attr['speaker_link'] = 'anchor'; 526 527 if ( ! in_array( $attr['session_link'], array( 'permalink', 'anchor', 'none' ) ) ) 528 $attr['session_link'] = 'permalink'; 529 530 $columns = array(); 531 $tracks = array(); 532 533 $query_args = array( 534 'post_type' => 'wcb_session', 535 'posts_per_page' => -1, 536 'meta_query' => array( 537 'relation' => 'AND', 538 array( 539 'key' => '_wcpt_session_time', 540 'compare' => 'EXISTS', 541 ), 542 ), 552 /** 553 * Enqueue style and scripts needed for [schedule] shortcode. 554 * 555 * @return void. 556 */ 557 function enqueue_schedule_shortcode_dependencies() { 558 wp_enqueue_style( 'dashicons' ); 559 wp_enqueue_script( 560 'favourite-sessions', 561 plugin_dir_url( __FILE__ ) . 'js/favourite-sessions.js', 562 array( 'jquery' ), 563 filemtime( plugin_dir_path( __FILE__ ) . 'js/favourite-sessions.js' ), 564 true 543 565 ); 544 566 545 if ( 'all' == $attr['tracks'] ) { 546 // Include all tracks. 547 $tracks = get_terms( 'wcb_track' ); 548 } else { 549 // Loop through given tracks and look for terms. 550 $terms = array_map( 'trim', explode( ',', $attr['tracks'] ) ); 551 foreach ( $terms as $term_slug ) { 552 $term = get_term_by( 'slug', $term_slug, 'wcb_track' ); 553 if ( $term ) 554 $tracks[ $term->term_id ] = $term; 555 } 556 557 // If tracks were provided, restrict the lookup in WP_Query. 558 if ( ! empty( $tracks ) ) { 559 $query_args['tax_query'][] = array( 560 'taxonomy' => 'wcb_track', 561 'field' => 'id', 562 'terms' => array_values( wp_list_pluck( $tracks, 'term_id' ) ), 563 ); 564 } 565 } 566 567 if ( $attr['date'] && strtotime( $attr['date'] ) ) { 568 $query_args['meta_query'][] = array( 569 'key' => '_wcpt_session_time', 570 'value' => array( 571 strtotime( $attr['date'] ), 572 strtotime( $attr['date'] . ' +1 day' ), 567 wp_localize_script( 568 'favourite-sessions', 569 'favSessionsPhpObject', 570 array( 571 'api_nonce' => wp_create_nonce( 'wp_rest' ), 572 'root' => esc_url_raw( rest_url() ), 573 'i18n' => array( 574 'reqTimeOut' => esc_html__( 'Sorry, the email request timed out.', 'wordcamporg' ), 575 'otherError' => esc_html__( 'Sorry, the email request failed.', 'wordcamporg' ), 573 576 ), 574 'compare' => 'BETWEEN', 575 'type' => 'NUMERIC', 576 ); 577 } 578 579 // Use tracks to form the columns. 580 if ( $tracks ) { 581 foreach ( $tracks as $track ) 582 $columns[ $track->term_id ] = $track->term_id; 583 } else { 584 $columns[ 0 ] = 0; 585 } 586 587 unset( $tracks ); 588 589 // Loop through all sessions and assign them into the formatted 590 // $sessions array: $sessions[ $time ][ $track ] = $session_id 591 // Use 0 as the track ID if no tracks exist 592 593 $sessions = array(); 594 $sessions_query = new WP_Query( $query_args ); 595 foreach ( $sessions_query->posts as $session ) { 596 $time = absint( get_post_meta( $session->ID, '_wcpt_session_time', true ) ); 597 $tracks = get_the_terms( $session->ID, 'wcb_track' ); 598 599 if ( ! isset( $sessions[ $time ] ) ) 600 $sessions[ $time ] = array(); 601 602 if ( empty( $tracks ) ) { 603 $sessions[ $time ][ 0 ] = $session->ID; 604 } else { 605 foreach ( $tracks as $track ) 606 $sessions[ $time ][ $track->term_id ] = $session->ID; 607 } 608 } 609 610 // Sort all sessions by their key (timestamp). 611 ksort( $sessions ); 612 613 // Remove empty columns unless tracks have been explicitly specified 614 if ( 'all' == $attr['tracks'] ) { 615 $used_terms = array(); 577 ) 578 ); 579 } 616 580 617 foreach ( $sessions as $time => $entry ) 618 if ( is_array( $entry ) ) 619 foreach ( $entry as $term_id => $session_id ) 620 $used_terms[ $term_id ] = $term_id; 581 /** 582 * The [schedule] shortcode callback (experimental) 583 * 584 * @todo implement date arg 585 * @todo implement anchor for session_link 586 * @todo maybe simplify $attr['custom'] 587 * @todo cleanup 588 */ 589 function shortcode_schedule( $attr, $content ) { 590 $this->enqueue_schedule_shortcode_dependencies(); 621 591 622 $columns = array_intersect( $columns, $used_terms ); 623 unset( $used_terms ); 624 } 592 $attr = preprocess_schedule_attributes( $attr ); 593 $tracks = get_schedule_tracks( $attr['tracks'] ); 594 $tracks_explicitly_specified = 'all' !== $attr['tracks']; 595 $sessions = get_schedule_sessions( $attr['date'], $tracks_explicitly_specified, $tracks ); 596 $columns = get_schedule_columns( $tracks, $sessions, $tracks_explicitly_specified ); 625 597 626 $html = '<table class="wcpt-schedule" border="0">';598 $html = '<table class="wcpt-schedule" border="0">'; 627 599 $html .= '<thead>'; 628 600 $html .= '<tr>'; 629 601 … … class WordCamp_Post_Types_Plugin { 707 679 $classes[] = 'wcpt-session-type-' . $session_type; 708 680 $classes[] = 'wcb-session-' . $session->post_name; 709 681 682 // Favourite session star-icon. 683 $content = '<div class="wcb-session-favourite-icon">'; 684 $content .= '<a class="fav-session-button"><span class="dashicons dashicons-star-filled"></span></a></div>'; 685 $content .= '<div class="wcb-session-cell-content">'; 686 687 710 688 // Determine the session title 711 689 if ( 'permalink' == $attr['session_link'] && 'session' == $session_type ) 712 690 $session_title_html = sprintf( '<a class="wcpt-session-title" href="%s">%s</a>', esc_url( get_permalink( $session->ID ) ), $session_title ); … … class WordCamp_Post_Types_Plugin { 715 693 else 716 694 $session_title_html = sprintf( '<span class="wcpt-session-title">%s</span>', $session_title ); 717 695 718 $content = $session_title_html;696 $content .= $session_title_html; 719 697 720 698 $speakers_names = array(); 721 699 foreach ( $speakers as $speaker ) { … … class WordCamp_Post_Types_Plugin { 738 716 if ( count( $speakers_names ) ) 739 717 $content .= sprintf( ' <span class="wcpt-session-speakers">%s</span>', implode( ', ', $speakers_names ) ); 740 718 719 // End of cell-content. 720 $content .= '</div>'; 721 741 722 $columns_clone = $columns; 742 723 743 724 // If the next element in the table is the same as the current one, use colspan … … class WordCamp_Post_Types_Plugin { 755 736 } 756 737 } 757 738 758 $columns_html .= sprintf( '<td colspan="%d" class="%s" data-track-title="%s" >%s</td>', $colspan, esc_attr( implode( ' ', $classes ) ), $session_track_titles, $content );739 $columns_html .= sprintf( '<td colspan="%d" class="%s" data-track-title="%s" data-session-id="%s">%s</td>', $colspan, esc_attr( implode( ' ', $classes ) ), $session_track_titles, esc_attr( $session->ID ), $content ); 759 740 } 760 741 761 742 $global_session = $colspan == count( $columns ) ? ' global-session' : ''; … … class WordCamp_Post_Types_Plugin { 769 750 770 751 $html .= '</tbody>'; 771 752 $html .= '</table>'; 753 $html .= $this->fav_session_email_form(); 772 754 return $html; 773 755 } 774 756