From c91c65cc8b4d6f9cf3656201d1cfd45a359acd74 Mon Sep 17 00:00:00 2001 From: Justin Foell <630830+jrfoell@users.noreply.github.com> Date: Fri, 29 Jan 2021 16:06:21 -0600 Subject: [PATCH] Add route block --- src/WPStrava.php | 16 +++- src/WPStrava/Blocks/Route.php | 98 +++++++++++++++++++++++++ src/WPStrava/RouteRenderer.php | 117 ++++++++++++++++++++++++++++++ src/WPStrava/RouteShortcode.php | 95 +----------------------- src/WPStrava/Routes.php | 31 +++++++- src/blocks/route/edit.js | 125 ++++++++++++++++++++++++++++++++ src/blocks/route/index.js | 29 ++++++++ 7 files changed, 413 insertions(+), 98 deletions(-) create mode 100644 src/WPStrava/Blocks/Route.php create mode 100644 src/WPStrava/RouteRenderer.php create mode 100644 src/blocks/route/edit.js create mode 100644 src/blocks/route/index.js diff --git a/src/WPStrava.php b/src/WPStrava.php index 94252f4..dddde35 100644 --- a/src/WPStrava.php +++ b/src/WPStrava.php @@ -62,6 +62,17 @@ class WPStrava { return self::$instance; } + /** + * Check if rest request to skip link rendering in block editor. + * + * @return boolean + * @author Justin Foell + * @since 2.6.0 + */ + public static function is_rest_request() { + return defined( 'REST_REQUEST' ) && REST_REQUEST; + } + /** * Function to install hooks at WP runtime. * @@ -188,7 +199,10 @@ class WPStrava { * @see https://wordpress.org/gutenberg/handbook/blocks/writing-your-first-block-type/#enqueuing-block-scripts */ public function register_blocks() { - static $blocks = array( 'WPStrava_Blocks_Activity' ); + static $blocks = array( + 'WPStrava_Blocks_Activity', + 'WPStrava_Blocks_Route', + ); // automatically load dependencies and version $asset_file = include WPSTRAVA_PLUGIN_DIR . 'build/index.asset.php'; diff --git a/src/WPStrava/Blocks/Route.php b/src/WPStrava/Blocks/Route.php new file mode 100644 index 0000000..eba2306 --- /dev/null +++ b/src/WPStrava/Blocks/Route.php @@ -0,0 +1,98 @@ + + * @since 2.6.0 + */ + private $add_script = false; + + /** + * Register the wp-strava/route block. + * + * @author Justin Foell + * @since 2.6.0 + */ + public function register_block() { + register_block_type( + 'wp-strava/route', + array( + 'style' => 'wp-strava-block', + 'editor_style' => 'wp-strava-block-editor', + 'editor_script' => 'wp-strava-block', + 'render_callback' => array( $this, 'render_block' ), + 'attributes' => array( + 'url' => array( + 'type' => 'string', + 'default' => '', + ), + 'imageOnly' => array( + 'type' => 'boolean', + 'default' => false, + ), + 'displayMarkers' => array( + 'type' => 'boolean', + 'default' => false, + ), + 'som' => array( + 'type' => 'string', + 'default' => null, + ), + ), + ) + ); + add_action( 'wp_footer', array( $this, 'print_scripts' ) ); + } + + /** + * Render for this block. + * + * @param array $attributes JSON attributes saved in the HTML comment for this block. + * @param string $content The content from JS save() for this block. + * @return string HTML for this block. + * @author Justin Foell + * @since 2.6.0 + */ + public function render_block( $attributes, $content ) { + if ( empty( $attributes['url'] ) ) { + return $content; + } + + $this->add_script = true; + + $matches = []; + preg_match( '/\/routes\/([0-9].*)$/', $attributes['url'], $matches ); + if ( $matches[1] ) { + // Transform from block attributes to shortcode standard. + $attributes = array( + 'id' => $matches[1], + 'image_only' => isset( $attributes['imageOnly'] ) ? $attributes['imageOnly'] : false, + 'markers' => isset( $attributes['displayMarkers'] ) ? $attributes['displayMarkers'] : false, + 'som' => ! empty( $attributes['som'] ) ? $attributes['som'] : null, + ); + + $renderer = new WPStrava_RouteRenderer(); + return $renderer->get_html( $attributes ); + } + return $content; + } + + /** + * Enqueue style if block is being used. + * + * @author Justin Foell + * @since 2.6.0 + */ + public function print_scripts() { + if ( $this->add_script ) { + wp_enqueue_style( 'wp-strava-style' ); + } + } +} diff --git a/src/WPStrava/RouteRenderer.php b/src/WPStrava/RouteRenderer.php new file mode 100644 index 0000000..0d13a3a --- /dev/null +++ b/src/WPStrava/RouteRenderer.php @@ -0,0 +1,117 @@ + + * @since 2.6.0 + */ + public function get_html( $atts ) { + $defaults = array( + 'id' => 0, + 'som' => WPStrava::get_instance()->settings->som, + 'map_width' => '480', + 'map_height' => '320', + 'client_id' => WPStrava::get_instance()->settings->get_default_id(), + 'markers' => false, + 'image_only' => false, + ); + + $atts = wp_parse_args( $atts, $defaults ); + + /* Make sure boolean values are actually boolean + * @see https://wordpress.stackexchange.com/a/119299 + */ + $atts['markers'] = filter_var( $atts['markers'], FILTER_VALIDATE_BOOLEAN ); + $atts['image_only'] = filter_var( $atts['image_only'], FILTER_VALIDATE_BOOLEAN ); + + $route = WPStrava::get_instance()->routes; + $route_details = null; + + try { + $route_details = $route->get_route( $atts['client_id'], $atts['id'] ); + } catch ( WPStrava_Exception $e ) { + return $e->to_html(); + } + + $route_output = ''; + if ( $route_details ) { + $route_output = '
'; + if ( ! $atts['image_only'] ) { + $route_output .= $this->get_table( $route_details, $atts['som'] ); + } + + // Sanitize width & height. + $map_width = str_replace( '%', '', $atts['map_width'] ); + $map_height = str_replace( '%', '', $atts['map_height'] ); + $map_width = str_replace( 'px', '', $map_width ); + $map_height = str_replace( 'px', '', $map_height ); + + $route_output .= $route->get_route_link( + $route_details->id, + WPStrava_StaticMap::get_image_tag( $route_details, $map_height, $map_width, $atts['markers'], $route_details->name ), + $route_details->name + ); + + if ( ! empty( $route_details->description ) ) { + $route_output .= '
' . esc_html( $route_details->description ) . '
'; + } + + $route_output .= '
'; + } // End if( $route_details ). + return $route_output; + } + + /** + * The the route details in in HTML table. + * + * @param stdClass $route_details route details from the route class. + * @param string $som System of measure (english/metric). + * @return string HTML Table of route details. + * @author Justin Foell + * @since 1.7.0 + */ + private function get_table( $route_details, $som ) { + $strava_som = WPStrava_SOM::get_som( $som ); + + $elevation_title = '' . __( 'Elevation Gain', 'wp-strava' ) . ''; + $elevation = '' . $strava_som->elevation( $route_details->elevation_gain ) . ''; + $elevation_label = '' . $strava_som->get_elevation_label() . ''; + + if ( WPStrava::get_instance()->settings->hide_elevation ) { + $elevation = ''; + $elevation_title = ''; + $elevation_label = ''; + } + + return ' + + + + + + ' . $elevation_title . ' + + + + + + + ' . $elevation . ' + + + + + ' . $elevation_label . ' + + +
' . __( 'Est. Moving Time', 'wp-strava' ) . '' . __( 'Distance', 'wp-strava' ) . '
' . $strava_som->time( $route_details->estimated_moving_time ) . '' . $strava_som->distance( $route_details->distance ) . '
' . $strava_som->get_time_label() . '' . $strava_som->get_distance_label() . '
+ '; + } +} diff --git a/src/WPStrava/RouteShortcode.php b/src/WPStrava/RouteShortcode.php index f56f6d3..4a9aa54 100644 --- a/src/WPStrava/RouteShortcode.php +++ b/src/WPStrava/RouteShortcode.php @@ -51,99 +51,8 @@ class WPStrava_RouteShortcode { $this->add_script = true; - $defaults = array( - 'id' => 0, - 'som' => WPStrava::get_instance()->settings->som, - 'map_width' => '480', - 'map_height' => '320', - 'client_id' => WPStrava::get_instance()->settings->get_default_id(), - 'markers' => false, - 'image_only' => false, - ); - - $atts = shortcode_atts( $defaults, $atts, 'route' ); - - /* Make sure boolean values are actually boolean - * @see https://wordpress.stackexchange.com/a/119299 - */ - $atts['markers'] = filter_var( $atts['markers'], FILTER_VALIDATE_BOOLEAN ); - $atts['image_only'] = filter_var( $atts['image_only'], FILTER_VALIDATE_BOOLEAN ); - - $route = WPStrava::get_instance()->routes; - $route_details = null; - - try { - $route_details = $route->get_route( $atts['id'] ); - } catch ( WPStrava_Exception $e ) { - return $e->to_html(); - } - - $route_output = ''; - if ( $route_details ) { - $route_output = '
'; - if ( ! $atts['image_only'] ) { - $route_output .= $this->get_table( $route_details, $atts['som'] ); - } - - // Sanitize width & height. - $map_width = str_replace( '%', '', $atts['map_width'] ); - $map_height = str_replace( '%', '', $atts['map_height'] ); - $map_width = str_replace( 'px', '', $map_width ); - $map_height = str_replace( 'px', '', $map_height ); - - $route_output .= '' . - WPStrava_StaticMap::get_image_tag( $route_details, $map_height, $map_width, $atts['markers'] ) . - ' -
'; - } // End if( $route_details ). - return $route_output; - } - - /** - * The the route details in in HTML table. - * - * @param stdClass $route_details route details from the route class. - * @param string $som System of measure (english/metric). - * @return string HTML Table of route details. - * @author Justin Foell - * @since 1.7.0 - */ - private function get_table( $route_details, $som ) { - $strava_som = WPStrava_SOM::get_som( $som ); - - $elevation_title = '' . __( 'Elevation Gain', 'wp-strava' ) . ''; - $elevation = '' . $strava_som->elevation( $route_details->elevation_gain ) . ''; - $elevation_label = '' . $strava_som->get_elevation_label() . ''; - - if ( WPStrava::get_instance()->settings->hide_elevation ) { - $elevation = ''; - $elevation_title = ''; - $elevation_label = ''; - } - - return ' - - - - - - ' . $elevation_title . ' - - - - - - - ' . $elevation . ' - - - - - ' . $elevation_label . ' - - -
' . __( 'Est. Moving Time', 'wp-strava' ) . '' . __( 'Distance', 'wp-strava' ) . '
' . $strava_som->time( $route_details->estimated_moving_time ) . '' . $strava_som->distance( $route_details->distance ) . '
' . $strava_som->get_time_label() . '' . $strava_som->get_distance_label() . '
- '; + $renderer = new WPStrava_RouteRenderer(); + return $renderer->get_html( $atts ); } /** diff --git a/src/WPStrava/Routes.php b/src/WPStrava/Routes.php index 7329084..6129493 100644 --- a/src/WPStrava/Routes.php +++ b/src/WPStrava/Routes.php @@ -12,11 +12,34 @@ class WPStrava_Routes { /** * Get single route by ID. * - * @param int $route_id ID of activity to retrieve. - * @return object stdClass representing this route. + * @param string $client_id Client ID of athlete to retrieve for + * @param int $route_id ID of route to retrieve. + * @return object stdClass representing this route. * @author Daniel Lintott + * + * @since 1.3.0 */ - public function get_route( $route_id ) { - return WPStrava::get_instance()->get_api()->get( "routes/{$route_id}" ); + public function get_route( $client_id, $route_id ) { + return WPStrava::get_instance()->get_api( $client_id )->get( "routes/{$route_id}" ); } + + /** + * Conditionally display a link based on settings. + * + * @param int $route_id Strava Route ID + * @param string $text Text (or HTML) that is the content of link. + * @param string $title Title attribute (default empty). + * @return void + * @author Justin Foell + * @since 2.6.0 + */ + public function get_route_link( $route_id, $text, $title = '' ) { + if ( WPStrava::is_rest_request() || WPStrava::get_instance()->settings->no_link ) { + return $text; + } + $url = esc_url( self::ROUTES_URL . $route_id ); + $title_attr = $title ? " title='" . esc_attr( $title ) . "'" : ''; + return "{$text}"; + } + } diff --git a/src/blocks/route/edit.js b/src/blocks/route/edit.js new file mode 100644 index 0000000..f890f7f --- /dev/null +++ b/src/blocks/route/edit.js @@ -0,0 +1,125 @@ +/* global wp, wpStrava */ +import EmbedPlaceholder from '../components/embed-placeholder'; +import EmbedControls from '../components/embed-controls'; +import SOMOverride from '../components/som-override'; + +const { __ } = wp.i18n; +const { Component } = wp.element; +const { InspectorControls } = wp.editor; +const { PanelBody, ToggleControl, ServerSideRender } = wp.components; +const { isEmpty } = lodash; + +class Edit extends Component { + + constructor() { + super( ...arguments ); + this.setUrl = this.setUrl.bind( this ); + this.switchBackToURLInput = this.switchBackToURLInput.bind( this ); + this.toggleImageOnly = this.toggleImageOnly.bind( this ); + this.toggleDisplayMarkers = this.toggleDisplayMarkers.bind( this ); + this.overrideSOM = this.overrideSOM.bind( this ); + + this.state = { + url: this.props.attributes.url, + imageOnly: this.props.attributes.imageOnly, + displayMarkers: this.props.attributes.displayMarkers, + som: this.props.attributes.som, + editingURL: isEmpty( this.props.attributes.url ) ? true : false, + }; + } + + setUrl( event ) { + if ( event ) { + event.preventDefault(); + } + this.setState( { editingURL: false } ); + this.props.setAttributes( { url: this.state.url } ); + } + + switchBackToURLInput() { + this.setState( { editingURL: true } ); + } + + toggleImageOnly( checked ) { + this.setState( { imageOnly: checked } ); + this.props.setAttributes( { imageOnly: checked } ); + } + + toggleDisplayMarkers( checked ) { + this.setState( { displayMarkers: checked } ); + this.props.setAttributes( { displayMarkers: checked } ); + } + + overrideSOM( newSOM ) { + this.setState( { som: newSOM } ); + this.props.setAttributes( { som: newSOM } ); + } + + render() { + const { + url, + editingURL, + imageOnly, + displayMarkers, + som + } = this.state; + + // Newly inserted block or we've clicked the edit button. + if ( editingURL ) { + return ( + + this.setState( { url: event.target.value } ) + } + /> + ); + } + + return ( + <> + + + + + this.toggleImageOnly( checked ) } + /> + this.toggleDisplayMarkers( checked ) } + /> + + + + + ); + } +} + +export default Edit; diff --git a/src/blocks/route/index.js b/src/blocks/route/index.js new file mode 100644 index 0000000..4f58688 --- /dev/null +++ b/src/blocks/route/index.js @@ -0,0 +1,29 @@ +/* global wp, wpStrava */ +import { registerBlockType } from '@wordpress/blocks'; +import edit from './edit'; + +registerBlockType( 'wp-strava/route', { + title: 'Strava Route', + icon: 'chart-line', + category: 'embed', + attributes: { + url: { + type: 'string', + default: '', + }, + imageOnly: { + type: 'boolean', + default: false, + }, + displayMarkers: { + type: 'boolean', + default: false, + }, + som: { + type: 'string', + default: null, + }, + }, + edit, + save: () => null, +} );