| @@ -5,22 +5,20 @@ | ||
| 5 | 5 | */ |
| 6 | 6 | |
| 7 | 7 | class CoolClock { |
| 8 | 8 | |
| 9 | - static $plugin_version; | |
| 9 | + static ?string $plugin_version; | |
| 10 | 10 | |
| 11 | 11 | static $script_version = '3.2.2'; |
| 12 | 12 | |
| 13 | - private static $plugin_url; | |
| 13 | + private static ?string $plugin_url; | |
| 14 | 14 | |
| 15 | - private static $plugin_basename; | |
| 15 | + private static ?string $plugin_basename; | |
| 16 | 16 | |
| 17 | 17 | private static $min = '.min'; |
| 18 | 18 | |
| 19 | 19 | static $add_script = false; |
| 20 | 20 | |
| 21 | - private static $done_excanvas = false; | |
| 22 | - | |
| 23 | 21 | static $defaults = array( |
| 24 | 22 | 'skin' => 'swissrail', |
| 25 | 23 | 'radius' => 100, |
| 26 | 24 | 'noseconds' => false, // true to hide second hand |
| @@ -74,13 +72,16 @@ | ||
| 74 | 72 | 'strong' => array(), |
| 75 | 73 | ); |
| 76 | 74 | |
| 77 | 75 | /** |
| 78 | - * INIT | |
| 76 | + * Constructor | |
| 77 | + * | |
| 78 | + * @since 4.3 | |
| 79 | + * | |
| 80 | + * @param string $plugin_file Plugin file path | |
| 81 | + * @param string $plugin_version Plugin version | |
| 79 | 82 | */ |
| 80 | - | |
| 81 | - public function __construct( $plugin_file, $plugin_version ) | |
| 82 | - { | |
| 83 | + public function __construct( string $plugin_file, string $plugin_version ) { | |
| 83 | 84 | // VARS |
| 84 | 85 | self::$plugin_url = plugins_url( '/', $plugin_file ); |
| 85 | 86 | self::$plugin_basename = plugin_basename( $plugin_file ); |
| 86 | 87 | self::$plugin_version = $plugin_version; |
| @@ -88,11 +89,8 @@ | ||
| 88 | 89 | if ( defined('WP_DEBUG') && WP_DEBUG ) { |
| 89 | 90 | self::$min = ''; |
| 90 | 91 | } |
| 91 | 92 | |
| 92 | - // text domain | |
| 93 | - add_action( 'plugins_loaded', array( __CLASS__, 'textdomain' ) ); | |
| 94 | - | |
| 95 | 93 | // widgets |
| 96 | 94 | add_action( 'widgets_init', array( __CLASS__, 'register_widget' ) ); |
| 97 | 95 | |
| 98 | 96 | // enqueue scripts but only if shortcode or widget has been used |
| @@ -115,12 +113,8 @@ | ||
| 115 | 113 | add_filter( 'no_texturize_shortcodes', array( 'CoolClock_Shortcode', 'no_wptexturize') ); |
| 116 | 114 | } |
| 117 | 115 | |
| 118 | 116 | /** |
| 119 | - * METHODS | |
| 120 | - */ | |
| 121 | - | |
| 122 | - /** | |
| 123 | 117 | * Build canvas output |
| 124 | 118 | * |
| 125 | 119 | * @since 2.0 |
| 126 | 120 | * |
| @@ -127,11 +121,9 @@ | ||
| 127 | 121 | * @param array $atts Array of sanitized attributes |
| 128 | 122 | * |
| 129 | 123 | * @return string Canvas tag |
| 130 | 124 | */ |
| 131 | - | |
| 132 | - public static function canvas( $atts ) | |
| 133 | - { | |
| 125 | + public static function canvas( $atts ) { | |
| 134 | 126 | /** |
| 135 | 127 | * ARRAY VALUES |
| 136 | 128 | * skin @param string Skin ID. Must be one of these: 'swissRail' (default skin), 'chunkySwiss', 'chunkySwissOnBlack', 'fancy', 'machine', 'simonbaird_com', 'classic', 'modern', 'simple', 'securephp', 'Tes2', 'Lev', 'Sand', 'Sun', 'Tor', 'Cold', 'Babosa', 'Tumb', 'Stone', 'Disc', 'watermelon' or 'mister'. |
| 137 | 129 | * If the Advanced extension is activated, there is also 'minimal' available. |
| @@ -178,19 +170,12 @@ | ||
| 178 | 170 | |
| 179 | 171 | // build output |
| 180 | 172 | $output = ''; |
| 181 | 173 | |
| 182 | - if ( ! self::$done_excanvas ){ | |
| 183 | - $output .= '<!--[if lte IE 8]>'; | |
| 184 | - $output .= '<script type="text/javascript" src="'. self::$plugin_url . 'js/excanvas' . self::$min . '.js"></script>'; | |
| 185 | - $output .= '<![endif]-->' . PHP_EOL; | |
| 186 | - self::$done_excanvas = true; | |
| 187 | - } | |
| 188 | - | |
| 189 | 174 | $styles = apply_filters( 'coolclock_canvas_styles', array(), $atts, $defaults ); |
| 190 | 175 | |
| 191 | 176 | // canvas parameters |
| 192 | - $output .= '<canvas class="' . implode(':',$fields) . '"' . CoolClock::inline_style( $styles ) . '></canvas>'; | |
| 177 | + $output .= '<canvas class="' . implode( ':', array_map('esc_attr', $fields) ) . '"' . CoolClock::inline_style( $styles ) . '></canvas>'; | |
| 193 | 178 | |
| 194 | 179 | // sub text |
| 195 | 180 | $subtext = ( isset( $atts['subtext'] ) ) ? $atts['subtext'] : $defaults['subtext']; |
| 196 | 181 | if ( !empty( $subtext ) ) |
| @@ -207,11 +192,9 @@ | ||
| 207 | 192 | * @param array $styles array with style parameters and their values |
| 208 | 193 | * |
| 209 | 194 | * @return string inline style attribute style="..." complete with leading space |
| 210 | 195 | */ |
| 211 | - | |
| 212 | - public static function inline_style( $styles ) | |
| 213 | - { | |
| 196 | + public static function inline_style( $styles ) { | |
| 214 | 197 | $style_arr = array(); |
| 215 | 198 | |
| 216 | 199 | foreach ( $styles as $key => $value ) { |
| 217 | 200 | $style_arr[] = $key . ':' . $value; |
| @@ -231,38 +214,39 @@ | ||
| 231 | 214 | * @param string $skin_parms skin parameters, preferably in JSON format |
| 232 | 215 | * |
| 233 | 216 | * @return string either found matching skin name or 'invalid_or_missing_skin' on failure |
| 234 | 217 | */ |
| 218 | + public static function parse_skin( $skin_name, $skin_parms = '' ) { | |
| 219 | + $skin = strtolower( $skin_name ); // user input, sanitize! | |
| 220 | + $skin = preg_replace( '/[^a-z0-9_-]/', '', $skin ); | |
| 235 | 221 | |
| 236 | - public static function parse_skin( $skin_name, $skin_parms = '' ) | |
| 237 | - { | |
| 238 | - $skin = strtolower($skin_name); | |
| 239 | - $skin_array = array(); | |
| 240 | - | |
| 241 | 222 | // check for empty or default skin first |
| 242 | - if ( empty($skin) || $skin == self::$defaults['skin'] ) | |
| 223 | + if ( empty($skin) || $skin === self::$defaults['skin'] ) { | |
| 243 | 224 | // return the matching skin name |
| 244 | 225 | return self::$defaults['skin']; |
| 226 | + } | |
| 245 | 227 | |
| 246 | 228 | // short-circuit if skin name is already in the config array |
| 247 | - if ( array_key_exists( $skin, self::$skins_config ) ) | |
| 229 | + if ( array_key_exists( $skin, self::$skins_config ) ) { | |
| 248 | 230 | // return the matching skin name |
| 249 | 231 | return $skin; |
| 232 | + } | |
| 250 | 233 | |
| 251 | 234 | // search in the more_skins and advanced_skins arrays |
| 252 | - $all_skins = self::get_all_skins(); | |
| 235 | + $all_skins = self::get_all_skins(); | |
| 236 | + $skin_array = array(); | |
| 237 | + | |
| 253 | 238 | if ( array_key_exists( $skin, $all_skins ) ) { |
| 254 | 239 | // fetch parameters from skins array |
| 255 | 240 | $skin_array = is_array( $all_skins[$skin] ) ? $all_skins[$skin] : self::skin_array( $all_skins[$skin] ); |
| 256 | - } | |
| 257 | - // try to build a skin config from passed parameters | |
| 258 | - else { | |
| 259 | - // fetch parameters from custom skin user input | |
| 241 | + } else { | |
| 242 | + // try to build a skin config from passed parameters (user input, sanitize!) | |
| 260 | 243 | $skin_array = self::skin_array( $skin_parms ); |
| 261 | 244 | |
| 262 | - if ( empty( $skin_array ) ) | |
| 245 | + if ( empty( $skin_array ) ) { | |
| 263 | 246 | // set faulty skin name |
| 264 | 247 | return 'no_skin_found'; |
| 248 | + } | |
| 265 | 249 | } |
| 266 | 250 | |
| 267 | 251 | // add found skin parameters to the config array |
| 268 | 252 | self::$skins_config[$skin] = $skin_array; |
| @@ -270,21 +254,29 @@ | ||
| 270 | 254 | // return the matching skin name |
| 271 | 255 | return $skin; |
| 272 | 256 | } |
| 273 | 257 | |
| 258 | + /** | |
| 259 | + * Get all available skins | |
| 260 | + * | |
| 261 | + * @since 3.2.0 | |
| 262 | + * | |
| 263 | + * @return array array of all skins | |
| 264 | + */ | |
| 274 | 265 | public static function get_all_skins() { |
| 275 | 266 | |
| 276 | 267 | if ( empty( self::$more_skins_config ) ) { |
| 277 | - | |
| 278 | - include COOLCLOCK_DIR . 'includes/moreskins.php'; | |
| 279 | - | |
| 280 | - self::$more_skins_config = $more_skins; | |
| 281 | - | |
| 268 | + self::$more_skins_config = include COOLCLOCK_DIR . 'includes/moreskins.php'; | |
| 282 | 269 | } |
| 283 | 270 | |
| 284 | 271 | return array_merge( self::$more_skins_config, self::$advanced_skins_config ); |
| 285 | 272 | } |
| 286 | 273 | |
| 274 | + /** | |
| 275 | + * Enqueue scripts | |
| 276 | + * | |
| 277 | + * @since 3.2.0 | |
| 278 | + */ | |
| 287 | 279 | public static function enqueue_scripts() { |
| 288 | 280 | // bail if we don't need script |
| 289 | 281 | if ( ! self::$add_script ) |
| 290 | 282 | return; |
| @@ -292,15 +284,14 @@ | ||
| 292 | 284 | $script = ''; |
| 293 | 285 | |
| 294 | 286 | if ( !empty( self::$skins_config ) ) { |
| 295 | 287 | |
| 296 | - /** | |
| 297 | - * Load IE 6/7 specific JSON polyfill | |
| 298 | - */ | |
| 299 | - wp_enqueue_script( 'json2' ); | |
| 288 | + $skins = wp_json_encode( self::$skins_config ); | |
| 289 | + if ( false === $skins ) { | |
| 290 | + $skins = '{} /* skin parse error, please fix your custom skin */'; | |
| 291 | + } | |
| 300 | 292 | |
| 301 | - $script .= 'CoolClock.config.skins = JSON.parse(\'' . json_encode( self::$skins_config ) . '\');'; | |
| 302 | - | |
| 293 | + $script .= 'CoolClock.config.skins = ' . $skins . ';'; | |
| 303 | 294 | } |
| 304 | 295 | |
| 305 | 296 | $script .= PHP_EOL . 'if(document.readyState!="loading"&&document.addEventListener){document.addEventListener("DOMContentLoaded",function(){CoolClock.findAndCreateClocks();})}else{CoolClock.findAndCreateClocks();};'; |
| 306 | 297 | |
| @@ -308,23 +299,24 @@ | ||
| 308 | 299 | |
| 309 | 300 | wp_add_inline_script( 'coolclock', $script ); |
| 310 | 301 | |
| 311 | 302 | // called late so should end up in the footer |
| 312 | - wp_enqueue_style( 'coolclock', self::$plugin_url . 'css/coolclock' . self::$min . '.css' ); | |
| 303 | + wp_enqueue_style( 'coolclock', self::$plugin_url . 'css/coolclock' . self::$min . '.css', array(), self::$script_version ); | |
| 313 | 304 | } |
| 314 | 305 | |
| 315 | - public static function textdomain() { | |
| 316 | - load_plugin_textdomain( 'coolclock', false, dirname(self::$plugin_basename).'/languages' ); | |
| 317 | - } | |
| 318 | - | |
| 319 | 306 | public static function register_widget() { |
| 320 | 307 | register_widget("CoolClock_Widget"); |
| 321 | 308 | } |
| 322 | 309 | |
| 323 | - // add links to plugin's description | |
| 324 | - public static function plugin_meta_links($links, $file) { | |
| 310 | + /** | |
| 311 | + * Add links to plugin's description in the plugins list | |
| 312 | + * | |
| 313 | + * @param array $links Array of links to display | |
| 314 | + * @param string $file Plugin file path | |
| 315 | + */ | |
| 316 | + public static function plugin_meta_links( array $links, string $file ) { | |
| 325 | 317 | $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/coolclock/">' . __('Support','coolclock') . '</a>'; |
| 326 | - $rate_link = '<a target="_blank" href="https://wordpress.org/support/plugin/coolclock/reviews/?filter=5#new-post">' . __('Rate ★★★★★','coolclock') . '</a>'; | |
| 318 | + $rate_link = '<a target="_blank" href="https://wordpress.org/support/plugin/coolclock/reviews/">' . __('Rate this plugin','coolclock') . '</a>'; | |
| 327 | 319 | |
| 328 | 320 | if ( $file == self::$plugin_basename ) { |
| 329 | 321 | $links[] = $support_link; |
| 330 | 322 | $links[] = $rate_link; |
| @@ -341,11 +333,9 @@ | ||
| 341 | 333 | * @param string $skin |
| 342 | 334 | * |
| 343 | 335 | * @return array array of skin parameters |
| 344 | 336 | */ |
| 345 | - | |
| 346 | - public static function skin_array( $skin ) | |
| 347 | - { | |
| 337 | + public static function skin_array( $skin ) { | |
| 348 | 338 | // remove everything following a ; to thwart any script injection |
| 349 | 339 | $parts = explode( ';', $skin, 2 ); |
| 350 | 340 | $sanitized = $parts[0]; |
| 351 | 341 | // strip all kind of whitespace |
| @@ -405,13 +395,14 @@ | ||
| 405 | 395 | * @param string $color |
| 406 | 396 | * |
| 407 | 397 | * @return string hex color value or text for named color |
| 408 | 398 | */ |
| 409 | - | |
| 410 | - public static function colorval( $color ) | |
| 411 | - { | |
| 399 | + public static function colorval( $color ) { | |
| 412 | 400 | $color = wp_strip_all_tags( $color ); |
| 413 | 401 | $color = trim( $color ); |
| 402 | + $color = str_replace( array( '"', '\'', ':'), '', $color ); | |
| 403 | + $color_arr = explode( ' ', $color, 2 ); | |
| 404 | + $color = esc_attr( $color_arr[0] ); | |
| 414 | 405 | |
| 415 | 406 | if ( substr($color, 0, 1) == '#' ) { |
| 416 | 407 | if ( ctype_xdigit( substr( $color, 1 ) ) ) |
| 417 | 408 | return $color; |
| @@ -432,11 +423,9 @@ | ||
| 432 | 423 | * @param string $content shortcode content |
| 433 | 424 | * |
| 434 | 425 | * @return string output |
| 435 | 426 | */ |
| 436 | - | |
| 437 | - public static function filter_shortcode( $output, $atts, $content ) | |
| 438 | - { | |
| 427 | + public static function filter_shortcode( $output, $atts, $content ) { | |
| 439 | 428 | // add backward compat filter |
| 440 | 429 | return apply_filters( 'coolclock_shortcode_advanced', $output, $atts, $content ); |
| 441 | 430 | } |
| 442 | 431 | |
| @@ -450,12 +439,9 @@ | ||
| 450 | 439 | * @param array $instance widget instance array parameters |
| 451 | 440 | * |
| 452 | 441 | * @return string output |
| 453 | 442 | */ |
| 454 | - | |
| 455 | - public static function filter_widget( $output, $args, $instance ) | |
| 456 | - { | |
| 443 | + public static function filter_widget( $output, $args, $instance ) { | |
| 457 | 444 | // add backward compat filter |
| 458 | 445 | return apply_filters( 'coolclock_widget_advanced', $output, $args, $instance ); |
| 459 | 446 | } |
| 460 | - | |
| 461 | 447 | } |