PluginProbe
CoolClock / trunk
CoolClock vtrunk
4.3.8 trunk 0.1 2.0 2.9.8 3.0 3.1 3.2 3.3 4.0 4.1 4.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7
coolclock / includes / class-coolclock.php

class-coolclock.php in CoolClock trunk, at includes/class-coolclock.php

448 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * CoolClock Class
5 */
6
7 class CoolClock {
8
9 static ?string $plugin_version;
10
11 static $script_version = '3.2.2';
12
13 private static ?string $plugin_url;
14
15 private static ?string $plugin_basename;
16
17 private static $min = '.min';
18
19 static $add_script = false;
20
21 static $defaults = array(
22 'skin' => 'swissrail',
23 'radius' => 100,
24 'noseconds' => false, // true to hide second hand
25 'gmtoffset' => '', // GMT offset
26 'showdigital' => '', // show digital time or date
27 'scale' => '', // Define alternative clock type: 'logClock' logarithmic or 'logClockRev' reversed
28 'font' => '', // Define font size and family for digital time, default '15px monospace'
29 'fontcolor' => '' // Define font color for digital time, default '#333'
30 );
31
32 static $showdigital_options = array(
33 '' => '',
34 'digital12' => 'showDigital'
35 );
36
37 static $advanced_defaults = array(
38 'subtext' => '',
39 'align' => 'center'
40 );
41
42 static $more_skins_config = array();
43
44 static $advanced_skins_config = array();
45
46 static $skins_config = array();
47
48 /**
49 * DEPRICATED ARGUMENTS
50 * keep these for backward compatibility with old advanced extension
51 */
52 static $default_skins = array('swissRail');
53 static $more_skins = array();
54 static $advanced_skins = array();
55 static $advanced = false;
56 /* end unused arguments */
57
58 static $clock_types = array(
59 'linear' => '',
60 'logclock' => 'logClock',
61 'logclockrev' => 'logClockRev'
62 );
63
64 static $allowed_tags = array(
65 'a' => array(
66 'href' => array(),
67 'title' => array(),
68 'target' => array()
69 ),
70 'br' => array(),
71 'em' => array(),
72 'strong' => array(),
73 );
74
75 /**
76 * Constructor
77 *
78 * @since 4.3
79 *
80 * @param string $plugin_file Plugin file path
81 * @param string $plugin_version Plugin version
82 */
83 public function __construct( string $plugin_file, string $plugin_version ) {
84 // VARS
85 self::$plugin_url = plugins_url( '/', $plugin_file );
86 self::$plugin_basename = plugin_basename( $plugin_file );
87 self::$plugin_version = $plugin_version;
88
89 if ( defined('WP_DEBUG') && WP_DEBUG ) {
90 self::$min = '';
91 }
92
93 // widgets
94 add_action( 'widgets_init', array( __CLASS__, 'register_widget' ) );
95
96 // enqueue scripts but only if shortcode or widget has been used
97 // so it has to be done as late as the wp_footer action
98 add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts' ), 1 );
99
100 add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_meta_links' ), 10, 2);
101
102 // backward compat
103 add_filter( 'coolclock_shortcode', array( __CLASS__, 'filter_shortcode'), 10, 3 );
104 add_filter( 'coolclock_widget', array( __CLASS__, 'filter_widget'), 10, 3 );
105
106 /**************
107 * SHORTCODE
108 **************/
109
110 add_shortcode( 'coolclock', array( 'CoolClock_Shortcode', 'handle_shortcode' ) );
111
112 // prevent texturizing shortcode content
113 add_filter( 'no_texturize_shortcodes', array( 'CoolClock_Shortcode', 'no_wptexturize') );
114 }
115
116 /**
117 * Build canvas output
118 *
119 * @since 2.0
120 *
121 * @param array $atts Array of sanitized attributes
122 *
123 * @return string Canvas tag
124 */
125 public static function canvas( $atts ) {
126 /**
127 * ARRAY VALUES
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'.
129 * If the Advanced extension is activated, there is also 'minimal' available.
130 * radius @param int Define the clock radius.
131 * noseconds @param bool True to hide the second hand.
132 * gmtoffset @param float Timezone offset relative the Greenwhich Mean Time
133 * showdigital @param string|bool Set to 'digital12' to show the time in 12h digital format (with am/pm).
134 * font @param string Set to a font size, family and style for the digital time
135 * fontcolor @param string Set to a color value to change the digital time color
136 * scale @param string Optional alternative clock scale 'logClock' or 'logClockRev'
137 * subtext @param string Optional text, centered below the clock
138 * align @param string Sets floating of the clock: 'left', 'right' or 'center'
139 */
140
141 // get defaults for missing attributes
142 $defaults = array_merge( self::$defaults, self::$advanced_defaults );
143
144 $atts = apply_filters( 'coolclock_atts', $atts, $defaults );
145
146 // CoolClock fields array
147 $fields = array();
148 $fields[] = 'CoolClock';
149 // skin id
150 $fields[] = !empty( $atts['skin'] ) ? $atts['skin'] : $defaults['skin'];
151 // radius
152 $fields[] = !empty( $atts['radius'] ) && is_numeric($atts['radius']) ? (int) $atts['radius'] : $defaults['radius'];
153 // noseconds
154 $noseconds = isset( $atts['noseconds'] ) ? (bool) $atts['noseconds'] : $defaults['noseconds'];
155 $fields[] = $noseconds ? 'noSeconds' : '';
156 // gmt offset
157 $fields[] = isset( $atts['gmtoffset'] ) && is_numeric( $atts['gmtoffset'] ) ? (float) $atts['gmtoffset'] : $defaults['gmtoffset'];
158 // show digital
159 $showdigital = isset($atts['showdigital']) ? $atts['showdigital'] : $defaults['showdigital'];
160 if ( true === $showdigital )
161 $showdigital = 'digital12';
162 $fields[] = isset( self::$showdigital_options[$showdigital] ) ? self::$showdigital_options[$showdigital] : '';
163 // clock type
164 $scale = isset( $atts['scale'] ) ? strtolower( $atts['scale'] ) : $defaults['scale'];
165 $fields[] = isset( self::$clock_types[$scale] ) ? self::$clock_types[$scale] : '';
166 // set font color
167 $fields[] = isset( $atts['fontcolor'] ) ? $atts['fontcolor'] : $defaults['fontcolor'];
168
169 $fields = apply_filters( 'coolclock_fields_array', $fields, $atts, $defaults );
170
171 // build output
172 $output = '';
173
174 $styles = apply_filters( 'coolclock_canvas_styles', array(), $atts, $defaults );
175
176 // canvas parameters
177 $output .= '<canvas class="' . implode( ':', array_map('esc_attr', $fields) ) . '"' . CoolClock::inline_style( $styles ) . '></canvas>';
178
179 // sub text
180 $subtext = ( isset( $atts['subtext'] ) ) ? $atts['subtext'] : $defaults['subtext'];
181 if ( !empty( $subtext ) )
182 $output .= '<div class="coolclock-subtext">' . $subtext . '</div>';
183
184 return $output;
185 }
186
187 /**
188 * Create inline style attribute from array
189 *
190 * @since 4.3
191 *
192 * @param array $styles array with style parameters and their values
193 *
194 * @return string inline style attribute style="..." complete with leading space
195 */
196 public static function inline_style( $styles ) {
197 $style_arr = array();
198
199 foreach ( $styles as $key => $value ) {
200 $style_arr[] = $key . ':' . $value;
201 }
202
203 $style = ! empty($style_arr) ? ' style="' . implode( ';', $style_arr ) . '"' : '';
204
205 return $style;
206 }
207
208 /**
209 * Parse skin name and user skin parameters
210 *
211 * @since 3.2.0
212 *
213 * @param string $skin_name skin name
214 * @param string $skin_parms skin parameters, preferably in JSON format
215 *
216 * @return string either found matching skin name or 'invalid_or_missing_skin' on failure
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 );
221
222 // check for empty or default skin first
223 if ( empty($skin) || $skin === self::$defaults['skin'] ) {
224 // return the matching skin name
225 return self::$defaults['skin'];
226 }
227
228 // short-circuit if skin name is already in the config array
229 if ( array_key_exists( $skin, self::$skins_config ) ) {
230 // return the matching skin name
231 return $skin;
232 }
233
234 // search in the more_skins and advanced_skins arrays
235 $all_skins = self::get_all_skins();
236 $skin_array = array();
237
238 if ( array_key_exists( $skin, $all_skins ) ) {
239 // fetch parameters from skins array
240 $skin_array = is_array( $all_skins[$skin] ) ? $all_skins[$skin] : self::skin_array( $all_skins[$skin] );
241 } else {
242 // try to build a skin config from passed parameters (user input, sanitize!)
243 $skin_array = self::skin_array( $skin_parms );
244
245 if ( empty( $skin_array ) ) {
246 // set faulty skin name
247 return 'no_skin_found';
248 }
249 }
250
251 // add found skin parameters to the config array
252 self::$skins_config[$skin] = $skin_array;
253
254 // return the matching skin name
255 return $skin;
256 }
257
258 /**
259 * Get all available skins
260 *
261 * @since 3.2.0
262 *
263 * @return array array of all skins
264 */
265 public static function get_all_skins() {
266
267 if ( empty( self::$more_skins_config ) ) {
268 self::$more_skins_config = include COOLCLOCK_DIR . 'includes/moreskins.php';
269 }
270
271 return array_merge( self::$more_skins_config, self::$advanced_skins_config );
272 }
273
274 /**
275 * Enqueue scripts
276 *
277 * @since 3.2.0
278 */
279 public static function enqueue_scripts() {
280 // bail if we don't need script
281 if ( ! self::$add_script )
282 return;
283
284 $script = '';
285
286 if ( !empty( self::$skins_config ) ) {
287
288 $skins = wp_json_encode( self::$skins_config );
289 if ( false === $skins ) {
290 $skins = '{} /* skin parse error, please fix your custom skin */';
291 }
292
293 $script .= 'CoolClock.config.skins = ' . $skins . ';';
294 }
295
296 $script .= PHP_EOL . 'if(document.readyState!="loading"&&document.addEventListener){document.addEventListener("DOMContentLoaded",function(){CoolClock.findAndCreateClocks();})}else{CoolClock.findAndCreateClocks();};';
297
298 wp_enqueue_script( 'coolclock', self::$plugin_url . 'js/coolclock' . self::$min . '.js', false, self::$script_version, true );
299
300 wp_add_inline_script( 'coolclock', $script );
301
302 // called late so should end up in the footer
303 wp_enqueue_style( 'coolclock', self::$plugin_url . 'css/coolclock' . self::$min . '.css', array(), self::$script_version );
304 }
305
306 public static function register_widget() {
307 register_widget("CoolClock_Widget");
308 }
309
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 ) {
317 $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/coolclock/">' . __('Support','coolclock') . '</a>';
318 $rate_link = '<a target="_blank" href="https://wordpress.org/support/plugin/coolclock/reviews/">' . __('Rate this plugin','coolclock') . '</a>';
319
320 if ( $file == self::$plugin_basename ) {
321 $links[] = $support_link;
322 $links[] = $rate_link;
323 }
324
325 return $links;
326 }
327
328 /**
329 * Turn custom skin user input into an array
330 *
331 * @since 2.0
332 *
333 * @param string $skin
334 *
335 * @return array array of skin parameters
336 */
337 public static function skin_array( $skin ) {
338 // remove everything following a ; to thwart any script injection
339 $parts = explode( ';', $skin, 2 );
340 $sanitized = $parts[0];
341 // strip all kind of whitespace
342 $sanitized = preg_replace("/\s+/", '', $sanitized);;
343 $sanitized = ltrim( $sanitized, '{' );
344 $sanitized = rtrim( $sanitized, '}' ) . '}';
345 // by now, the string should start with a double quote
346 if ( 0 !== strpos( $sanitized, '"' ) ) {
347 // regex for (re)wrapping all keys in double quotes
348 $sanitized = preg_replace( "/(['\"])?([a-zA-Z0-9_]+)(['\"])?:([^\/])/", '"$2":$4', $sanitized );
349 }
350
351 // fist attempt to decode json string
352 $skin_array = json_decode( '{' . $sanitized . '}', true );
353
354 // not valid json? then do it the hard way
355 if ( null === $skin_array ) :
356
357 // remove all spaces and tabs
358 $sanitized = str_replace( array( ' ', "\t", '"', "'" ), '', $sanitized );
359
360 // break it all up in little parts
361 // this converts valid skin javascript object into json, but messes up all else :/
362 $elements = explode( '},', $sanitized );
363
364 // start fresh
365 $skin_array = array();
366
367 // and build array
368 foreach( $elements as $element ) {
369 $pair = explode( ':{', $element );
370 // validate key
371 $part = str_replace( '{', '', $pair[0] );
372 if ( ! in_array( $part, array('outerBorder','smallIndicator','largeIndicator','hourHand','minuteHand','secondHand','secondDecoration') ) )
373 continue;
374 // constuct value
375 $value = array();
376 $parameters = explode( ',', $pair[1] );
377 for( $i=0; $i < count( $parameters ); $i++ ) {
378 $pairs = explode( ':', $parameters[$i] );
379 $parm_val = str_replace( '}', '', $pairs[1] );
380 $value[$pairs[0]] = is_numeric($parm_val) ? (float) $parm_val : (string) $parm_val;
381 }
382 $skin_array[$part] = $value;
383 }
384
385 endif;
386
387 return $skin_array;
388 }
389
390 /**
391 * Sanitize color value user input
392 *
393 * @since 4.2
394 *
395 * @param string $color
396 *
397 * @return string hex color value or text for named color
398 */
399 public static function colorval( $color ) {
400 $color = wp_strip_all_tags( $color );
401 $color = trim( $color );
402 $color = str_replace( array( '"', '\'', ':'), '', $color );
403 $color_arr = explode( ' ', $color, 2 );
404 $color = esc_attr( $color_arr[0] );
405
406 if ( substr($color, 0, 1) == '#' ) {
407 if ( ctype_xdigit( substr( $color, 1 ) ) )
408 return $color;
409
410 return substr( $color, 1 );
411 }
412
413 return ctype_xdigit( $color ) ? '#'.$color : $color;
414 }
415
416 /**
417 * Filter shortcode output
418 *
419 * @since 4.3
420 *
421 * @param string $output output
422 * @param array $atts array of shortcode attributes
423 * @param string $content shortcode content
424 *
425 * @return string output
426 */
427 public static function filter_shortcode( $output, $atts, $content ) {
428 // add backward compat filter
429 return apply_filters( 'coolclock_shortcode_advanced', $output, $atts, $content );
430 }
431
432 /**
433 * Filter widget output
434 *
435 * @since 4.3
436 *
437 * @param string $output output
438 * @param array $args array of widget parameters
439 * @param array $instance widget instance array parameters
440 *
441 * @return string output
442 */
443 public static function filter_widget( $output, $args, $instance ) {
444 // add backward compat filter
445 return apply_filters( 'coolclock_widget_advanced', $output, $args, $instance );
446 }
447 }
448