Making WordPress.org

Changeset 6268


Ignore:
Timestamp:
12/13/2017 10:42:47 PM (7 years ago)
Author:
iandunn
Message:

WordCamp Post Types: Add ability for attendees to "favorite" sessions.

This contains the code for emailing the saved sessions to yourself, but the UI for that is temporarily disabled pending a discussion with the Systems team about potential abuse mitigations.

See #2733
Props egmanekki

Location:
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wc-post-types
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wc-post-types/css/shortcodes.css

    r3602 r6268  
    22 * [schedule]
    33 */
     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
     42div.wcb-session-favourite-icon a.fav-session-button {
     43    color: #e8e8e8;
     44    text-decoration: none;
     45}
     46
     47
     48div.wcb-session-favourite-icon a.fav-session-button:hover,
     49#content a.fav-session-button:hover {
     50    color: #fff689;
     51    text-decoration: none;
     52}
     53
     54
     55td.wcb-favourite-session a.fav-session-button {
     56    color: #fff689;
     57}
     58
     59.fav-session-email-wait-spinner {
     60    display: none;
     61    border: 2px solid #f3f3f3;
     62    border-radius: 50%;
     63    border-top: 2px solid #777;
     64    width: 16px;
     65    height: 16px;
     66    margin: 10px auto;
     67    -webkit-animation: spin 2s linear infinite;
     68    animation: spin 2s linear infinite;
     69}
     70
     71@-webkit-keyframes spin {
     72    0% { -webkit-transform: rotate( 0deg ); }
     73    100% { -webkit-transform: rotate( 360deg ); }
     74}
     75
     76@keyframes spin {
     77    0% { transform: rotate( 0deg ); }
     78    100% { transform: rotate( 360deg ); }
     79}
     80
     81
     82/*
     83 * CSS slide for email form for favourite sessions
     84 */
     85.fav-session-email-form-hide {
     86    overflow: hidden;
     87    max-height: 0;
     88    padding-top: 0;
     89    padding-bottom: 0;
     90    margin-top: 0;
     91    margin-bottom: 0;
     92    -moz-transition-duration: 0.5s;
     93    -webkit-transition-duration: 0.5s;
     94    -o-transition-duration: 0.5s;
     95    transition-duration: 0.5s;
     96    -moz-transition-timing-function: cubic-bezier( 0, 1, 0.5, 1 );
     97    -webkit-transition-timing-function: cubic-bezier( 0, 1, 0.5, 1 );
     98    -o-transition-timing-function: cubic-bezier( 0, 1, 0.5, 1 );
     99    transition-timing-function: cubic-bezier( 0, 1, 0.5, 1 );
     100}
     101
     102.fav-session-email-form-show {
     103    -moz-transition-duration: 0.5s;
     104    -webkit-transition-duration: 0.5s;
     105    -o-transition-duration: 0.5s;
     106    transition-duration: 0.5s;
     107    -moz-transition-timing-function: ease-in;
     108    -webkit-transition-timing-function: ease-in;
     109    -o-transition-timing-function: ease-in;
     110    transition-timing-function: ease-in;
     111    max-height: 1000px;
     112    overflow: hidden;
     113}
     114
     115.show-email-form {
     116    display: none;
     117    position: fixed;
     118    bottom: 20px;
     119    right: 100px;
     120    padding: 5px 8px 1px 7px;
     121    border: 1px solid #a1a1a1;
     122    background: #dddddd;
     123    border-radius: 6px;
     124    color: #a1a1a1;
     125    z-index: 9999;
     126}
     127
     128.email-form {
     129    position: fixed;
     130    bottom: 50px;
     131    right: 100px;
     132    width: 200px;
     133    background: #dcdcdc;
     134    font-size: 12px;
     135    z-index: 9999;
     136    border-radius: 6px;
     137}
     138
     139#fav-session-email-form {
     140    margin: 10px;
     141}
     142
     143#fav-sessions-email-address {
     144    width: 100%;
     145    margin-bottom: 5px;
     146    padding: 1px 5px;
     147}
     148
     149.fav-session-email-result {
     150    display: none;
     151    margin: 10px;
     152}
     153
     154.fav-sessions-form {
     155    display: none;
     156}
     157
     158.show-email-form,
     159.entry-content a.show-email-form {
     160    color: #a1a1a1;
     161    text-decoration: none;
     162}
     163
     164.show-email-form:hover,
     165.entry-content a.show-email-form:hover,
     166#content a.show-email-form:hover {
     167    color: #555;
     168    text-decoration: none;
     169}
     170
     171.show-email-form .dashicons-star-filled {
     172    font-size: 14px;
     173    padding-top: 2px;
     174    width: 14px;
     175}
     176
    4177@media screen and ( max-width: 700px ) {
    5178    .wcpt-schedule {
     
    73246        color: #21759b;
    74247    }
     248
     249    .show-email-form,
     250    .email-form {
     251        right: 20px;
     252    }
     253
    75254}
    76255
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wc-post-types/inc/rest-api.php

    r5533 r6268  
    77
    88namespace WordCamp\Post_Types\REST_API;
     9use WP_Rest_Server;
     10
    911defined( 'WPINC' ) || die();
     12
     13require_once( 'favorite-schedule-shortcode.php' );
    1014
    1115/**
     
    112116}
    113117
     118
    114119add_action( 'rest_api_init', __NAMESPACE__ . '\register_additional_rest_fields' );
     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 */
     129function 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
     147                'session-list' => array(
     148                    'required' => true,
     149                    'validate_callback' => function( $value, $request, $param ) {
     150                        $session_ids = explode( ',', $value );
     151                        $session_count = count( $session_ids );
     152                        for ( $i = 0; $i < $session_count; $i++ ) {
     153                            if ( ! is_numeric( $session_ids[ $i ] ) ) {
     154                                return false;
     155                            }
     156                        }
     157                        return true;
     158                    },
     159                    'sanitize_callback' => function( $value, $request, $param ) {
     160                        $session_ids = explode( ',', $value );
     161                        return implode( ',', array_filter( $session_ids, 'is_numeric' ) );
     162                    },
     163                ),
     164            )
     165        )
     166    );
     167}
     168add_action( 'rest_api_init', __NAMESPACE__ . '\register_fav_sessions_email' );
    115169
    116170/**
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wc-post-types/wc-post-types.php

    r6227 r6268  
    66
    77require( 'inc/back-compat.php' );
     8require_once( 'inc/favorite-schedule-shortcode.php' );
    89
    910class WordCamp_Post_Types_Plugin {
     
    514515     */
    515516    function shortcode_schedule( $attr, $content ) {
    516         $attr = shortcode_atts( array(
    517             'date'         => null,
    518             'tracks'       => 'all',
    519             'speaker_link' => 'anchor', // anchor|wporg|permalink|none
    520             'session_link' => 'permalink', // permalink|anchor|none
    521         ), $attr );
    522 
    523         foreach ( array( 'tracks', 'speaker_link', 'session_link' ) as $key_for_case_sensitive_value ) {
    524             $attr[ $key_for_case_sensitive_value ] = strtolower( $attr[ $key_for_case_sensitive_value ] );
    525         }
    526 
    527         if ( ! in_array( $attr['speaker_link'], array( 'anchor', 'wporg', 'permalink', 'none' ) ) )
    528             $attr['speaker_link'] = 'anchor';
    529 
    530         if ( ! in_array( $attr['session_link'], array( 'permalink', 'anchor', 'none' ) ) )
    531             $attr['session_link'] = 'permalink';
    532 
    533         $columns = array();
    534         $tracks = array();
    535 
    536         $query_args = array(
    537             'post_type'      => 'wcb_session',
    538             'posts_per_page' => -1,
    539             'meta_query'     => array(
    540                 'relation'   => 'AND',
    541                 array(
    542                     'key'     => '_wcpt_session_time',
    543                     'compare' => 'EXISTS',
    544                 ),
    545             ),
    546         );
    547 
    548         if ( 'all' == $attr['tracks'] ) {
    549             // Include all tracks.
    550             $tracks = get_terms( 'wcb_track' );
    551         } else {
    552             // Loop through given tracks and look for terms.
    553             $terms = array_map( 'trim', explode( ',', $attr['tracks'] ) );
    554             foreach ( $terms as $term_slug ) {
    555                 $term = get_term_by( 'slug', $term_slug, 'wcb_track' );
    556                 if ( $term )
    557                     $tracks[ $term->term_id ] = $term;
    558             }
    559 
    560             // If tracks were provided, restrict the lookup in WP_Query.
    561             if ( ! empty( $tracks ) ) {
    562                 $query_args['tax_query'][] = array(
    563                     'taxonomy' => 'wcb_track',
    564                     'field'    => 'id',
    565                     'terms'    => array_values( wp_list_pluck( $tracks, 'term_id' ) ),
    566                 );
    567             }
    568         }
    569 
    570         if ( $attr['date'] && strtotime( $attr['date'] ) ) {
    571             $query_args['meta_query'][] = array(
    572                 'key'   => '_wcpt_session_time',
    573                 'value' => array(
    574                     strtotime( $attr['date'] ),
    575                     strtotime( $attr['date'] . ' +1 day' ),
    576                 ),
    577                 'compare' => 'BETWEEN',
    578                 'type'    => 'NUMERIC',
    579             );
    580         }
    581 
    582         // Use tracks to form the columns.
    583         if ( $tracks ) {
    584             foreach ( $tracks as $track )
    585                 $columns[ $track->term_id ] = $track->term_id;
    586         } else {
    587             $columns[ 0 ] = 0;
    588         }
    589 
    590         unset( $tracks );
    591 
    592         // Loop through all sessions and assign them into the formatted
    593         // $sessions array: $sessions[ $time ][ $track ] = $session_id
    594         // Use 0 as the track ID if no tracks exist
    595 
    596         $sessions = array();
    597         $sessions_query = new WP_Query( $query_args );
    598         foreach ( $sessions_query->posts as $session ) {
    599             $time = absint( get_post_meta( $session->ID, '_wcpt_session_time', true ) );
    600             $tracks = get_the_terms( $session->ID, 'wcb_track' );
    601 
    602             if ( ! isset( $sessions[ $time ] ) )
    603                 $sessions[ $time ] = array();
    604 
    605             if ( empty( $tracks ) ) {
    606                 $sessions[ $time ][ 0 ] = $session->ID;
    607             } else {
    608                 foreach ( $tracks as $track )
    609                     $sessions[ $time ][ $track->term_id ] = $session->ID;
    610             }
    611         }
    612 
    613         // Sort all sessions by their key (timestamp).
    614         ksort( $sessions );
    615 
    616         // Remove empty columns unless tracks have been explicitly specified
    617         if ( 'all' == $attr['tracks'] ) {
    618             $used_terms = array();
    619 
    620             foreach ( $sessions as $time => $entry )
    621                 if ( is_array( $entry ) )
    622                     foreach ( $entry as $term_id => $session_id )
    623                         $used_terms[ $term_id ] = $term_id;
    624 
    625             $columns = array_intersect( $columns, $used_terms );
    626             unset( $used_terms );
    627         }
    628 
    629         $html = '<table class="wcpt-schedule" border="0">';
     517        $this->enqueue_schedule_shortcode_dependencies();
     518
     519        $attr                        = preprocess_schedule_attributes( $attr );
     520        $tracks                      = get_schedule_tracks( $attr['tracks'] );
     521        $tracks_explicitly_specified = 'all' !== $attr['tracks'];
     522        $sessions                    = get_schedule_sessions( $attr['date'], $tracks_explicitly_specified, $tracks );
     523        $columns                     = get_schedule_columns( $tracks, $sessions, $tracks_explicitly_specified );
     524
     525        $html  = '<table class="wcpt-schedule" border="0">';
    630526        $html .= '<thead>';
    631527        $html .= '<tr>';
     
    718614                $classes[] = 'wcb-session-' . $session->post_name;
    719615
     616                // Favourite session star-icon.
     617                $content = '<div class="wcb-session-favourite-icon">';
     618                $content .= '<a class="fav-session-button"><span class="dashicons dashicons-star-filled"></span></a></div>';
     619                $content .= '<div class="wcb-session-cell-content">';
     620
    720621                // Determine the session title
    721622                if ( 'permalink' == $attr['session_link'] && 'session' == $session_type )
     
    726627                    $session_title_html = sprintf( '<span class="wcpt-session-title">%s</span>', $session_title );
    727628
    728                 $content = $session_title_html;
     629                $content .= $session_title_html;
    729630
    730631                $speakers_names = array();
     
    748649                if ( count( $speakers_names ) )
    749650                    $content .= sprintf( ' <span class="wcpt-session-speakers">%s</span>', implode( ', ', $speakers_names ) );
     651
     652                // End of cell-content.
     653                $content .= '</div>';
    750654
    751655                $columns_clone = $columns;
     
    766670                }
    767671
    768                 $columns_html .= sprintf( '<td colspan="%d" class="%s" data-track-title="%s">%s</td>', $colspan, esc_attr( implode( ' ', $classes ) ), $session_track_titles, $content );
     672                $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 );
    769673            }
    770674
     
    780684        $html .= '</tbody>';
    781685        $html .= '</table>';
     686        $html .= $this->fav_session_email_form();
    782687        return $html;
    783688    }
    784689
    785690    /**
     691     * Enqueue style and scripts needed for [schedule] shortcode.
     692     */
     693    function enqueue_schedule_shortcode_dependencies() {
     694        wp_enqueue_style( 'dashicons' );
     695
     696        wp_enqueue_script(
     697            'favourite-sessions',
     698            plugin_dir_url( __FILE__ ) . 'js/favourite-sessions.js',
     699            array( 'jquery' ),
     700            filemtime( plugin_dir_path( __FILE__ ) . 'js/favourite-sessions.js' ),
     701            true
     702        );
     703
     704        wp_localize_script(
     705            'favourite-sessions',
     706            'favSessionsPhpObject',
     707            array(
     708                'root' => esc_url_raw( rest_url() ),
     709                'i18n' => array(
     710                    'reqTimeOut' => esc_html__( 'Sorry, the email request timed out.', 'wordcamporg' ),
     711                    'otherError' => esc_html__( 'Sorry, the email request failed.',    'wordcamporg' ),
     712                ),
     713            )
     714        );
     715    }
     716
     717    /**
     718     * Return HTML code for email form used to send/share favourite sessions over email.
     719     *
     720     * Both form and button/link to show/hide the form can be styled using classes email-form
     721     * and show-email-form, respectively.
     722     *
     723     * @return string HTML code that represents the form to send emails and a link to show and hide it.
     724     */
     725    function fav_session_email_form() {
     726        static $email_form_count = 0;
     727
     728        // Skip email form if it is disabled or it was already added to document.
     729        if ( email_fav_sessions_disabled() || $email_form_count !== 0 ) {
     730            return '';
     731        }
     732
     733        ob_start();
     734        ?>
     735
     736        <div class="email-form fav-session-email-form-hide">
     737            <div id="fav-session-email-form">
     738                <?php esc_html_e( 'Send me my favorite sessions:', 'wordcamporg' ); ?>
     739
     740                <form id="fav-sessions-form">
     741                    <input type="text" name="email_address" id="fav-sessions-email-address" placeholder="my@email.com" />
     742                    <input type="submit" value="<?php esc_attr_e( 'Send', 'wordcamporg' ); ?>" />
     743                </form>
     744            </div>
     745            <div class="fav-session-email-wait-spinner"></div>
     746            <div class="fav-session-email-result"></div>
     747        </div>
     748
     749        <a class="show-email-form" href="javascript:">
     750            <span class="dashicons dashicons-star-filled"></span>
     751            <span class="dashicons dashicons-email-alt"></span>
     752        </a>
     753
     754        <?php
     755        $email_form = ob_end_flush();
     756
     757        $email_form_count++;
     758
     759        return $email_form;
     760    }
     761
     762    /**
    786763     * Returns a speaker's WordPress.org profile url (if username set)
    787764     *
    788765     * @param $speaker_id int The speaker's post id.
     766     *
     767     * @return NULL|string
    789768     */
    790769    function get_speaker_wporg_permalink( $speaker_id ) {
Note: See TracChangeset for help on using the changeset viewer.