'swissrail', 'radius' => 100, 'noseconds' => false, // true to hide second hand 'gmtoffset' => '', // GMT offset 'showdigital' => '', // show digital time or date 'scale' => '', // Define alternative clock type: 'logClock' logarithmic or 'logClockRev' reversed 'font' => '', // Define font size and family for digital time, default '15px monospace' 'fontcolor' => '' // Define font color for digital time, default '#333' ); static $showdigital_options = array( '' => '', 'digital12' => 'showDigital' ); static $advanced_defaults = array( 'subtext' => '', 'align' => 'center' ); static $more_skins_config = array(); static $advanced_skins_config = array(); static $skins_config = array(); /** * DEPRICATED ARGUMENTS * keep these for backward compatibility with old advanced extension */ static $default_skins = array('swissRail'); static $more_skins = array(); static $advanced_skins = array(); static $advanced = false; /* end unused arguments */ static $clock_types = array( 'linear' => '', 'logclock' => 'logClock', 'logclockrev' => 'logClockRev' ); static $allowed_tags = array( 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ), 'br' => array(), 'em' => array(), 'strong' => array(), ); /** * Constructor * * @since 4.3 * * @param string $plugin_file Plugin file path * @param string $plugin_version Plugin version */ public function __construct( string $plugin_file, string $plugin_version ) { // VARS self::$plugin_url = plugins_url( '/', $plugin_file ); self::$plugin_basename = plugin_basename( $plugin_file ); self::$plugin_version = $plugin_version; if ( defined('WP_DEBUG') && WP_DEBUG ) { self::$min = ''; } // widgets add_action( 'widgets_init', array( __CLASS__, 'register_widget' ) ); // enqueue scripts but only if shortcode or widget has been used // so it has to be done as late as the wp_footer action add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts' ), 1 ); add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_meta_links' ), 10, 2); // backward compat add_filter( 'coolclock_shortcode', array( __CLASS__, 'filter_shortcode'), 10, 3 ); add_filter( 'coolclock_widget', array( __CLASS__, 'filter_widget'), 10, 3 ); /************** * SHORTCODE **************/ add_shortcode( 'coolclock', array( 'CoolClock_Shortcode', 'handle_shortcode' ) ); // prevent texturizing shortcode content add_filter( 'no_texturize_shortcodes', array( 'CoolClock_Shortcode', 'no_wptexturize') ); } /** * Build canvas output * * @since 2.0 * * @param array $atts Array of sanitized attributes * * @return string Canvas tag */ public static function canvas( $atts ) { /** * ARRAY VALUES * 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'. * If the Advanced extension is activated, there is also 'minimal' available. * radius @param int Define the clock radius. * noseconds @param bool True to hide the second hand. * gmtoffset @param float Timezone offset relative the Greenwhich Mean Time * showdigital @param string|bool Set to 'digital12' to show the time in 12h digital format (with am/pm). * font @param string Set to a font size, family and style for the digital time * fontcolor @param string Set to a color value to change the digital time color * scale @param string Optional alternative clock scale 'logClock' or 'logClockRev' * subtext @param string Optional text, centered below the clock * align @param string Sets floating of the clock: 'left', 'right' or 'center' */ // get defaults for missing attributes $defaults = array_merge( self::$defaults, self::$advanced_defaults ); $atts = apply_filters( 'coolclock_atts', $atts, $defaults ); // CoolClock fields array $fields = array(); $fields[] = 'CoolClock'; // skin id $fields[] = !empty( $atts['skin'] ) ? $atts['skin'] : $defaults['skin']; // radius $fields[] = !empty( $atts['radius'] ) && is_numeric($atts['radius']) ? (int) $atts['radius'] : $defaults['radius']; // noseconds $noseconds = isset( $atts['noseconds'] ) ? (bool) $atts['noseconds'] : $defaults['noseconds']; $fields[] = $noseconds ? 'noSeconds' : ''; // gmt offset $fields[] = isset( $atts['gmtoffset'] ) && is_numeric( $atts['gmtoffset'] ) ? (float) $atts['gmtoffset'] : $defaults['gmtoffset']; // show digital $showdigital = isset($atts['showdigital']) ? $atts['showdigital'] : $defaults['showdigital']; if ( true === $showdigital ) $showdigital = 'digital12'; $fields[] = isset( self::$showdigital_options[$showdigital] ) ? self::$showdigital_options[$showdigital] : ''; // clock type $scale = isset( $atts['scale'] ) ? strtolower( $atts['scale'] ) : $defaults['scale']; $fields[] = isset( self::$clock_types[$scale] ) ? self::$clock_types[$scale] : ''; // set font color $fields[] = isset( $atts['fontcolor'] ) ? $atts['fontcolor'] : $defaults['fontcolor']; $fields = apply_filters( 'coolclock_fields_array', $fields, $atts, $defaults ); // build output $output = ''; $styles = apply_filters( 'coolclock_canvas_styles', array(), $atts, $defaults ); // canvas parameters $output .= ''; // sub text $subtext = ( isset( $atts['subtext'] ) ) ? $atts['subtext'] : $defaults['subtext']; if ( !empty( $subtext ) ) $output .= '
' . $subtext . '
'; return $output; } /** * Create inline style attribute from array * * @since 4.3 * * @param array $styles array with style parameters and their values * * @return string inline style attribute style="..." complete with leading space */ public static function inline_style( $styles ) { $style_arr = array(); foreach ( $styles as $key => $value ) { $style_arr[] = $key . ':' . $value; } $style = ! empty($style_arr) ? ' style="' . implode( ';', $style_arr ) . '"' : ''; return $style; } /** * Parse skin name and user skin parameters * * @since 3.2.0 * * @param string $skin_name skin name * @param string $skin_parms skin parameters, preferably in JSON format * * @return string either found matching skin name or 'invalid_or_missing_skin' on failure */ public static function parse_skin( $skin_name, $skin_parms = '' ) { $skin = strtolower( $skin_name ); // user input, sanitize! $skin = preg_replace( '/[^a-z0-9_-]/', '', $skin ); // check for empty or default skin first if ( empty($skin) || $skin === self::$defaults['skin'] ) { // return the matching skin name return self::$defaults['skin']; } // short-circuit if skin name is already in the config array if ( array_key_exists( $skin, self::$skins_config ) ) { // return the matching skin name return $skin; } // search in the more_skins and advanced_skins arrays $all_skins = self::get_all_skins(); $skin_array = array(); if ( array_key_exists( $skin, $all_skins ) ) { // fetch parameters from skins array $skin_array = is_array( $all_skins[$skin] ) ? $all_skins[$skin] : self::skin_array( $all_skins[$skin] ); } else { // try to build a skin config from passed parameters (user input, sanitize!) $skin_array = self::skin_array( $skin_parms ); if ( empty( $skin_array ) ) { // set faulty skin name return 'no_skin_found'; } } // add found skin parameters to the config array self::$skins_config[$skin] = $skin_array; // return the matching skin name return $skin; } /** * Get all available skins * * @since 3.2.0 * * @return array array of all skins */ public static function get_all_skins() { if ( empty( self::$more_skins_config ) ) { self::$more_skins_config = include COOLCLOCK_DIR . 'includes/moreskins.php'; } return array_merge( self::$more_skins_config, self::$advanced_skins_config ); } /** * Enqueue scripts * * @since 3.2.0 */ public static function enqueue_scripts() { // bail if we don't need script if ( ! self::$add_script ) return; $script = ''; if ( !empty( self::$skins_config ) ) { $skins = wp_json_encode( self::$skins_config ); if ( false === $skins ) { $skins = '{} /* skin parse error, please fix your custom skin */'; } $script .= 'CoolClock.config.skins = ' . $skins . ';'; } $script .= PHP_EOL . 'if(document.readyState!="loading"&&document.addEventListener){document.addEventListener("DOMContentLoaded",function(){CoolClock.findAndCreateClocks();})}else{CoolClock.findAndCreateClocks();};'; wp_enqueue_script( 'coolclock', self::$plugin_url . 'js/coolclock' . self::$min . '.js', false, self::$script_version, true ); wp_add_inline_script( 'coolclock', $script ); // called late so should end up in the footer wp_enqueue_style( 'coolclock', self::$plugin_url . 'css/coolclock' . self::$min . '.css', array(), self::$script_version ); } public static function register_widget() { register_widget("CoolClock_Widget"); } /** * Add links to plugin's description in the plugins list * * @param array $links Array of links to display * @param string $file Plugin file path */ public static function plugin_meta_links( array $links, string $file ) { $support_link = '' . __('Support','coolclock') . ''; $rate_link = '' . __('Rate this plugin','coolclock') . ''; if ( $file == self::$plugin_basename ) { $links[] = $support_link; $links[] = $rate_link; } return $links; } /** * Turn custom skin user input into an array * * @since 2.0 * * @param string $skin * * @return array array of skin parameters */ public static function skin_array( $skin ) { // remove everything following a ; to thwart any script injection $parts = explode( ';', $skin, 2 ); $sanitized = $parts[0]; // strip all kind of whitespace $sanitized = preg_replace("/\s+/", '', $sanitized);; $sanitized = ltrim( $sanitized, '{' ); $sanitized = rtrim( $sanitized, '}' ) . '}'; // by now, the string should start with a double quote if ( 0 !== strpos( $sanitized, '"' ) ) { // regex for (re)wrapping all keys in double quotes $sanitized = preg_replace( "/(['\"])?([a-zA-Z0-9_]+)(['\"])?:([^\/])/", '"$2":$4', $sanitized ); } // fist attempt to decode json string $skin_array = json_decode( '{' . $sanitized . '}', true ); // not valid json? then do it the hard way if ( null === $skin_array ) : // remove all spaces and tabs $sanitized = str_replace( array( ' ', "\t", '"', "'" ), '', $sanitized ); // break it all up in little parts // this converts valid skin javascript object into json, but messes up all else :/ $elements = explode( '},', $sanitized ); // start fresh $skin_array = array(); // and build array foreach( $elements as $element ) { $pair = explode( ':{', $element ); // validate key $part = str_replace( '{', '', $pair[0] ); if ( ! in_array( $part, array('outerBorder','smallIndicator','largeIndicator','hourHand','minuteHand','secondHand','secondDecoration') ) ) continue; // constuct value $value = array(); $parameters = explode( ',', $pair[1] ); for( $i=0; $i < count( $parameters ); $i++ ) { $pairs = explode( ':', $parameters[$i] ); $parm_val = str_replace( '}', '', $pairs[1] ); $value[$pairs[0]] = is_numeric($parm_val) ? (float) $parm_val : (string) $parm_val; } $skin_array[$part] = $value; } endif; return $skin_array; } /** * Sanitize color value user input * * @since 4.2 * * @param string $color * * @return string hex color value or text for named color */ public static function colorval( $color ) { $color = wp_strip_all_tags( $color ); $color = trim( $color ); $color = str_replace( array( '"', '\'', ':'), '', $color ); $color_arr = explode( ' ', $color, 2 ); $color = esc_attr( $color_arr[0] ); if ( substr($color, 0, 1) == '#' ) { if ( ctype_xdigit( substr( $color, 1 ) ) ) return $color; return substr( $color, 1 ); } return ctype_xdigit( $color ) ? '#'.$color : $color; } /** * Filter shortcode output * * @since 4.3 * * @param string $output output * @param array $atts array of shortcode attributes * @param string $content shortcode content * * @return string output */ public static function filter_shortcode( $output, $atts, $content ) { // add backward compat filter return apply_filters( 'coolclock_shortcode_advanced', $output, $atts, $content ); } /** * Filter widget output * * @since 4.3 * * @param string $output output * @param array $args array of widget parameters * @param array $instance widget instance array parameters * * @return string output */ public static function filter_widget( $output, $args, $instance ) { // add backward compat filter return apply_filters( 'coolclock_widget_advanced', $output, $args, $instance ); } }