Moved code to src directory

This commit is contained in:
Justin Foell
2020-01-31 11:51:37 -06:00
parent a73ec38ed3
commit 4c5198fa1c
27 changed files with 3 additions and 3 deletions
+153
View File
@@ -0,0 +1,153 @@
<?php
/**
* Polyline
*
* PHP Version 5.2 (forked)
*
* A simple class to handle polyline-encoding for Google Maps
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Mapping
* @package Polyline
* @author E. McConville <emcconville@emcconville.com>
* @copyright 2009-2015 E. McConville
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3
* @version GIT: $Id: db01b3fea5d96533da928252135ac8f247c1b250 $
* @link https://github.com/emcconville/google-map-polyline-encoding-tool
*/
/**
* Polyline encoding & decoding class
*
* Convert list of points to encoded string following Google's Polyline
* Algorithm.
*
* @category Mapping
* @package Polyline
* @author E. McConville <emcconville@emcconville.com>
* @license http://www.gnu.org/licenses/lgpl.html LGPL v3
* @link https://github.com/emcconville/google-map-polyline-encoding-tool
*/
class Polyline {
/**
* Default precision level of 1e-5.
*
* Overwrite this property in extended class to adjust precision of numbers.
* !!!CAUTION!!!
* 1) Adjusting this value will not guarantee that third party
* libraries will understand the change.
* 2) Float point arithmetic IS NOT real number arithmetic. PHP's internal
* float precision may contribute to undesired rounding.
*
* @var int $precision
*/
protected static $precision = 5;
// To remove PHP 5.3 requirement.
protected static $flatten = array();
/**
* Apply Google Polyline algorithm to list of points.
*
* @param array $points List of points to encode. Can be a list of tuples,
* or a flat on dimensional array.
*
* @return string encoded string
*/
final public static function encode( $points ) {
$points = self::flatten( $points );
$encoded_string = '';
$index = 0;
$previous = array( 0, 0 );
foreach ( $points as $number ) {
$number = (float) $number;
$number = (int) round( $number * pow( 10, static::$precision ) );
$diff = $number - $previous[ $index % 2 ];
$previous[ $index % 2 ] = $number;
$number = $diff;
$index++;
$number = ( $number < 0 ) ? ~( $number << 1 ) : ( $number << 1 );
$chunk = '';
while ( $number >= 0x20 ) {
$chunk .= chr( ( 0x20 | ( $number & 0x1f ) ) + 63 );
$number >>= 5;
}
$chunk .= chr( $number + 63 );
$encoded_string .= $chunk;
}
return $encoded_string;
}
/**
* Reverse Google Polyline algorithm on encoded string.
*
* @param string $string Encoded string to extract points from.
*
* @return array points
*/
final public static function decode( $string ) {
$points = array();
$index = $i = 0; // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
$previous = array( 0, 0 );
while ( $i < strlen( $string ) ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
$shift = $result = 0x00; // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found
do {
$bit = ord( substr( $string, $i++ ) ) - 63;
$result |= ( $bit & 0x1f ) << $shift;
$shift += 5;
} while ( $bit >= 0x20 );
$diff = ( $result & 1 ) ? ~( $result >> 1 ) : ( $result >> 1 );
$number = $previous[ $index % 2 ] + $diff;
$previous[ $index % 2 ] = $number;
$index++;
$points[] = $number * 1 / pow( 10, static::$precision );
}
return $points;
}
/**
* Reduce multi-dimensional to single list
*
* @param array $array Subject array to flatten.
*
* @return array flattened
*/
final public static function flatten( $array ) {
self::$flatten = array();
array_walk_recursive( $array, array( __CLASS__, 'flatten_callback' ) );
return self::$flatten;
}
final public static function flatten_callback( $value ) {
self::$flatten[] = $value;
}
/**
* Concat list into pairs of points
*
* @param array $list One-dimensional array to segment into list of tuples.
*
* @return array pairs
*/
final public static function pair( $list ) {
return is_array( $list ) ? array_chunk( $list, 2 ) : array();
}
}
+179
View File
@@ -0,0 +1,179 @@
<?php
/*
* Main Strava plugin class.
*/
class WPStrava {
/**
* Instance of this class (singleton).
* @var WPStrava
*/
private static $instance = null;
/**
* Settings object to access settings.
* @var WPStrava_Settings
*/
private $settings = null;
/**
* Authorization object.
* @var WPStrava_Auth
*/
private $auth = null;
/**
* Array of WPStrava_API objects (one for each athlete).
*
* @var array
*/
private $api = array();
/**
* Activity object to get activities.
* @var WPStrava_Activity
*/
private $activity = null;
/**
* Route object to get routes.
* @var WPStrava_Routes
*/
private $routes = null;
/**
* Private constructor (singleton).
*/
private function __construct() {
$this->settings = new WPStrava_Settings();
$this->auth = WPStrava_Auth::get_auth( 'refresh' );
}
/**
* Get a singleton instance.
*
* @return WPStrava
*/
public static function get_instance() {
if ( ! self::$instance ) {
$class = __CLASS__;
self::$instance = new $class();
}
return self::$instance;
}
/**
* Function to install hooks at WP runtime.
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function hook() {
$this->auth->hook();
if ( is_admin() ) {
$this->settings->hook();
} else {
add_action( 'init', array( $this, 'register_shortcodes' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) );
}
// Register widgets.
add_action( 'widgets_init', array( $this, 'register_widgets' ) );
}
/**
* Magic method to access activity, routes, settings, etc.
*
* @param string $name One of activity, routes, settings.
* @return mixed|null
*/
public function __get( $name ) {
// On-demand classes.
if ( 'activity' === $name ) {
return $this->get_activity();
}
if ( 'routes' === $name ) {
return $this->get_routes();
}
if ( isset( $this->{$name} ) ) {
return $this->{$name};
}
return null;
}
/**
* Get an API object for the given Client ID.
*
* @param string $id Client ID.
* @return WPStrava_API
*/
public function get_api( $id = null ) {
if ( ! $id ) {
$id = $this->settings->get_default_id();
}
if ( empty( $this->api[ $id ] ) ) {
$this->api[ $id ] = new WPStrava_API( $id );
}
return $this->api[ $id ];
}
/**
* Get the activity object.
*
* @return WPStrava_Activity
*/
public function get_activity() {
if ( ! $this->activity ) {
$this->activity = new WPStrava_Activity();
}
return $this->activity;
}
/**
* Get the routes object.
*
* @return WPStrava_Routes
*/
public function get_routes() {
if ( ! $this->routes ) {
$this->routes = new WPStrava_Routes();
}
return $this->routes;
}
/**
* Register the wp-strava stylesheet.
*/
public function register_scripts() {
// Register a personalized stylesheet.
wp_register_style( 'wp-strava-style', WPSTRAVA_PLUGIN_URL . 'css/wp-strava.css', array(), WPSTRAVA_PLUGIN_VERSION );
}
/**
* Register the widgets.
*/
public function register_widgets() {
register_widget( 'WPStrava_LatestActivitiesWidget' );
register_widget( 'WPStrava_LatestMapWidget' );
}
/**
* Register the shortcodes.
*/
public function register_shortcodes() {
// Initialize short code classes.
new WPStrava_ActivityShortcode();
new WPStrava_LatestActivitiesShortcode();
new WPStrava_RouteShortcode();
new WPStrava_LatestMapShortcode();
}
}
+196
View File
@@ -0,0 +1,196 @@
<?php
/*
* API class for all remote calls.
*/
class WPStrava_API {
const STRAVA_V3_API = 'https://www.strava.com/api/v3/';
private $client_id = null;
/**
* Constructor.
*
* @param int $client_id Strava API Client ID representing an athlete.
* @author Justin Foell <justin@foell.org>
*/
public function __construct( $client_id = null ) {
$this->client_id = $client_id;
}
/**
* POST something to the Strava API.
*
* @param string $uri Path within the Strava API.
* @param array $data Data to POST.
* @return stdClass Strava API response.
* @throws WPStrava_Exception
* @author Justin Foell <justin@foell.org>
*/
public function post( $uri, $data = null ) {
$url = self::STRAVA_V3_API;
$args = array(
'body' => http_build_query( $data ),
'sslverify' => false,
'headers' => array(),
'timeout' => 30,
);
$access_token = $this->get_access_token();
if ( $access_token ) {
$args['headers']['Authorization'] = 'Bearer ' . $access_token;
}
$response = wp_remote_post( $url . $uri, $args );
if ( is_wp_error( $response ) ) {
throw WPStrava_Exception::from_wp_error( $response );
}
if ( 200 != $response['response']['code'] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
// See if there's useful info in the body.
$body = json_decode( $response['body'] );
$error = '';
if ( ! empty( $body->error ) ) {
$error = $body->error;
} else {
$error = print_r( $response, true ); // phpcs:ignore -- Debug output.
}
// Throw an informational exception with a detailed debug exception.
throw new WPStrava_Exception(
$response['response']['message'],
$response['response']['code'],
new WPStrava_Exception( $error )
);
}
return json_decode( $response['body'] );
}
/**
* GET something from the Strava API - checking the cache first.
*
* @param string $uri Path within the Strava API.
* @param array $args Request arguments.
* @return stdClass Strava API response.
* @throws WPStrava_Exception
* @author Justin Foell <justin@foell.org>
*/
public function get( $uri, $args = null ) {
// @see https://stackoverflow.com/a/3764390/2146022
$arg_suffix = is_array( $args ) ? '_' . substr( md5( wp_json_encode( $args ) ), 0, 12 ) : '';
$transient_key = 'strava_api_data_' . $this->client_id . '_' . $uri . $arg_suffix;
$data = get_transient( $transient_key );
if ( $data ) {
return $data;
}
$data = $this->remote_get( $uri, $args );
set_transient( $transient_key, $data, HOUR_IN_SECONDS );
return $data;
}
/**
* GET something from the Strava API.
*
* @param string $uri Path within the Strava API.
* @param array $args Request arguments.
* @return stdClass Strava API response.
* @throws WPStrava_Exception
* @author Justin Foell <justin@foell.org>
* @since 2.0.1
*/
private function remote_get( $uri, $args = null ) {
static $retry = true;
$url = self::STRAVA_V3_API;
$url .= $uri;
if ( ! empty( $args ) ) {
$url = add_query_arg( $args, $url );
}
$get_args = array(
'headers' => array(),
'sslverify' => false,
'timeout' => 30,
);
$access_token = $this->get_access_token();
if ( $access_token ) {
$get_args['headers']['Authorization'] = 'Bearer ' . $access_token;
}
$response = wp_remote_get( $url, $get_args );
if ( is_wp_error( $response ) ) {
throw WPStrava_Exception::from_wp_error( $response );
}
// Try *one* real-time token refresh if 404.
if ( $retry && 404 == $response['response']['code'] ) {
$retry = false;
$auth = WPStrava::get_instance()->auth;
if ( $auth instanceof WPStrava_AuthRefresh ) {
$auth->auth_refresh();
return $this->remote_get( $uri, $args );
}
} else {
$retry = true;
}
if ( 200 != $response['response']['code'] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
// See if there's useful info in the body.
$body = json_decode( $response['body'] );
$error = '';
if ( ! empty( $body->error ) ) {
$error = $body->error;
} elseif ( 503 == $response['response']['code'] ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
$error = __( 'Strava Temporarily Unavailable', 'wp-strava' );
} else {
$error = print_r( $response, true ); // phpcs:ignore -- Debug output.
}
// Throw an informational exception with a detailed debug exception.
throw new WPStrava_Exception(
$response['response']['message'],
$response['response']['code'],
new WPStrava_Exception( $error )
);
}
return json_decode( $response['body'] );
}
/**
* Get the (ever changing) access token based on current Client ID.
*
* @return string|null String for access token, null if not found.
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
private function get_access_token() {
// If client_id not set (OAuth set-up), don't even look.
if ( ! $this->client_id ) {
return null;
}
$settings = WPStrava::get_instance()->settings;
$info = $settings->info;
if ( ! empty( $info[ $this->client_id ]->access_token ) ) {
return $info[ $this->client_id ]->access_token;
}
return null;
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
/*
* Activity is a class wrapper for the Strava REST API functions.
*/
class WPStrava_Activity {
const ACTIVITIES_URL = 'https://strava.com/activities/';
const ATHLETES_URL = 'https://strava.com/athletes/';
/**
* Get single activity by ID.
*
* @param string $client_id Client ID of athlete to retrieve for
* @param int $activity_id ID of activity to retrieve.
* @return object stdClass Representing this activity.
* @author Justin Foell <justin@foell.org>
*/
public function get_activity( $client_id, $activity_id ) {
return WPStrava::get_instance()->get_api( $client_id )->get( "activities/{$activity_id}" );
}
/**
* Get activity list from Strava API.
*
* @author Justin Foell <justin@foell.org>
* @param string $client_id Client ID of athlete to retrieve for
* @param int $club_id Club ID of all club riders (optional).
* @param int|null $quantity Number of records to retrieve (optional).
* @return array Array of activities.
*/
public function get_activities( $client_id, $club_id = null, $quantity = null ) {
$api = WPStrava::get_instance()->get_api( $client_id );
$data = null;
$args = $quantity ? array( 'per_page' => $quantity ) : null;
//Get the json results using the constructor specified values.
if ( is_numeric( $club_id ) ) {
$data = $api->get( "clubs/{$club_id}/activities", $args );
} else {
$data = $api->get( 'athlete/activities', $args );
}
if ( is_array( $data ) ) {
return $data;
}
return array();
}
/**
* Get activities with a distance longer than specified length.
*
* @param array $activities
* @param float $dist Distance in default system of measure (km/mi).
* @return void
* @author Justin Foell <justin@foell.org>
*/
public function get_activities_longer_than( $activities, $dist ) {
$som = WPStrava_SOM::get_som();
$meters = $som->distance_inverse( $dist );
$long_activities = array();
foreach ( $activities as $activity_info ) {
if ( $activity_info->distance > $meters ) {
$long_activities[] = $activity_info;
}
}
return $long_activities;
}
}
+197
View File
@@ -0,0 +1,197 @@
<?php
/**
* Activity Shortcode [activity].
* @package WPStrava
*/
/**
* Activity Shortcode class (converted from Ride).
*
* @author Justin Foell <justin@foell.org>
* @since 1.0
*/
class WPStrava_ActivityShortcode {
/**
* Whether or not to enqueue styles (if shortcode is present).
*
* @var boolean
* @author Justin Foell <justin@foell.org>
* @since 1.0
*/
private $add_script = false;
/**
* Constructor (converted from static init()).
*
* @author Justin Foell <justin@foell.org>
* @since 1.0
*/
public function __construct() {
add_shortcode( 'ride', array( $this, 'handler' ) ); // @deprecated 1.1
add_shortcode( 'activity', array( $this, 'handler' ) );
add_action( 'wp_footer', array( $this, 'print_scripts' ) );
}
/**
* Shortcode handler for [activity].
*
* [activity id=id som=metric map_width="100%" map_height="400px" markers=false]
*
* @param array $atts Array of attributes (id, map_width, etc.).
* @return string Shortcode output
* @author Justin Foell <justin@foell.org>
* @since 1.0
*/
public function handler( $atts ) {
if ( isset( $atts['athlete_token'] ) ) {
// Translators: Message shown when using deprecated athlete_token parameter.
return __( 'The <code>athlete_token</code> parameter is deprecated as of WP-Strava version 2 and should be replaced with <code>client_id</code>.', 'wp-strava' );
}
$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, 'activity' );
/* 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 );
$activity = WPStrava::get_instance()->activity;
$activity_details = null;
try {
$activity_details = $activity->get_activity( $atts['client_id'], $atts['id'] );
} catch ( WPStrava_Exception $e ) {
return $e->to_html();
}
$activity_output = '';
if ( $activity_details ) {
$activity_output .= '<div id="activity-header-' . $atts['id'] . '" class="wp-strava-activity-container">';
if ( ! $atts['image_only'] ) {
$activity_output .= $this->get_table( $activity_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 );
$activity_output .= '<a title="' . $activity_details->name . '" href="' . WPStrava_Activity::ACTIVITIES_URL . $activity_details->id . '">' .
WPStrava_StaticMap::get_image_tag( $activity_details, $map_height, $map_width, $atts['markers'] ) .
'</a>
</div>';
} // End if( $activity_details ).
return $activity_output;
}
/**
* The the activity details in in HTML table.
*
* @param string $activity_details Activity details from the activity class.
* @param string $som System of measure (english/metric).
* @return string HTML Table of activity details.
* @author Justin Foell <justin@foell.org>
* @author Sebastian Erb <mail@sebastianerb.com>
* @since 1.7.0
*/
private function get_table( $activity_details, $som ) {
$strava_som = WPStrava_SOM::get_som( $som );
$strava_activitytype = WPStrava_ActivityType::get_type_group( $activity_details->type );
$avg_title = '<th>' . __( 'Average Speed', 'wp-strava' ) . '</th>';
$max_title = '<th>' . __( 'Max Speed', 'wp-strava' ) . '</th>';
$elevation_title = '<th>' . __( 'Elevation Gain', 'wp-strava' ) . '</th>';
$avg_speed = '';
$max_speed = '';
$elevation = '<td>' . $strava_som->elevation( $activity_details->total_elevation_gain ) . '</td>';
$speed_label = '';
$elevation_label = '<td>' . $strava_som->get_elevation_label() . '</td>';
switch ( $strava_activitytype ) {
case WPStrava_ActivityType::TYPE_GROUP_PACE:
$avg_speed = '<td>' . $strava_som->pace( $activity_details->average_speed ) . '</td>';
$max_speed = '<td>' . $strava_som->pace( $activity_details->max_speed ) . '</td>';
$speed_label = '<td>' . $strava_som->get_pace_label() . '</td>';
break;
case WPStrava_ActivityType::TYPE_GROUP_SPEED:
$avg_speed = '<td>' . $strava_som->speed( $activity_details->average_speed ) . '</td>';
$max_speed = '<td>' . $strava_som->speed( $activity_details->max_speed ) . '</td>';
$speed_label = '<td>' . $strava_som->get_speed_label() . '</td>';
break;
case WPStrava_ActivityType::TYPE_GROUP_PACE:
$avg_speed = '<td>' . $strava_som->swimpace( $activity_details->average_speed ) . '</td>';
$max_speed = '<td>' . $strava_som->swimpace( $activity_details->max_speed ) . '</td>';
$speed_label = '<td>' . $strava_som->get_swimpace_label() . '</td>';
break;
default:
$avg_title = '';
$max_title = '';
break;
}
if ( WPStrava::get_instance()->settings->hide_elevation ) {
$elevation = '';
$elevation_title = '';
$elevation_label = '';
}
return '
<table id="activity-details-table">
<thead>
<tr>
<th>' . __( 'Elapsed Time', 'wp-strava' ) . '</th>
<th>' . __( 'Moving Time', 'wp-strava' ) . '</th>
<th>' . __( 'Distance', 'wp-strava' ) . '</th>
' . $avg_title . '
' . $max_title . '
' . $elevation_title . '
</tr>
</thead>
<tbody>
<tr class="activity-details-table-info">
<td>' . $strava_som->time( $activity_details->elapsed_time ) . '</td>
<td>' . $strava_som->time( $activity_details->moving_time ) . '</td>
<td>' . $strava_som->distance( $activity_details->distance ) . '</td>
' . $avg_speed . '
' . $max_speed . '
' . $elevation . '
</tr>
<tr class="activity-details-table-units">
<td>' . $strava_som->get_time_label() . '</td>
<td>' . $strava_som->get_time_label() . '</td>
<td>' . $strava_som->get_distance_label() . '</td>
' . $speed_label . '
' . $speed_label . '
' . $elevation_label . '
</tr>
</tbody>
</table>
';
}
/**
* Enqueue style if shortcode is being used.
*
* @author Justin Foell <justin@foell.org>
* @since 1.0
*/
public function print_scripts() {
if ( $this->add_script ) {
wp_enqueue_style( 'wp-strava-style' );
}
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
/**
* ActivityType [activitytype].
* @package WPStrava
*/
/**
* ActivityType class.
*
* @author Sebastian Erb <mail@sebastianerb.com>
* @since 1.7.0
*/
class WPStrava_ActivityType {
const TYPE_ALPINESKI = 'AlpineSki';
const TYPE_BACKCOUNTRYSKI = 'BackcountrySki';
const TYPE_CANOEING = 'Canoeing';
const TYPE_CROSSFIT = 'Crossfit';
const TYPE_EBIKERIDE = 'EBikeRide';
const TYPE_ELLIPTICAL = 'Elliptical';
const TYPE_HANDCYCLE = 'Hike';
const TYPE_HIKE = 'IceSkate';
const TYPE_ICESKATE = 'InlineSkate';
const TYPE_INLINESKATE = 'AlpineSki';
const TYPE_KAYAKING = 'Kayaking';
const TYPE_KITESURF = 'Kitesurf';
const TYPE_NORDICSKI = 'NordicSki';
const TYPE_RIDE = 'Ride';
const TYPE_ROCKCLIMBING = 'RockClimbing';
const TYPE_ROLLERSKI = 'RollerSki';
const TYPE_ROWING = 'Rowing';
const TYPE_RUN = 'Run';
const TYPE_SNOWBOARD = 'Snowboard';
const TYPE_SNOWSHOE = 'Snowshoe';
const TYPE_STAIRSTEPPER = 'StairStepper';
const TYPE_STANDUPPADDLING = 'StandUpPaddling';
const TYPE_SURFING = 'Surfing';
const TYPE_SWIM = 'Swim';
const TYPE_VIRTUALRIDE = 'VirtualRide';
const TYPE_VIRTUALRUN = 'VirtualRun';
const TYPE_WALK = 'Walk';
const TYPE_WEIGHTTRAINING = 'WeightTraining';
const TYPE_WHEELCHAIR = 'Wheelchair';
const TYPE_WINDSURF = 'Windsurf';
const TYPE_WORKOUT = 'Workout';
const TYPE_YOGA = 'Yoga';
const TYPE_DEFAULT = self::TYPE_RIDE;
private static $water_types = array( self::TYPE_SWIM );
private static $pace_types = array( self::TYPE_CANOEING, self::TYPE_HIKE, self::TYPE_RUN, self::TYPE_SNOWSHOE, self::TYPE_VIRTUALRUN, self::TYPE_WALK );
private static $speed_types = array( self::TYPE_ALPINESKI, self::TYPE_BACKCOUNTRYSKI, self::TYPE_EBIKERIDE, self::TYPE_ELLIPTICAL, self::TYPE_HANDCYCLE, self::TYPE_ICESKATE, self::TYPE_INLINESKATE, self::TYPE_KAYAKING, self::TYPE_KITESURF, self::TYPE_NORDICSKI, self::TYPE_RIDE, self::TYPE_ROCKCLIMBING, self::TYPE_ROLLERSKI, self::TYPE_ROWING, self::TYPE_SNOWBOARD, self::TYPE_STAIRSTEPPER, self::TYPE_STANDUPPADDLING, self::TYPE_SURFING, self::TYPE_VIRTUALRIDE, self::TYPE_WHEELCHAIR, self::TYPE_WINDSURF );
const TYPE_GROUP_WATER = 'water';
const TYPE_GROUP_PACE = 'pace';
const TYPE_GROUP_SPEED = 'speed';
const TYPE_GROUP_OTHER = 'other';
/**
* Get the type of activity.
*
* @param string $type Type provided by Strava.
* @return string Type group (water/pace/speed/other).
* @author Sebastian Erb <mail@sebastianerb.com>
* @since 1.7.0
*/
public static function get_type_group( $type ) {
if ( in_array( $type, self::$pace_types, true ) ) {
return self::TYPE_GROUP_PACE;
}
if ( in_array( $type, self::$speed_types, true ) ) {
return self::TYPE_GROUP_SPEED;
}
if ( in_array( $type, self::$water_types, true ) ) {
return self::TYPE_GROUP_WATER;
}
return self::TYPE_GROUP_OTHER;
}
}
+150
View File
@@ -0,0 +1,150 @@
<?php
abstract class WPStrava_Auth {
protected $auth_url = 'https://www.strava.com/oauth/authorize?response_type=code';
protected $feedback = '';
/**
* Factory method to get the correct Auth class based on specified string
* or by the options setting.
*
* @param string $auth 'refresh' or 'forever' (default 'refresh').
* @return WPStrava_Auth Instance of Auth
* @author Justin Foell <justin@foell.org>
*/
public static function get_auth( $auth = 'refresh' ) {
if ( 'forever' === $auth ) {
return new WPStrava_AuthForever();
}
// Default to refresh.
return new WPStrava_AuthRefresh();
}
abstract protected function get_authorize_url( $client_id );
public function hook() {
if ( is_admin() ) {
add_filter( 'pre_set_transient_settings_errors', array( $this, 'maybe_oauth' ) );
add_action( 'admin_init', array( $this, 'init' ) );
}
}
/**
* This runs after options are saved
*/
public function maybe_oauth( $value ) {
$settings = WPStrava::get_instance()->settings;
// User is clearing to start-over, don't oauth, ignore other errors.
$input_args = array(
'strava_id' => array(
'filter' => FILTER_SANITIZE_NUMBER_INT,
'flags' => FILTER_REQUIRE_ARRAY,
),
'strava_client_id' => array(
'filter' => FILTER_SANITIZE_NUMBER_INT,
'flags' => FILTER_REQUIRE_SCALAR,
),
'strava_client_secret' => FILTER_SANITIZE_STRING,
);
$input = filter_input_array( INPUT_POST, $input_args );
if ( is_array( $input['strava_id'] ) && $settings->ids_empty( $input['strava_id'] ) ) {
return array();
}
// Redirect only if all the right options are in place.
if ( $settings->is_settings_updated( $value ) && $settings->is_option_page() ) {
// Only re-auth if client ID and secret were saved.
if ( ! empty( $input['strava_client_id'] ) && ! empty( $input['strava_client_secret'] ) ) {
wp_redirect( $this->get_authorize_url( $input['strava_client_id'] ) );
exit();
}
}
return $value;
}
public function init() {
$settings = WPStrava::get_instance()->settings;
$input_args = array(
'settings-updated' => FILTER_SANITIZE_STRING,
'code' => FILTER_SANITIZE_STRING,
);
$input = filter_input_array( INPUT_GET, $input_args );
//only update when redirected back from strava
if ( ! isset( $input['settings-updated'] ) && $settings->is_settings_page() ) {
if ( isset( $input['code'] ) ) {
$info = $this->token_exchange_initial( $input['code'] );
if ( isset( $info->access_token ) ) {
// Translators: New strava token
add_settings_error( 'strava_token', 'strava_token', sprintf( __( 'New Strava token retrieved. %s', 'wp-strava' ), $this->feedback ), 'updated' );
} else {
// throw new WPStrava_Exception( '' );
add_settings_error( 'strava_token', 'strava_token', $this->feedback );
}
} elseif ( isset( $_GET['error'] ) ) {
// Translators: authentication error mess
add_settings_error( 'strava_token', 'strava_token', sprintf( __( 'Error authenticating at Strava: %s', 'wp-strava' ), str_replace( '_', ' ', $_GET['error'] ) ) );
}
}
}
protected function get_redirect_param() {
$page_name = WPStrava::get_instance()->settings->get_page_name();
return rawurlencode( admin_url( "options-general.php?page={$page_name}" ) );
}
// Was fetch_token();
private function token_exchange_initial( $code ) {
$settings = WPStrava::get_instance()->settings;
$client_id = $settings->client_id;
$client_secret = $settings->client_secret;
$settings->delete_id_secret();
if ( $client_id && $client_secret ) {
$data = array(
'client_id' => $client_id,
'client_secret' => $client_secret,
'code' => $code,
);
$data = $this->add_initial_params( $data );
$strava_info = $this->token_request( $data );
if ( isset( $strava_info->access_token ) ) {
$settings->add_id( $client_id );
$settings->save_info( $client_id, $client_secret, $strava_info );
$this->feedback .= __( 'Successfully authenticated.', 'wp-strava' );
return $strava_info;
}
// Translators: error message from Strava
$this->feedback .= sprintf( __( 'There was an error receiving data from Strava: <pre>%s</pre>', 'wp-strava' ), print_r( $strava_info, true ) ); // phpcs:ignore -- Debug output.
return false;
}
$this->feedback .= __( 'Missing Client ID or Client Secret.', 'wp-strava' );
return false;
}
protected function token_request( $data ) {
$api = new WPStrava_API();
return $api->post( 'oauth/token', $data );
}
protected function add_initial_params( $data ) {
return $data;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* This functionality is deprecated and will be shut down on October 15, 2019
*
* @see https://developers.strava.com/docs/oauth-updates/#migration-instructions
*/
/**
* AuthForever Class
*
* @since 2.0.0
* @see http://developers.strava.com/docs/authentication/
*/
class WPStrava_AuthForever extends WPStrava_Auth {
/**
* Authorize URL for old style forever tokens.
*
* @param int $client_id Strava API Client ID.
* @return string URL to authorize against.
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
protected function get_authorize_url( $client_id ) {
return add_query_arg(
array(
'client_id' => $client_id,
'redirect_uri' => $this->get_redirect_param(),
'approval_prompt' => 'force',
),
$this->auth_url
);
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
/**
* AuthRefresh class
*
* @since 2.0.0
* @see http://developers.strava.com/docs/authentication/
*/
class WPStrava_AuthRefresh extends WPStrava_Auth {
/**
* Hooks.
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function hook() {
parent::hook();
// Refresh tokens via cronjob.
add_action( 'init', array( $this, 'setup_auth_refresh_cron' ) );
add_action( 'wp_strava_auth_refresh_cron', array( $this, 'auth_refresh' ) );
}
/**
* Set up cron to refresh the auth token hourly (expires after 6-hours).
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function setup_auth_refresh_cron() {
// Schedule the cron job to purge all expired transients.
if ( ! wp_next_scheduled( 'wp_strava_auth_refresh_cron' ) ) {
wp_schedule_event( time(), 'hourly', 'wp_strava_auth_refresh_cron' );
}
}
/**
* Cron method to refresh auth tokens from Strava.
*
* @author Justin Foell
* @since 2.0.0
*/
public function auth_refresh() {
$settings = WPStrava::get_instance()->settings;
foreach ( $settings->info as $client_id => $info ) {
if ( ! empty( $info->refresh_token ) ) {
$this->token_exchange_refresh( $client_id, $info );
}
}
}
/**
* Authorize URL for new style refresh token auth.
*
* @param int $client_id Strava API Client ID.
* @return string URL to authorize against.
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
protected function get_authorize_url( $client_id ) {
return add_query_arg(
array(
'client_id' => $client_id,
'redirect_uri' => $this->get_redirect_param(),
'approval_prompt' => 'auto',
'scope' => 'activity:read,read',
),
$this->auth_url
);
}
/**
* Add 'authorization_code' grand type to first API request (when authenticating a new user).
*
* @param array $data Request array for the Strava API.
* @return array Data array with 'grant_type' => 'authorization_code' added.
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
protected function add_initial_params( $data ) {
$data['grant_type'] = 'authorization_code';
return $data;
}
/**
* Extend access by contacting strava with a refresh token.
*
* @param int $client_id
* @param stdClass $info
* @return boolean True if refreshed successfully, false otherwise.
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
protected function token_exchange_refresh( $client_id, $info ) {
$data = array(
'client_id' => $client_id,
'client_secret' => $info->client_secret,
'refresh_token' => $info->refresh_token,
'grant_type' => 'refresh_token',
);
$strava_info = $this->token_request( $data );
if ( isset( $strava_info->access_token ) ) {
// Translators: Token refresh success message.
$this->feedback .= __( 'ID %s successfully re-authenticated.', 'wp-strava' );
if ( $strava_info->access_token != $info->access_token ) {
// Translators: New token created message.
$this->feedback .= __( 'ID %s access extended.', 'wp-strava' );
$settings = WPStrava::get_instance()->settings;
$settings->save_info( $client_id, $info->client_secret, $strava_info );
}
return true;
}
// @TODO how to determine if refresh wasn't successful?
$this->feedback .= sprintf( __( 'There was an error receiving data from Strava: <pre>%s</pre>', 'wp-strava' ), print_r( $strava_info, true ) ); // phpcs:ignore -- Debug output.
return false;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
/**
* WPStrava Exception(s).
*/
// phpcs:disable Generic.Files.OneClassPerFile.MultipleFound, Generic.Classes.DuplicateClassName.Found
/*
* PHP 5.2 Nonsense
* @see http://php.net/manual/en/exception.getprevious.php#106020
*/
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
abstract class WPStrava_Abstract_Exception extends Exception {}
} else {
abstract class WPStrava_52_Exception extends Exception {
protected $previous;
public function __construct( $message, $code = 0, Exception $previous = null ) {
$this->previous = $previous;
parent::__construct( $message, $code );
}
public function getPrevious() {
return $this->previous;
}
}
abstract class WPStrava_Abstract_Exception extends WPStrava_52_Exception {}
}
/*
* Exception class for error handling/display.
*/
class WPStrava_Exception extends WPStrava_Abstract_Exception {
/**
* Create a WPStrava_Exception from a WP_Error.
*
* @param WP_Error $error
* @return WPStrava_Exception
* @author Justin Foell <justin@foell.org>
* @since 1.6.0
*/
public static function from_wp_error( WP_Error $error ) {
$class = __CLASS__;
return new $class( $error->get_error_message( $error->get_error_code() ) );
}
/**
* HTML version of this exception.
*
* @return string The exception string wrapped in <pre> tags.
* @author Justin Foell <justin@foell.org>
* @since 1.6.0
*/
public function to_html() {
return '<pre>' . $this . '</pre>';
}
/**
* Magic method to convert this exception to a string.
*
* @return string
* @author Justin Foell <justin@foell.org>
* @since 1.6.0
*/
public function __toString() {
if ( WPSTRAVA_DEBUG && $this->getPrevious() ) {
return $this->get_formatted_message( $this->getPrevious() );
}
return $this->get_formatted_message( $this );
}
/**
* Exception message with extra formatting.
*
* @param Exception $exception
* @return string Formatted exception message.
* @author Justin Foell <justin@foell.org>
* @since 1.6.0
*/
public function get_formatted_message( $exception ) {
$code = $exception->getCode();
if ( $exception->getPrevious() ) {
if ( $code ) {
// Translators: Message shown when there's an exception thrown with a code and there's more details available.
return sprintf( __( 'WP Strava ERROR %1$s %2$s - See full error by adding<br/><code>define( \'WPSTRAVA_DEBUG\', true );</code><br/>to wp-config.php', 'wp-strava' ), $code, $this->getMessage() );
}
// Translators: Message shown when there's an exception thrown (no code) and there's more details available.
return sprintf( __( 'WP Strava ERROR %1$s - See full error by adding<br/><code>define( \'WPSTRAVA_DEBUG\', true );</code><br/>to wp-config.php', 'wp-strava' ), $this->getMessage() );
}
if ( $code ) {
// Translators: Message shown when there's an exception thrown with a code.
return sprintf( __( 'WP Strava ERROR %1$s %2$s', 'wp-strava' ), $code, $exception->getMessage() );
}
// Translators: Message shown when there's an exception thrown without a code.
return sprintf( __( 'WP Strava ERROR %1$s', 'wp-strava' ), $exception->getMessage() );
}
}
// phpcs:enable
+85
View File
@@ -0,0 +1,85 @@
<?php
class WPStrava_LatestActivities {
public static function get_activities_html( $args ) {
if ( isset( $args['athlete_token'] ) ) {
// Translators: Message shown when using deprecated athlete_token parameter.
return __( 'The <code>athlete_token</code> parameter is deprecated as of WP-Strava version 2 and should be replaced with <code>client_id</code>.', 'wp-strava' );
}
$defaults = array(
'client_id' => WPStrava::get_instance()->settings->get_default_id(),
'strava_club_id' => null,
'quantity' => 5,
'som' => WPStrava::get_instance()->settings->som,
);
$args = wp_parse_args( $args, $defaults );
$som = WPStrava_SOM::get_som( $args['som'] );
$strava_activity = WPStrava::get_instance()->activity;
$activities = array();
try {
$activities = $strava_activity->get_activities( $args['client_id'], $args['strava_club_id'], $args['quantity'] );
} catch ( WPStrava_Exception $e ) {
return $e->to_html();
}
$response = "<ul id='activities'>";
foreach ( $activities as $activity ) {
$response .= "<li class='activity'>";
$response .= empty( $activity->id ) ?
$activity->name :
"<a href='" . WPStrava_Activity::ACTIVITIES_URL . $activity->id . "'>" . $activity->name . '</a>';
$response .= "<div class='activity-item'>";
if ( ! empty( $activity->start_date_local ) ) {
$unixtime = strtotime( $activity->start_date_local );
$response .= sprintf(
// Translators: Shows something like "On <date> <[went 10 miles] [during 2 hours] [climbing 100 feet]>."
__( 'On %1$s %2$s', 'wp-strava' ),
date_i18n( get_option( 'date_format' ), $unixtime ),
self::get_activity_time( $unixtime )
);
}
if ( is_numeric( $args['strava_club_id'] ) ) {
$name = $activity->athlete->firstname . ' ' . $activity->athlete->lastname;
$response .= empty( $activity->athlete->id ) ?
" {$name}" :
" <a href='" . WPStrava_Activity::ATHLETES_URL . $activity->athlete->id . "'>" . $name . '</a>';
}
// Translators: "went 10 miles"
$response .= sprintf( __( ' went %1$s %2$s', 'wp-strava' ), $som->distance( $activity->distance ), $som->get_distance_label() );
// Translators: "during 2 hours"
$response .= sprintf( __( ' during %1$s %2$s', 'wp-strava' ), $som->time( $activity->elapsed_time ), $som->get_time_label() );
if ( ! WPStrava::get_instance()->settings->hide_elevation ) {
// Translators: "climbing 100 ft."
$response .= sprintf( __( ' climbing %1$s %2$s', 'wp-strava' ), $som->elevation( $activity->total_elevation_gain ), $som->get_elevation_label() );
}
$response .= '</div></li>';
}
$response .= '</ul>';
return $response;
}
/**
* Get the activity time, possibly hiding it.
*
* @param int $unixtime
* @return string Formatted time, or empty string depending on hide_time option.
* @author Justin Foell <justin@foell.org>
* @since 1.7.1
*/
public static function get_activity_time( $unixtime ) {
if ( WPStrava::get_instance()->settings->hide_time ) {
return '';
}
return date_i18n( get_option( 'time_format' ), $unixtime );
}
}
@@ -0,0 +1,61 @@
<?php
/**
* Latest Activities Shortcode [activities].
* @package WPStrava
*/
/**
* Latest Activities Shortcode class (converted from LatestRides).
*
* @author Justin Foell <justin@foell.org>
* @since 1.3.0
*/
class WPStrava_LatestActivitiesShortcode {
/**
* Whether or not to enqueue styles (if shortcode is present).
*
* @var boolean
* @author Justin Foell <justin@foell.org>
* @since 1.3.0
*/
private $add_script = false;
/**
* Constructor (converted from static init()).
*
* @author Justin Foell <justin@foell.org>
* @since 1.3.0
*/
public function __construct() {
add_shortcode( 'activities', array( $this, 'handler' ) );
add_action( 'wp_footer', array( $this, 'print_scripts' ) );
}
/**
* Shortcode handler for [activities].
*
* [activities som=metric quantity=5 client_id=xxx|strava_club_id=yyy]
*
* @param array $atts Array of attributes (id, som, etc.).
* @return string Shortcode output
* @author Justin Foell <justin@foell.org>
* @since 1.3.0
*/
public function handler( $atts ) {
$this->add_script = true;
return WPStrava_LatestActivities::get_activities_html( $atts );
}
/**
* Enqueue style if shortcode is being used.
*
* @author Justin Foell <justin@foell.org>
* @since 1.3.0
*/
public function print_scripts() {
if ( $this->add_script ) {
wp_enqueue_style( 'wp-strava-style' );
}
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
/**
* WP Strava Latest Activities Widget Class
*/
class WPStrava_LatestActivitiesWidget extends WP_Widget {
public function __construct() {
$widget_ops = array(
'classname' => 'LatestActivitiesWidget',
'description' => __( 'Will show your latest activities from strava.com.', 'wp-strava' ),
);
parent::__construct( 'wp-strava', __( 'Strava Latest Activities List', 'wp-strava' ), $widget_ops );
add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue' ) );
}
public function maybe_enqueue() {
if ( is_active_widget( false, false, $this->id_base ) ) {
wp_enqueue_style( 'wp-strava-style' ); // Only load this when widget is loaded.
}
}
/** @see WP_Widget::widget */
public function widget( $args, $instance ) {
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Latest Activities', 'wp-strava' ) : $instance['title'] );
$activities_args = array(
'client_id' => isset( $instance['client_id'] ) ? $instance['client_id'] : null,
'strava_club_id' => isset( $instance['strava_club_id'] ) ? $instance['strava_club_id'] : null,
'quantity' => isset( $instance['quantity'] ) ? $instance['quantity'] : null,
);
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Widget OK.
echo $args['before_widget'];
if ( $title ) {
echo $args['before_title'] . $title . $args['after_title'];
}
echo WPStrava_LatestActivities::get_activities_html( $activities_args );
echo $args['after_widget'];
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
}
/** @see WP_Widget::update */
public function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = wp_strip_all_tags( $new_instance['title'] );
$instance['client_id'] = wp_strip_all_tags( $new_instance['client_id'] );
$instance['strava_club_id'] = wp_strip_all_tags( $new_instance['strava_club_id'] );
$instance['quantity'] = $new_instance['quantity'];
return $instance;
}
/** @see WP_Widget::form */
public function form( $instance ) {
$title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : __( 'Latest Activities', 'wp-strava' );
$all_ids = WPStrava::get_instance()->settings->get_all_ids();
$client_id = isset( $instance['client_id'] ) ? esc_attr( $instance['client_id'] ) : WPStrava::get_instance()->settings->get_default_id();
$strava_club_id = isset( $instance['strava_club_id'] ) ? esc_attr( $instance['strava_club_id'] ) : '';
$quantity = isset( $instance['quantity'] ) ? absint( $instance['quantity'] ) : 5;
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'wp-strava' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'client_id' ) ); ?>"><?php esc_html_e( 'Athlete:', 'wp-strava' ); ?></label>
<select name="<?php echo esc_attr( $this->get_field_name( 'client_id' ) ); ?>">
<?php foreach ( $all_ids as $id => $nickname ) : ?>
<option value="<?php echo esc_attr( $id ); ?>"<?php selected( $id, $client_id ); ?>><?php echo esc_html( $nickname ); ?></option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'strava_club_id' ) ); ?>"><?php esc_html_e( 'Club ID (leave blank to show single Athlete):', 'wp-strava' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'strava_club_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'strava_club_id' ) ); ?>" type="text" value="<?php echo esc_attr( $strava_club_id ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'quantity' ) ); ?>"><?php esc_html_e( 'Quantity:', 'wp-strava' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'quantity' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'quantity' ) ); ?>" type="text" value="<?php echo esc_html( $quantity ); ?>" />
</p>
<?php
}
} // class LatestActivitiesWidget
+46
View File
@@ -0,0 +1,46 @@
<?php
class WPStrava_LatestMap {
public static function get_map_html( $args ) {
$defaults = array(
'client_id' => WPStrava::get_instance()->settings->get_default_id(),
'strava_club_id' => null,
'distance_min' => 0,
);
$args = wp_parse_args( $args, $defaults );
$strava_activity = WPStrava::get_instance()->activity;
$activities = array();
try {
$activities = $strava_activity->get_activities( $args['client_id'], $args['strava_club_id'] );
} catch ( WPStrava_Exception $e ) {
// If athlete_token is still set, warn about that first and foremost.
if ( isset( $args['athlete_token'] ) ) {
// Translators: Message shown when using deprecated athlete_token parameter.
echo wp_kses_post( __( 'The <code>athlete_token</code> parameter is deprecated as of WP-Strava version 2 and should be replaced with <code>client_id</code>.', 'wp-strava' ) );
} else {
echo $e->to_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Debug only.
}
}
if ( ! empty( $activities ) ) {
if ( ! empty( $args['distance_min'] ) ) {
$activities = $strava_activity->get_activities_longer_than( $activities, $args['distance_min'] );
}
$activity = current( $activities );
echo empty( $activity->map ) ?
// Translators: Text with activity name shown in place of image if not available.
esc_html( sprintf( __( 'Map not available for activity "%s"', 'wp-strava' ), $activity->name ) ) :
"<a title='" . esc_attr( $activity->name ) . "' href='" . esc_attr( WPStrava_Activity::ACTIVITIES_URL . $activity->id ) . "'>" .
WPStrava_StaticMap::get_image_tag( $activity ) . // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Image OK.
'</a>';
}
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* Latest Map Shortcode [latest_map].
* @package WPStrava
*/
/**
* Latest Map Shortcode class.
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.1
*/
class WPStrava_LatestMapShortcode {
/**
* Constructor.
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.1
*/
public function __construct() {
add_shortcode( 'latest_map', array( $this, 'handler' ) );
}
/**
* Shortcode handler for [latest_map].
*
* [latest_map som=metric distance_min=10 client_id=xxx|strava_club_id=yyy]
*
* @param array $atts Array of attributes (client_id, som, etc.).
* @return string Shortcode output
* @author Justin Foell <justin@foell.org>
* @since 2.0.1
*/
public function handler( $atts ) {
return WPStrava_LatestMap::get_map_html( $atts );
}
}
+90
View File
@@ -0,0 +1,90 @@
<?php
class WPStrava_LatestMapWidget extends WP_Widget {
private $som;
public function __construct() {
$this->som = WPStrava_SOM::get_som();
parent::__construct(
false,
__( 'Strava Latest Map', 'wp-strava' ), // Name
array( 'description' => __( 'Strava latest activity using static google map image', 'wp-strava' ) ) // Args.
);
}
public function form( $instance ) {
// outputs the options form on admin
$title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : __( 'Latest Activity Map', 'wp-strava' );
$all_ids = WPStrava::get_instance()->settings->get_all_ids();
$client_id = isset( $instance['client_id'] ) ? esc_attr( $instance['client_id'] ) : WPStrava::get_instance()->settings->get_default_id();
$distance_min = isset( $instance['distance_min'] ) ? esc_attr( $instance['distance_min'] ) : '';
$strava_club_id = isset( $instance['strava_club_id'] ) ? esc_attr( $instance['strava_club_id'] ) : '';
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
<?php // Translator: Widget Title. ?>
<?php esc_html_e( 'Title:', 'wp-strava' ); ?>
</label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'client_id' ) ); ?>"><?php esc_html_e( 'Athlete:', 'wp-strava' ); ?></label>
<select name="<?php echo esc_attr( $this->get_field_name( 'client_id' ) ); ?>">
<?php foreach ( $all_ids as $id => $nickname ) : ?>
<option value="<?php echo esc_attr( $id ); ?>"<?php selected( $id, $client_id ); ?>><?php echo esc_html( $nickname ); ?></option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'distance_min' ) ); ?>">
<?php // Translators: Label for minimum distance input. ?>
<?php echo esc_html( sprintf( __( 'Min. Distance (%s):', 'wp-strava' ), $this->som->get_distance_label() ) ); ?>
</label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'distance_min' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'distance_min' ) ); ?>" type="text" value="<?php echo esc_attr( $distance_min ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'strava_club_id' ) ); ?>"><?php esc_html_e( 'Club ID (leave blank to show Athlete):', 'wp-strava' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'strava_club_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'strava_club_id' ) ); ?>" type="text" value="<?php echo esc_attr( $strava_club_id ); ?>" />
</p>
<?php
}
public function update( $new_instance, $old_instance ) {
// Processes widget options to be saved from the admin.
$instance = $old_instance;
$instance['title'] = wp_strip_all_tags( $new_instance['title'] );
$instance['client_id'] = wp_strip_all_tags( $new_instance['client_id'] );
$instance['strava_club_id'] = wp_strip_all_tags( $new_instance['strava_club_id'] );
$instance['distance_min'] = wp_strip_all_tags( $new_instance['distance_min'] );
return $instance;
}
/**
* Method to render the widget on the front end.
*
* @param array $args Arguments from the widget settings.
* @param array $instance Settings for this particular widget.
*/
public function widget( $args, $instance ) {
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Latest Activity Map', 'wp-strava' ) : $instance['title'] );
$activities_args = array(
'client_id' => isset( $instance['client_id'] ) ? $instance['client_id'] : null,
'strava_club_id' => isset( $instance['strava_club_id'] ) ? $instance['strava_club_id'] : null,
'distance_min' => isset( $instance['distance_min'] ) ? absint( $instance['distance_min'] ) : 0,
);
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Widget OK.
echo $args['before_widget'];
if ( $title ) {
echo $args['before_title'] . $title . $args['after_title'];
}
echo WPStrava_LatestMap::get_map_html( $activities_args );
echo $args['after_widget'];
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
/**
* Route Shortcode [route].
* @package WPStrava
*/
/**
* Route Shortcode class.
*
* @author Daniel Lintott
* @since 1.3.0
*/
class WPStrava_RouteShortcode {
/**
* Whether or not to enqueue styles (if shortcode is present).
*
* @var boolean
* @author Daniel Lintott
* @since 1.3.0
*/
private $add_script = false;
/**
* Constructor (converted from static init()).
*
* @author Daniel Lintott
* @author Justin Foell <justin@foell.org>
* @since 1.6.0
*/
public function __construct() {
add_shortcode( 'route', array( $this, 'handler' ) );
add_action( 'wp_footer', array( $this, 'print_scripts' ) );
}
/**
* Shortcode handler for [route].
*
* [route id=id som=metric map_width="100%" map_height="400px" markers=false]
*
* @param array $atts Array of attributes (id, map_width, etc.).
* @return string Shortcode output
* @author Daniel Lintott
* @since 1.3.0
*/
public function handler( $atts ) {
if ( isset( $atts['athlete_token'] ) ) {
// Translators: Message shown when using deprecated athlete_token parameter.
return __( 'The <code>athlete_token</code> parameter is deprecated as of WP-Strava version 2 and should be replaced with <code>client_id</code>.', 'wp-strava' );
}
$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 = '<div id="activity-header-' . $atts['id'] . '" class="wp-strava-activity-container">';
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 .= '<a title="' . $route_details->name . '" href="' . WPStrava_Routes::ROUTES_URL . $route_details->id . '">' .
WPStrava_StaticMap::get_image_tag( $route_details, $map_height, $map_width, $atts['markers'] ) .
'</a>
</div>';
} // End if( $route_details ).
return $route_output;
}
/**
* The the route details in in HTML table.
*
* @param string $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 <justin@foell.org>
* @since 1.7.0
*/
private function get_table( $route_details, $som ) {
$strava_som = WPStrava_SOM::get_som( $som );
$elevation_title = '<th>' . __( 'Elevation Gain', 'wp-strava' ) . '</th>';
$elevation = '<td>' . $strava_som->elevation( $route_details->elevation_gain ) . '</td>';
$elevation_label = '<td>' . $strava_som->get_elevation_label() . '</td>';
if ( WPStrava::get_instance()->settings->hide_elevation ) {
$elevation = '';
$elevation_title = '';
$elevation_label = '';
}
return '
<table id="activity-details-table">
<thead>
<tr>
<th>' . __( 'Est. Moving Time', 'wp-strava' ) . '</th>
<th>' . __( 'Distance', 'wp-strava' ) . '</th>
' . $elevation_title . '
</tr>
</thead>
<tbody>
<tr class="activity-details-table-info">
<td>' . $strava_som->time( $route_details->estimated_moving_time ) . '</td>
<td>' . $strava_som->distance( $route_details->distance ) . '</td>
' . $elevation . '
</tr>
<tr class="activity-details-table-units">
<td>' . $strava_som->get_time_label() . '</td>
<td>' . $strava_som->get_distance_label() . '</td>
' . $elevation_label . '
</tr>
</tbody>
</table>
';
}
/**
* Enqueue style if shortcode is being used.
*
* @author Daniel Lintott
* @since 1.3.0
*/
public function print_scripts() {
if ( $this->add_script ) {
wp_enqueue_style( 'wp-strava-style' );
}
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
/**
* Routes is a class wrapper for the Strava REST API functions.
*
* @author Daniel Lintott
* @since 1.3.0
*/
class WPStrava_Routes {
const ROUTES_URL = 'https://strava.com/routes/';
/**
* Get single route by ID.
*
* @param int $route_id ID of activity to retrieve.
* @return object stdClass representing this route.
* @author Daniel Lintott
*/
public function get_route( $route_id ) {
return WPStrava::get_instance()->get_api()->get( "routes/{$route_id}" );
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
abstract class WPStrava_SOM {
/**
* Factory method to get the correct SOM class based on specified units
* or by the options setting.
*
* @param string $som 'english' or 'metric'
* @return WPStrava_SOM Instance of SOM
* @author Justin Foell <justin@foell.org>
*/
public static function get_som( $som = null ) {
$som = $som ? $som : WPStrava::get_instance()->settings->som;
if ( 'english' === $som ) {
return new WPStrava_SOMEnglish();
}
// Default to metric.
return new WPStrava_SOMMetric();
}
abstract public function distance( $m );
abstract public function distance_inverse( $dist );
abstract public function get_distance_label();
abstract public function speed( $mps );
abstract public function get_speed_label();
abstract public function elevation( $m );
abstract public function get_elevation_label();
abstract public function pace( $mps );
abstract public function get_pace_label();
/**
* Create a time string of hours:minutes:seconds from just seconds.
*
* @return string Time formatted as 'H:i:s'.
* @see https://stackoverflow.com/a/20870843/2146022
*/
public function time( $seconds ) {
$zero = new DateTime( '@0' );
$offset = new DateTime( "@{$seconds}" );
$diff = $zero->diff( $offset );
return sprintf( '%02d:%02d:%02d', $diff->days * 24 + $diff->h, $diff->i, $diff->s );
}
/**
* Label for hours.
*
* @return string 'hours'
*/
public function get_time_label() {
return __( 'hours', 'wp-strava' );
}
/**
* Abbreviated label for this system of measure's pace - Minutes Per 100 Meters: min/100m. Same for English/metric.
*
* @return string 'min/100m'
*/
public function get_swimpace_label() {
return __( 'min/100m', 'wp-strava' );
}
/**
* Change meters per second to Minutes Per 100 Meters. Same for English/metric.
*
* @param float|string $mps Meters per second.
* @return string Minutes Per 100 Meters.
*/
public function swimpace( $mps ) {
$kmh = $mps * 3.6;
$s = 3600 / $kmh / 10;
$ss = $s / 60;
$ms = floor( $ss ) * 60;
$sec = sprintf( '%02d', round( $s - $ms ) );
$min = floor( $ss );
return "{$min}:{$sec}";
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
/**
* SOM English class.
*
* All conversions are limited to 2 decimal places.
*/
class WPStrava_SOMEnglish extends WPStrava_SOM {
/**
* Change meters to miles.
*
* @param float|string $m Distance in meters.
* @return string Distance in miles.
*/
public function distance( $m ) {
return number_format_i18n( $m / 1609.344, 2 );
}
/**
* Change miles to meters.
*
* @param float $dist Distance in miles.
* @return float Distance in meters.
*/
public function distance_inverse( $dist ) {
return (float) number_format( $dist * 1609.344, 2, '.', '' );
}
/**
* Abbreviated label for this system of measure's distance - Miles: mi.
*
* @return string 'mi.'
*/
public function get_distance_label() {
return __( 'mi.', 'wp-strava' );
}
/**
* Change meters per second to miles per hour.
*
* @param float|string $mps Meters per second.
* @return string Miles per hour.
*/
public function speed( $mps ) {
return number_format_i18n( $mps * 2.2369, 2 );
}
/**
* Abbreviated label for this system of measure's speed - Miles Per Hour: mph
*
* @return string 'mph'
*/
public function get_speed_label() {
return __( 'mph', 'wp-strava' );
}
/**
* Change meters per second to minutes per mile.
*
* @param float|string $mps Meters per second.
* @return string Minutes Per Mile.
*/
public function pace( $mps ) {
if ( ! $mps ) {
return __( 'N/A', 'wp-strava' );
}
$mph = $mps * 2.2369;
$s = 3600 / $mph;
$ss = $s / 60;
$ms = floor( $ss ) * 60;
$sec = sprintf( '%02d', round( $s - $ms ) );
$min = floor( $ss );
return "{$min}:{$sec}";
}
/**
* Abbreviated label for this system of measure's pace - Minutes Per Mile: min/mile
*
* @return string 'min/mile'
*/
public function get_pace_label() {
return __( 'min/mile', 'wp-strava' );
}
/**
* Change meters to feet.
*
* @param float|string $m Elevation in meters.
* @return string Elevation in feet.
*/
public function elevation( $m ) {
return number_format_i18n( $m / 0.3048, 2 );
}
/**
* Abbreviated label for this system of measure's elevation - Feet: ft.
*
* @return string 'ft.'
*/
public function get_elevation_label() {
return __( 'ft.', 'wp-strava' );
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
/**
* SOM Metric class.
*
* All conversions are limited to 2 decimal places.
*/
class WPStrava_SOMMetric extends WPStrava_SOM {
/**
* Change meters to kilometers.
*
* @param float|string $m Distance in meters.
* @return string Distance in kilometers.
*/
public function distance( $m ) {
return number_format_i18n( $m / 1000, 2 );
}
/**
* Change kilometers to meters.
*
* @param float $dist Distance in kilometers.
* @return float Distance in meters.
*/
public function distance_inverse( $dist ) {
return (float) number_format( $dist * 1000, 2, '.', '' );
}
/**
* Abbreviated label for this system of measure's distance - Kilometers: km
*
* @return string 'km'
*/
public function get_distance_label() {
return __( 'km', 'wp-strava' );
}
/**
* Change meters per second to kilometers per hour.
*
* @param float|string $mps Meters per second.
* @return string Kilometers per hour.
*/
public function speed( $mps ) {
return number_format_i18n( $mps * 3.6, 2 );
}
/**
* Abbreviated label for this system of measure's speed - Kilometers Per Hour: km/h
*
* @return string 'km/h'
*/
public function get_speed_label() {
return __( 'km/h', 'wp-strava' );
}
/**
* Change meters per second to minutes per kilometer.
*
* @param float|string $mps Meters per second.
* @return string Minutes per kilometer.
*/
public function pace( $mps ) {
if ( ! $mps ) {
return __( 'N/A', 'wp-strava' );
}
// 4 m/s => 14,4 km/h => 4:10 min/km
$kmh = $mps * 3.6;
$s = 3600 / $kmh;
$ss = $s / 60;
$ms = floor( $ss ) * 60;
$sec = sprintf( '%02d', round( $s - $ms ) );
$min = floor( $ss );
return "{$min}:{$sec}";
}
/**
* Abbreviated label for this system of measure's speed - Minutes Per Kilometers: min/km
*
* @return string 'min/km'
*/
public function get_pace_label() {
return __( 'min/km', 'wp-strava' );
}
/**
* Change meters to meters };^)
*
* @param float|string $m Elevation in meters.
* @return string Elevation in meters.
*/
public function elevation( $m ) {
return number_format_i18n( $m, 2 );
}
/**
* Abbreviated label for this system of measure's elevation - Meters: meters
*
* @return string 'meters'
*/
public function get_elevation_label() {
return __( 'meters', 'wp-strava' );
}
}
+761
View File
@@ -0,0 +1,761 @@
<?php
/**
* V3 - http://strava.github.io/api/v3/oauth/
*
* Set up an "API Application" at Strava
* Save the Client ID and Client Secret in WordPress - redirect to strava oauth/authorize URL for permission
* Get redirected back to this settings page with ?code= or ?error=
* Use code to retrieve auth token
*/
class WPStrava_Settings {
private $ids = array();
private $page_name = 'wp-strava-options';
private $option_page = 'wp-strava-settings-group';
private $adding_athlete = true;
/**
* Register actions & filters for menus and authentication.
*
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function hook() {
// Load IDs for any subsequent actions.
$this->ids = $this->get_ids();
add_action( 'admin_init', array( $this, 'register_strava_settings' ) );
add_action( 'admin_menu', array( $this, 'add_strava_menu' ) );
add_filter( 'plugin_action_links_' . WPSTRAVA_PLUGIN_NAME, array( $this, 'settings_link' ) );
add_action( 'in_plugin_update_message-wp-strava/wp-strava.php', array( $this, 'plugin_update_message' ), 10, 2 );
add_action( 'after_plugin_row_wp-strava/wp-strava.php', array( $this, 'ms_plugin_update_message' ), 10, 2 );
}
/**
* Add the strava settings menu.
*
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function add_strava_menu() {
add_options_page(
__( 'Strava Settings', 'wp-strava' ),
__( 'Strava', 'wp-strava' ),
'manage_options',
$this->page_name,
array( $this, 'print_strava_options' )
);
}
/**
* Register settings using the WP Settings API.
*
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function register_strava_settings() {
add_settings_section( 'strava_api', __( 'Strava API', 'wp-strava' ), array( $this, 'print_api_instructions' ), 'wp-strava' );
$this->adding_athlete = $this->is_adding_athlete();
if ( $this->ids_empty( $this->ids ) ) {
register_setting( $this->option_page, 'strava_client_id', array( $this, 'sanitize_client_id' ) );
register_setting( $this->option_page, 'strava_client_secret', array( $this, 'sanitize_client_secret' ) );
register_setting( $this->option_page, 'strava_nickname', array( $this, 'sanitize_nickname' ) );
add_settings_field( 'strava_client_id', __( 'Strava Client ID', 'wp-strava' ), array( $this, 'print_client_input' ), 'wp-strava', 'strava_api' );
add_settings_field( 'strava_client_secret', __( 'Strava Client Secret', 'wp-strava' ), array( $this, 'print_secret_input' ), 'wp-strava', 'strava_api' );
add_settings_field( 'strava_nickname', __( 'Strava Nickname', 'wp-strava' ), array( $this, 'print_nickname_input' ), 'wp-strava', 'strava_api' );
} else {
register_setting( $this->option_page, 'strava_id', array( $this, 'sanitize_id' ) );
add_settings_field( 'strava_id', __( 'Saved ID', 'wp-strava' ), array( $this, 'print_id_input' ), 'wp-strava', 'strava_api' );
// Add additional fields
register_setting( $this->option_page, 'strava_client_id', array( $this, 'sanitize_client_id' ) );
register_setting( $this->option_page, 'strava_client_secret', array( $this, 'sanitize_client_secret' ) );
register_setting( $this->option_page, 'strava_nickname', array( $this, 'sanitize_nickname' ) );
add_settings_field( 'strava_client_id', __( 'Additional Athlete Client ID', 'wp-strava' ), array( $this, 'print_client_input' ), 'wp-strava', 'strava_api' );
add_settings_field( 'strava_client_secret', __( 'Additional Athlete Client Secret', 'wp-strava' ), array( $this, 'print_secret_input' ), 'wp-strava', 'strava_api' );
add_settings_field( 'strava_nickname', __( 'Additional Athlete Nickname', 'wp-strava' ), array( $this, 'print_nickname_input' ), 'wp-strava', 'strava_api' );
}
// Google Maps API.
register_setting( $this->option_page, 'strava_gmaps_key', array( $this, 'sanitize_gmaps_key' ) );
add_settings_section( 'strava_gmaps', __( 'Google Maps', 'wp-strava' ), array( $this, 'print_gmaps_instructions' ), 'wp-strava' );
add_settings_field( 'strava_gmaps_key', __( 'Static Maps Key', 'wp-strava' ), array( $this, 'print_gmaps_key_input' ), 'wp-strava', 'strava_gmaps' );
// System of Measurement.
register_setting( $this->option_page, 'strava_som', array( $this, 'sanitize_som' ) );
add_settings_section( 'strava_options', __( 'Options', 'wp-strava' ), null, 'wp-strava' );
add_settings_field( 'strava_som', __( 'System of Measurement', 'wp-strava' ), array( $this, 'print_som_input' ), 'wp-strava', 'strava_options' );
// Hide Options.
register_setting( $this->option_page, 'strava_hide_time', array( $this, 'sanitize_hide_time' ) );
add_settings_field( 'strava_hide_time', __( 'Hide Activity Time', 'wp-strava' ), array( $this, 'print_hide_time_input' ), 'wp-strava', 'strava_options' );
register_setting( $this->option_page, 'strava_hide_elevation', array( $this, 'sanitize_hide_elevation' ) );
add_settings_field( 'strava_hide_elevation', __( 'Hide Activity Elevation', 'wp-strava' ), array( $this, 'print_hide_elevation_input' ), 'wp-strava', 'strava_options' );
// Clear cache.
register_setting( $this->option_page, 'strava_cache_clear', array( $this, 'sanitize_cache_clear' ) );
add_settings_section( 'strava_cache', __( 'Cache', 'wp-strava' ), null, 'wp-strava' );
add_settings_field( 'strava_cache_clear', __( 'Clear cache (images & transient data)', 'wp-strava' ), array( $this, 'print_clear_input' ), 'wp-strava', 'strava_cache' );
}
/**
* Print the Strava setup instructions.
*
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function print_api_instructions() {
$settings_url = 'https://www.strava.com/settings/api';
$icon_url = 'https://plugins.svn.wordpress.org/wp-strava/assets/icon-128x128.png';
$blog_name = get_bloginfo( 'name' );
// Translators: Strava "app" name
$app_name = sprintf( __( '%s Strava', 'wp-strava' ), $blog_name );
$site_url = site_url();
// Translators: Strava "app" description
$description = sprintf( __( 'WP-Strava for %s', 'wp-strava' ), $blog_name );
echo wp_kses_post(
sprintf(
__(
"<p>Steps:</p>
<ol>
<li>Create your free API Application/Connection here: <a href='%1\$s'>%2\$s</a> using the following information:</li>
<ul>
<li>App Icon: <strong>upload <a href='%3\$s'>this image</a></strong></li>
<li>Application Name: <strong>%4\$s</strong></li>
<li>Category: OK to leave at default 'other'</li>
<li>Club: OK to leave blank</li>
<li>Website: <strong>%5\$s</strong></li>
<li>Application Description: <strong>%6\$s</strong></li>
<li>Authorization Callback Domain: <strong>%7\$s</strong></li>
</ul>
<li>Once you've created your API Application at strava.com, enter the <strong>Client ID</strong> and <strong>Client Secret</strong> below, which can now be found on that same strava API Settings page.
<li>After saving your Client ID and Secret, you'll be redirected to strava to authorize your API Application. If successful, your Strava ID will display in a table, next to your nickname.</li>
<li>If you need to re-authorize your API Application, erase your Strava ID next to your nickname and click 'Save Changes' to start over.</li>
</ol>",
'wp-strava'
),
$settings_url,
$settings_url,
$icon_url,
$app_name,
$site_url,
$description,
wp_parse_url( $site_url, PHP_URL_HOST )
)
);
}
/**
* Print the google maps instructions.
*
* @author Justin Foell <justin@foell.org>
* @since 1.1
*/
public function print_gmaps_instructions() {
$maps_url = 'https://developers.google.com/maps/documentation/static-maps/';
echo wp_kses_post(
sprintf(
__(
"<p>Steps:</p>
<ol>
<li>To use Google map images, you must create a Static Maps API Key. Create a free key by going here: <a href='%1\$s'>%2\$s</a> and clicking <strong>Get a Key</strong></li>
<li>Once you've created your Google Static Maps API Key, enter the key below.
</ol>",
'wp-strava'
),
$maps_url,
$maps_url
)
);
}
/**
* Print the settings page container.
*
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function print_strava_options() {
include WPSTRAVA_PLUGIN_DIR . 'templates/admin-settings.php';
}
/**
* Print the client ID input
*
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function print_client_input() {
?>
<input type="text" id="strava_client_id" name="strava_client_id" value="" />
<?php
}
/**
* Print the client secret input
*
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function print_secret_input() {
?>
<input type="text" id="strava_client_secret" name="strava_client_secret" value="" />
<?php
}
/**
* Print the nickname input
*
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function print_nickname_input() {
$nickname = $this->ids_empty( $this->ids ) ? __( 'Default', 'wp-strava' ) : '';
?>
<input type="text" name="strava_nickname[]" value="<?php echo esc_attr( $nickname ); ?>" />
<?php
}
/**
* Print the strava ID(s).
*
* Renamed from print_token_input().
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function print_id_input() {
foreach ( $this->get_all_ids() as $id => $nickname ) {
?>
<input type="text" name="strava_id[]" value="<?php echo esc_attr( $id ); ?>" />
<input type="text" name="strava_nickname[]" value="<?php echo esc_attr( $nickname ); ?>" />
<br/>
<?php
}
}
/**
* Sanitize the client ID.
*
* @param string $client_id
* @return string
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function sanitize_client_id( $client_id ) {
// Return early if not trying to add an additional athlete.
if ( ! $this->adding_athlete ) {
return $client_id;
}
if ( ! is_numeric( $client_id ) ) {
add_settings_error( 'strava_client_id', 'strava_client_id', __( 'Client ID must be a number.', 'wp-strava' ) );
}
return $client_id;
}
/**
* Sanitize the client secret.
*
* @param string $client_secret
* @return string
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function sanitize_client_secret( $client_secret ) {
// Return early if not trying to add an additional athlete.
if ( ! $this->adding_athlete ) {
return $client_secret;
}
if ( '' === trim( $client_secret ) ) {
add_settings_error( 'strava_client_secret', 'strava_client_secret', __( 'Client Secret is required.', 'wp-strava' ) );
}
return $client_secret;
}
/**
* Sanitize the nicknames - make sure we've got the same number of nicknames and IDs.
*
* @param array $nicknames Nicknames for the athletes saved.
* @return array
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function sanitize_nickname( $nicknames ) {
if ( ! $this->adding_athlete ) {
$input_args = array(
'strava_id' => array(
'filter' => FILTER_SANITIZE_NUMBER_INT,
'flags' => FILTER_REQUIRE_ARRAY,
),
);
$input = filter_input_array( INPUT_POST, $input_args );
// Chop $nicknames to same size as ids.
$nicknames = array_slice( $nicknames, 0, count( $input['strava_id'] ) );
// Remove indexes from $nicknames that have empty ids.
foreach ( $input['strava_id'] as $index => $id ) {
$id = trim( $id );
if ( empty( $id ) ) {
unset( $nicknames[ $index ] );
}
}
// Process $nicknames so indexes start with zero.
$nicknames = array_merge( $nicknames, array() );
}
foreach ( $nicknames as $index => $nickname ) {
if ( '' === trim( $nickname ) ) {
add_settings_error( 'strava_nickname', 'strava_nickname', __( 'Nickname is required.', 'wp-strava' ) );
return $nicknames;
}
}
return $nicknames;
}
/**
* Sanitize the ID.
*
* Renamed from sanitize_token().
*
* @param string $id Client ID.
* @return string
* @author Justin Foell <justin@foell.org>
* @since 2.0
*/
public function sanitize_id( $id ) {
return $id;
}
/**
* Print the GMaps key input.
*
* @author Justin Foell <justin@foell.org>
* @since 1.1
*/
public function print_gmaps_key_input() {
?>
<input type="text" id="strava_gmaps_key" name="strava_gmaps_key" value="<?php echo esc_attr( $this->gmaps_key ); ?>" />
<?php
}
/**
* Sanitize GMaps key input.
*
* @param string $key
* @return string
* @author Justin Foell <justin@foell.org>
* @since 1.1
*/
public function sanitize_gmaps_key( $key ) {
return $key;
}
/**
* Print System of Measure option.
*
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function print_som_input() {
?>
<select id="strava_som" name="strava_som">
<option value="metric" <?php selected( $this->som, 'metric' ); ?>><?php esc_html_e( 'Metric', 'wp-strava' ); ?></option>
<option value="english" <?php selected( $this->som, 'english' ); ?>><?php esc_html_e( 'English', 'wp-strava' ); ?></option>
</select>
<?php
}
/**
* Sanitize System of Measure input.
*
* @param string $som Input from System of Measure dropdown.
* @return string
* @author Justin Foell <justin@foell.org>
* @since 0.62
*/
public function sanitize_som( $som ) {
return $som;
}
/**
* Display the Hide Time Checkbox.
*
* @author Justin Foell <justin@foell.org>
* @since 1.7.1
*/
public function print_hide_time_input() {
?>
<input type="checkbox" id="strava_hide_time" name="strava_hide_time" <?php checked( $this->hide_time, 'on' ); ?>/>
<?php
}
/**
* Sanitize the Hide Time Checkbox.
*
* @param string $checked 'on' or null.
* @return string 'on' if checked.
* @author Justin Foell <justin@foell.org>
* @since 1.7.1
*/
public function sanitize_hide_time( $checked ) {
if ( 'on' === $checked ) {
return $checked;
}
return null;
}
/**
* Display the Hide Elevation Checkbox.
*
* @author Justin Foell <justin@foell.org>
* @since 1.7.2
*/
public function print_hide_elevation_input() {
?>
<input type="checkbox" id="strava_hide_elevation" name="strava_hide_elevation" <?php checked( $this->hide_elevation, 'on' ); ?>/>
<?php
}
/**
* Sanitize the Hide Elevation Checkbox.
*
* @param string $checked 'on' or null.
* @return string 'on' if checked.
* @author Justin Foell <justin@foell.org>
* @since 1.7.2
*/
public function sanitize_hide_elevation( $checked ) {
if ( 'on' === $checked ) {
return $checked;
}
return null;
}
/**
* Print checkbox option to clear cache.
*
* @author Justin Foell <justin@foell.org>
* @since 1.1
*/
public function print_clear_input() {
?>
<input type="checkbox" id="strava_cache_clear" name="strava_cache_clear" />
<?php
}
/**
* Clear Strava cache if checkbox is checked.
*
* @param string $checked Clear cache checkbox status.
* @return void
* @author Justin Foell <justin@foell.org>
* @since 1.1
*/
public function sanitize_cache_clear( $checked ) {
if ( 'on' === $checked ) {
global $wpdb;
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE '_transient_timeout_strava_api_data_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE '_transient_strava_api_data_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE '_transient_timeout_strava_latest_map_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE '_transient_strava_latest_map_%'" );
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE 'strava_latest_map%'" );
// Old options.
delete_option( 'strava_token' );
delete_option( 'strava_email' );
delete_option( 'strava_password' );
}
return null;
}
/**
* Gets all saved strava ids as an array.
*
* @return array
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function get_ids() {
$ids = get_option( 'strava_id' );
if ( ! is_array( $ids ) ) {
$ids = array( $ids );
}
foreach ( $ids as $index => $id ) {
if ( empty( $id ) ) {
unset( $ids[ $index ] );
$ids = array_values( $ids ); // Rebase array keys after unset @see https://stackoverflow.com/a/5943165/2146022
}
}
return $ids;
}
/**
* Returns first (default) ID saved.
*
* @return string|null
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*/
public function get_default_id() {
$ids = $this->get_ids();
return isset( $ids[0] ) ? $ids[0] : null;
}
/**
* Get all IDs and their nicknames in one array.
*
* @return void
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function get_all_ids() {
$ids = $this->get_ids();
$nicknames = $this->nickname;
$all = array();
$number = 1;
foreach ( $ids as $index => $id ) {
if ( ! empty( $nicknames[ $index ] ) ) {
$all[ $id ] = $nicknames[ $index ];
} else {
$all[ $id ] = $this->get_default_nickname( $number );
}
$number++;
}
return $all;
}
/**
* Returns default nickname 'Default' / 'Athlete n'.
*
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*
* @param integer $number Athlete number (default 1).
* @return string
*/
private function get_default_nickname( $number = 1 ) {
// Translators: Athlete number if no nickname present.
return ( 1 === $number ) ? __( 'Default', 'wp-strava' ) : sprintf( __( 'Athlete %s', 'wp-strava' ), $number );
}
/**
* Checks for valid IDs.
*
* @author Justin Foell <justin@foell.org>
* @since 1.2.0
*
* @param string|array Single ID or array of IDs.
* @return boolean True if empty.
*/
public function ids_empty( $ids ) {
if ( empty( $ids ) ) {
return true;
}
if ( is_array( $ids ) ) {
foreach ( $ids as $id ) {
if ( ! empty( $id ) ) {
return false;
}
}
}
return true;
}
/**
* Add an ID if it's not already there, and save to the DB.
*
* @param string $id
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function add_id( $id ) {
if ( false === array_search( $id, $this->ids, true ) ) {
$this->ids[] = $id;
update_option( 'strava_id', $this->ids );
}
}
/**
* Update options with new Client ID and Info.
*
* @param int $id Strava API Client ID
* @param string $secret Strava API Client Secret
* @param stdClass $info
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function save_info( $id, $secret, $info ) {
$infos = get_option( 'strava_info', array() );
$infos = array_filter( $infos, array( $this, 'filter_by_id' ), ARRAY_FILTER_USE_KEY ); // Remove old IDs.
$info->client_secret = $secret;
$infos[ $id ] = $info;
update_option( 'strava_info', $infos );
}
/**
* array_filter() callback to remove info for IDs we no longer have.
*
* @param int $key Strava Client ID
* @return boolean True if Client ID is in $this->ids, false otherwise.
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function filter_by_id( $key ) {
if ( in_array( $key, $this->ids ) ) {
return true;
}
return false;
}
/**
* Remove the client ID and Secret (they're saved in the strava_info option).
*
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function delete_id_secret() {
delete_option( 'strava_client_id' );
delete_option( 'strava_client_secret' );
}
/**
* Check to see if settings have been updated.
*
* @param array $value Data array from pre_set_transient_settings_errors filter.
* @return boolean
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function is_settings_updated( $value ) {
return ( isset( $value[0]['type'] ) && ( 'updated' === $value[0]['type'] || 'success' === $value[0]['type'] ) );
}
/**
* Whether or not we're on the options page.
*
* @return boolean
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function is_option_page() {
return filter_input( INPUT_POST, 'option_page', FILTER_SANITIZE_STRING ) === $this->option_page;
}
/**
* Whether or not we're on the WP-Strava settings page.
*
* @return boolean
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function is_settings_page() {
return filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING ) === $this->page_name;
}
/**
* Get the WP-Strava settings page name.
*
* @return string
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
public function get_page_name() {
return $this->page_name;
}
/**
* Whether or not we're adding a new athlete.
*
* @return boolean
* @author Justin Foell <justin@foell.org>
* @since 2.0.0
*/
private function is_adding_athlete() {
return filter_input( INPUT_POST, 'strava_client_id', FILTER_SANITIZE_NUMBER_INT ) && filter_input( INPUT_POST, 'strava_client_secret', FILTER_SANITIZE_STRING );
}
/**
* Getter for Strava settings in wp_options.
*
* @param string $name Option name without the 'strava_' prefix.
* @return mixed
* @since 0.62
*/
public function __get( $name ) {
if ( ! strpos( 'strava_', $name ) ) {
$name = "strava_{$name}";
}
// Else.
return get_option( $name );
}
/**
* Link to the settings on the plugin list page.
*
* @param array $links Array of plugin links.
* @return array Links with settings added.
* @author Justin Foell <justin@foell.org>
* @since 1.0
*/
public function settings_link( $links ) {
$settings_link = '<a href="' . admin_url( "options-general.php?page={$this->page_name}" ) . '">' . __( 'Settings', 'wp-strava' ) . '</a>';
$links[] = $settings_link;
return $links;
}
/**
* Plugin Upgrade Notice.
*
* @param array $data Plugin data with readme additions.
* @param array $response Response from wp.org.
* @author Justin Foell <justin@foell.org>
* @since 1.7.3
*/
public function plugin_update_message( $data, $response ) {
if ( isset( $data['upgrade_notice'] ) ) {
echo wp_kses_post( $data['upgrade_notice'] );
}
}
/**
* Plugin Upgrade Notice (multisite).
*
* @param string $file Relative path to plugin, i.e. wp-strava/wp-strava.php.
* @param array $plugin Plugin data with readme additions.
* @author Justin Foell <justin@foell.org>
* @since 1.7.3
*/
public function ms_plugin_update_message( $file, $plugin ) {
if ( is_multisite() && ! is_network_admin() && version_compare( $plugin['Version'], $plugin['new_version'], '<' ) ) {
$wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
echo wp_kses_post(
sprintf(
'<tr class="plugin-update-tr"><td colspan="%s" class="plugin-update update-message notice inline notice-warning notice-alt"><div class="update-message"><h4 style="margin: 0; font-size: 14px;">%s</h4>%s</div></td></tr>',
$wp_list_table->get_column_count(),
$plugin['Name'],
$plugin['upgrade_notice']
)
);
}
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
class WPStrava_StaticMap {
/**
* Get an image tag to a static google map. Will render with
* detailed polyline if not greater than 1865 chars, otherwise
* rendering will use summary polyline.
*
* @static
* @access public
* @param object $activity Activity object to get image tag for.
* @param int $height Height of map in pixels.
* @param int $width Width of map in pixels.
* @param bool $markers Display start and finish markers.
* @return string HTML img tag with static map image.
*/
public static function get_image_tag( $activity, $height = 320, $width = 480, $markers = false, $link = true ) {
$key = WPStrava::get_instance()->settings->gmaps_key;
// Short circuit if missing key or activity object doesn't have the data we need.
if ( empty( $key ) || empty( $activity->map ) ) {
return '';
}
$url = "https://maps.googleapis.com/maps/api/staticmap?maptype=terrain&size={$width}x{$height}&scale=2&sensor=false&key={$key}&path=color:0xFF0000BF|weight:2|enc:";
$url_len = strlen( $url );
$max_chars = 1865;
$polyline = '';
if ( ! empty( $activity->map->polyline ) && ( $url_len + strlen( $activity->map->polyline ) < $max_chars ) ) {
$polyline = $activity->map->polyline;
} elseif ( ! empty( $activity->map->summary_polyline ) ) {
$polyline = $activity->map->summary_polyline;
}
$url .= $polyline;
if ( $markers ) {
$points = self::decode_start_finish( $polyline );
$markers = '&markers=color:green|' . $points['start'][0] . ',' . $points['start'][1] .
'&markers=color:red|' . $points['finish'][0] . ',' . $points['finish'][1];
$url .= $markers;
}
return "<img class='wp-strava-img' src='{$url}' />";
}
/**
* From an encoded polyline, get the start and finish points for
* the purposes of displaying start and finish markers.
*
* @static
* @see https://developers.google.com/maps/documentation/utilities/polylinealgorithm
* @access private
* @param string $enc Encoded polyline.
* @return array {
* Indexes of start & finish containing lat/lon for each.
* @type array $start {
* @type float $0 Latitude
* @type float $1 Longitude
* }
* @type array $finish {
* @type float $0 Latitude
* @type float $1 Longitude
* }
* }
*/
private static function decode_start_finish( $enc ) {
require_once WPSTRAVA_PLUGIN_DIR . 'includes/Polyline.php';
$points = Polyline::decode( $enc );
$points = Polyline::pair( $points );
$start = $points[0];
$finish = $points[ count( $points ) - 1 ];
return array(
'start' => $start,
'finish' => $finish,
);
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
/**
* Autoloads files with classes when needed.
*
* @since 1.6.0
* @param string $class_name Name of the class being requested.
* @return void
*/
function wpstrava_autoload_classes( $class_name ) {
$parts = explode( '_', $class_name );
// If our class doesn't have our namespace, don't load it.
if ( empty( $parts[0] ) || 'WPStrava' !== $parts[0] ) {
return;
}
// @TODO Add directory searching if they get created.
$file = WPSTRAVA_PLUGIN_DIR . '/src/' . implode( '/', $parts ) . '.php';
if ( file_exists( $file ) ) {
include_once $file;
}
}
spl_autoload_register( 'wpstrava_autoload_classes' );
+2
View File
@@ -0,0 +1,2 @@
<?php