Block.php
1 month ago
BlockFinder.php
1 year ago
DynamicData.php
1 year ago
HasOneRelationship.php
1 year ago
Integration.php
1 year ago
Utility.php
1 month ago
Utility.php
334 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Utility class for Presto Player. |
| 4 | * |
| 5 | * @package PrestoPlayer |
| 6 | * @subpackage Support |
| 7 | */ |
| 8 | |
| 9 | namespace PrestoPlayer\Support; |
| 10 | |
| 11 | /** |
| 12 | * Utility class containing helper functions. |
| 13 | */ |
| 14 | class Utility { |
| 15 | |
| 16 | /** |
| 17 | * Presto Player admin page screen IDs. |
| 18 | * |
| 19 | * @var array |
| 20 | */ |
| 21 | const PRESTO_PLAYER_SCREENS = array( |
| 22 | 'toplevel_page_presto-dashboard', // Main dashboard (React SPA). |
| 23 | 'edit-pp_video_block', // Media-hub page. |
| 24 | 'edit-pp_email_submission', // Email Submissions list/edit page. |
| 25 | ); |
| 26 | |
| 27 | /** |
| 28 | * Detect whether a string contains an HTML tag (`<word` or `</word`). |
| 29 | * |
| 30 | * Centralized so the save-time and render-time CSS sanitizers stay in lock-step. |
| 31 | * |
| 32 | * @param string $value String to inspect. |
| 33 | * @return bool |
| 34 | */ |
| 35 | public static function hasHtmlMarkup( $value ) { |
| 36 | if ( ! is_string( $value ) || '' === $value ) { |
| 37 | return false; |
| 38 | } |
| 39 | return (bool) preg_match( '#</?\w+#', $value ); |
| 40 | } |
| 41 | |
| 42 | /** |
| 43 | * Sanitize CSS input. |
| 44 | * |
| 45 | * Strips HTML tags rather than wiping the value, so a single stray `<` |
| 46 | * in a legitimate selector doesn't blank the user's entire stylesheet. |
| 47 | * The remaining CSS still runs through `wp_kses_post()` + `esc_attr()` at |
| 48 | * render time (see `Support\Block::getCSS()`), so this is defense in depth. |
| 49 | * |
| 50 | * @param string $css CSS to sanitize. |
| 51 | * @return string Sanitized CSS. |
| 52 | */ |
| 53 | public static function sanitizeCSS( $css ) { |
| 54 | $css = $css ?? ''; |
| 55 | if ( ! self::hasHtmlMarkup( $css ) ) { |
| 56 | return $css; |
| 57 | } |
| 58 | if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { |
| 59 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log |
| 60 | error_log( 'Presto Player: HTML markup stripped from custom player CSS.' ); |
| 61 | } |
| 62 | return wp_strip_all_tags( $css ); |
| 63 | } |
| 64 | |
| 65 | /** |
| 66 | * Insert a string after another string. |
| 67 | * |
| 68 | * @param string $str The original string. |
| 69 | * @param string $search The string to search for. |
| 70 | * @param string $insert The string to insert. |
| 71 | * @return string Modified string. |
| 72 | */ |
| 73 | public static function insertAfterString( $str, $search, $insert ) { |
| 74 | $index = strpos( $str, $search ); |
| 75 | if ( false === $index ) { |
| 76 | return $str; |
| 77 | } |
| 78 | return substr_replace( $str, $search . $insert, $index, strlen( $search ) ); |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Convert snake_case to camelCase. |
| 83 | * |
| 84 | * @param string $input The input string in snake_case. |
| 85 | * @return string The output string in camelCase. |
| 86 | */ |
| 87 | public static function snakeToCamel( $input ) { |
| 88 | return lcfirst( str_replace( ' ', '', ucwords( str_replace( '_', ' ', $input ) ) ) ); |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Convert a duration to human readable format. |
| 93 | * |
| 94 | * @since 5.1.0. |
| 95 | * |
| 96 | * @param string $duration Duration will be in string format (HH:ii:ss) OR (ii:ss), |
| 97 | * with a possible prepended negative sign (-). |
| 98 | * @return string|false A human readable duration string, false on failure. |
| 99 | */ |
| 100 | public static function human_readable_duration( $duration = '' ) { |
| 101 | if ( ( empty( $duration ) || ! is_string( $duration ) ) ) { |
| 102 | return __( '0 seconds', 'presto-player' ); |
| 103 | } |
| 104 | |
| 105 | $duration = trim( $duration ); |
| 106 | |
| 107 | // Remove prepended negative sign. |
| 108 | if ( '-' === substr( $duration, 0, 1 ) ) { |
| 109 | $duration = substr( $duration, 1 ); |
| 110 | } |
| 111 | |
| 112 | // Extract duration parts. |
| 113 | $duration_parts = array_reverse( explode( ':', $duration ) ); |
| 114 | $duration_count = count( $duration_parts ); |
| 115 | |
| 116 | $hour = null; |
| 117 | $minute = null; |
| 118 | $second = null; |
| 119 | |
| 120 | if ( 3 === $duration_count ) { |
| 121 | // Validate HH:ii:ss duration format. |
| 122 | if ( ! ( (bool) preg_match( '/^([0-9]+):([0-5]?[0-9]):([0-5]?[0-9])$/', $duration ) ) ) { |
| 123 | return false; |
| 124 | } |
| 125 | // Three parts: hours, minutes & seconds. |
| 126 | list($second, $minute, $hour) = $duration_parts; |
| 127 | } elseif ( 2 === $duration_count ) { |
| 128 | // Validate ii:ss duration format. |
| 129 | if ( ! ( (bool) preg_match( '/^([0-5]?[0-9]):([0-5]?[0-9])$/', $duration ) ) ) { |
| 130 | return false; |
| 131 | } |
| 132 | // Two parts: minutes & seconds. |
| 133 | list($second, $minute) = $duration_parts; |
| 134 | } else { |
| 135 | return false; |
| 136 | } |
| 137 | |
| 138 | $human_readable_duration = array(); |
| 139 | |
| 140 | // Add the hour part to the string. |
| 141 | if ( is_numeric( $hour ) && $hour > 0 ) { |
| 142 | /* translators: %s: Time duration in hour or hours. */ |
| 143 | $human_readable_duration[] = sprintf( _n( '%s hour', '%s hours', $hour ), (int) $hour ); |
| 144 | } |
| 145 | |
| 146 | // Add the minute part to the string. |
| 147 | if ( is_numeric( $minute ) && $minute > 0 ) { |
| 148 | /* translators: %s: Time duration in minute or minutes. */ |
| 149 | $human_readable_duration[] = sprintf( _n( '%s minute', '%s minutes', $minute ), (int) $minute ); |
| 150 | } |
| 151 | |
| 152 | // Add the second part to the string. |
| 153 | if ( is_numeric( $second ) && $second > 0 ) { |
| 154 | /* translators: %s: Time duration in second or seconds. */ |
| 155 | $human_readable_duration[] = sprintf( _n( '%s second', '%s seconds', $second ), (int) $second ); |
| 156 | } |
| 157 | |
| 158 | return implode( ', ', $human_readable_duration ); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Get the IP address. |
| 163 | * |
| 164 | * @param string $ip_address Optional. IP address to validate. |
| 165 | * @return string Valid IP address or empty string. |
| 166 | */ |
| 167 | public static function getIPAddress( $ip_address = '' ) { |
| 168 | $ip = $ip_address ? $ip_address : ( isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '' ); |
| 169 | |
| 170 | if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) { |
| 171 | return $ip; |
| 172 | } else { |
| 173 | return ''; |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * Insert an array into another array before/after a certain key. |
| 179 | * |
| 180 | * @param array $array The initial array. |
| 181 | * @param array $pairs The array to insert. |
| 182 | * @param string $key The certain key. |
| 183 | * @param string $position Wether to insert the array before or after the key. |
| 184 | * @return array |
| 185 | */ |
| 186 | public static function arrayInsert( $array, $pairs, $key, $position = 'after' ) { |
| 187 | $key_pos = array_search( $key, array_keys( $array ), true ); |
| 188 | |
| 189 | if ( 'after' === $position ) { |
| 190 | ++$key_pos; |
| 191 | } |
| 192 | |
| 193 | if ( false !== $key_pos ) { |
| 194 | $result = array_slice( $array, 0, $key_pos ); |
| 195 | $result = array_merge( $result, $pairs ); |
| 196 | $result = array_merge( $result, array_slice( $array, $key_pos ) ); |
| 197 | } else { |
| 198 | $result = array_merge( $array, $pairs ); |
| 199 | } |
| 200 | |
| 201 | return $result; |
| 202 | } |
| 203 | |
| 204 | /** |
| 205 | * Inserts a new key/value before the specified key in the array. |
| 206 | * |
| 207 | * @param string $key The key to insert before. |
| 208 | * @param array $array An array to insert into. |
| 209 | * @param string $new_key The key to insert. |
| 210 | * @param mixed $new_value The value to insert. |
| 211 | * |
| 212 | * @return array|false The new array if the key exists, FALSE otherwise. |
| 213 | */ |
| 214 | public static function arrayInsertBefore( $key, array &$array, $new_key, $new_value ) { |
| 215 | if ( array_key_exists( $key, $array ) ) { |
| 216 | $new = array(); |
| 217 | foreach ( $array as $k => $value ) { |
| 218 | if ( $k === $key ) { |
| 219 | $new[ $new_key ] = $new_value; |
| 220 | } |
| 221 | $new[ $k ] = $value; |
| 222 | } |
| 223 | return $new; |
| 224 | } |
| 225 | return false; |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Inserts a new key/value pair after a specific key in an array. |
| 230 | * |
| 231 | * @param string $key The key to insert after. |
| 232 | * @param array $array The array to insert into. |
| 233 | * @param string $new_key The new key to insert. |
| 234 | * @param mixed $new_value The new value to insert. |
| 235 | * |
| 236 | * @return array|false The new array if the key exists, FALSE otherwise. |
| 237 | */ |
| 238 | public static function arrayInsertAfter( $key, array &$array, $new_key, $new_value ) { |
| 239 | if ( array_key_exists( $key, $array ) ) { |
| 240 | $new = array(); |
| 241 | foreach ( $array as $k => $value ) { |
| 242 | $new[ $k ] = $value; |
| 243 | if ( $k === $key ) { |
| 244 | $new[ $new_key ] = $new_value; |
| 245 | } |
| 246 | } |
| 247 | return $new; |
| 248 | } |
| 249 | return false; |
| 250 | } |
| 251 | |
| 252 | /** |
| 253 | * Convert hexadecimal color to RGBA. |
| 254 | * |
| 255 | * This function takes a hexadecimal color code and an optional opacity value |
| 256 | * and converts it to an RGBA color string. |
| 257 | * |
| 258 | * @param string $color The hexadecimal color code. |
| 259 | * @param float $opacity Optional. The opacity value between 0 and 1. Default false. |
| 260 | * |
| 261 | * @return string The RGBA color string. |
| 262 | */ |
| 263 | public static function hex2rgba( $color, $opacity = false ) { |
| 264 | |
| 265 | $default_color = 'rgb(0,0,0)'; |
| 266 | |
| 267 | // Return default color if no color provided. |
| 268 | if ( empty( $color ) ) { |
| 269 | return $default_color; |
| 270 | } |
| 271 | |
| 272 | // Ignore "#" if provided. |
| 273 | if ( '#' === $color[0] ) { |
| 274 | $color = substr( $color, 1 ); |
| 275 | } |
| 276 | |
| 277 | // Check if color has 6 or 3 characters, get values. |
| 278 | if ( 6 === strlen( $color ) ) { |
| 279 | $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] ); |
| 280 | } elseif ( 3 === strlen( $color ) ) { |
| 281 | $hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] ); |
| 282 | } else { |
| 283 | return $default_color; |
| 284 | } |
| 285 | |
| 286 | // Convert hex values to rgb values. |
| 287 | $rgb = array_map( 'hexdec', $hex ); |
| 288 | |
| 289 | // Check if opacity is set(rgba or rgb). |
| 290 | if ( $opacity ) { |
| 291 | if ( abs( $opacity ) > 1 ) { |
| 292 | $opacity = 1.0; |
| 293 | } |
| 294 | $output = 'rgba(' . implode( ',', $rgb ) . ',' . $opacity . ')'; |
| 295 | } else { |
| 296 | $output = 'rgb(' . implode( ',', $rgb ) . ')'; |
| 297 | } |
| 298 | |
| 299 | // Return rgb(a) color string. |
| 300 | return $output; |
| 301 | } |
| 302 | |
| 303 | /** |
| 304 | * Flatten an array. |
| 305 | * |
| 306 | * @param array $array Array. |
| 307 | * |
| 308 | * @return array |
| 309 | */ |
| 310 | public static function flatten( array $array ) { |
| 311 | $return = array(); |
| 312 | array_walk_recursive( |
| 313 | $array, |
| 314 | function ( $a ) use ( &$return ) { |
| 315 | $return[] = $a; |
| 316 | } |
| 317 | ); |
| 318 | return $return; |
| 319 | } |
| 320 | |
| 321 | /** |
| 322 | * Check if the current admin page is a Presto Player page. |
| 323 | * |
| 324 | * @return bool True if on a Presto Player page, false otherwise. |
| 325 | */ |
| 326 | public static function isPrestoPlayerPage() { |
| 327 | $current_screen = get_current_screen(); |
| 328 | if ( ! $current_screen ) { |
| 329 | return false; |
| 330 | } |
| 331 | return in_array( $current_screen->id, self::PRESTO_PLAYER_SCREENS, true ); |
| 332 | } |
| 333 | } |
| 334 |