PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.8
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
← All changes | classes/Visualizer/Source.php +236 -8 3.1.03.10.8 View file →
@@ -57,8 +57,16 @@
57 57 */
58 58 protected $_series = array();
59 59
60 60 /**
61 + * The error message.
62 + *
63 + * @access protected
64 + * @var string
65 + */
66 + protected $_error;
67 +
68 + /**
61 69 * Return allowed types
62 70 *
63 71 * @since 1.0.1
64 72 *
@@ -83,9 +91,9 @@
83 91 * @return boolean TRUE if sereis types are valid, otherwise FALSE.
84 92 */
85 93 protected static function _validateTypes( $types ) {
86 94 foreach ( $types as $type ) {
87 - if ( ! in_array( $type, self::$allowed_types ) ) {
95 + if ( ! in_array( $type, self::$allowed_types, true ) ) {
88 96 return false;
89 97 }
90 98 }
91 99
@@ -132,9 +140,12 @@
132 140 *
133 141 * @access public
134 142 * @return string The serialized array of data.
135 143 */
136 - public function getData() {
144 + public function getData( $fetch_from_editable_table = false ) {
145 + if ( $fetch_from_editable_table ) {
146 + $this->_fetchDataFromEditableTable();
147 + }
137 148 return serialize( $this->_data );
138 149 }
139 150
140 151 /**
@@ -144,9 +155,12 @@
144 155 *
145 156 * @access public
146 157 * @return array
147 158 */
148 - public function getRawData() {
159 + public function getRawData( $fetch_from_editable_table = false ) {
160 + if ( $fetch_from_editable_table ) {
161 + $this->_fetchDataFromEditableTable();
162 + }
149 163 return $this->_data;
150 164 }
151 165
152 166 /**
@@ -193,9 +207,8 @@
193 207 * @return array Normalized row of data.
194 208 */
195 209 protected function _normalizeData( $data ) {
196 210 // normalize values
197 - // error_log(print_r($data,true));
198 211 foreach ( $this->_series as $i => $series ) {
199 212 // if no value exists for the seires, then add null
200 213 if ( ! isset( $data[ $i ] ) ) {
201 214 $data[ $i ] = null;
@@ -204,12 +217,14 @@
204 217 continue;
205 218 }
206 219 switch ( $series['type'] ) {
207 220 case 'number':
221 + $data[ $i ] = preg_replace( '/[\x{200B}-\x{200D}\x{FEFF}]/u', '', $data[ $i ] );
208 222 $data[ $i ] = ( is_numeric( $data[ $i ] ) ) ? floatval( $data[ $i ] ) : ( is_numeric( str_replace( ',', '', $data[ $i ] ) ) ? floatval( str_replace( ',', '', $data[ $i ] ) ) : null );
209 223 break;
210 224 case 'boolean':
211 - $data[ $i ] = ! empty( $data[ $i ] ) ? filter_var( $data[ $i ], FILTER_VALIDATE_BOOLEAN ) : null;
225 + $datum = trim( strval( $data[ $i ] ) );
226 + $data[ $i ] = in_array( $datum, array( 'true', 'yes', '1' ), true ) ? 'true' : 'false';
212 227 break;
213 228 case 'timeofday':
214 229 $date = new DateTime( '1984-03-16T' . $data[ $i ] );
215 230 if ( $date ) {
@@ -220,16 +235,23 @@
220 235 0,
221 236 );
222 237 }
223 238 break;
239 + case 'datetime':
240 + // let's check if the date is a Unix epoch
241 + $value = DateTime::createFromFormat( 'U', $data[ $i ] );
242 + if ( $value !== false && ! is_wp_error( $value ) ) {
243 + $data[ $i ] = $value->format( 'Y-m-d H:i:s' );
244 + }
245 + break;
224 246 case 'string':
225 - $data[ $i ] = $this->toUTF8( $data[ $i ] );
247 + // if a ' is provided, strip the backslash
248 + $data[ $i ] = stripslashes( $this->toUTF8( $data[ $i ] ) );
226 249 break;
227 250 }
228 251 }
229 252
230 - // error_log(print_r($data,true));
231 - return $data;
253 + return apply_filters( 'visualizer_format_data', $data, $this->_series );
232 254 }
233 255
234 256
235 257 /**
@@ -246,6 +268,212 @@
246 268 $datum = \ForceUTF8\Encoding::toUTF8( $datum );
247 269 }
248 270 return $datum;
249 271 }
272 +
273 + /**
274 + * Determines the formats of date/time columns.
275 + *
276 + * @access public
277 + *
278 + * @param array $series The actual array of series.
279 + * @param array $data The actual array of data.
280 + *
281 + * @return array
282 + */
283 + public static final function get_date_formats_if_exists( $series, $data ) {
284 + $date_formats = array();
285 + $types = array();
286 + $index = 0;
287 + foreach ( $series as $column ) {
288 + if ( in_array( $column['type'], array( 'date', 'datetime', 'timeofday' ), true ) ) {
289 + $types[] = array( 'index' => $index, 'type' => $column['type'] );
290 + }
291 + $index++;
292 + }
293 +
294 + if ( ! $types ) {
295 + return $date_formats;
296 + }
297 +
298 + $random = $data;
299 + // let's randomly pick 5 data points instead of cycling through the entire data set.
300 + if ( count( $data ) > 5 ) {
301 + $random = array();
302 + for ( $x = 0; $x < 5; $x++ ) {
303 + $random[] = $data[ rand( 0, count( $data ) - 1 ) ];
304 + }
305 + }
306 +
307 + foreach ( $types as $type ) {
308 + $formats = array();
309 + foreach ( $random as $datum ) {
310 + $f = self::determine_date_format( $datum[ $type['index'] ], $type['type'] );
311 + if ( $f ) {
312 + $formats[] = $f;
313 + }
314 + }
315 + // if there are multiple formats, use the most frequent format.
316 + $formats = array_filter( $formats );
317 + if ( $formats ) {
318 + $formats = array_count_values( $formats );
319 + arsort( $formats );
320 + $formats = array_keys( $formats );
321 + $final_format = reset( $formats );
322 + // we have determined the PHP format; now we have to change this into the JS format where m = MM, d = DD etc.
323 + $date_formats[] = array( 'index' => $type['index'], 'format' => str_replace( array( 'Y', 'm', 'd', 'H', 'i', 's' ), array( 'YYYY', 'MM', 'DD', 'HH', 'mm', 'ss' ), $final_format ) );
324 + }
325 + }
326 + return $date_formats;
327 + }
328 +
329 + /**
330 + * Determines the date/time format of the given string.
331 + *
332 + * @access private
333 + *
334 + * @param string $value The string.
335 + * @param string $type 'date', 'timeofday' or 'datetime'.
336 + *
337 + * @return string|null
338 + */
339 + private static function determine_date_format( $value, $type ) {
340 + if ( version_compare( phpversion(), '5.3.0', '<' ) ) {
341 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'PHP version %s not supported', phpversion() ), 'error', __FILE__, __LINE__ );
342 + return null;
343 + }
344 +
345 + $formats = array(
346 + 'Y/m/d',
347 + 'Y-m-d',
348 + 'm/d/Y',
349 + 'm-d-Y',
350 + 'd-m-Y',
351 + 'd/m/Y',
352 + );
353 +
354 + switch ( $type ) {
355 + case 'datetime':
356 + $formats = array_merge(
357 + $formats, array(
358 + 'U',
359 + 'Y/m/d H:i:s',
360 + 'Y-m-d H:i:s',
361 + 'm/d/Y H:i:s',
362 + 'm-d-Y H:i:s',
363 + )
364 + );
365 + break;
366 + case 'timeofday':
367 + $formats = array_merge(
368 + $formats, array(
369 + 'H:i:s',
370 + 'H:i',
371 + )
372 + );
373 + break;
374 + }
375 +
376 + $formats = apply_filters( 'visualizer_date_formats', $formats, $type );
377 +
378 + foreach ( $formats as $format ) {
379 + $return = DateTime::createFromFormat( $format, $value );
380 + if ( $return !== false && ! is_wp_error( $return ) ) {
381 + return $format;
382 + }
383 + }
384 + // invalid format
385 + return null;
386 + }
387 +
388 + /**
389 + * Returns the error, if any.
390 + *
391 + * @access public
392 + * @return string
393 + */
394 + public function get_error() {
395 + return $this->_error;
396 + }
397 +
398 + /**
399 + * Fetches information from the editable table and parses it to build series and data arrays.
400 + *
401 + * @since ?
402 + *
403 + * @access public
404 + * @return boolean TRUE on success, otherwise FALSE.
405 + */
406 + public function fetchFromEditableTable() {
407 + if ( empty( $this->_args ) ) {
408 + return false;
409 + }
410 +
411 + $this->_fetchSeriesFromEditableTable();
412 + $this->_fetchDataFromEditableTable();
413 + return true;
414 + }
415 +
416 + /**
417 + * Fetches series information from the editable table. This is fetched only through the UI and not while refreshing the chart data.
418 + *
419 + * @since 1.0.0
420 + *
421 + * @access private
422 + */
423 + private function _fetchSeriesFromEditableTable() {
424 + $params = $this->_args;
425 + $headers = array_filter( $params['header'] );
426 + $types = array_filter( $params['type'] );
427 + $header_row = $type_row = array();
428 + if ( $headers ) {
429 + foreach ( $headers as $header ) {
430 + if ( ! empty( $types[ $header ] ) ) {
431 + $this->_series[] = array(
432 + 'label' => $header,
433 + 'type' => $types[ $header ],
434 + );
435 + }
436 + }
437 + }
438 +
439 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Series found for %s = %s', print_r( $this->_args, true ), print_r( $this->_series, true ) ), 'debug', __FILE__, __LINE__ );
440 +
441 + return true;
442 + }
443 +
444 +
445 + /**
446 + * Fetches data information from the editable table.
447 + *
448 + * @since 1.0.0
449 + *
450 + * @access private
451 + */
452 + private function _fetchDataFromEditableTable() {
453 + $headers = wp_list_pluck( $this->_series, 'label' );
454 + $this->fetch();
455 +
456 + $data = $this->_data;
457 + $this->_data = array();
458 +
459 + foreach ( $data as $line ) {
460 + $data_row = array();
461 + // we have to make sure we are fetching the data in the right order
462 + // in case the columns have been reordered
463 + foreach ( $headers as $header ) {
464 + $value = $line[ $header ];
465 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
466 + if ( in_array( $header, $headers ) ) {
467 + $data_row[] = $value;
468 + }
469 + }
470 + $this->_data[] = $this->_normalizeData( $data_row );
471 + }
472 +
473 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Data found for %s = %s', print_r( $this->_args, true ), print_r( $this->_data, true ) ), 'debug', __FILE__, __LINE__ );
474 +
475 + return true;
476 + }
477 +
250 478
251 479 }