WordPress Charts and Graphs Lite - Version 3.0.8

Version Description

  • 2018-06-27
Download this release

Release Info

Developer codeinwp
Plugin Icon WordPress Charts and Graphs Lite
Version 3.0.8
Comparing to
See all releases

Code changes from version 3.0.7 to 3.0.8

Files changed (39) hide show
  1. CHANGELOG.md +8 -0
  2. classes/Visualizer/Module.php +56 -0
  3. classes/Visualizer/Module/Admin.php +67 -0
  4. classes/Visualizer/Module/Chart.php +33 -9
  5. classes/Visualizer/Module/Frontend.php +10 -2
  6. classes/Visualizer/Module/Setup.php +1 -0
  7. classes/Visualizer/Plugin.php +16 -1
  8. classes/Visualizer/Render/Page/Data.php +11 -0
  9. classes/Visualizer/Render/Sidebar/Type/Pie.php +2 -0
  10. classes/Visualizer/Source.php +18 -4
  11. classes/Visualizer/Source/Csv.php +3 -0
  12. classes/Visualizer/Source/Csv/Remote.php +2 -2
  13. css/library.css +4 -0
  14. css/media.css +1 -1
  15. index.php +6 -2
  16. js/frame.js +11 -0
  17. readme.md +9 -0
  18. readme.txt +9 -0
  19. themeisle-hash.json +1 -1
  20. vendor/autoload.php +1 -1
  21. vendor/autoload_52.php +1 -1
  22. vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-feedback-deactivate.php +6 -6
  23. vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-licenser.php +48 -57
  24. vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-logger.php +10 -10
  25. vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-product.php +84 -11
  26. vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-widget-dashboard-blog.php +106 -92
  27. vendor/codeinwp/themeisle-sdk/load.php +1 -1
  28. vendor/composer/autoload_namespaces.php +1 -0
  29. vendor/composer/autoload_real.php +5 -5
  30. vendor/composer/autoload_real_52.php +3 -3
  31. vendor/composer/installed.json +40 -4
  32. vendor/neitanod/forceutf8/README.md +61 -0
  33. vendor/neitanod/forceutf8/composer.json +20 -0
  34. vendor/neitanod/forceutf8/src/ForceUTF8/Encoding.php +347 -0
  35. vendor/neitanod/forceutf8/test/ForceUTF8Test.php +101 -0
  36. vendor/neitanod/forceutf8/test/Test.class.php +62 -0
  37. vendor/neitanod/forceutf8/test/data/russian.txt +1 -0
  38. vendor/neitanod/forceutf8/test/data/test1.txt +1 -0
  39. vendor/neitanod/forceutf8/test/data/test1Latin.txt +1 -0
CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
 
 
 
 
 
 
 
 
 
2
  ### v3.0.7 - 2018-03-26
3
  **Changes:**
4
  * Adds insert button in chart library.
1
 
2
+ ### v3.0.8 - 2018-06-27
3
+ **Changes:**
4
+ * Added revision support for the chart post type
5
+ * Added both % and Value to the Pie Slice
6
+ * Use the blog locale for Visualizer's options
7
+ * Fixed issue with data being fetched from the remote source every single time the chart was shown
8
+ * Fixed issue with scheduled charts not being updated if one of the scheduled charts is deleted
9
+
10
  ### v3.0.7 - 2018-03-26
11
  **Changes:**
12
  * Adds insert button in chart library.
classes/Visualizer/Module.php CHANGED
@@ -63,6 +63,10 @@ class Visualizer_Module {
63
 
64
  $this->_wpdb = $wpdb;
65
  $this->_plugin = $plugin;
 
 
 
 
66
  }
67
 
68
  /**
@@ -323,6 +327,57 @@ class Visualizer_Module {
323
  );
324
  }
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  /**
327
  * Returns the language of the locale.
328
  *
@@ -339,4 +394,5 @@ class Visualizer_Module {
339
  }
340
  return reset( $array );
341
  }
 
342
  }
63
 
64
  $this->_wpdb = $wpdb;
65
  $this->_plugin = $plugin;
66
+
67
+ $this->_addFilter( Visualizer_Plugin::FILTER_UNDO_REVISIONS, 'undoRevisions', 10, 2 );
68
+ $this->_addFilter( Visualizer_Plugin::FILTER_HANDLE_REVISIONS, 'handleExistingRevisions', 10, 2 );
69
+
70
  }
71
 
72
  /**
327
  );
328
  }
329
 
330
+ /**
331
+ * Undo revisions for the chart, and if necessary, restore the earliest version.
332
+ *
333
+ * @return bool If any revisions were found.
334
+ */
335
+ public final function undoRevisions( $chart_id, $restore = false ) {
336
+ do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'undoRevisions for %d with%s restore', $chart_id, ( $restore ? '' : 'out' ) ), 'debug', __FILE__, __LINE__ );
337
+
338
+ $revisions = wp_get_post_revisions( $chart_id, array( 'order' => 'ASC' ) );
339
+ if ( count( $revisions ) > 1 ) {
340
+ $revision_ids = array_keys( $revisions );
341
+
342
+ do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'found %d revisions = %s', count( $revisions ), print_r( $revision_ids, true ) ), 'debug', __FILE__, __LINE__ );
343
+
344
+ // when we restore, a new revision is likely to be created. so, let's disable revisions for the time being.
345
+ add_filter( 'wp_revisions_to_keep', '__return_false' );
346
+
347
+ if ( $restore ) {
348
+ // restore to the oldest one i.e. the first one.
349
+ wp_restore_post_revision( array_shift( $revision_ids ) );
350
+ }
351
+
352
+ // delete all revisions.
353
+ foreach ( $revision_ids as $id ) {
354
+ wp_delete_post_revision( $id );
355
+ }
356
+
357
+ return true;
358
+ }
359
+ return false;
360
+ }
361
+
362
+ /**
363
+ * If existing revisions exist for the chart, restore the earliest version and then create a new revision to initiate editing.
364
+ */
365
+ public final function handleExistingRevisions( $chart_id, $chart ) {
366
+ do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'handleExistingRevisions for %d', $chart_id ), 'debug', __FILE__, __LINE__ );
367
+
368
+ // undo revisions.
369
+ $revisions_found = $this->undoRevisions( $chart_id, true );
370
+
371
+ // create revision for the edit action.
372
+ wp_save_post_revision( $chart_id );
373
+
374
+ if ( $revisions_found ) {
375
+ // fetch chart data again in case it was updated by an earlier revision.
376
+ $chart = get_post( $chart_id );
377
+ }
378
+ return $chart;
379
+ }
380
+
381
  /**
382
  * Returns the language of the locale.
383
  *
394
  }
395
  return reset( $array );
396
  }
397
+
398
  }
classes/Visualizer/Module/Admin.php CHANGED
@@ -62,9 +62,71 @@ class Visualizer_Module_Admin extends Visualizer_Module {
62
  $this->_addFilter( 'visualizer_get_chart_counts', 'getChartCountsByTypeAndMeta' );
63
  $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
64
 
 
 
 
 
 
65
  $this->_addAction( 'admin_init', 'init' );
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  /**
69
  * Admin init.
70
  *
@@ -441,6 +503,10 @@ class Visualizer_Module_Admin extends Visualizer_Module {
441
  $query = new WP_Query( $query_args );
442
  while ( $query->have_posts() ) {
443
  $chart = $query->next_post();
 
 
 
 
444
  // fetch and update settings
445
  $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
446
  unset( $settings['height'], $settings['width'] );
@@ -469,6 +535,7 @@ class Visualizer_Module_Admin extends Visualizer_Module {
469
  array(
470
  'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
471
  'library' => 'yes',
 
472
  ), $ajaxurl
473
  ),
474
  'edit' => add_query_arg(
62
  $this->_addFilter( 'visualizer_get_chart_counts', 'getChartCountsByTypeAndMeta' );
63
  $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
64
 
65
+ // revision support.
66
+ $this->_addFilter( 'wp_revisions_to_keep', 'limitRevisions', null, 10, 2 );
67
+ $this->_addAction( '_wp_put_post_revision', 'addRevision', null, 10, 1 );
68
+ $this->_addAction( 'wp_restore_post_revision', 'restoreRevision', null, 10, 2 );
69
+
70
  $this->_addAction( 'admin_init', 'init' );
71
  }
72
 
73
+ /**
74
+ * No limits on revisions.
75
+ */
76
+ public function limitRevisions( $num, $post ) {
77
+ if ( Visualizer_Plugin::CPT_VISUALIZER === $post->post_type ) {
78
+ return -1;
79
+ }
80
+ return $num;
81
+ }
82
+
83
+ /**
84
+ * Add a revision.
85
+ */
86
+ public function addRevision( $revision_id ) {
87
+ $parent_id = wp_is_post_revision( $revision_id );
88
+ if ( Visualizer_Plugin::CPT_VISUALIZER === get_post_type( $parent_id ) ) {
89
+ // add the meta data to this revision.
90
+ $meta = get_post_meta( $parent_id, '', true );
91
+
92
+ if ( $meta ) {
93
+ foreach ( $meta as $key => $value ) {
94
+ if ( 0 === strpos( $key, 'visualizer' ) ) {
95
+ add_metadata( 'post', $revision_id, $key, maybe_unserialize( $value[0] ) );
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Restore a revision.
104
+ */
105
+ public function restoreRevision( $post_id, $revision_id ) {
106
+ if ( Visualizer_Plugin::CPT_VISUALIZER === get_post_type( $post_id ) ) {
107
+ // get the meta information from the revision.
108
+ $meta = get_metadata( 'post', $revision_id, '', true );
109
+
110
+ // delete all meta information from the post before adding.
111
+ $post_meta = get_post_meta( $post_id, '', true );
112
+ if ( $post_meta ) {
113
+ foreach ( $meta as $key => $value ) {
114
+ if ( 0 === strpos( $key, 'visualizer' ) ) {
115
+ delete_post_meta( $post_id, $key );
116
+ }
117
+ }
118
+ }
119
+
120
+ if ( $meta ) {
121
+ foreach ( $meta as $key => $value ) {
122
+ if ( 0 === strpos( $key, 'visualizer' ) ) {
123
+ add_post_meta( $post_id, $key, maybe_unserialize( $value[0] ) );
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
  /**
131
  * Admin init.
132
  *
503
  $query = new WP_Query( $query_args );
504
  while ( $query->have_posts() ) {
505
  $chart = $query->next_post();
506
+
507
+ // if the user has updated a chart and instead of saving it, has closed the modal. If the user refreshes, they should get the original chart.
508
+ $chart = $this->handleExistingRevisions( $chart->ID, $chart );
509
+
510
  // fetch and update settings
511
  $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
512
  unset( $settings['height'], $settings['width'] );
535
  array(
536
  'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
537
  'library' => 'yes',
538
+ 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
539
  ), $ajaxurl
540
  ),
541
  'edit' => add_query_arg(
classes/Visualizer/Module/Chart.php CHANGED
@@ -57,9 +57,7 @@ class Visualizer_Module_Chart extends Visualizer_Module {
57
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
60
- // Added by Ash/Upwork
61
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
62
- // Added by Ash/Upwork
63
  }
64
 
65
  /**
@@ -211,7 +209,7 @@ class Visualizer_Module_Chart extends Visualizer_Module {
211
  // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
212
  $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
213
  if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
214
- $default_type = 'line';
215
  $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
216
  $source->fetch();
217
  $chart_id = wp_insert_post(
@@ -269,10 +267,21 @@ class Visualizer_Module_Chart extends Visualizer_Module {
269
  $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
270
 
271
  // skip chart type pages only for existing charts.
272
- if ( VISUALIZER_SKIP_CHART_TYPE_PAGE && 'auto-draft' !== $this->_chart->post_status ) {
273
  $tab = 'settings';
274
  }
275
 
 
 
 
 
 
 
 
 
 
 
 
276
  switch ( $tab ) {
277
  case 'settings':
278
  $this->_handleDataAndSettingsPage();
@@ -357,6 +366,9 @@ class Visualizer_Module_Chart extends Visualizer_Module {
357
  $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
358
  ? esc_html__( 'Save Chart', 'visualizer' )
359
  : esc_html__( 'Create Chart', 'visualizer' );
 
 
 
360
  } else {
361
  $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
362
  }
@@ -426,18 +438,30 @@ class Visualizer_Module_Chart extends Visualizer_Module {
426
  * @access public
427
  */
428
  public function uploadData() {
 
 
 
 
429
  // validate nonce
430
- // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
431
  if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
 
 
 
432
  status_header( 403 );
433
  exit;
434
  }
 
435
  // check chart, if chart exists
 
436
  $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
437
  if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
 
 
 
438
  status_header( 400 );
439
  exit;
440
  }
 
441
  if ( ! isset( $_POST['vz-import-time'] ) ) {
442
  apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
443
  }
@@ -456,10 +480,8 @@ class Visualizer_Module_Chart extends Visualizer_Module {
456
  }
457
  } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
458
  $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
459
- // Added by Ash/Upwork
460
  } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
461
  $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
462
- // Added by Ash/Upwork
463
  } else {
464
  $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
465
  }
@@ -477,9 +499,10 @@ class Visualizer_Module_Chart extends Visualizer_Module {
477
  }
478
  }
479
  $render->render();
480
- if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
481
- defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
482
  }
 
483
  }
484
 
485
  /**
@@ -599,4 +622,5 @@ class Visualizer_Module_Chart extends Visualizer_Module {
599
  $this->_addAction( 'admin_head', 'renderFlattrScript' );
600
  wp_iframe( array( $render, 'render' ) );
601
  }
 
602
  }
57
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
 
60
  $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
 
61
  }
62
 
63
  /**
209
  // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
210
  $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
211
  if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
212
+ $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
213
  $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
214
  $source->fetch();
215
  $chart_id = wp_insert_post(
267
  $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
268
 
269
  // skip chart type pages only for existing charts.
270
+ if ( VISUALIZER_SKIP_CHART_TYPE_PAGE && 'auto-draft' !== $this->_chart->post_status && ( ! empty( $_GET['tab'] ) && 'visualizer' === $_GET['tab'] ) ) {
271
  $tab = 'settings';
272
  }
273
 
274
+ if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
275
+ // if the cancel button is clicked.
276
+ $this->undoRevisions( $chart_id, true );
277
+ } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
278
+ // if the save button is clicked.
279
+ $this->undoRevisions( $chart_id, false );
280
+ } else {
281
+ // if the edit button is clicked.
282
+ $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
283
+ }
284
+
285
  switch ( $tab ) {
286
  case 'settings':
287
  $this->_handleDataAndSettingsPage();
366
  $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
367
  ? esc_html__( 'Save Chart', 'visualizer' )
368
  : esc_html__( 'Create Chart', 'visualizer' );
369
+ if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
370
+ $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
371
+ }
372
  } else {
373
  $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
374
  }
438
  * @access public
439
  */
440
  public function uploadData() {
441
+ // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
442
+ // otherwise, assume this is a normal web request.
443
+ $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
444
+
445
  // validate nonce
 
446
  if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'] ) ) {
447
+ if ( ! $can_die ) {
448
+ return;
449
+ }
450
  status_header( 403 );
451
  exit;
452
  }
453
+
454
  // check chart, if chart exists
455
+ // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
456
  $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
457
  if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
458
+ if ( ! $can_die ) {
459
+ return;
460
+ }
461
  status_header( 400 );
462
  exit;
463
  }
464
+
465
  if ( ! isset( $_POST['vz-import-time'] ) ) {
466
  apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
467
  }
480
  }
481
  } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
482
  $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
 
483
  } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
484
  $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
 
485
  } else {
486
  $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
487
  }
499
  }
500
  }
501
  $render->render();
502
+ if ( ! $can_die ) {
503
+ return;
504
  }
505
+ defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
506
  }
507
 
508
  /**
622
  $this->_addAction( 'admin_head', 'renderFlattrScript' );
623
  wp_iframe( array( $render, 'render' ) );
624
  }
625
+
626
  }
classes/Visualizer/Module/Frontend.php CHANGED
@@ -197,6 +197,11 @@ class Visualizer_Module_Frontend extends Visualizer_Module {
197
  return '';
198
  }
199
 
 
 
 
 
 
200
  $id = 'visualizer-' . $atts['id'];
201
  $defaultClass = 'visualizer-front';
202
  $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
@@ -230,9 +235,12 @@ class Visualizer_Module_Frontend extends Visualizer_Module {
230
 
231
  $id = $id . '-' . rand();
232
  $arguments = array( '', $id, $settings );
 
233
  apply_filters_ref_array( 'visualizer_pro_inline_css', array( &$arguments ) );
234
- $css = $arguments[0];
235
- $settings = $arguments[2];
 
 
236
 
237
  // add chart to the array
238
  $this->_charts[ $id ] = array(
197
  return '';
198
  }
199
 
200
+ // in case revisions exist.
201
+ if ( true === ( $revisions = $this->undoRevisions( $chart->ID, true ) ) ) {
202
+ $chart = get_post( $chart->ID );
203
+ }
204
+
205
  $id = 'visualizer-' . $atts['id'];
206
  $defaultClass = 'visualizer-front';
207
  $class = apply_filters( Visualizer_Plugin::FILTER_CHART_WRAPPER_CLASS, $atts['class'], $atts['id'] );
235
 
236
  $id = $id . '-' . rand();
237
  $arguments = array( '', $id, $settings );
238
+ $css = '';
239
  apply_filters_ref_array( 'visualizer_pro_inline_css', array( &$arguments ) );
240
+ if ( ! empty( $arguments ) ) {
241
+ $css = $arguments[0];
242
+ $settings = $arguments[2];
243
+ }
244
 
245
  // add chart to the array
246
  $this->_charts[ $id ] = array(
classes/Visualizer/Module/Setup.php CHANGED
@@ -110,6 +110,7 @@ class Visualizer_Module_Setup extends Visualizer_Module {
110
  Visualizer_Plugin::CPT_VISUALIZER, array(
111
  'label' => 'Visualizer Charts',
112
  'public' => false,
 
113
  )
114
  );
115
  }
110
  Visualizer_Plugin::CPT_VISUALIZER, array(
111
  'label' => 'Visualizer Charts',
112
  'public' => false,
113
+ 'supports' => array( 'revisions' ),
114
  )
115
  );
116
  }
classes/Visualizer/Plugin.php CHANGED
@@ -28,7 +28,7 @@
28
  class Visualizer_Plugin {
29
 
30
  const NAME = 'visualizer';
31
- const VERSION = '3.0.7';
32
 
33
  // custom post types
34
  const CPT_VISUALIZER = 'visualizer';
@@ -62,6 +62,8 @@ class Visualizer_Plugin {
62
  const FILTER_GET_CHART_SERIES = 'visualizer-get-chart-series';
63
  const FILTER_GET_CHART_DATA = 'visualizer-get-chart-data';
64
  const FILTER_GET_CHART_SETTINGS = 'visualizer-get-chart-settings';
 
 
65
 
66
  const CF_CHART_URL = 'visualizer-chart-url';
67
  const CF_CHART_SCHEDULE = 'visualizer-chart-schedule';
@@ -97,6 +99,9 @@ class Visualizer_Plugin {
97
  * @access private
98
  */
99
  private function __construct() {
 
 
 
100
  }
101
 
102
  /**
@@ -184,4 +189,14 @@ class Visualizer_Plugin {
184
  private function __clone() {
185
  }
186
 
 
 
 
 
 
 
 
 
 
 
187
  }
28
  class Visualizer_Plugin {
29
 
30
  const NAME = 'visualizer';
31
+ const VERSION = '3.0.8';
32
 
33
  // custom post types
34
  const CPT_VISUALIZER = 'visualizer';
62
  const FILTER_GET_CHART_SERIES = 'visualizer-get-chart-series';
63
  const FILTER_GET_CHART_DATA = 'visualizer-get-chart-data';
64
  const FILTER_GET_CHART_SETTINGS = 'visualizer-get-chart-settings';
65
+ const FILTER_UNDO_REVISIONS = 'visualizer-undo-revisions';
66
+ const FILTER_HANDLE_REVISIONS = 'visualizer-handle-revisions';
67
 
68
  const CF_CHART_URL = 'visualizer-chart-url';
69
  const CF_CHART_SCHEDULE = 'visualizer-chart-schedule';
99
  * @access private
100
  */
101
  private function __construct() {
102
+ if ( VISUALIZER_DEBUG ) {
103
+ add_action( 'themeisle_log_event', array( $this, 'themeisle_log_event_debug' ), 10, 5 );
104
+ }
105
  }
106
 
107
  /**
189
  private function __clone() {
190
  }
191
 
192
+ /**
193
+ * For local testing, overrides the 'themeisle_log_event' hook and redirects to error.log.
194
+ */
195
+ final function themeisle_log_event_debug( $name, $message, $type, $file, $line ) {
196
+ if ( Visualizer_Plugin::NAME !== $name ) {
197
+ return;
198
+ }
199
+ error_log( sprintf( '%s (%s): %s in %s:%s', $name, $type, $message, $file, $line ) );
200
+ }
201
+
202
  }
classes/Visualizer/Render/Page/Data.php CHANGED
@@ -254,6 +254,10 @@ class Visualizer_Render_Page_Data extends Visualizer_Render_Page {
254
  <form id="settings-form" action="<?php echo add_query_arg( 'nonce', wp_create_nonce() ); ?>"
255
  method="post">
256
  <?php echo $this->sidebar; ?>
 
 
 
 
257
  </form>
258
  </ul>
259
  </li>
@@ -310,6 +314,10 @@ class Visualizer_Render_Page_Data extends Visualizer_Render_Page {
310
  * @access private
311
  */
312
  private function permissionsSidebar() {
 
 
 
 
313
  Visualizer_Render_Sidebar::_renderGroupStart(
314
  esc_html__( 'Who can see this chart?', 'visualizer' ) . '<span
315
  class="dashicons dashicons-lock"></span>', '', apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'chart-permissions' )
@@ -415,6 +423,9 @@ class Visualizer_Render_Page_Data extends Visualizer_Render_Page {
415
  echo '</div>';
416
  }
417
  echo '<input type="submit" id="settings-button" class="button button-primary button-large push-right" value="', $this->button, '">';
 
 
 
418
  }
419
 
420
  }
254
  <form id="settings-form" action="<?php echo add_query_arg( 'nonce', wp_create_nonce() ); ?>"
255
  method="post">
256
  <?php echo $this->sidebar; ?>
257
+ <input type="hidden" name="save" value="1">
258
+ </form>
259
+ <form id="cancel-form" action="<?php echo add_query_arg( 'nonce', wp_create_nonce() ); ?>" method="post">
260
+ <input type="hidden" name="cancel" value="1">
261
  </form>
262
  </ul>
263
  </li>
314
  * @access private
315
  */
316
  private function permissionsSidebar() {
317
+ // ignore for unit tests because Travis throws the error "Indirect modification of overloaded property Visualizer_Render_Page_Data::$permissions has no effect".
318
+ if ( defined( 'WP_TESTS_DOMAIN' ) ) {
319
+ return;
320
+ }
321
  Visualizer_Render_Sidebar::_renderGroupStart(
322
  esc_html__( 'Who can see this chart?', 'visualizer' ) . '<span
323
  class="dashicons dashicons-lock"></span>', '', apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'chart-permissions' )
423
  echo '</div>';
424
  }
425
  echo '<input type="submit" id="settings-button" class="button button-primary button-large push-right" value="', $this->button, '">';
426
+ if ( isset( $this->cancel_button ) ) {
427
+ echo '<input type="submit" id="cancel-button" class="button button-secondary button-large push-right" value="', $this->cancel_button, '">';
428
+ }
429
  }
430
 
431
  }
classes/Visualizer/Render/Sidebar/Type/Pie.php CHANGED
@@ -102,6 +102,8 @@ class Visualizer_Render_Sidebar_Type_Pie extends Visualizer_Render_Sidebar {
102
  'percentage' => esc_html__( 'The percentage of the slice size out of the total', 'visualizer' ),
103
  'value' => esc_html__( 'The quantitative value of the slice', 'visualizer' ),
104
  'label' => esc_html__( 'The name of the slice', 'visualizer' ),
 
 
105
  'none' => esc_html__( 'No text is displayed', 'visualizer' ),
106
  ),
107
  esc_html__( 'The content of the text displayed on the slice.', 'visualizer' )
102
  'percentage' => esc_html__( 'The percentage of the slice size out of the total', 'visualizer' ),
103
  'value' => esc_html__( 'The quantitative value of the slice', 'visualizer' ),
104
  'label' => esc_html__( 'The name of the slice', 'visualizer' ),
105
+ // the below option is undocumented.
106
+ 'value-and-percentage' => esc_html__( 'The quantitative value and percentage of the slice', 'visualizer' ),
107
  'none' => esc_html__( 'No text is displayed', 'visualizer' ),
108
  ),
109
  esc_html__( 'The content of the text displayed on the slice.', 'visualizer' )
classes/Visualizer/Source.php CHANGED
@@ -222,10 +222,7 @@ abstract class Visualizer_Source {
222
  }
223
  break;
224
  case 'string':
225
- // condition introduced for Issue with non-English text #240 where languages such as Hebrew get messed up.
226
- if ( function_exists( 'mb_detect_encoding' ) && mb_detect_encoding( $data[ $i ] ) !== 'UTF-8' ) {
227
- $data[ $i ] = utf8_encode( $data[ $i ] );
228
- }
229
  break;
230
  }
231
  }
@@ -234,4 +231,21 @@ abstract class Visualizer_Source {
234
  return $data;
235
  }
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  }
222
  }
223
  break;
224
  case 'string':
225
+ $data[ $i ] = $this->toUTF8( $data[ $i ] );
 
 
 
226
  break;
227
  }
228
  }
231
  return $data;
232
  }
233
 
234
+
235
+ /**
236
+ * Converts values to UTF8, if required.
237
+ *
238
+ * @access protected
239
+ *
240
+ * @param string $datum The data to convert.
241
+ *
242
+ * @return string The converted data.
243
+ */
244
+ protected final function toUTF8( $datum ) {
245
+ if ( ! function_exists( 'mb_detect_encoding' ) || mb_detect_encoding( $datum ) !== 'ASCII' ) {
246
+ $datum = \ForceUTF8\Encoding::toUTF8( $datum );
247
+ }
248
+ return $datum;
249
+ }
250
+
251
  }
classes/Visualizer/Source/Csv.php CHANGED
@@ -83,6 +83,9 @@ class Visualizer_Source_Csv extends Visualizer_Source {
83
 
84
  for ( $i = 0, $len = count( $labels ); $i < $len; $i++ ) {
85
  $default_type = $i == 0 ? 'string' : 'number';
 
 
 
86
  $this->_series[] = array(
87
  'label' => $labels[ $i ],
88
  'type' => isset( $types[ $i ] ) ? $types[ $i ] : $default_type,
83
 
84
  for ( $i = 0, $len = count( $labels ); $i < $len; $i++ ) {
85
  $default_type = $i == 0 ? 'string' : 'number';
86
+
87
+ $labels[ $i ] = $this->toUTF8( $labels[ $i ] );
88
+
89
  $this->_series[] = array(
90
  'label' => $labels[ $i ],
91
  'type' => isset( $types[ $i ] ) ? $types[ $i ] : $default_type,
classes/Visualizer/Source/Csv/Remote.php CHANGED
@@ -97,7 +97,7 @@ class Visualizer_Source_Csv_Remote extends Visualizer_Source_Csv {
97
  * @return array The re populated array of data or old one.
98
  */
99
  public function repopulateData( $data, $chart_id ) {
100
- return $this->_repopulate( $chart_id ) ? $this->_data : $data;
101
  }
102
 
103
  /**
@@ -111,7 +111,7 @@ class Visualizer_Source_Csv_Remote extends Visualizer_Source_Csv {
111
  * @return array The re populated array of series or old one.
112
  */
113
  public function repopulateSeries( $series, $chart_id ) {
114
- return $this->_repopulate( $chart_id ) ? $this->_series : $series;
115
  }
116
 
117
  /**
97
  * @return array The re populated array of data or old one.
98
  */
99
  public function repopulateData( $data, $chart_id ) {
100
+ return array_key_exists( 'data', $data ) ? $data['data'] : $data;
101
  }
102
 
103
  /**
111
  * @return array The re populated array of series or old one.
112
  */
113
  public function repopulateSeries( $series, $chart_id ) {
114
+ return $series;
115
  }
116
 
117
  /**
css/library.css CHANGED
@@ -287,3 +287,7 @@ input:checked + .visualizer-slider:before {
287
  font-weight: bold;
288
  text-align: center;
289
  }
 
 
 
 
287
  font-weight: bold;
288
  text-align: center;
289
  }
290
+
291
+ button.media-modal-close {
292
+ display: none !important;
293
+ }
css/media.css CHANGED
@@ -1,5 +1,5 @@
1
  /*
2
- Version: 3.0.7
3
  */
4
  #visualizer-library-view {
5
  padding: 30px 10px 10px 30px;
1
  /*
2
+ Version: 3.0.8
3
  */
4
  #visualizer-library-view {
5
  padding: 30px 10px 10px 30px;
index.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: Visualizer: Charts and Graphs Lite
5
  Plugin URI: https://themeisle.com/plugins/visualizer-charts-and-graphs-lite/
6
  Description: A simple, easy to use and quite powerful tool to create, manage and embed interactive charts into your WordPress posts and pages. The plugin uses Google Visualization API to render charts, which supports cross-browser compatibility (adopting VML for older IE versions) and cross-platform portability to iOS and new Android releases.
7
- Version: 3.0.7
8
  Author: Themeisle
9
  Author URI: http://themeisle.com
10
  License: GPL v2.0 or later
@@ -75,13 +75,17 @@ function visualizer_launch() {
75
  if ( ! defined( 'VISUALIZER_CSV_ENCLOSURE' ) ) {
76
  define( 'VISUALIZER_CSV_ENCLOSURE', '"' );
77
  }
 
 
 
 
78
  // instantiate the plugin
79
  $plugin = Visualizer_Plugin::instance();
80
  // set general modules
81
  $plugin->setModule( Visualizer_Module_Setup::NAME );
82
  $plugin->setModule( Visualizer_Module_Sources::NAME );
83
  $plugin->setModule( Visualizer_Module_Chart::NAME );
84
- if ( is_admin() ) {
85
  // set admin modules
86
  $plugin->setModule( Visualizer_Module_Admin::NAME );
87
  } else {
4
  Plugin Name: Visualizer: Charts and Graphs Lite
5
  Plugin URI: https://themeisle.com/plugins/visualizer-charts-and-graphs-lite/
6
  Description: A simple, easy to use and quite powerful tool to create, manage and embed interactive charts into your WordPress posts and pages. The plugin uses Google Visualization API to render charts, which supports cross-browser compatibility (adopting VML for older IE versions) and cross-platform portability to iOS and new Android releases.
7
+ Version: 3.0.8
8
  Author: Themeisle
9
  Author URI: http://themeisle.com
10
  License: GPL v2.0 or later
75
  if ( ! defined( 'VISUALIZER_CSV_ENCLOSURE' ) ) {
76
  define( 'VISUALIZER_CSV_ENCLOSURE', '"' );
77
  }
78
+ if ( ! defined( 'VISUALIZER_DEBUG' ) ) {
79
+ define( 'VISUALIZER_DEBUG', false );
80
+ }
81
+
82
  // instantiate the plugin
83
  $plugin = Visualizer_Plugin::instance();
84
  // set general modules
85
  $plugin->setModule( Visualizer_Module_Setup::NAME );
86
  $plugin->setModule( Visualizer_Module_Sources::NAME );
87
  $plugin->setModule( Visualizer_Module_Chart::NAME );
88
+ if ( is_admin() || defined( 'WP_TESTS_DOMAIN' ) ) {
89
  // set admin modules
90
  $plugin->setModule( Visualizer_Module_Admin::NAME );
91
  } else {
js/frame.js CHANGED
@@ -3,6 +3,13 @@
3
  /* global alert */
4
 
5
  (function ($) {
 
 
 
 
 
 
 
6
  $(document).ready(function () {
7
  init_permissions();
8
 
@@ -72,6 +79,10 @@
72
  return false;
73
  });
74
 
 
 
 
 
75
  });
76
 
77
  function init_permissions(){
3
  /* global alert */
4
 
5
  (function ($) {
6
+ $(window).load(function(){
7
+ // scroll to the selected chart type.
8
+ if($('label.type-label.type-label-selected').length > 0) {
9
+ $('label.type-label.type-label-selected')[0].scrollIntoView();
10
+ }
11
+ });
12
+
13
  $(document).ready(function () {
14
  init_permissions();
15
 
79
  return false;
80
  });
81
 
82
+ $('#cancel-button').click(function () {
83
+ $('#cancel-form').submit();
84
+ });
85
+
86
  });
87
 
88
  function init_permissions(){
readme.md CHANGED
@@ -144,6 +144,15 @@ Pay attention that to turn your shortcodes into graphs, your theme has to have `
144
  5. Charts library
145
 
146
  ## Changelog ##
 
 
 
 
 
 
 
 
 
147
  ### 3.0.7 - 2018-03-26 ###
148
 
149
  * Adds insert button in chart library.
144
  5. Charts library
145
 
146
  ## Changelog ##
147
+ ### 3.0.8 - 2018-06-27 ###
148
+
149
+ * Added revision support for the chart post type
150
+ * Added both % and Value to the Pie Slice
151
+ * Use the blog locale for Visualizer's options
152
+ * Fixed issue with data being fetched from the remote source every single time the chart was shown
153
+ * Fixed issue with scheduled charts not being updated if one of the scheduled charts is deleted
154
+
155
+
156
  ### 3.0.7 - 2018-03-26 ###
157
 
158
  * Adds insert button in chart library.
readme.txt CHANGED
@@ -144,6 +144,15 @@ Pay attention that to turn your shortcodes into graphs, your theme has to have `
144
  5. Charts library
145
 
146
  == Changelog ==
 
 
 
 
 
 
 
 
 
147
  = 3.0.7 - 2018-03-26 =
148
 
149
  * Adds insert button in chart library.
144
  5. Charts library
145
 
146
  == Changelog ==
147
+ = 3.0.8 - 2018-06-27 =
148
+
149
+ * Added revision support for the chart post type
150
+ * Added both % and Value to the Pie Slice
151
+ * Use the blog locale for Visualizer's options
152
+ * Fixed issue with data being fetched from the remote source every single time the chart was shown
153
+ * Fixed issue with scheduled charts not being updated if one of the scheduled charts is deleted
154
+
155
+
156
  = 3.0.7 - 2018-03-26 =
157
 
158
  * Adds insert button in chart library.
themeisle-hash.json CHANGED
@@ -1 +1 @@
1
- {"index.php":"87f5029281c969a4b6d0869f7594a04a"}
1
+ {"index.php":"9f0ab01e57b855474e893f3970fd7cff"}
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit180b25c8cbb6e9bfbb510080413f14ee::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInitbabe00cd6f6536e75c2f39c81b5f777b::getLoader();
vendor/autoload_52.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
 
7
- return ComposerAutoloaderInit80180cf529cca34ef51131272161f120::getLoader();
4
 
5
  require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
 
7
+ return ComposerAutoloaderInit7aed832689ae7a27f14633dbfa23d4bc::getLoader();
vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-feedback-deactivate.php CHANGED
@@ -515,12 +515,12 @@ if ( ! class_exists( 'ThemeIsle_SDK_Feedback_Deactivate' ) ) :
515
  return '<div id="' . $this->product->get_key() . '">'
516
  . '<ul class="ti-list">' . $list . '</ul>'
517
  . '<div class="actions">'
518
- . get_submit_button(
519
- $button_submit, 'secondary', $this->product->get_key() . 'ti-deactivate-yes', false, array(
520
- 'data-after-text' => $button_submit,
521
- 'disabled' => true,
522
- )
523
- )
524
  . get_submit_button( $button_cancel, 'primary', $this->product->get_key() . 'ti-deactivate-no', false )
525
  . '</div></div>';
526
  }
515
  return '<div id="' . $this->product->get_key() . '">'
516
  . '<ul class="ti-list">' . $list . '</ul>'
517
  . '<div class="actions">'
518
+ . get_submit_button(
519
+ $button_submit, 'secondary', $this->product->get_key() . 'ti-deactivate-yes', false, array(
520
+ 'data-after-text' => $button_submit,
521
+ 'disabled' => true,
522
+ )
523
+ )
524
  . get_submit_button( $button_cancel, 'primary', $this->product->get_key() . 'ti-deactivate-no', false )
525
  . '</div></div>';
526
  }
vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-licenser.php CHANGED
@@ -49,7 +49,8 @@ if ( ! class_exists( 'ThemeIsle_SDK_Licenser' ) ) :
49
  * @param ThemeIsle_SDK_Product $product The product object.
50
  */
51
  public function __construct( $product ) {
52
- $this->product = $product;
 
53
  $this->product_key = $this->product->get_key() . '-update-response';
54
  if ( ! $this->product->requires_license() ) {
55
  $this->license_key = 'free';
@@ -517,6 +518,39 @@ if ( ! class_exists( 'ThemeIsle_SDK_Licenser' ) ) :
517
  delete_transient( $this->product_key );
518
  }
519
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  /**
521
  * Check for updates
522
  *
@@ -525,45 +559,22 @@ if ( ! class_exists( 'ThemeIsle_SDK_Licenser' ) ) :
525
  function check_for_update() {
526
  $theme = wp_get_theme( $this->product->get_slug() );
527
  $update_data = get_transient( $this->product_key );
 
528
  if ( false === $update_data ) {
529
  $failed = false;
530
- if ( empty( $this->license_key ) ) {
531
- return false;
532
- }
533
- $api_params = array(
534
- 'edd_action' => 'get_version',
535
- 'version' => $this->product->get_version(),
536
- 'license' => $this->license_key,
537
- 'name' => $this->product->get_name(),
538
- 'slug' => $this->product->get_slug(),
539
- 'author' => $this->product->get_store_name(),
540
- 'url' => rawurlencode( home_url() ),
541
- );
542
- $response = wp_remote_post(
543
- $this->product->get_store_url(), array(
544
- 'timeout' => 15,
545
- 'sslverify' => false,
546
- 'body' => $api_params,
547
- )
548
- );
549
- // make sure the response was successful
550
- if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
551
- $failed = true;
552
- }
553
- $update_data = json_decode( wp_remote_retrieve_body( $response ) );
554
- if ( ! is_object( $update_data ) ) {
555
  $failed = true;
556
  }
557
- // if the response failed, try again in 30 minutes
558
  if ( $failed ) {
559
  $data = new stdClass;
560
  $data->new_version = $this->product->get_version();
561
  set_transient( $this->product_key, $data, strtotime( '+30 minutes' ) );
562
 
563
  return false;
564
- }
565
- // if the status is 'ok', return the update arguments
566
- if ( ! $failed ) {
567
  $update_data->sections = maybe_unserialize( $update_data->sections );
568
  set_transient( $this->product_key, $update_data, strtotime( '+12 hours' ) );
569
  }
@@ -618,34 +629,14 @@ if ( ! class_exists( 'ThemeIsle_SDK_Licenser' ) ) :
618
  * @return false||object
619
  */
620
  private function api_request( $_action = '', $_data = '' ) {
621
- if ( empty( $this->license_key ) ) {
622
- return;
623
- }
624
- $api_params = array(
625
- 'edd_action' => 'get_version',
626
- 'license' => $this->license_key,
627
- 'name' => rawurlencode( $this->product->get_name() ),
628
- 'slug' => rawurlencode( $this->product->get_slug() ),
629
- 'author' => $this->product->get_store_name(),
630
- 'url' => rawurlencode( home_url() ),
631
- );
632
- $request = wp_remote_post(
633
- $this->product->get_store_url(), array(
634
- 'timeout' => 15,
635
- 'sslverify' => false,
636
- 'body' => $api_params,
637
- )
638
- );
639
- if ( ! is_wp_error( $request ) ) :
640
- $request = json_decode( wp_remote_retrieve_body( $request ) );
641
- if ( $request && isset( $request->sections ) ) {
642
- $request->sections = maybe_unserialize( $request->sections );
643
- }
644
-
645
- return $request;
646
- else :
647
  return false;
648
- endif;
 
 
 
 
649
  }
650
 
651
  /**
49
  * @param ThemeIsle_SDK_Product $product The product object.
50
  */
51
  public function __construct( $product ) {
52
+ $this->product = $product;
53
+
54
  $this->product_key = $this->product->get_key() . '-update-response';
55
  if ( ! $this->product->requires_license() ) {
56
  $this->license_key = 'free';
518
  delete_transient( $this->product_key );
519
  }
520
 
521
+ /**
522
+ * Check remote api for latest version.
523
+ *
524
+ * @return bool|mixed Update api response.
525
+ */
526
+ private function get_version_data() {
527
+ $api_params = array(
528
+ 'edd_action' => 'get_version',
529
+ 'version' => $this->product->get_version(),
530
+ 'license' => $this->license_key,
531
+ 'name' => $this->product->get_name(),
532
+ 'slug' => $this->product->get_slug(),
533
+ 'author' => $this->product->get_store_name(),
534
+ 'url' => rawurlencode( home_url() ),
535
+ );
536
+ $response = wp_remote_post(
537
+ $this->product->get_store_url(), array(
538
+ 'timeout' => 15,
539
+ 'sslverify' => false,
540
+ 'body' => $api_params,
541
+ )
542
+ );
543
+ if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
544
+ return false;
545
+ }
546
+ $update_data = json_decode( wp_remote_retrieve_body( $response ) );
547
+ if ( ! is_object( $update_data ) ) {
548
+ return false;
549
+ }
550
+
551
+ return $update_data;
552
+ }
553
+
554
  /**
555
  * Check for updates
556
  *
559
  function check_for_update() {
560
  $theme = wp_get_theme( $this->product->get_slug() );
561
  $update_data = get_transient( $this->product_key );
562
+
563
  if ( false === $update_data ) {
564
  $failed = false;
565
+
566
+ $update_data = $this->get_version_data();
567
+ if ( empty( $update_data ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  $failed = true;
569
  }
570
+ // If the response failed, try again in 30 minutes.
571
  if ( $failed ) {
572
  $data = new stdClass;
573
  $data->new_version = $this->product->get_version();
574
  set_transient( $this->product_key, $data, strtotime( '+30 minutes' ) );
575
 
576
  return false;
577
+ } else {
 
 
578
  $update_data->sections = maybe_unserialize( $update_data->sections );
579
  set_transient( $this->product_key, $update_data, strtotime( '+12 hours' ) );
580
  }
629
  * @return false||object
630
  */
631
  private function api_request( $_action = '', $_data = '' ) {
632
+ $update_data = $this->get_version_data();
633
+ if ( empty( $update_data ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  return false;
635
+ }
636
+ if ( $update_data && isset( $update_data->sections ) ) {
637
+ $update_data->sections = maybe_unserialize( $update_data->sections );
638
+ }
639
+ return $update_data;
640
  }
641
 
642
  /**
vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-logger.php CHANGED
@@ -170,16 +170,16 @@ if ( ! class_exists( 'ThemeIsle_SDK_Logger' ) ) :
170
  return '<div >'
171
  . '<p>' . $heading . '</p>'
172
  . '<div class="actions">'
173
- . get_submit_button(
174
- $button_submit, 'primary ' . $this->product->get_key() . '-ti-logger', $this->product->get_key() . 'ti-logger-yes', false, array(
175
- 'data-ti-log-enable' => 1,
176
- )
177
- )
178
- . get_submit_button(
179
- $button_cancel, 'secondary ' . $this->product->get_key() . '-ti-logger', $this->product->get_key() . 'ti-logger-no', false, array(
180
- 'data-ti-log-enable' => 0,
181
- )
182
- )
183
  . '</div></div>';
184
  }
185
 
170
  return '<div >'
171
  . '<p>' . $heading . '</p>'
172
  . '<div class="actions">'
173
+ . get_submit_button(
174
+ $button_submit, 'primary ' . $this->product->get_key() . '-ti-logger', $this->product->get_key() . 'ti-logger-yes', false, array(
175
+ 'data-ti-log-enable' => 1,
176
+ )
177
+ )
178
+ . get_submit_button(
179
+ $button_cancel, 'secondary ' . $this->product->get_key() . '-ti-logger', $this->product->get_key() . 'ti-logger-no', false, array(
180
+ 'data-ti-log-enable' => 0,
181
+ )
182
+ )
183
  . '</div></div>';
184
  }
185
 
vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-product.php CHANGED
@@ -60,11 +60,38 @@ if ( ! class_exists( 'ThemeIsle_SDK_Product' ) ) :
60
  /**
61
  * @var array $allowed_authors The allowed authors.
62
  */
63
- private $allowed_authors = array( 'proteusthemes.com', 'anarieldesign.com', 'prothemedesign.com', 'cssigniter.com' );
 
 
 
 
 
64
  /**
65
  * @var array $allowed_external_products The allowed external_products.
66
  */
67
- private $allowed_products = array( 'zermatt', 'neto', 'olsen', 'benson', 'romero', 'carmack', 'puzzle', 'broadsheet', 'girlywp', 'veggie', 'zeko', 'maishawp', 'didi', 'liber', 'medicpress-pt', 'adrenaline-pt', 'consultpress-pt', 'legalpress-pt', 'gympress-pt', 'readable-pt', 'bolts-pt' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  /**
69
  * @var bool $requires_license Either user needs to activate it with license.
70
  */
@@ -285,6 +312,52 @@ if ( ! class_exists( 'ThemeIsle_SDK_Product' ) ) :
285
  return $versions;
286
  }
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  /**
289
  * Return theme versions.
290
  *
@@ -322,20 +395,20 @@ if ( ! class_exists( 'ThemeIsle_SDK_Product' ) ) :
322
  * @return array Array of versions.
323
  */
324
  private function get_api_versions() {
325
- if ( ! $this->is_wordpress_available() ) {
326
- return array();
327
- }
328
 
329
  $cache_key = $this->get_key() . '_' . preg_replace( '/[^0-9a-zA-Z ]/m', '', $this->version ) . 'versions';
330
  $cache_versions = get_transient( $this->get_key() . '_' . preg_replace( '/[^0-9a-zA-Z ]/m', '', $this->version ) . 'versions' );
331
  if ( false == $cache_versions ) {
332
  $versions = array();
333
- if ( $this->get_type() === 'plugin' ) {
334
- $versions = $this->get_plugin_versions();
335
- }
336
-
337
- if ( $this->get_type() === 'theme' ) {
338
- $versions = $this->get_theme_versions();
 
 
 
339
  }
340
  set_transient( $cache_key, $versions, MONTH_IN_SECONDS );
341
  } else {
60
  /**
61
  * @var array $allowed_authors The allowed authors.
62
  */
63
+ private $allowed_authors = array(
64
+ 'proteusthemes.com',
65
+ 'anarieldesign.com',
66
+ 'prothemedesign.com',
67
+ 'cssigniter.com',
68
+ );
69
  /**
70
  * @var array $allowed_external_products The allowed external_products.
71
  */
72
+ private $allowed_products = array(
73
+ 'zermatt',
74
+ 'neto',
75
+ 'olsen',
76
+ 'benson',
77
+ 'romero',
78
+ 'carmack',
79
+ 'puzzle',
80
+ 'broadsheet',
81
+ 'girlywp',
82
+ 'veggie',
83
+ 'zeko',
84
+ 'maishawp',
85
+ 'didi',
86
+ 'liber',
87
+ 'medicpress-pt',
88
+ 'adrenaline-pt',
89
+ 'consultpress-pt',
90
+ 'legalpress-pt',
91
+ 'gympress-pt',
92
+ 'readable-pt',
93
+ 'bolts-pt',
94
+ );
95
  /**
96
  * @var bool $requires_license Either user needs to activate it with license.
97
  */
312
  return $versions;
313
  }
314
 
315
+ /**
316
+ * @return string Return license key, if available.
317
+ */
318
+ private function get_license() {
319
+ $license_data = get_option( $this->get_key() . '_license_data', '' );
320
+
321
+ if ( empty( $license_data ) ) {
322
+ return '';
323
+ }
324
+ if ( ! isset( $license_data->key ) ) {
325
+ return '';
326
+ }
327
+
328
+ return $license_data->key;
329
+ }
330
+
331
+ /**
332
+ * @return array Array of available versions.
333
+ */
334
+ private function get_pro_versions() {
335
+ $license = $this->get_license();
336
+ $store_url = trailingslashit( $this->store_url );
337
+ $url = sprintf( '%s?edd_action=get_versions&name=%s&url=%s&license=%s', $store_url, urlencode( $this->get_name() ), urlencode( get_site_url() ), $license );
338
+ $response = wp_remote_get( $url );
339
+ if ( is_wp_error( $response ) ) {
340
+ return array();
341
+ }
342
+ $response = wp_remote_retrieve_body( $response );
343
+ $response = json_decode( $response );
344
+ if ( ! is_object( $response ) ) {
345
+ return array();
346
+ }
347
+ if ( ! isset( $response->versions ) ) {
348
+ return array();
349
+ }
350
+ $versions = array();
351
+ foreach ( $response->versions as $key => $version ) {
352
+ $versions[] = array(
353
+ 'version' => $version->version,
354
+ 'url' => $version->file,
355
+ );
356
+ }
357
+
358
+ return $versions;
359
+ }
360
+
361
  /**
362
  * Return theme versions.
363
  *
395
  * @return array Array of versions.
396
  */
397
  private function get_api_versions() {
 
 
 
398
 
399
  $cache_key = $this->get_key() . '_' . preg_replace( '/[^0-9a-zA-Z ]/m', '', $this->version ) . 'versions';
400
  $cache_versions = get_transient( $this->get_key() . '_' . preg_replace( '/[^0-9a-zA-Z ]/m', '', $this->version ) . 'versions' );
401
  if ( false == $cache_versions ) {
402
  $versions = array();
403
+ if ( ! $this->is_wordpress_available() ) {
404
+ $versions = $this->get_pro_versions();
405
+ } else {
406
+ if ( $this->get_type() === 'plugin' ) {
407
+ $versions = $this->get_plugin_versions();
408
+ }
409
+ if ( $this->get_type() === 'theme' ) {
410
+ $versions = $this->get_theme_versions();
411
+ }
412
  }
413
  set_transient( $cache_key, $versions, MONTH_IN_SECONDS );
414
  } else {
vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-widget-dashboard-blog.php CHANGED
@@ -110,142 +110,156 @@ if ( ! class_exists( 'ThemeIsle_SDK_Widget_Dashboard_Blog' ) ) :
110
  $this->setup_feeds();
111
  ?>
112
  <style type="text/css">
113
- #themeisle ul {
114
- margin-bottom: 0px;
115
- }
116
-
117
  #themeisle ul li.ti-dw-recommend-item {
118
-
119
  padding-left: 7px;
120
  border-top: 1px solid #eee;
121
-
122
  margin-bottom: 0px;
123
  padding-top: 6px;
124
  }
125
-
126
  #themeisle h2.hndle {
127
- background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAYAAABWk2cPAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAALfSURBVEhL7VW/ayJBFL7/5ZqziBzIcSCHxUE4hAiCgnAQsBASOAikEAIeCAaCiJAyIBICQURYwoJoEUmxa6MWioWxURu30mq77b57s86acX+oOQhp8sFjdmbfzrfve2/efMI74IP0VTB0gz/th/8iNbQxOt0eOmoN6WgAn78cwJeoYcLf78KrSPVneU3isEQZoz0D3pt03jhDrDTERJXxWM3A5yAOIPmgce/t2IvUGFzjkDa+7C5RTx0gWbxG0v8bBXUG3fLReigk4rh55gtbsAfpGDfhVTS+VA19bYbH7BkqU/56AzNU7nrYpbI7qfbEcxfBkSOHJGO27Vk0htqEYoXvARdS+tsEJyAJY1GRkOxnGTdq2zuaRZOkJ1Wi1+h7ODlJB/l1kYRKZVyKhMwoSmUw5M4uWMhIcl9f1l1qB2m/aMkZQIE2d5BeDTGiSD0VfC4jZPmSKiO+LMJGuoRE1bkiOIW00CAdW3NuxzLmxhD1hyHmug7dbBRD9KdLc4dR6Zfgn0ffXN2EjVSMLIcOrWxuwiyyPhYGI7X0m/agqPeIib5+yit/LWILKYuUlrTa5kbMwjko7J0AXc1RLZwifRF58Us1YXMzYSMV5WQ5ZWsG5VnYaG1BxM7zKBRzOAkHac460gxK9sUnVBqbu9rhKCS9lRaq1/poCeUq7tL6uPnjSLeoBRo9/KXjslqP4Na1gbiQssiULK9gP+VVqPnFQMblnwi+WmTfIji5amLES1mn/mz9yCFVuddZdiFlIJlTK+JtH2/AaPMogzipzrZ+4yQ1qCL1JSaNDL6bf81y1aOb5R6Vhow6G1tPNK9Borl0R2NLRiEaxNEF9Wa3yrHBSarTbeHot7ySqVmYR4AucDYuaJyzkfptZ0e/FeGa03m3jOQPizCAo0QG9W2ktL5HgGt45JRgaBh1x9R1WHY0KCTnLZOW5Kw0miuZq01ITGa6Z18Db9I3xAfpm+IdSIF/du91gSA2+I8AAAAASUVORK5CYII=');
128
  background-repeat: no-repeat;
129
- background-position: 90% 50%;
130
- background-size: 29px;
131
  }
132
-
133
- .ti-dw-feed-item {
 
 
 
 
 
 
 
134
  display: flex;
135
  align-items: center;
 
 
 
 
136
  }
137
-
138
- .ti-dw-feed-item a {
139
- float: left;
140
- width: 89.9%;
141
  }
142
-
143
- .ti-dw-feed-item .ti-dw-day-container {
144
- width: 100%;
145
- letter-spacing: 3px;
146
- display: block;
147
  }
148
-
149
  .ti-dw-feed-item .ti-dw-month-container {
150
-
151
- width: 100%;
152
- display: block;
153
- font-weight: 600;
154
- padding: 0px;
155
- margin-top: -6px;
156
  text-transform: uppercase;
157
  font-size: 10px;
158
  letter-spacing: 1px;
 
159
  }
160
-
161
  .ti-dw-feed-item .ti-dw-date-container {
162
- float: left;
163
- min-height: 30px;
164
- margin-right: 0.1%;
165
- width: 10%;
 
166
  text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
168
-
169
  .ti-dw-recommend-item span {
170
  color: #72777c;
171
  }
172
-
173
  .ti-dw-powered-by {
174
- font-style: italic;
175
- text-align: right;
176
- margin-top:3px;
 
 
 
 
177
  }
178
 
 
179
  </style>
180
- <ul>
181
  <?php
182
  foreach ( $this->items as $item ) {
183
  ?>
184
- <li class="ti-dw-feed-item"><span class="ti-dw-date-container"><span
185
- class="ti-dw-day-container"><?php echo date( 'd', $item['date'] ); ?></span> <span
186
- class="ti-dw-month-container"><?php echo substr( date( 'M', $item['date'] ), 0, 3 ); ?></span></span><a
187
- href="
188
- <?php
189
- echo add_query_arg(
190
- array(
191
- 'utm_campaign' => 'feed',
192
- 'utm_medium' => 'dashboard_widget',
193
- ), $item['link']
194
- );
195
- ?>
196
- " target="_blank"><?php echo $item['title']; ?></a>
197
- <div class="clear"></div>
 
198
  </li>
199
  <?php
200
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
- $recommend = apply_filters( 'themeisle_sdk_recommend_plugin_or_theme', array() );
203
- if ( is_array( $recommend ) && ! empty( $recommend ) ) {
204
 
205
- $type = $recommend['type'];
206
- if ( ( $type == 'theme' && current_user_can( 'install_themes' ) ) || ( $type == 'plugin' && current_user_can( 'install_plugins' ) ) ) {
207
- add_thickbox();
208
  $url = add_query_arg(
209
  array(
210
- 'theme' => $recommend['slug'],
211
- ), network_admin_url( 'theme-install.php' )
 
212
  );
213
-
214
- if ( 'plugin' === $type ) {
215
-
216
- $url = add_query_arg(
217
- array(
218
- 'tab' => 'plugin-information',
219
- 'plugin' => $recommend['slug'],
220
- ), network_admin_url( 'plugin-install.php' )
221
- );
222
- }
223
- ?>
224
- <li class="ti-dw-recommend-item ">
225
  <span class="ti-dw-recommend"><?php echo apply_filters( 'themeisle_sdk_dashboard_popular_label', sprintf( 'Popular %s', ucwords( $type ) ) ); ?>
226
  : </span>
227
- <?php
228
- echo trim(
229
- str_replace(
230
- array(
231
- 'lite',
232
- 'Lite',
233
- ), '', $recommend['name']
234
- )
235
- );
236
- ?>
237
- (<a class="thickbox open-plugin-details-modal"
238
- href="<?php echo $url . '&TB_iframe=true&width=600&height=500'; ?>"><?php echo apply_filters( 'themeisle_sdk_dashboard_install_label', 'Install' ); ?></a>)
239
- </li>
240
- <li class="ti-dw-powered-by">
241
- Powered by <?php echo esc_attr( $this->product->get_friendly_name() ); ?>
242
- </li>
243
-
244
  <?php
245
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  }
247
- ?>
248
- </ul>
249
 
250
  <?php
251
 
110
  $this->setup_feeds();
111
  ?>
112
  <style type="text/css">
 
 
 
 
113
  #themeisle ul li.ti-dw-recommend-item {
 
114
  padding-left: 7px;
115
  border-top: 1px solid #eee;
 
116
  margin-bottom: 0px;
117
  padding-top: 6px;
118
  }
 
119
  #themeisle h2.hndle {
120
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3Ny4xMiA3Ny4xMiI+PHRpdGxlPkFzc2V0IDM8L3RpdGxlPjxwYXRoIGQ9Ik03NS43Niw0Ny42cS0xLjUsNi40Ni00LjA3LDkuMjZBMzYuMjIsMzYuMjIsMCwwLDEsNjMuMjYsNjNhNTEsNTEsMCwwLDEtOS43OSw0LjZoLS4xNXY0LjgyQTQuNjYsNC42NiwwLDAsMSw1Miw3NS43Niw0LjA3LDQuMDcsMCwwLDEsNDksNzcuMTJhNCw0LDAsMCwxLTIuMTgtLjYxQTQuOTQsNC45NCwwLDAsMSw0NS4xOSw3NWE0LjQyLDQuNDIsMCwwLDEtNy41NCwwLDQuOTQsNC45NCwwLDAsMS0xLjU4LDEuNSwzLjc1LDMuNzUsMCwwLDEtMiwuNjFBNC4yNyw0LjI3LDAsMCwxLDMwLjI3LDc1YTQuOTQsNC45NCwwLDAsMS0xLjU4LDEuNSw0LDQsMCwwLDEtMi4xOC42MSwzLjkzLDMuOTMsMCwwLDEtMy4xNi0xLjQzLDUuMyw1LjMsMCwwLDEtMS4yMS0zLjU0LDEzLjgxLDEzLjgxLDAsMCwxLTguNTgsMywxMy4yMywxMy4yMywwLDAsMS05LTMuNDdBMTguMjEsMTguMjEsMCwwLDAsOSw3MC43OWExNC4yNiwxNC4yNiwwLDAsMS03LjgzLTUuNDIsMTYuMTIsMTYuMTIsMCwwLDAsNS4xOS4wNywxMi44OSwxMi44OSwwLDAsMCw0LjktMS43M1EzLjE1LDU1LjQzLDEuMDUsNTAuNDZhMTgsMTgsMCwwLDEtMS02LjkzQTQwLDQwLDAsMCwxLDEuMjEsMzQsMjYuNTksMjYuNTksMCwwLDEsNS4yNywyNC4xUTguNDQsMTkuNDMsMTMsMTkuNDNhMy40MSwzLjQxLDAsMCwxLDMuNDcsMi4yNmMuNi0uOCwxLjItMS41NiwxLjgxLTIuMjZsLjQ1LS40NS43NS0uNzUsMS4wNS0xLjgxYTM1LjIsMzUuMiwwLDAsMSwyLjExLTMuMzIsMjguNTcsMjguNTcsMCwwLDEsOS4xMi03LjY4QTIzLjQsMjMuNCwwLDAsMSw0Mi45MywyLjU2YTIyLjIyLDIyLjIyLDAsMCwxLDIuNzEuMTVBOC4xNiw4LjE2LDAsMCwxLDUxLjgxLDAsOC42OSw4LjY5LDAsMCwxLDU4LDIuNDFhMS41MSwxLjUxLDAsMCwxLC4xNS42OGMwLC4yNS0uNTUuOTMtMS42NiwybC0uMy4zYTkuNjksOS42OSwwLDAsMSwxLjU4LjgzYy41NS4zNS44My42NS44My45YTEuNjIsMS42MiwwLDAsMS0uNiwxbC0uMTUuMTVxNi43OCw2LDguNTgsMTYuMjdBMy4zLDMuMywwLDAsMSw2OSwyMy4zNWE2LjQ4LDYuNDgsMCwwLDEsNC4yMiwyLjFxMy45MiwzLjMyLDMuOTIsMTEuOUE0Mi4wNSw0Mi4wNSwwLDAsMSw3NS43Niw0Ny42Wk01My42Miw1NC4wN2EyNCwyNCwwLDAsMS0xNCw0LjUyQTIxLjQxLDIxLjQxLDAsMCwxLDI3LDU0LjY3LDI0LjI4LDI0LjI4LDAsMCwxLDE4LjUzLDQ1YTI5LDI5LDAsMCwxLTMtMTMsMzEuNzgsMzEuNzgsMCwwLDEsLjkxLTcuNTNBMzIuMTcsMzIuMTcsMCwwLDAsMTEuOSw0MS4yN2EzMy41LDMzLjUsMCwwLDAsMi43OSwxMy40LDI5LjEsMjkuMSwwLDAsMCw3LjksMTAuODUsNC42OCw0LjY4LDAsMCwxLDEuNjYtMS44OCw0LjE3LDQuMTcsMCwwLDEsNC40NC0uMDgsNC41LDQuNSwwLDAsMSwxLjU4LDEuNjZBNC4yLDQuMiwwLDAsMSwzNCw2M2EzLjgzLDMuODMsMCwwLDEsMiwuNiw0LjUsNC41LDAsMCwxLDEuNTgsMS42Niw0LjI3LDQuMjcsMCwwLDEsNy41NCwwLDQuNSw0LjUsMCwwLDEsMS41OC0xLjY2LDQuMTcsNC4xNywwLDAsMSw0LjQ0LjA4LDMuODYsMy44NiwwLDAsMSwxLjUxLDEuNzMsMzAuMTcsMzAuMTcsMCwwLDAsNy42OC05Ljk0LDMxLjE4LDMxLjE4LDAsMCwwLDMuMTYtMTIuMzVBMjguMzksMjguMzksMCwwLDEsNTMuNjIsNTQuMDdaTTI4Ljc3LDY1LjM3YTMuMSwzLjEsMCwwLDAtNC4zNywwLDMuMDYsMy4wNiwwLDAsMC0uOSwyLjI2djQuODJhMy4zMiwzLjMyLDAsMCwwLC45LDIuMzMsMi45MywyLjkzLDAsMCwwLDQuMzcsMCwzLjMyLDMuMzIsMCwwLDAsLjktMi4zM1Y2Ny42M0EzLjA2LDMuMDYsMCwwLDAsMjguNzcsNjUuMzdabS45LTQ4YTQuMjQsNC4yNCwwLDAsMCwzLjQ3LDEuNzMsNC40NSw0LjQ1LDAsMCwwLDMuNTQtMS43Myw2LDYsMCwwLDAsMS40My0zLjkyLDUuNyw1LjcsMCwwLDAtMS40My0zLjg0QTQuNTMsNC41MywwLDAsMCwzMy4xNCw4YTQuMzEsNC4zMSwwLDAsMC0zLjQ3LDEuNjYsNS43OSw1Ljc5LDAsMCwwLTEuMzUsMy43N0E2LjMzLDYuMzMsMCwwLDAsMjkuNjcsMTcuNFptNi40OCw0OGEzLjEsMy4xLDAsMCwwLTQuMzcsMCwzLjA2LDMuMDYsMCwwLDAtLjksMi4yNnY0LjgyYTMuMzIsMy4zMiwwLDAsMCwuOSwyLjMzLDIuOTMsMi45MywwLDAsMCw0LjM3LDAsMy4zMiwzLjMyLDAsMCwwLC45LTIuMzNWNjcuNjNBMy4wNiwzLjA2LDAsMCwwLDM2LjE1LDY1LjM3Wm0tNC4wNy01NC4zYTIuMzcsMi4zNywwLDAsMSwyLTEsMi4xNywyLjE3LDAsMCwxLDEuODgsMSw0LjEsNC4xLDAsMCwxLDAsNC41MiwyLjE3LDIuMTcsMCwwLDEtMS44OCwxLDIuMzcsMi4zNywwLDAsMS0yLTEsMy43MiwzLjcyLDAsMCwxLS43NS0yLjM0QTMuNDksMy40OSwwLDAsMSwzMi4wOCwxMS4wN1ptNSw5LjQxYTIwLjYxLDIwLjYxLDAsMCwwLTMuNTQsMTIuMjgsMTcuMTUsMTcuMTUsMCwwLDAsNC4wNywxMSwxMi40MywxMi40MywwLDAsMCw5Ljg3LDQuNDUsMTUuMywxNS4zLDAsMCwwLDktMywxMywxMywwLDAsMCwuNi0zLjMycTAtMy4zMi0zLjE2LTMuMzFhOC41OCw4LjU4LDAsMCwwLTIsLjNxLTcuODMsMS41LTExLjU5LS4zLTQuNTMtMi4xMi00LjUyLTkuMzRBMzcuOTIsMzcuOTIsMCwwLDEsMzcuMDUsMjAuNDhaTTYxLjQ1LDE3QTEyLjg0LDEyLjg0LDAsMCwwLDUxLjIxLDEyLjJhMTUuMTEsMTUuMTEsMCwwLDAtNi40LDEuNDMsMTcuODMsMTcuODMsMCwwLDAtNS41LDRxLTYuMTgsMjAuNjQsNi4xOCwyMC42NEEyNC43NSwyNC43NSwwLDAsMCw0OSwzNy44MWEzOC45MiwzOC45MiwwLDAsMSw0LjgyLS40NmMxLjgxLDAsMy4wNi40NiwzLjc3LDEuMzZxMS4zNSwxLjUuNzUsNS4xMmEyMS43MiwyMS43MiwwLDAsMCw1LTcuMTVBMjEuMzgsMjEuMzgsMCwwLDAsNjUuMDcsMjgsMTcuNDQsMTcuNDQsMCwwLDAsNjEuNDUsMTdaTTQzLjYsNjUuMzdhMi43MiwyLjcyLDAsMCwwLTIuMS0uOSwzLDMsMCwwLDAtMi4xOS45LDMuMDYsMy4wNiwwLDAsMC0uOSwyLjI2djQuODJhMy4zMiwzLjMyLDAsMCwwLC45LDIuMzMsMi44NCwyLjg0LDAsMCwwLDIuMTksMSwyLjYxLDIuNjEsMCwwLDAsMi4xLTEsMy40NSwzLjQ1LDAsMCwwLC44My0yLjMzVjY3LjYzQTMuMTcsMy4xNywwLDAsMCw0My42LDY1LjM3Wm03LjQ2LDBhMywzLDAsMCwwLTIuMTgtLjksMi43MywyLjczLDAsMCwwLTIuMTEuOSwzLjE3LDMuMTcsMCwwLDAtLjgzLDIuMjZ2NC44MmEzLjQ1LDMuNDUsMCwwLDAsLjgzLDIuMzMsMi42MSwyLjYxLDAsMCwwLDIuMTEsMSwyLjgzLDIuODMsMCwwLDAsMi4xOC0xLDMuMzIsMy4zMiwwLDAsMCwuOS0yLjMzVjY3LjYzQTMuMDYsMy4wNiwwLDAsMCw1MS4wNiw2NS4zN1oiLz48L3N2Zz4=');
121
  background-repeat: no-repeat;
122
+ background-position: 92% 50%;
123
+ background-size: 30px;
124
  }
125
+ #themeisle .inside {
126
+ padding: 0;
127
+ }
128
+ .ti-feed-list {
129
+ padding: 0 12px 5px;
130
+ margin-bottom: 10px;
131
+ border-bottom: 1px solid #eee;
132
+ }
133
+ .ti-dw-feed-item a {
134
  display: flex;
135
  align-items: center;
136
+ margin-bottom: 5px;
137
+ padding: 5px;
138
+ transition: .2s ease;
139
+ border-radius: 3px;
140
  }
141
+ .ti-dw-feed-item a:hover {
142
+ background-color: #f8f8f8;
 
 
143
  }
144
+ .ti-dw-feed-item a:hover .ti-dw-date-container {
145
+ opacity: .9;
 
 
 
146
  }
 
147
  .ti-dw-feed-item .ti-dw-month-container {
148
+ margin-top: -5px;
 
 
 
 
 
149
  text-transform: uppercase;
150
  font-size: 10px;
151
  letter-spacing: 1px;
152
+ font-weight: 700;
153
  }
 
154
  .ti-dw-feed-item .ti-dw-date-container {
155
+ border-radius: 3px;
156
+ transition: .2s ease;
157
+ min-height: 35px;
158
+ margin-right: 5px;
159
+ min-width: 35px;
160
  text-align: center;
161
+ border: 1px solid #2a6f97;
162
+ color: #fff;
163
+ background: #2ea2cc;
164
+ display: flex;
165
+ flex-direction: column;
166
+ justify-content: center;
167
+ }
168
+ .ti-dw-footer {
169
+ padding: 0 12px 5px;
170
+ text-align: center;
171
+ }
172
+ .ti-dw-recommend-item {
173
+ display: block;
174
  }
 
175
  .ti-dw-recommend-item span {
176
  color: #72777c;
177
  }
 
178
  .ti-dw-powered-by {
179
+ font-size: 11px;
180
+ margin-top: 3px;
181
+ display: block;
182
+ color: #72777c;
183
+ }
184
+ .ti-dw-powered-by span {
185
+ font-weight: 600;
186
  }
187
 
188
+
189
  </style>
190
+ <ul class="ti-feed-list">
191
  <?php
192
  foreach ( $this->items as $item ) {
193
  ?>
194
+ <li class="ti-dw-feed-item">
195
+ <a href="
196
+ <?php
197
+ echo add_query_arg(
198
+ array(
199
+ 'utm_campaign' => 'feed',
200
+ 'utm_medium' => 'dashboard_widget',
201
+ ), $item['link']
202
+ );
203
+ ?>
204
+ " target="_blank">
205
+ <span class="ti-dw-date-container"><span
206
+ class="ti-dw-day-container"><?php echo date( 'd', $item['date'] ); ?></span> <span
207
+ class="ti-dw-month-container"><?php echo substr( date( 'M', $item['date'] ), 0, 3 ); ?></span></span><?php echo $item['title']; ?>
208
+ </a>
209
  </li>
210
  <?php
211
  }
212
+ ?>
213
+ </ul>
214
+ <?php
215
+ $recommend = apply_filters( 'themeisle_sdk_recommend_plugin_or_theme', array() );
216
+ if ( is_array( $recommend ) && ! empty( $recommend ) ) {
217
+
218
+ $type = $recommend['type'];
219
+ if ( ( $type == 'theme' && current_user_can( 'install_themes' ) ) || ( $type == 'plugin' && current_user_can( 'install_plugins' ) ) ) {
220
+ add_thickbox();
221
+ $url = add_query_arg(
222
+ array(
223
+ 'theme' => $recommend['slug'],
224
+ ), network_admin_url( 'theme-install.php' )
225
+ );
226
 
227
+ if ( 'plugin' === $type ) {
 
228
 
 
 
 
229
  $url = add_query_arg(
230
  array(
231
+ 'tab' => 'plugin-information',
232
+ 'plugin' => $recommend['slug'],
233
+ ), network_admin_url( 'plugin-install.php' )
234
  );
235
+ }
236
+ ?>
237
+ <div class="ti-dw-footer">
238
+ <span class="ti-dw-recommend-item ">
 
 
 
 
 
 
 
 
239
  <span class="ti-dw-recommend"><?php echo apply_filters( 'themeisle_sdk_dashboard_popular_label', sprintf( 'Popular %s', ucwords( $type ) ) ); ?>
240
  : </span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  <?php
242
+ echo trim(
243
+ str_replace(
244
+ array(
245
+ 'lite',
246
+ 'Lite',
247
+ ), '', $recommend['name']
248
+ )
249
+ );
250
+ ?>
251
+ (<a class="thickbox open-plugin-details-modal"
252
+ href="<?php echo $url . '&TB_iframe=true&width=600&height=500'; ?>"><?php echo apply_filters( 'themeisle_sdk_dashboard_install_label', 'Install' ); ?></a>)
253
+ </span>
254
+ <span class="ti-dw-powered-by">
255
+ Powered by <span><?php echo esc_attr( $this->product->get_friendly_name() ); ?></span>
256
+ </span>
257
+ </div>
258
+
259
+ <?php
260
  }
261
+ }
262
+ ?>
263
 
264
  <?php
265
 
vendor/codeinwp/themeisle-sdk/load.php CHANGED
@@ -11,7 +11,7 @@
11
  */
12
 
13
  // Current SDK version and path.
14
- $themeisle_sdk_version = '2.1.2';
15
  $themeisle_sdk_path = dirname( __FILE__ );
16
 
17
  global $themeisle_sdk_max_version;
11
  */
12
 
13
  // Current SDK version and path.
14
+ $themeisle_sdk_version = '2.2.5';
15
  $themeisle_sdk_path = dirname( __FILE__ );
16
 
17
  global $themeisle_sdk_max_version;
vendor/composer/autoload_namespaces.php CHANGED
@@ -8,4 +8,5 @@ $baseDir = dirname($vendorDir);
8
  return array(
9
  'xrstf\\Composer52' => array($vendorDir . '/xrstf/composer-php52/lib'),
10
  'PHPExcel' => array($vendorDir . '/phpoffice/phpexcel/Classes'),
 
11
  );
8
  return array(
9
  'xrstf\\Composer52' => array($vendorDir . '/xrstf/composer-php52/lib'),
10
  'PHPExcel' => array($vendorDir . '/phpoffice/phpexcel/Classes'),
11
+ 'ForceUTF8\\' => array($vendorDir . '/neitanod/forceutf8/src'),
12
  );
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit180b25c8cbb6e9bfbb510080413f14ee
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit180b25c8cbb6e9bfbb510080413f14ee
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit180b25c8cbb6e9bfbb510080413f14ee', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit180b25c8cbb6e9bfbb510080413f14ee', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
@@ -42,14 +42,14 @@ class ComposerAutoloaderInit180b25c8cbb6e9bfbb510080413f14ee
42
 
43
  $includeFiles = require __DIR__ . '/autoload_files.php';
44
  foreach ($includeFiles as $fileIdentifier => $file) {
45
- composerRequire180b25c8cbb6e9bfbb510080413f14ee($fileIdentifier, $file);
46
  }
47
 
48
  return $loader;
49
  }
50
  }
51
 
52
- function composerRequire180b25c8cbb6e9bfbb510080413f14ee($fileIdentifier, $file)
53
  {
54
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
55
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInitbabe00cd6f6536e75c2f39c81b5f777b
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInitbabe00cd6f6536e75c2f39c81b5f777b', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitbabe00cd6f6536e75c2f39c81b5f777b', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
42
 
43
  $includeFiles = require __DIR__ . '/autoload_files.php';
44
  foreach ($includeFiles as $fileIdentifier => $file) {
45
+ composerRequirebabe00cd6f6536e75c2f39c81b5f777b($fileIdentifier, $file);
46
  }
47
 
48
  return $loader;
49
  }
50
  }
51
 
52
+ function composerRequirebabe00cd6f6536e75c2f39c81b5f777b($fileIdentifier, $file)
53
  {
54
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
55
  require $file;
vendor/composer/autoload_real_52.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real_52.php generated by xrstf/composer-php52
4
 
5
- class ComposerAutoloaderInit80180cf529cca34ef51131272161f120 {
6
  private static $loader;
7
 
8
  public static function loadClassLoader($class) {
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit80180cf529cca34ef51131272161f120 {
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit80180cf529cca34ef51131272161f120', 'loadClassLoader'), true /*, true */);
23
  self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit80180cf529cca34ef51131272161f120', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(dirname(__FILE__));
27
  $baseDir = dirname($vendorDir);
2
 
3
  // autoload_real_52.php generated by xrstf/composer-php52
4
 
5
+ class ComposerAutoloaderInit7aed832689ae7a27f14633dbfa23d4bc {
6
  private static $loader;
7
 
8
  public static function loadClassLoader($class) {
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInit7aed832689ae7a27f14633dbfa23d4bc', 'loadClassLoader'), true /*, true */);
23
  self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit7aed832689ae7a27f14633dbfa23d4bc', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(dirname(__FILE__));
27
  $baseDir = dirname($vendorDir);
vendor/composer/installed.json CHANGED
@@ -6,15 +6,15 @@
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/Codeinwp/themeisle-sdk.git",
9
- "reference": "18bcad4b82796f4b6996dea42832ce3518924f25"
10
  },
11
  "dist": {
12
  "type": "zip",
13
- "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/18bcad4b82796f4b6996dea42832ce3518924f25",
14
- "reference": "18bcad4b82796f4b6996dea42832ce3518924f25",
15
  "shasum": ""
16
  },
17
- "time": "2018-03-19 21:12:50",
18
  "type": "library",
19
  "installation-source": "dist",
20
  "autoload": {
@@ -131,5 +131,41 @@
131
  "xlsx"
132
  ],
133
  "abandoned": "phpoffice/phpspreadsheet"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  }
135
  ]
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/Codeinwp/themeisle-sdk.git",
9
+ "reference": "33d470402379047bc430c39ecab9a8a4a850d0d2"
10
  },
11
  "dist": {
12
  "type": "zip",
13
+ "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/33d470402379047bc430c39ecab9a8a4a850d0d2",
14
+ "reference": "33d470402379047bc430c39ecab9a8a4a850d0d2",
15
  "shasum": ""
16
  },
17
+ "time": "2018-06-06 14:49:32",
18
  "type": "library",
19
  "installation-source": "dist",
20
  "autoload": {
131
  "xlsx"
132
  ],
133
  "abandoned": "phpoffice/phpspreadsheet"
134
+ },
135
+ {
136
+ "name": "neitanod/forceutf8",
137
+ "version": "v2.0.1",
138
+ "version_normalized": "2.0.1.0",
139
+ "source": {
140
+ "type": "git",
141
+ "url": "https://github.com/neitanod/forceutf8.git",
142
+ "reference": "47c883ab2739e7938a8bb0bfd1c29d48c88858de"
143
+ },
144
+ "dist": {
145
+ "type": "zip",
146
+ "url": "https://api.github.com/repos/neitanod/forceutf8/zipball/47c883ab2739e7938a8bb0bfd1c29d48c88858de",
147
+ "reference": "47c883ab2739e7938a8bb0bfd1c29d48c88858de",
148
+ "shasum": ""
149
+ },
150
+ "require": {
151
+ "php": ">=5.3.0"
152
+ },
153
+ "time": "2017-05-22 18:50:57",
154
+ "type": "library",
155
+ "installation-source": "dist",
156
+ "autoload": {
157
+ "psr-0": {
158
+ "ForceUTF8\\": "src/"
159
+ }
160
+ },
161
+ "notification-url": "https://packagist.org/downloads/",
162
+ "authors": [
163
+ {
164
+ "name": "Sebastián Grignoli",
165
+ "email": "grignoli@gmail.com"
166
+ }
167
+ ],
168
+ "description": "PHP Class Encoding featuring popular Encoding::toUTF8() function --formerly known as forceUTF8()-- that fixes mixed encoded strings.",
169
+ "homepage": "https://github.com/neitanod/forceutf8"
170
  }
171
  ]
vendor/neitanod/forceutf8/README.md ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ forceutf8
2
+ =========
3
+
4
+ PHP Class Encoding featuring popular \ForceUTF8\Encoding::toUTF8() function --formerly known as forceUTF8()-- that fixes mixed encoded strings.
5
+
6
+ Description
7
+ ===========
8
+
9
+ If you apply the PHP function utf8_encode() to an already-UTF8 string it will return a garbled UTF8 string.
10
+
11
+ This class addresses this issue and provides a handy static function called \ForceUTF8\Encoding::toUTF8().
12
+
13
+ You don't need to know what the encoding of your strings is. It can be Latin1 (iso 8859-1), Windows-1252 or UTF8, or the string can have a mix of them. \ForceUTF8\Encoding::toUTF8() will convert everything to UTF8.
14
+
15
+ Sometimes you have to deal with services that are unreliable in terms of encoding, possibly mixing UTF8 and Latin1 in the same string.
16
+
17
+ Update:
18
+
19
+ I've included another function, \ForceUTF8\Encoding::fixUTF8(), which will fix the double (or multiple) encoded UTF8 string that looks garbled.
20
+
21
+ Usage:
22
+ ======
23
+
24
+ use \ForceUTF8\Encoding;
25
+
26
+ $utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);
27
+
28
+ $latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);
29
+
30
+ also:
31
+
32
+ $utf8_string = Encoding::fixUTF8($garbled_utf8_string);
33
+
34
+ Examples:
35
+
36
+ use \ForceUTF8\Encoding;
37
+
38
+ echo Encoding::fixUTF8("Fédération Camerounaise de Football\n");
39
+ echo Encoding::fixUTF8("Fédération Camerounaise de Football\n");
40
+ echo Encoding::fixUTF8("Fédération Camerounaise de Football\n");
41
+ echo Encoding::fixUTF8("Fédération Camerounaise de Football\n");
42
+
43
+ will output:
44
+
45
+ Fédération Camerounaise de Football
46
+ Fédération Camerounaise de Football
47
+ Fédération Camerounaise de Football
48
+ Fédération Camerounaise de Football
49
+
50
+ Install via composer:
51
+ =====================
52
+ Edit your composer.json file to include the following:
53
+
54
+ ```json
55
+ {
56
+ "require": {
57
+ "neitanod/forceutf8": "~2.0"
58
+ }
59
+ }
60
+ ```
61
+
vendor/neitanod/forceutf8/composer.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "neitanod/forceutf8",
3
+ "homepage": "https://github.com/neitanod/forceutf8",
4
+ "type": "library",
5
+ "description": "PHP Class Encoding featuring popular Encoding::toUTF8() function --formerly known as forceUTF8()-- that fixes mixed encoded strings.",
6
+ "require": {
7
+ "php": ">=5.3.0"
8
+ },
9
+ "authors": [
10
+ {
11
+ "name": "Sebastián Grignoli",
12
+ "email": "grignoli@gmail.com"
13
+ }
14
+ ],
15
+ "autoload": {
16
+ "psr-0": {
17
+ "ForceUTF8\\": "src/"
18
+ }
19
+ }
20
+ }
vendor/neitanod/forceutf8/src/ForceUTF8/Encoding.php ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright (c) 2008 Sebastián Grignoli
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions
8
+ are met:
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+ 3. Neither the name of copyright holders nor the names of its
15
+ contributors may be used to endorse or promote products derived
16
+ from this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
22
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ /**
32
+ * @author "Sebastián Grignoli" <grignoli@gmail.com>
33
+ * @package Encoding
34
+ * @version 2.0
35
+ * @link https://github.com/neitanod/forceutf8
36
+ * @example https://github.com/neitanod/forceutf8
37
+ * @license Revised BSD
38
+ */
39
+
40
+ namespace ForceUTF8;
41
+
42
+ class Encoding {
43
+
44
+ const ICONV_TRANSLIT = "TRANSLIT";
45
+ const ICONV_IGNORE = "IGNORE";
46
+ const WITHOUT_ICONV = "";
47
+
48
+ protected static $win1252ToUtf8 = array(
49
+ 128 => "\xe2\x82\xac",
50
+
51
+ 130 => "\xe2\x80\x9a",
52
+ 131 => "\xc6\x92",
53
+ 132 => "\xe2\x80\x9e",
54
+ 133 => "\xe2\x80\xa6",
55
+ 134 => "\xe2\x80\xa0",
56
+ 135 => "\xe2\x80\xa1",
57
+ 136 => "\xcb\x86",
58
+ 137 => "\xe2\x80\xb0",
59
+ 138 => "\xc5\xa0",
60
+ 139 => "\xe2\x80\xb9",
61
+ 140 => "\xc5\x92",
62
+
63
+ 142 => "\xc5\xbd",
64
+
65
+
66
+ 145 => "\xe2\x80\x98",
67
+ 146 => "\xe2\x80\x99",
68
+ 147 => "\xe2\x80\x9c",
69
+ 148 => "\xe2\x80\x9d",
70
+ 149 => "\xe2\x80\xa2",
71
+ 150 => "\xe2\x80\x93",
72
+ 151 => "\xe2\x80\x94",
73
+ 152 => "\xcb\x9c",
74
+ 153 => "\xe2\x84\xa2",
75
+ 154 => "\xc5\xa1",
76
+ 155 => "\xe2\x80\xba",
77
+ 156 => "\xc5\x93",
78
+
79
+ 158 => "\xc5\xbe",
80
+ 159 => "\xc5\xb8"
81
+ );
82
+
83
+ protected static $brokenUtf8ToUtf8 = array(
84
+ "\xc2\x80" => "\xe2\x82\xac",
85
+
86
+ "\xc2\x82" => "\xe2\x80\x9a",
87
+ "\xc2\x83" => "\xc6\x92",
88
+ "\xc2\x84" => "\xe2\x80\x9e",
89
+ "\xc2\x85" => "\xe2\x80\xa6",
90
+ "\xc2\x86" => "\xe2\x80\xa0",
91
+ "\xc2\x87" => "\xe2\x80\xa1",
92
+ "\xc2\x88" => "\xcb\x86",
93
+ "\xc2\x89" => "\xe2\x80\xb0",
94
+ "\xc2\x8a" => "\xc5\xa0",
95
+ "\xc2\x8b" => "\xe2\x80\xb9",
96
+ "\xc2\x8c" => "\xc5\x92",
97
+
98
+ "\xc2\x8e" => "\xc5\xbd",
99
+
100
+
101
+ "\xc2\x91" => "\xe2\x80\x98",
102
+ "\xc2\x92" => "\xe2\x80\x99",
103
+ "\xc2\x93" => "\xe2\x80\x9c",
104
+ "\xc2\x94" => "\xe2\x80\x9d",
105
+ "\xc2\x95" => "\xe2\x80\xa2",
106
+ "\xc2\x96" => "\xe2\x80\x93",
107
+ "\xc2\x97" => "\xe2\x80\x94",
108
+ "\xc2\x98" => "\xcb\x9c",
109
+ "\xc2\x99" => "\xe2\x84\xa2",
110
+ "\xc2\x9a" => "\xc5\xa1",
111
+ "\xc2\x9b" => "\xe2\x80\xba",
112
+ "\xc2\x9c" => "\xc5\x93",
113
+
114
+ "\xc2\x9e" => "\xc5\xbe",
115
+ "\xc2\x9f" => "\xc5\xb8"
116
+ );
117
+
118
+ protected static $utf8ToWin1252 = array(
119
+ "\xe2\x82\xac" => "\x80",
120
+
121
+ "\xe2\x80\x9a" => "\x82",
122
+ "\xc6\x92" => "\x83",
123
+ "\xe2\x80\x9e" => "\x84",
124
+ "\xe2\x80\xa6" => "\x85",
125
+ "\xe2\x80\xa0" => "\x86",
126
+ "\xe2\x80\xa1" => "\x87",
127
+ "\xcb\x86" => "\x88",
128
+ "\xe2\x80\xb0" => "\x89",
129
+ "\xc5\xa0" => "\x8a",
130
+ "\xe2\x80\xb9" => "\x8b",
131
+ "\xc5\x92" => "\x8c",
132
+
133
+ "\xc5\xbd" => "\x8e",
134
+
135
+
136
+ "\xe2\x80\x98" => "\x91",
137
+ "\xe2\x80\x99" => "\x92",
138
+ "\xe2\x80\x9c" => "\x93",
139
+ "\xe2\x80\x9d" => "\x94",
140
+ "\xe2\x80\xa2" => "\x95",
141
+ "\xe2\x80\x93" => "\x96",
142
+ "\xe2\x80\x94" => "\x97",
143
+ "\xcb\x9c" => "\x98",
144
+ "\xe2\x84\xa2" => "\x99",
145
+ "\xc5\xa1" => "\x9a",
146
+ "\xe2\x80\xba" => "\x9b",
147
+ "\xc5\x93" => "\x9c",
148
+
149
+ "\xc5\xbe" => "\x9e",
150
+ "\xc5\xb8" => "\x9f"
151
+ );
152
+
153
+ static function toUTF8($text){
154
+ /**
155
+ * Function \ForceUTF8\Encoding::toUTF8
156
+ *
157
+ * This function leaves UTF8 characters alone, while converting almost all non-UTF8 to UTF8.
158
+ *
159
+ * It assumes that the encoding of the original string is either Windows-1252 or ISO 8859-1.
160
+ *
161
+ * It may fail to convert characters to UTF-8 if they fall into one of these scenarios:
162
+ *
163
+ * 1) when any of these characters: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
164
+ * are followed by any of these: ("group B")
165
+ * ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶•¸¹º»¼½¾¿
166
+ * For example: %ABREPRESENT%C9%BB. «REPRESENTÉ»
167
+ * The "«" (%AB) character will be converted, but the "É" followed by "»" (%C9%BB)
168
+ * is also a valid unicode character, and will be left unchanged.
169
+ *
170
+ * 2) when any of these: àáâãäåæçèéêëìíîï are followed by TWO chars from group B,
171
+ * 3) when any of these: ðñòó are followed by THREE chars from group B.
172
+ *
173
+ * @name toUTF8
174
+ * @param string $text Any string.
175
+ * @return string The same string, UTF8 encoded
176
+ *
177
+ */
178
+
179
+ if(is_array($text))
180
+ {
181
+ foreach($text as $k => $v)
182
+ {
183
+ $text[$k] = self::toUTF8($v);
184
+ }
185
+ return $text;
186
+ }
187
+
188
+ if(!is_string($text)) {
189
+ return $text;
190
+ }
191
+
192
+ $max = self::strlen($text);
193
+
194
+ $buf = "";
195
+ for($i = 0; $i < $max; $i++){
196
+ $c1 = $text{$i};
197
+ if($c1>="\xc0"){ //Should be converted to UTF8, if it's not UTF8 already
198
+ $c2 = $i+1 >= $max? "\x00" : $text{$i+1};
199
+ $c3 = $i+2 >= $max? "\x00" : $text{$i+2};
200
+ $c4 = $i+3 >= $max? "\x00" : $text{$i+3};
201
+ if($c1 >= "\xc0" & $c1 <= "\xdf"){ //looks like 2 bytes UTF8
202
+ if($c2 >= "\x80" && $c2 <= "\xbf"){ //yeah, almost sure it's UTF8 already
203
+ $buf .= $c1 . $c2;
204
+ $i++;
205
+ } else { //not valid UTF8. Convert it.
206
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
207
+ $cc2 = ($c1 & "\x3f") | "\x80";
208
+ $buf .= $cc1 . $cc2;
209
+ }
210
+ } elseif($c1 >= "\xe0" & $c1 <= "\xef"){ //looks like 3 bytes UTF8
211
+ if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf"){ //yeah, almost sure it's UTF8 already
212
+ $buf .= $c1 . $c2 . $c3;
213
+ $i = $i + 2;
214
+ } else { //not valid UTF8. Convert it.
215
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
216
+ $cc2 = ($c1 & "\x3f") | "\x80";
217
+ $buf .= $cc1 . $cc2;
218
+ }
219
+ } elseif($c1 >= "\xf0" & $c1 <= "\xf7"){ //looks like 4 bytes UTF8
220
+ if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf" && $c4 >= "\x80" && $c4 <= "\xbf"){ //yeah, almost sure it's UTF8 already
221
+ $buf .= $c1 . $c2 . $c3 . $c4;
222
+ $i = $i + 3;
223
+ } else { //not valid UTF8. Convert it.
224
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
225
+ $cc2 = ($c1 & "\x3f") | "\x80";
226
+ $buf .= $cc1 . $cc2;
227
+ }
228
+ } else { //doesn't look like UTF8, but should be converted
229
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
230
+ $cc2 = (($c1 & "\x3f") | "\x80");
231
+ $buf .= $cc1 . $cc2;
232
+ }
233
+ } elseif(($c1 & "\xc0") == "\x80"){ // needs conversion
234
+ if(isset(self::$win1252ToUtf8[ord($c1)])) { //found in Windows-1252 special cases
235
+ $buf .= self::$win1252ToUtf8[ord($c1)];
236
+ } else {
237
+ $cc1 = (chr(ord($c1) / 64) | "\xc0");
238
+ $cc2 = (($c1 & "\x3f") | "\x80");
239
+ $buf .= $cc1 . $cc2;
240
+ }
241
+ } else { // it doesn't need conversion
242
+ $buf .= $c1;
243
+ }
244
+ }
245
+ return $buf;
246
+ }
247
+
248
+ static function toWin1252($text, $option = self::WITHOUT_ICONV) {
249
+ if(is_array($text)) {
250
+ foreach($text as $k => $v) {
251
+ $text[$k] = self::toWin1252($v, $option);
252
+ }
253
+ return $text;
254
+ } elseif(is_string($text)) {
255
+ return static::utf8_decode($text, $option);
256
+ } else {
257
+ return $text;
258
+ }
259
+ }
260
+
261
+ static function toISO8859($text) {
262
+ return self::toWin1252($text);
263
+ }
264
+
265
+ static function toLatin1($text) {
266
+ return self::toWin1252($text);
267
+ }
268
+
269
+ static function fixUTF8($text, $option = self::WITHOUT_ICONV){
270
+ if(is_array($text)) {
271
+ foreach($text as $k => $v) {
272
+ $text[$k] = self::fixUTF8($v, $option);
273
+ }
274
+ return $text;
275
+ }
276
+
277
+ $last = "";
278
+ while($last <> $text){
279
+ $last = $text;
280
+ $text = self::toUTF8(static::utf8_decode($text, $option));
281
+ }
282
+ $text = self::toUTF8(static::utf8_decode($text, $option));
283
+ return $text;
284
+ }
285
+
286
+ static function UTF8FixWin1252Chars($text){
287
+ // If you received an UTF-8 string that was converted from Windows-1252 as it was ISO8859-1
288
+ // (ignoring Windows-1252 chars from 80 to 9F) use this function to fix it.
289
+ // See: http://en.wikipedia.org/wiki/Windows-1252
290
+
291
+ return str_replace(array_keys(self::$brokenUtf8ToUtf8), array_values(self::$brokenUtf8ToUtf8), $text);
292
+ }
293
+
294
+ static function removeBOM($str=""){
295
+ if(substr($str, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
296
+ $str=substr($str, 3);
297
+ }
298
+ return $str;
299
+ }
300
+
301
+ protected static function strlen($text){
302
+ return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload')) & 2) ?
303
+ mb_strlen($text,'8bit') : strlen($text);
304
+ }
305
+
306
+ public static function normalizeEncoding($encodingLabel)
307
+ {
308
+ $encoding = strtoupper($encodingLabel);
309
+ $encoding = preg_replace('/[^a-zA-Z0-9\s]/', '', $encoding);
310
+ $equivalences = array(
311
+ 'ISO88591' => 'ISO-8859-1',
312
+ 'ISO8859' => 'ISO-8859-1',
313
+ 'ISO' => 'ISO-8859-1',
314
+ 'LATIN1' => 'ISO-8859-1',
315
+ 'LATIN' => 'ISO-8859-1',
316
+ 'UTF8' => 'UTF-8',
317
+ 'UTF' => 'UTF-8',
318
+ 'WIN1252' => 'ISO-8859-1',
319
+ 'WINDOWS1252' => 'ISO-8859-1'
320
+ );
321
+
322
+ if(empty($equivalences[$encoding])){
323
+ return 'UTF-8';
324
+ }
325
+
326
+ return $equivalences[$encoding];
327
+ }
328
+
329
+ public static function encode($encodingLabel, $text)
330
+ {
331
+ $encodingLabel = self::normalizeEncoding($encodingLabel);
332
+ if($encodingLabel == 'ISO-8859-1') return self::toLatin1($text);
333
+ return self::toUTF8($text);
334
+ }
335
+
336
+ protected static function utf8_decode($text, $option)
337
+ {
338
+ if ($option == self::WITHOUT_ICONV || !function_exists('iconv')) {
339
+ $o = utf8_decode(
340
+ str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), self::toUTF8($text))
341
+ );
342
+ } else {
343
+ $o = iconv("UTF-8", "Windows-1252" . ($option == self::ICONV_TRANSLIT ? '//TRANSLIT' : ($option == self::ICONV_IGNORE ? '//IGNORE' : '')), $text);
344
+ }
345
+ return $o;
346
+ }
347
+ }
vendor/neitanod/forceutf8/test/ForceUTF8Test.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__)."/Test.class.php");
3
+ require_once(dirname(dirname(__FILE__))."/src/ForceUTF8/Encoding.php");
4
+
5
+ use \ForceUTF8\Encoding;
6
+
7
+ // Test the testing class itself.
8
+ Test::is("'yes' is true", 'yes', true);
9
+ Test::not("1 is not false", 1, false);
10
+ Test::identical("true is identical to true", true, true);
11
+ Test::true("1 is true", 1);
12
+
13
+ // ForceUTF8 tests.
14
+ Test::not("Source files must not use the same encoding before conversion.",
15
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
16
+ file_get_contents(dirname(__FILE__)."/data/test1Latin.txt"));
17
+
18
+ Test::identical("Simple Encoding works.",
19
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
20
+ Encoding::toUTF8(file_get_contents(dirname(__FILE__)."/data/test1Latin.txt")));
21
+
22
+ function test_arrays_are_different(){
23
+ $arr1 = array(
24
+ file_get_contents(dirname(__FILE__)."/data/test1Latin.txt"),
25
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
26
+ file_get_contents(dirname(__FILE__)."/data/test1Latin.txt"));
27
+ $arr2 = array(
28
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
29
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
30
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"));
31
+ return $arr1 != $arr2;
32
+ }
33
+
34
+ function test_encoding_of_arrays(){
35
+ $arr1 = array(
36
+ file_get_contents(dirname(__FILE__)."/data/test1Latin.txt"),
37
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
38
+ file_get_contents(dirname(__FILE__)."/data/test1Latin.txt"));
39
+ $arr2 = array(
40
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
41
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
42
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"));
43
+ return Encoding::toUTF8($arr1) == $arr2;
44
+ }
45
+
46
+ Test::true("Source arrays are different.", test_arrays_are_different());
47
+ Test::true("Encoding of array works.", test_encoding_of_arrays());
48
+
49
+ Test::identical("fixUTF8() maintains UTF-8 string.",
50
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
51
+ Encoding::fixUTF8(file_get_contents(dirname(__FILE__)."/data/test1.txt")));
52
+
53
+ Test::not("An UTF-8 double encoded string differs from a correct UTF-8 string.",
54
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
55
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1.txt")));
56
+
57
+ Test::identical("fixUTF8() reverts to UTF-8 a double encoded string.",
58
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
59
+ Encoding::fixUTF8(utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1.txt"))));
60
+
61
+ function test_double_encoded_arrays_are_different(){
62
+ $arr1 = array(
63
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1Latin.txt")),
64
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1.txt")),
65
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1Latin.txt")));
66
+ $arr2 = array(
67
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
68
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
69
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"));
70
+ return $arr1 != $arr2;
71
+ }
72
+
73
+ function test_double_encoded_arrays_fix(){
74
+ $arr1 = array(
75
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1Latin.txt")),
76
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1.txt")),
77
+ utf8_encode(file_get_contents(dirname(__FILE__)."/data/test1Latin.txt")));
78
+ $arr2 = array(
79
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
80
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"),
81
+ file_get_contents(dirname(__FILE__)."/data/test1.txt"));
82
+ return Encoding::fixUTF8($arr1) == $arr2;
83
+ }
84
+
85
+ Test::true("Source arrays are different (fixUTF8).", test_double_encoded_arrays_are_different());
86
+ Test::true("Fixing of double encoded array works.", test_double_encoded_arrays_fix());
87
+
88
+ Test::identical("fixUTF8() Example 1 still working.",
89
+ Encoding::fixUTF8("Fédération Camerounaise de Football\n"),
90
+ "Fédération Camerounaise de Football\n");
91
+ Test::identical("fixUTF8() Example 2 still working.",
92
+ Encoding::fixUTF8("Fédération Camerounaise de Football\n"),
93
+ "Fédération Camerounaise de Football\n");
94
+ Test::identical("fixUTF8() Example 3 still working.",
95
+ Encoding::fixUTF8("Fédération Camerounaise de Football\n"),
96
+ "Fédération Camerounaise de Football\n");
97
+ Test::identical("fixUTF8() Example 4 still working.",
98
+ Encoding::fixUTF8("Fédération Camerounaise de Football\n"),
99
+ "Fédération Camerounaise de Football\n");
100
+
101
+ Test::totals();
vendor/neitanod/forceutf8/test/Test.class.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Test {
3
+ protected static $passed = 0;
4
+ protected static $failed = 0;
5
+ protected static $last_echoed;
6
+
7
+ public static function true($test_name, $result){
8
+ return static::is($test_name, $result, TRUE);
9
+ }
10
+
11
+ public static function is($test_name, $result, $expected){
12
+ if($result == $expected) {
13
+ static::passed($test_name);
14
+ } else {
15
+ static::failed($test_name);
16
+ }
17
+ }
18
+
19
+ public static function not($test_name, $result, $expected){
20
+ if($result == $expected) {
21
+ static::failed($test_name);
22
+ } else {
23
+ static::passed($test_name);
24
+ }
25
+ }
26
+
27
+ public static function identical($test_name, $result, $expected){
28
+ if($result === $expected) {
29
+ static::passed($test_name);
30
+ } else {
31
+ static::failed($test_name);
32
+ }
33
+ }
34
+
35
+ public static function totals(){
36
+ echo "\n";
37
+ echo static::$passed." tests passed.\n";
38
+ echo static::$failed." tests failed.\n";
39
+ }
40
+
41
+ private static function failed($test_name){
42
+ echo "\n".$test_name." -> FAILED\n";
43
+ static::$failed++;
44
+ }
45
+
46
+ private static function passed($test_name){
47
+ static::character(".");
48
+ static::$passed++;
49
+ }
50
+
51
+ private static function character($char){
52
+ echo $char;
53
+ static::$last_echoed = 'char';
54
+ }
55
+
56
+ private static function line($msg){
57
+ if(static::$last_echoed == 'char') echo "\n";
58
+ echo $msg."\n";
59
+ static::$last_echoed = 'line';
60
+ }
61
+ }
62
+
vendor/neitanod/forceutf8/test/data/russian.txt ADDED
@@ -0,0 +1 @@
 
1
+ hello žš, привет
vendor/neitanod/forceutf8/test/data/test1.txt ADDED
@@ -0,0 +1 @@
 
1
+ Hírek
vendor/neitanod/forceutf8/test/data/test1Latin.txt ADDED
@@ -0,0 +1 @@
 
1
+ H�rek