Unyson - Version 2.6.14

Version Description

  • Fixed infinite loop when php memory limit is -1
  • Minor changes
Download this release

Release Info

Developer Unyson
Plugin Icon 128x128 Unyson
Version 2.6.14
Comparing to
See all releases

Code changes from version 2.6.13 to 2.6.14

framework/autoload.php CHANGED
@@ -103,6 +103,7 @@ function _fw_autoload_helper_classes($class) {
103
  static $class_to_file = array(
104
  'FW_Dumper' => 'class-fw-dumper',
105
  'FW_Cache' => 'class-fw-cache',
 
106
  'FW_Access_Key' => 'class-fw-access-key',
107
  'FW_WP_Filesystem' => 'class-fw-wp-filesystem',
108
  'FW_Form' => 'class-fw-form',
103
  static $class_to_file = array(
104
  'FW_Dumper' => 'class-fw-dumper',
105
  'FW_Cache' => 'class-fw-cache',
106
+ 'FW_Callback' => 'class-fw-callback',
107
  'FW_Access_Key' => 'class-fw-access-key',
108
  'FW_WP_Filesystem' => 'class-fw-wp-filesystem',
109
  'FW_Form' => 'class-fw-form',
framework/core/components/backend.php CHANGED
@@ -347,14 +347,21 @@ final class _FW_Component_Backend {
347
  'FW_URI' => fw_get_framework_directory_uri(),
348
  'SITE_URI' => site_url(),
349
  'LOADER_URI' => apply_filters( 'fw_loader_image', fw_get_framework_directory_uri() . '/static/img/logo.svg' ),
350
- 'l10n' => array(
351
- 'done' => __( 'Done', 'fw' ),
352
- 'ah_sorry' => __( 'Ah, Sorry', 'fw' ),
353
- 'save' => __( 'Save', 'fw' ),
354
- 'reset' => __( 'Reset', 'fw' ),
355
- 'apply' => __( 'Apply', 'fw' ),
356
- 'cancel' => __( 'Cancel', 'fw' ),
357
- 'ok' => __( 'Ok', 'fw' )
 
 
 
 
 
 
 
358
  ),
359
  'options_modal' => array(
360
  /** @since 2.6.13 */
@@ -577,6 +584,14 @@ final class _FW_Component_Backend {
577
  * @param WP_Post $post
578
  */
579
  public function _action_create_post_meta_boxes( $post_type, $post ) {
 
 
 
 
 
 
 
 
580
  $options = fw()->theme->get_post_options( $post_type );
581
 
582
  if ( empty( $options ) ) {
347
  'FW_URI' => fw_get_framework_directory_uri(),
348
  'SITE_URI' => site_url(),
349
  'LOADER_URI' => apply_filters( 'fw_loader_image', fw_get_framework_directory_uri() . '/static/img/logo.svg' ),
350
+ 'l10n' => array_merge(
351
+ $l10n = array(
352
+ 'modal_save_btn' => __( 'Save', 'fw' ),
353
+ 'done' => __( 'Done', 'fw' ),
354
+ 'ah_sorry' => __( 'Ah, Sorry', 'fw' ),
355
+ 'reset' => __( 'Reset', 'fw' ),
356
+ 'apply' => __( 'Apply', 'fw' ),
357
+ 'cancel' => __( 'Cancel', 'fw' ),
358
+ 'ok' => __( 'Ok', 'fw' )
359
+ ),
360
+ /**
361
+ * fixes https://github.com/ThemeFuse/Unyson/issues/2381
362
+ * @since 2.6.14
363
+ */
364
+ apply_filters('fw_js_l10n', $l10n)
365
  ),
366
  'options_modal' => array(
367
  /** @since 2.6.13 */
584
  * @param WP_Post $post
585
  */
586
  public function _action_create_post_meta_boxes( $post_type, $post ) {
587
+ if ('comment' === $post_type) {
588
+ /**
589
+ * This is wrong, comment is not a post(type)
590
+ * it is stored in a separate db table and has a separate meta (wp_comments and wp_commentmeta)
591
+ */
592
+ return;
593
+ }
594
+
595
  $options = fw()->theme->get_post_options( $post_type );
596
 
597
  if ( empty( $options ) ) {
framework/core/components/extensions/manager/class--fw-extensions-manager.php CHANGED
@@ -1021,6 +1021,14 @@ final class _FW_Extensions_Manager
1021
  $skin->after(array(
1022
  'extensions_page_link' => $this->get_link()
1023
  ));
 
 
 
 
 
 
 
 
1024
  } else {
1025
  echo '<form method="post">';
1026
 
1021
  $skin->after(array(
1022
  'extensions_page_link' => $this->get_link()
1023
  ));
1024
+
1025
+ if ($supported && $install_result === true) {
1026
+ /**
1027
+ * @since 2.6.14
1028
+ * Fixes https://github.com/ThemeFuse/Unyson/issues/2330
1029
+ */
1030
+ do_action( 'fw_after_supported_extensions_install_success' );
1031
+ }
1032
  } else {
1033
  echo '<form method="post">';
1034
 
framework/core/components/extensions/manager/static/extensions-page.css CHANGED
@@ -245,3 +245,17 @@
245
  }
246
 
247
  /* end: form ajax loading */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
 
247
  /* end: form ajax loading */
248
+
249
+
250
+ /* anchor */
251
+
252
+ .fw-extensions-list .fw-extensions-list-item a.fw-ext-anchor {
253
+ position: absolute;
254
+ top: -15px;
255
+ }
256
+
257
+ body.admin-bar .fw-extensions-list .fw-extensions-list-item a.fw-ext-anchor {
258
+ top: -50px;
259
+ }
260
+
261
+ /* end: anchor */
framework/core/components/extensions/manager/views/extension.php CHANGED
@@ -64,6 +64,7 @@ if (!$installed_data && !$is_compatible) {
64
  }
65
  ?>
66
  <div class="<?php echo esc_attr($wrapper_class) ?>" id="fw-ext-<?php echo esc_attr($name) ?>">
 
67
  <div class="inner">
68
  <div class="fw-extension-list-item-table">
69
  <div class="fw-extension-list-item-table-row">
64
  }
65
  ?>
66
  <div class="<?php echo esc_attr($wrapper_class) ?>" id="fw-ext-<?php echo esc_attr($name) ?>">
67
+ <a class="fw-ext-anchor" name="ext-<?php echo esc_attr($name) ?>"></a>
68
  <div class="inner">
69
  <div class="fw-extension-list-item-table">
70
  <div class="fw-extension-list-item-table-row">
framework/core/extends/class-fw-option-type.php CHANGED
@@ -111,6 +111,10 @@ abstract class FW_Option_Type
111
  }
112
  }
113
 
 
 
 
 
114
  /**
115
  * Fixes and prepare defaults
116
  *
@@ -184,7 +188,7 @@ abstract class FW_Option_Type
184
 
185
  $this->enqueue_static($id, $option, $data);
186
 
187
- return $this->_render($id, $option, $data);
188
  }
189
 
190
  /**
@@ -264,7 +268,7 @@ abstract class FW_Option_Type
264
  )
265
  );
266
 
267
- return $this->_get_value_from_input($option, $input_value);
268
  }
269
 
270
  /**
@@ -403,4 +407,12 @@ abstract class FW_Option_Type
403
 
404
  return self::$access_key;
405
  }
 
 
 
 
 
 
 
 
406
  }
111
  }
112
  }
113
 
114
+ public function __construct() {
115
+
116
+ }
117
+
118
  /**
119
  * Fixes and prepare defaults
120
  *
188
 
189
  $this->enqueue_static($id, $option, $data);
190
 
191
+ return $this->_render( $id, $this->load_callbacks( $option ), $data );
192
  }
193
 
194
  /**
268
  )
269
  );
270
 
271
+ return $this->_get_value_from_input( $this->load_callbacks( $option ), $input_value);
272
  }
273
 
274
  /**
407
 
408
  return self::$access_key;
409
  }
410
+
411
+ protected function load_callbacks( $data ) {
412
+ if ( ! is_array( $data ) ) {
413
+ return $data;
414
+ }
415
+
416
+ return array_map( 'fw_call', $data );
417
+ }
418
  }
framework/helpers/class-fw-cache.php CHANGED
@@ -65,6 +65,10 @@ class FW_Cache
65
  {
66
  $memory_limit = ini_get('memory_limit');
67
 
 
 
 
 
68
  switch (substr($memory_limit, -1)) {
69
  case 'M': return intval($memory_limit) * 1024 * 1024;
70
  case 'K': return intval($memory_limit) * 1024;
65
  {
66
  $memory_limit = ini_get('memory_limit');
67
 
68
+ if ($memory_limit === '-1') { // This happens in WP CLI
69
+ return 256 * 1024 * 1024;
70
+ }
71
+
72
  switch (substr($memory_limit, -1)) {
73
  case 'M': return intval($memory_limit) * 1024 * 1024;
74
  case 'K': return intval($memory_limit) * 1024;
framework/helpers/class-fw-callback.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'FW' ) ) {
2
+ die( 'Forbidden' );
3
+ }
4
+
5
+ /**
6
+ * Class FW_Callback
7
+ *
8
+ * @since 2.6.14
9
+ */
10
+ class FW_Callback {
11
+ /**
12
+ * @var $callback string|array
13
+ */
14
+ private $callback;
15
+
16
+ /**
17
+ * @var array $args
18
+ */
19
+ private $args;
20
+
21
+ /**
22
+ * @var bool
23
+ */
24
+ private $cache;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ private $id;
30
+
31
+ /**
32
+ * FW_Callback constructor.
33
+ *
34
+ * @param string|array $callback Callback function
35
+ * @param array $args Callback arguments
36
+ * @param bool $cache Whenever you want to cache the function value after it's first call or not
37
+ * Recommend when the function call may require many resources or time (database requests) , or the value is small
38
+ * Not recommended using on very large values
39
+ *
40
+ */
41
+ public function __construct( $callback, array $args = array(), $cache = true ) {
42
+ $this->callback = $callback;
43
+ $this->args = $args;
44
+ $this->cache = (bool) $cache;
45
+ }
46
+
47
+ /**
48
+ * Return callback function
49
+ * @return array|string
50
+ */
51
+ public function get_callback() {
52
+ return $this->callback;
53
+ }
54
+
55
+ /**
56
+ * Return callback function arguments
57
+ * @return array
58
+ */
59
+ public function get_args() {
60
+ return $this->args;
61
+ }
62
+
63
+ /**
64
+ * Execute callback function
65
+ * @return mixed
66
+ */
67
+ public function execute() {
68
+ if ( $this->cache ) {
69
+ try {
70
+ return FW_Cache::get( $this->get_id() );
71
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
72
+ FW_Cache::set(
73
+ $this->get_id(),
74
+ $value = $this->get_value()
75
+ );
76
+
77
+ return $value;
78
+ }
79
+ } else {
80
+ return $this->get_value();
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Whenever you want to clear the cached value or the function
86
+ */
87
+ public function clear_cache() {
88
+ FW_Cache::del( $this->get_id() );
89
+ }
90
+
91
+ /**
92
+ * Get raw callback value, ignoring the cache
93
+ * @return mixed
94
+ */
95
+ protected function get_value() {
96
+ return call_user_func_array( $this->callback, $this->args );
97
+ }
98
+
99
+ protected function get_id() {
100
+ if ( ! is_string( $this->id ) ) {
101
+ $this->id = 'fw-callback-' . md5( $this->serialize_callback() . serialize( $this->args ) );
102
+ }
103
+
104
+ return $this->id;
105
+ }
106
+
107
+ protected function serialize_callback() {
108
+ //Closures cannot be serialized and at the moment do not have a solution
109
+ //So the Closures will be replaced with a unique Id
110
+ return ( $this->callback instanceof Closure )
111
+ ? uniqid( 'fw-callback-' )
112
+ : (
113
+ is_string( $this->callback )
114
+ ? $this->callback
115
+ : serialize( $this->callback ) );
116
+ }
117
+ }
framework/helpers/class-fw-db-options-model.php CHANGED
@@ -172,10 +172,28 @@ abstract class FW_Db_Options_Model {
172
  FW_Cache::set($cache_key_values_processed, true);
173
 
174
  // Complete missing db values with default values from options array
175
- $values = array_merge(
176
- fw_get_options_values_from_input($options, array()),
177
- $values
178
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  foreach ($options as $id => $option) {
181
  $values[$id] = fw()->backend->option_type($option['type'])->storage_load(
172
  FW_Cache::set($cache_key_values_processed, true);
173
 
174
  // Complete missing db values with default values from options array
175
+ {
176
+ try {
177
+ $skip_types_process = FW_Cache::get($cache_key = 'fw:options-default-values:skip-types');
178
+ } catch (FW_Cache_Not_Found_Exception $e) {
179
+ FW_Cache::set(
180
+ $cache_key,
181
+ $skip_types_process = apply_filters('fw:options-default-values:skip-types', array(
182
+ // 'type' => true
183
+ ))
184
+ );
185
+ }
186
+
187
+ foreach (array_diff_key(fw_extract_only_options( $options ), $values) as $id => $option) {
188
+ $values[ $id ] = isset($skip_types_process[ $option['type'] ])
189
+ ? (
190
+ isset($option['value'])
191
+ ? $option['value']
192
+ : fw()->backend->option_type( $option['type'] )->get_defaults( 'value' )
193
+ )
194
+ : fw()->backend->option_type($option['type'])->get_value_from_input($option, null);
195
+ }
196
+ }
197
 
198
  foreach ($options as $id => $option) {
199
  $values[$id] = fw()->backend->option_type($option['type'])->storage_load(
framework/helpers/class-fw-flash-messages.php CHANGED
@@ -201,9 +201,18 @@ class FW_Flash_Messages
201
  $messages = self::get_messages();
202
 
203
  if ($clear) {
204
- self::set_messages(array());
205
  }
206
 
207
  return $messages;
208
  }
 
 
 
 
 
 
 
 
 
209
  }
201
  $messages = self::get_messages();
202
 
203
  if ($clear) {
204
+ self::_clear();
205
  }
206
 
207
  return $messages;
208
  }
209
+
210
+ /**
211
+ * Clear the FW_Flash_Messages messages
212
+ *
213
+ * @since 2.6.14
214
+ */
215
+ public static function _clear() {
216
+ self::set_messages(array());
217
+ }
218
  }
framework/helpers/class-fw-form.php CHANGED
@@ -178,12 +178,14 @@ class FW_Form {
178
  * Some forms (like Forms extension frontend form) uses the same FW_Form instance for all sub-forms
179
  * and they must be differentiated somehow.
180
  * Fixes https://github.com/ThemeFuse/Unyson/issues/2033
 
181
  * @param array $render_data
 
182
  * @return string
183
  * @since 2.6.6
184
  */
185
- private function get_nonce_name($render_data = array()) {
186
- return '_nonce_' . md5( $this->id . apply_filters('fw:form:nonce-name-data', '', $this, $render_data) );
187
  }
188
 
189
  protected function save() {
@@ -221,7 +223,9 @@ class FW_Form {
221
  }
222
 
223
  protected function is_ajax() {
224
- return defined( 'DOING_AJAX' ) && DOING_AJAX;
 
 
225
  }
226
 
227
  /**
@@ -265,11 +269,11 @@ class FW_Form {
265
  {
266
  $flash_messages = array();
267
 
268
- foreach (FW_Flash_Messages::_get_messages(true) as $type => $messages) {
269
- $flash_messages[$type] = array();
270
 
271
- foreach ($messages as $id => $message_data) {
272
- $flash_messages[$type][$id] = $message_data['message'];
273
  }
274
  }
275
 
@@ -297,9 +301,9 @@ class FW_Form {
297
  */
298
 
299
  if ( $this->is_valid() ) {
300
- wp_send_json_success($json_data);
301
  } else {
302
- wp_send_json_error($json_data);
303
  }
304
  } else {
305
  if ( ! $this->is_valid() ) {
@@ -349,8 +353,8 @@ class FW_Form {
349
  */
350
  'html' => null,
351
  ),
352
- 'data' => $data,
353
- 'attr' => $this->attr,
354
  );
355
 
356
  unset( $data );
@@ -374,7 +378,7 @@ class FW_Form {
374
 
375
  // display form errors in frontend
376
  do {
377
- if (is_admin()) {
378
  // errors in admin side are displayed by a script at the end of this file
379
  break;
380
  }
@@ -390,9 +394,9 @@ class FW_Form {
390
  break;
391
  }
392
 
393
- unset($submitted_form); // not needed anymore, below will be used only with $this (because it's the same form)
394
 
395
- if ( ! isset( $_POST[ $this->get_nonce_name($render_data) ] )) {
396
  break;
397
  }
398
 
@@ -403,7 +407,7 @@ class FW_Form {
403
  /**
404
  * Use this action to customize errors display in your theme
405
  */
406
- do_action('fw_form_display_errors_frontend', $this);
407
 
408
  if ( $this->errors_accessed() ) {
409
  // already displayed, prevent/cancel default display
@@ -412,13 +416,13 @@ class FW_Form {
412
 
413
  $errors = $this->get_errors();
414
 
415
- if (empty($errors)) {
416
  break;
417
  }
418
 
419
  echo '<ul class="fw-form-errors">';
420
 
421
- foreach ($errors as $input_name => $error_message) {
422
  echo fw_html_tag(
423
  'li',
424
  array(
@@ -430,21 +434,22 @@ class FW_Form {
430
 
431
  echo '</ul>';
432
 
433
- unset($errors);
434
- } while(false);
435
 
436
- echo '<form '. fw_attr_to_html( $render_data['attr'] ) .' >';
437
 
438
- do_action('fw_form_display:before', $this);
439
 
440
- echo fw_html_tag('input', array(
441
- 'type' => 'hidden',
442
- 'name' => self::$id_input_name,
443
- 'value' => $this->id,
444
- ));
 
445
 
446
  if ( $render_data['attr']['method'] == 'post' ) {
447
- wp_nonce_field( 'submit_fwf', $this->get_nonce_name($render_data) );
448
  }
449
 
450
  if ( ! empty( $render_data['attr']['action'] ) && $render_data['attr']['method'] == 'get' ) {
@@ -456,11 +461,12 @@ class FW_Form {
456
 
457
  if ( ! empty( $query_vars ) ) {
458
  foreach ( $query_vars as $var_name => $var_value ) {
459
- echo fw_html_tag('input', array(
460
- 'type' => 'hidden',
461
- 'name' => $var_name,
462
- 'value' => $var_value,
463
- ));
 
464
  }
465
  }
466
  }
@@ -471,13 +477,14 @@ class FW_Form {
471
  if ( isset( $render_data['submit']['html'] ) ) {
472
  echo $render_data['submit']['html'];
473
  } else {
474
- echo fw_html_tag('input', array(
475
- 'type' => 'submit',
476
- 'value' => $render_data['submit']['value']
477
- ));
 
478
  }
479
 
480
- do_action('fw_form_display:after', $this);
481
 
482
  echo '</form>';
483
  }
@@ -487,8 +494,8 @@ class FW_Form {
487
  * @return bool
488
  */
489
  public function is_submitted() {
490
- if (is_null($this->is_submitted)) {
491
- switch (strtoupper( $this->attr( 'method' ) )) {
492
  case 'POST':
493
  $this->is_submitted = (
494
  isset( $_POST[ self::$id_input_name ] )
@@ -540,8 +547,7 @@ class FW_Form {
540
  return $this->errors;
541
  }
542
 
543
- public function errors_accessed()
544
- {
545
  return $this->errors_accessed;
546
  }
547
 
178
  * Some forms (like Forms extension frontend form) uses the same FW_Form instance for all sub-forms
179
  * and they must be differentiated somehow.
180
  * Fixes https://github.com/ThemeFuse/Unyson/issues/2033
181
+ *
182
  * @param array $render_data
183
+ *
184
  * @return string
185
  * @since 2.6.6
186
  */
187
+ private function get_nonce_name( $render_data = array() ) {
188
+ return '_nonce_' . md5( $this->id . apply_filters( 'fw:form:nonce-name-data', '', $this, $render_data ) );
189
  }
190
 
191
  protected function save() {
223
  }
224
 
225
  protected function is_ajax() {
226
+ return ( defined( 'DOING_AJAX' ) && DOING_AJAX )
227
+ ||
228
+ strtolower( fw_akg( 'HTTP_X_REQUESTED_WITH', $_SERVER ) ) == 'xmlhttprequest';
229
  }
230
 
231
  /**
269
  {
270
  $flash_messages = array();
271
 
272
+ foreach ( FW_Flash_Messages::_get_messages( true ) as $type => $messages ) {
273
+ $flash_messages[ $type ] = array();
274
 
275
+ foreach ( $messages as $id => $message_data ) {
276
+ $flash_messages[ $type ][ $id ] = $message_data['message'];
277
  }
278
  }
279
 
301
  */
302
 
303
  if ( $this->is_valid() ) {
304
+ wp_send_json_success( $json_data );
305
  } else {
306
+ wp_send_json_error( $json_data );
307
  }
308
  } else {
309
  if ( ! $this->is_valid() ) {
353
  */
354
  'html' => null,
355
  ),
356
+ 'data' => $data,
357
+ 'attr' => $this->attr,
358
  );
359
 
360
  unset( $data );
378
 
379
  // display form errors in frontend
380
  do {
381
+ if ( is_admin() ) {
382
  // errors in admin side are displayed by a script at the end of this file
383
  break;
384
  }
394
  break;
395
  }
396
 
397
+ unset( $submitted_form ); // not needed anymore, below will be used only with $this (because it's the same form)
398
 
399
+ if ( ! isset( $_POST[ $this->get_nonce_name( $render_data ) ] ) ) {
400
  break;
401
  }
402
 
407
  /**
408
  * Use this action to customize errors display in your theme
409
  */
410
+ do_action( 'fw_form_display_errors_frontend', $this );
411
 
412
  if ( $this->errors_accessed() ) {
413
  // already displayed, prevent/cancel default display
416
 
417
  $errors = $this->get_errors();
418
 
419
+ if ( empty( $errors ) ) {
420
  break;
421
  }
422
 
423
  echo '<ul class="fw-form-errors">';
424
 
425
+ foreach ( $errors as $input_name => $error_message ) {
426
  echo fw_html_tag(
427
  'li',
428
  array(
434
 
435
  echo '</ul>';
436
 
437
+ unset( $errors );
438
+ } while ( false );
439
 
440
+ echo '<form ' . fw_attr_to_html( $render_data['attr'] ) . ' >';
441
 
442
+ do_action( 'fw_form_display:before', $this );
443
 
444
+ echo fw_html_tag( 'input',
445
+ array(
446
+ 'type' => 'hidden',
447
+ 'name' => self::$id_input_name,
448
+ 'value' => $this->id,
449
+ ) );
450
 
451
  if ( $render_data['attr']['method'] == 'post' ) {
452
+ wp_nonce_field( 'submit_fwf', $this->get_nonce_name( $render_data ) );
453
  }
454
 
455
  if ( ! empty( $render_data['attr']['action'] ) && $render_data['attr']['method'] == 'get' ) {
461
 
462
  if ( ! empty( $query_vars ) ) {
463
  foreach ( $query_vars as $var_name => $var_value ) {
464
+ echo fw_html_tag( 'input',
465
+ array(
466
+ 'type' => 'hidden',
467
+ 'name' => $var_name,
468
+ 'value' => $var_value,
469
+ ) );
470
  }
471
  }
472
  }
477
  if ( isset( $render_data['submit']['html'] ) ) {
478
  echo $render_data['submit']['html'];
479
  } else {
480
+ echo fw_html_tag( 'input',
481
+ array(
482
+ 'type' => 'submit',
483
+ 'value' => $render_data['submit']['value']
484
+ ) );
485
  }
486
 
487
+ do_action( 'fw_form_display:after', $this );
488
 
489
  echo '</form>';
490
  }
494
  * @return bool
495
  */
496
  public function is_submitted() {
497
+ if ( is_null( $this->is_submitted ) ) {
498
+ switch ( strtoupper( $this->attr( 'method' ) ) ) {
499
  case 'POST':
500
  $this->is_submitted = (
501
  isset( $_POST[ self::$id_input_name ] )
547
  return $this->errors;
548
  }
549
 
550
+ public function errors_accessed() {
 
551
  return $this->errors_accessed;
552
  }
553
 
framework/helpers/general.php CHANGED
@@ -1,19 +1,23 @@
1
- <?php if (!defined('FW')) die('Forbidden');
 
 
2
  // Useful functions
3
 
4
  /**
5
  * Convert to Unix style directory separators
6
  */
7
- function fw_fix_path($path) {
8
- $windows_network_path = isset($_SERVER['windir']) && in_array(substr($path, 0, 2), array('//', '\\\\'), true);
9
- $fixed_path = untrailingslashit( str_replace(array('//', '\\'), array('/', '/'), $path) );
 
 
10
 
11
- if (empty($fixed_path) && !empty($path)) {
12
  $fixed_path = '/';
13
  }
14
 
15
- if ($windows_network_path) {
16
- $fixed_path = '//'. ltrim($fixed_path, '/');
17
  }
18
 
19
  return $fixed_path;
@@ -21,16 +25,18 @@ function fw_fix_path($path) {
21
 
22
  /**
23
  * Relative path of the framework customizations directory
 
24
  * @param string $append
 
25
  * @return string
26
  */
27
- function fw_get_framework_customizations_dir_rel_path($append = '') {
28
  try {
29
- $dir = FW_Cache::get($cache_key = 'fw_customizations_dir_rel_path');
30
- } catch (FW_Cache_Not_Found_Exception $e) {
31
  FW_Cache::set(
32
  $cache_key,
33
- $dir = apply_filters('fw_framework_customizations_dir_rel_path', '/framework-customizations')
34
  );
35
  }
36
 
@@ -41,12 +47,14 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
41
  {
42
  /**
43
  * Full path to the child-theme framework customizations directory
 
44
  * @param string $rel_path
 
45
  * @return null|string
46
  */
47
- function fw_get_stylesheet_customizations_directory($rel_path = '') {
48
- if (is_child_theme()) {
49
- return get_stylesheet_directory() . fw_get_framework_customizations_dir_rel_path($rel_path);
50
  } else {
51
  // check is_child_theme() before using this function
52
  return null;
@@ -55,12 +63,14 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
55
 
56
  /**
57
  * URI to the child-theme framework customizations directory
 
58
  * @param string $rel_path
 
59
  * @return null|string
60
  */
61
- function fw_get_stylesheet_customizations_directory_uri($rel_path = '') {
62
- if (is_child_theme()) {
63
- return get_stylesheet_directory_uri() . fw_get_framework_customizations_dir_rel_path($rel_path);
64
  } else {
65
  // check is_child_theme() before using this function
66
  return null;
@@ -72,13 +82,15 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
72
  {
73
  /**
74
  * Full path to the parent-theme framework customizations directory
 
75
  * @param string $rel_path
 
76
  * @return string
77
  */
78
- function fw_get_template_customizations_directory($rel_path = '') {
79
  try {
80
- $dir = FW_Cache::get($cache_key = 'fw_template_customizations_dir');
81
- } catch (FW_Cache_Not_Found_Exception $e) {
82
  FW_Cache::set(
83
  $cache_key,
84
  $dir = get_template_directory() . fw_get_framework_customizations_dir_rel_path()
@@ -90,13 +102,15 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
90
 
91
  /**
92
  * URI to the parent-theme framework customizations directory
 
93
  * @param string $rel_path
 
94
  * @return string
95
  */
96
- function fw_get_template_customizations_directory_uri($rel_path = '') {
97
  try {
98
- $dir = FW_Cache::get($cache_key = 'fw_template_customizations_dir_uri');
99
- } catch (FW_Cache_Not_Found_Exception $e) {
100
  FW_Cache::set(
101
  $cache_key,
102
  $dir = get_template_directory_uri() . fw_get_framework_customizations_dir_rel_path()
@@ -111,18 +125,20 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
111
  {
112
  /**
113
  * Full path to the parent-theme/framework directory
 
114
  * @param string $rel_path
 
115
  * @return string
116
  */
117
- function fw_get_framework_directory($rel_path = '') {
118
  try {
119
- $dir = FW_Cache::get($cache_key = 'fw_framework_dir');
120
- } catch (FW_Cache_Not_Found_Exception $e) {
121
  FW_Cache::set(
122
  $cache_key,
123
  $dir = apply_filters(
124
  'fw_framework_directory',
125
- fw_fix_path(dirname(dirname(__FILE__))) // double dirname() to remove '/helpers', use parent dir
126
  )
127
  );
128
  }
@@ -132,18 +148,20 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
132
 
133
  /**
134
  * URI to the parent-theme/framework directory
 
135
  * @param string $rel_path
 
136
  * @return string
137
  */
138
- function fw_get_framework_directory_uri($rel_path = '') {
139
  try {
140
- $uri = FW_Cache::get($cache_key = 'fw_framework_dir_uri');
141
- } catch (FW_Cache_Not_Found_Exception $e) {
142
  FW_Cache::set(
143
  $cache_key,
144
  $uri = apply_filters(
145
  'fw_framework_directory_uri',
146
- ($uri = fw_get_path_url(fw_get_framework_directory()))
147
  ? $uri
148
  : get_template_directory_uri() . '/framework'
149
  )
@@ -161,41 +179,44 @@ function fw_get_framework_customizations_dir_rel_path($append = '') {
161
  * @param array|object $array_or_object
162
  * @param null|mixed $default_value
163
  * @param string $keys_delimiter
 
164
  * @return null|mixed
165
  */
166
- function fw_akg($keys, $array_or_object, $default_value = null, $keys_delimiter = '/') {
167
- if (!is_array($keys)) {
168
  $keys = explode( $keys_delimiter, (string) $keys );
169
  }
170
 
171
- $key_or_property = array_shift($keys);
172
- if ($key_or_property === null) {
 
 
173
  return $default_value;
174
  }
175
 
176
- $is_object = is_object($array_or_object);
177
 
178
- if ($is_object) {
179
- if (!property_exists($array_or_object, $key_or_property)) {
180
  return $default_value;
181
  }
182
  } else {
183
- if (!is_array($array_or_object) || !array_key_exists($key_or_property, $array_or_object)) {
184
  return $default_value;
185
  }
186
  }
187
 
188
- if (isset($keys[0])) { // not used count() for performance reasons
189
- if ($is_object) {
190
- return fw_akg($keys, $array_or_object->{$key_or_property}, $default_value);
191
  } else {
192
- return fw_akg($keys, $array_or_object[$key_or_property], $default_value);
193
  }
194
  } else {
195
- if ($is_object) {
196
  return $array_or_object->{$key_or_property};
197
  } else {
198
- return $array_or_object[$key_or_property];
199
  }
200
  }
201
  }
@@ -208,59 +229,61 @@ function fw_akg($keys, $array_or_object, $default_value = null, $keys_delimiter
208
  * @param array|object $array_or_object
209
  * @param string $keys_delimiter
210
  */
211
- function fw_aks($keys, $value, &$array_or_object, $keys_delimiter = '/') {
212
- if (!is_array($keys)) {
213
- $keys = explode($keys_delimiter, (string)$keys);
214
  }
215
 
216
- $key_or_property = array_shift($keys);
217
- if ($key_or_property === null) {
218
  return;
219
  }
220
 
221
- $is_object = is_object($array_or_object);
222
 
223
- if ($is_object) {
224
- if (!property_exists($array_or_object, $key_or_property)
225
- || !(is_array($array_or_object->{$key_or_property}) || is_object($array_or_object->{$key_or_property}))
226
  ) {
227
- if ($key_or_property === '') {
228
  // this happens when use 'empty keys' like: abc/d/e////i/j//foo/
229
- trigger_error('Cannot push value to object like in array ($arr[] = $val)', E_USER_WARNING);
230
  } else {
231
  $array_or_object->{$key_or_property} = array();
232
  }
233
  }
234
  } else {
235
- if (!is_array($array_or_object)) {
236
  $array_or_object = array();
237
  }
238
 
239
- if (!array_key_exists($key_or_property, $array_or_object) || !is_array($array_or_object[$key_or_property])) {
240
- if ($key_or_property === '') {
 
 
241
  // this happens when use 'empty keys' like: abc.d.e....i.j..foo.
242
  $array_or_object[] = array();
243
 
244
  // get auto created key (last)
245
- end($array_or_object);
246
- $key_or_property = key($array_or_object);
247
  } else {
248
- $array_or_object[$key_or_property] = array();
249
  }
250
  }
251
  }
252
 
253
- if (isset($keys[0])) { // not used count() for performance reasons
254
- if ($is_object) {
255
- fw_aks($keys, $value, $array_or_object->{$key_or_property});
256
  } else {
257
- fw_aks($keys, $value, $array_or_object[$key_or_property]);
258
  }
259
  } else {
260
- if ($is_object) {
261
  $array_or_object->{$key_or_property} = $value;
262
  } else {
263
- $array_or_object[$key_or_property] = $value;
264
  }
265
  }
266
  }
@@ -272,39 +295,39 @@ function fw_aks($keys, $value, &$array_or_object, $keys_delimiter = '/') {
272
  * @param array|object $array_or_object
273
  * @param string $keys_delimiter
274
  */
275
- function fw_aku($keys, &$array_or_object, $keys_delimiter = '/') {
276
- if (!is_array($keys)) {
277
- $keys = explode($keys_delimiter, (string)$keys);
278
  }
279
 
280
- $key_or_property = array_shift($keys);
281
- if ($key_or_property === null || $key_or_property === '') {
282
  return;
283
  }
284
 
285
- $is_object = is_object($array_or_object);
286
 
287
- if ($is_object) {
288
- if (!property_exists($array_or_object, $key_or_property)) {
289
  return;
290
  }
291
  } else {
292
- if (!is_array($array_or_object) || !array_key_exists($key_or_property, $array_or_object)) {
293
  return;
294
  }
295
  }
296
 
297
- if (isset($keys[0])) { // not used count() for performance reasons
298
- if ($is_object) {
299
- fw_aku($keys, $array_or_object->{$key_or_property});
300
  } else {
301
- fw_aku($keys, $array_or_object[$key_or_property]);
302
  }
303
  } else {
304
- if ($is_object) {
305
- unset($array_or_object->{$key_or_property});
306
  } else {
307
- unset($array_or_object[$key_or_property]);
308
  }
309
 
310
  return;
@@ -315,12 +338,13 @@ function fw_aku($keys, &$array_or_object, $keys_delimiter = '/') {
315
  * Generate random unique md5
316
  */
317
  function fw_rand_md5() {
318
- return md5(time() .'-'. uniqid(rand(), true) .'-'. mt_rand(1, 1000));
319
  }
320
 
321
  function fw_unique_increment() {
322
  static $i = 0;
323
- return ++$i;
 
324
  }
325
 
326
  /**
@@ -328,10 +352,10 @@ function fw_unique_increment() {
328
  *
329
  * @param mixed $value Value to debug
330
  */
331
- function fw_print($value) {
332
  static $first_time = true;
333
 
334
- if ($first_time) {
335
  ob_start();
336
  echo '<style type="text/css">
337
  div.fw_print_r {
@@ -372,19 +396,19 @@ function fw_print($value) {
372
  border-width: 0;
373
  }
374
  </style>';
375
- echo str_replace(array(' ', "\n"), '', ob_get_clean());
376
 
377
  $first_time = false;
378
  }
379
 
380
- if (func_num_args() == 1) {
381
  echo '<div class="fw_print_r"><pre>';
382
- echo fw_htmlspecialchars(FW_Dumper::dump($value));
383
  echo '</pre></div>';
384
  } else {
385
  echo '<div class="fw_print_r_group">';
386
- foreach (func_get_args() as $param) {
387
- fw_print($param);
388
  }
389
  echo '</div>';
390
  }
@@ -396,20 +420,21 @@ function fw_print($value) {
396
  * @param string $tag Tag name
397
  * @param array $attr Tag attributes
398
  * @param bool|string $end Append closing tag. Also accepts body content
 
399
  * @return string The tag's html
400
  */
401
- function fw_html_tag($tag, $attr = array(), $end = false) {
402
- $html = '<'. $tag .' '. fw_attr_to_html($attr);
403
 
404
- if ($end === true) {
405
  # <script></script>
406
- $html .= '></'. $tag .'>';
407
- } else if ($end === false) {
408
  # <br/>
409
  $html .= '/>';
410
  } else {
411
  # <div>content</div>
412
- $html .= '>'. $end .'</'. $tag .'>';
413
  }
414
 
415
  return $html;
@@ -417,18 +442,20 @@ function fw_html_tag($tag, $attr = array(), $end = false) {
417
 
418
  /**
419
  * Generate attributes string for html tag
 
420
  * @param array $attr_array array('href' => '/', 'title' => 'Test')
 
421
  * @return string 'href="/" title="Test"'
422
  */
423
- function fw_attr_to_html(array $attr_array) {
424
  $html_attr = '';
425
 
426
- foreach ($attr_array as $attr_name => $attr_val) {
427
- if ($attr_val === false) {
428
  continue;
429
  }
430
 
431
- $html_attr .= $attr_name .'="'. fw_htmlspecialchars($attr_val) .'" ';
432
  }
433
 
434
  return $html_attr;
@@ -437,30 +464,30 @@ function fw_attr_to_html(array $attr_array) {
437
  /**
438
  * Strip slashes from values, and from keys if magic_quotes_gpc = On
439
  */
440
- function fw_stripslashes_deep_keys($value) {
441
  static $magic_quotes = null;
442
- if ($magic_quotes === null) {
443
  $magic_quotes = get_magic_quotes_gpc();
444
  }
445
 
446
- if (is_array($value)) {
447
- if ($magic_quotes) {
448
  $new_value = array();
449
- foreach ($value as $key => $val) {
450
- $new_value[ is_string($key) ? stripslashes($key) : $key ] = fw_stripslashes_deep_keys($val);
451
  }
452
  $value = $new_value;
453
- unset($new_value);
454
  } else {
455
- $value = array_map('fw_stripslashes_deep_keys', $value);
456
  }
457
- } elseif (is_object($value)) {
458
- $vars = get_object_vars($value);
459
- foreach ($vars as $key=>$data) {
460
- $value->{$key} = fw_stripslashes_deep_keys($data);
461
  }
462
- } elseif (is_string($value)) {
463
- $value = stripslashes($value);
464
  }
465
 
466
  return $value;
@@ -469,30 +496,30 @@ function fw_stripslashes_deep_keys($value) {
469
  /**
470
  * Add slashes to values, and to keys if magic_quotes_gpc = On
471
  */
472
- function fw_addslashes_deep_keys($value) {
473
  static $magic_quotes = null;
474
- if ($magic_quotes === null) {
475
  $magic_quotes = get_magic_quotes_gpc();
476
  }
477
 
478
- if (is_array($value)) {
479
- if ($magic_quotes) {
480
  $new_value = array();
481
- foreach ($value as $key=>$value) {
482
- $new_value[ is_string($key) ? addslashes($key) : $key ] = fw_addslashes_deep_keys($value);
483
  }
484
  $value = $new_value;
485
- unset($new_value);
486
  } else {
487
- $value = array_map('fw_addslashes_deep_keys', $value);
488
  }
489
- } elseif (is_object($value)) {
490
- $vars = get_object_vars($value);
491
- foreach ($vars as $key=>$data) {
492
- $value->{$key} = fw_addslashes_deep_keys($data);
493
  }
494
- } elseif (is_string($value)) {
495
- $value = addslashes($value);
496
  }
497
 
498
  return $value;
@@ -500,10 +527,12 @@ function fw_addslashes_deep_keys($value) {
500
 
501
  /**
502
  * Check if current screen pass/match give rules
 
503
  * @param array $rules Rules for current screen
 
504
  * @return bool
505
  */
506
- function fw_current_screen_match(array $rules) {
507
  $available_options = array(
508
  'action' => true,
509
  'base' => true,
@@ -516,7 +545,7 @@ function fw_current_screen_match(array $rules) {
516
  'taxonomy' => true,
517
  );
518
 
519
- if (empty($rules)) {
520
  return true;
521
  }
522
 
@@ -528,13 +557,13 @@ function fw_current_screen_match(array $rules) {
528
  $rules
529
  );
530
 
531
- if (empty($rules['exclude']) && empty($rules['only'])) {
532
  return true;
533
  }
534
 
535
  global $current_screen;
536
 
537
- if (gettype($current_screen) != 'object') {
538
  return false;
539
  }
540
 
@@ -542,93 +571,93 @@ function fw_current_screen_match(array $rules) {
542
  do {
543
  $only = $rules['only'];
544
 
545
- if (empty($only)) {
546
  break;
547
  }
548
 
549
- if (!isset($only[0])) { // if not array of arrays
550
- $only = array($only);
551
  }
552
 
553
  $found_one = false;
554
- $counter = 0;
555
- foreach ($only as $rule) {
556
- if (!count($rule)) {
557
  continue;
558
  }
559
 
560
  $match = true;
561
 
562
- foreach ($rule as $r_key => $r_val) {
563
- if (!isset($available_options[$r_key])) {
564
  continue;
565
  }
566
 
567
- if (gettype($r_val) != 'array') {
568
- $r_val = array($r_val);
569
  }
570
 
571
- $counter++;
572
 
573
- if (!in_array($current_screen->{$r_key}, $r_val)) {
574
  $match = false;
575
  break;
576
  }
577
  }
578
 
579
- if ($match) {
580
  $found_one = true;
581
  break;
582
  }
583
  }
584
 
585
- if (!$found_one && $counter) {
586
  return false;
587
  }
588
- } while(false);
589
 
590
  // check if current screen passes the "exclude" rules
591
  do {
592
  $exclude = $rules['exclude'];
593
 
594
- if (empty($exclude)) {
595
  break;
596
  }
597
 
598
- if (!isset($exclude[0])) { // if not array of arrays
599
- $exclude = array($exclude);
600
  }
601
 
602
- foreach ($exclude as $rule) {
603
- if (!count($rule)) {
604
  continue;
605
  }
606
 
607
  $match = true;
608
  $counter = 0;
609
 
610
- foreach ($rule as $r_key => $r_val) {
611
- if (!isset($available_options[$r_key])) {
612
  continue;
613
  }
614
 
615
- if (gettype($r_val) != 'array') {
616
- $r_val = array($r_val);
617
  }
618
 
619
- $counter++;
620
 
621
- if (!in_array($current_screen->{$r_key}, $r_val)) {
622
  $match = false;
623
  break;
624
  }
625
  }
626
 
627
- if ($match && $counter) {
628
  return false;
629
  }
630
  }
631
- } while(false);
632
 
633
  return true;
634
  }
@@ -637,15 +666,16 @@ function fw_current_screen_match(array $rules) {
637
  * Search relative path in child then in parent theme directory and return URI
638
  *
639
  * @param string $rel_path '/some/path_to_dir' or '/some/path_to_file.php'
 
640
  * @return string URI
641
  */
642
- function fw_locate_theme_path_uri($rel_path) {
643
- if (is_child_theme() && file_exists(get_stylesheet_directory() . $rel_path)) {
644
  return get_stylesheet_directory_uri() . $rel_path;
645
- } elseif (file_exists(get_template_directory() . $rel_path)) {
646
  return get_template_directory_uri() . $rel_path;
647
  } else {
648
- return 'about:blank#theme-file-not-found:'. $rel_path;
649
  }
650
  }
651
 
@@ -653,12 +683,13 @@ function fw_locate_theme_path_uri($rel_path) {
653
  * Search relative path in child then in parent theme directory and return full path
654
  *
655
  * @param string $rel_path '/some/path_to_dir' or '/some/path_to_file.php'
 
656
  * @return string URI
657
  */
658
- function fw_locate_theme_path($rel_path) {
659
- if (is_child_theme() && file_exists(get_stylesheet_directory() . $rel_path)) {
660
  return get_stylesheet_directory() . $rel_path;
661
- } elseif (file_exists(get_template_directory() . $rel_path)) {
662
  return get_template_directory() . $rel_path;
663
  } else {
664
  return false;
@@ -671,47 +702,52 @@ function fw_locate_theme_path($rel_path) {
671
  * https://github.com/ThemeFuse/Unyson/commit/07be8b1f4b50eaf0f1f7e85ea1c6912a0415d241#diff-cf866bf08b8f747e3120221a6b1b07cfR48
672
  * it throws fatal error because this function here is defined after that
673
  */
674
- if (!function_exists('fw_render_view')):
675
- /**
676
- * Safe render a view and return html
677
- * In view will be accessible only passed variables
678
- * Use this function to not include files directly and to not give access to current context variables (like $this)
679
- * @param string $file_path
680
- * @param array $view_variables
681
- * @param bool $return In some cases, for memory saving reasons, you can disable the use of output buffering
682
- * @return string HTML
683
- */
684
- function fw_render_view($file_path, $view_variables = array(), $return = true) {
685
- extract($view_variables, EXTR_REFS);
686
- unset($view_variables);
 
 
687
 
688
- if ($return) {
689
- ob_start();
690
- require $file_path;
691
- return ob_get_clean();
692
- } else {
693
- require $file_path;
 
 
694
  }
695
- }
696
  endif;
697
 
698
  /**
699
  * Safe load variables from an file
700
  * Use this function to not include files directly and to not give access to current context variables (like $this)
 
701
  * @param string $file_path
702
  * @param array $_extract_variables Extract these from file array('variable_name' => 'default_value')
703
  * @param array $_set_variables Set these to be available in file (like variables in view)
 
704
  * @return array
705
  */
706
- function fw_get_variables_from_file($file_path, array $_extract_variables, array $_set_variables = array()) {
707
- extract($_set_variables, EXTR_REFS);
708
- unset($_set_variables);
709
 
710
  require $file_path;
711
 
712
- foreach ($_extract_variables as $variable_name => $default_value) {
713
- if (isset($$variable_name)) {
714
- $_extract_variables[$variable_name] = $$variable_name;
715
  }
716
  }
717
 
@@ -720,17 +756,20 @@ function fw_get_variables_from_file($file_path, array $_extract_variables, array
720
 
721
  /**
722
  * Use this function to not include files directly and to not give access to current context variables (like $this)
 
723
  * @param string $file_path
724
  * @param bool $once
 
725
  * @return bool If was included or not
726
  */
727
- function fw_include_file_isolated($file_path, $once = false) {
728
- if (file_exists($file_path)) {
729
  if ( (bool) $once ) {
730
  include_once $file_path;
731
  } else {
732
  include $file_path;
733
  }
 
734
  return true;
735
  } else {
736
  return false;
@@ -739,19 +778,22 @@ function fw_include_file_isolated($file_path, $once = false) {
739
 
740
  /**
741
  * Extract only input options (without containers)
 
742
  * @param array $options
 
743
  * @return array {option_id => option}
744
  */
745
- function fw_extract_only_options(array $options) {
746
  $collected = array();
747
 
748
- fw_collect_options($collected, $options);
749
 
750
  return $collected;
751
  }
752
 
753
  /**
754
  * Collect correct options from the first level of the array and group them
 
755
  * @param array $collected Will be filled with found correct options
756
  * @param array $options
757
  *
@@ -760,12 +802,12 @@ function fw_extract_only_options(array $options) {
760
  * but this function is hardcoded only for tab,box,group.
761
  * Use fw_collect_options()
762
  */
763
- function fw_collect_first_level_options(&$collected, &$options) {
764
- if (empty($options)) {
765
  return;
766
  }
767
 
768
- if (empty($collected)) {
769
  $collected['tabs'] =
770
  $collected['boxes'] =
771
  $collected['groups'] =
@@ -774,41 +816,41 @@ function fw_collect_first_level_options(&$collected, &$options) {
774
  $collected['all'] = array();
775
  }
776
 
777
- foreach ($options as $option_id => &$option) {
778
- if (isset($option['options'])) {
779
  // this is container for other options
780
 
781
- switch ($option['type']) {
782
  case 'tab':
783
- $collected['tabs'][$option_id] =& $option;
784
  break;
785
  case 'box':
786
- $collected['boxes'][$option_id] =& $option;
787
  break;
788
  case 'group':
789
- $collected['groups'][$option_id] =& $option;
790
- $collected['groups_and_options'][$option_id] =& $option;
791
  break;
792
  default:
793
- trigger_error('Invalid option container type: '. $option['type'], E_USER_WARNING);
794
  continue 2;
795
  }
796
 
797
- $collected['all'][ $option['type'] .':~:'. $option_id ] = array(
798
  'type' => $option['type'],
799
  'id' => $option_id,
800
  'option' => &$option,
801
  );
802
  } elseif (
803
- is_int($option_id)
804
  &&
805
- is_array($option)
806
  &&
807
  /**
808
  * make sure the array key was generated automatically
809
  * and it's not an associative array with numeric keys created like this: $options[1] = array();
810
  */
811
- isset($options[0])
812
  ) {
813
  /**
814
  * Array "without key" containing options.
@@ -834,22 +876,22 @@ function fw_collect_first_level_options(&$collected, &$options) {
834
  * ),
835
  * )
836
  */
837
- fw_collect_first_level_options($collected, $option);
838
- } elseif (isset($option['type'])) {
839
  // simple option, last possible level in options array
840
- $collected['options'][$option_id] =& $option;
841
- $collected['groups_and_options'][$option_id] =& $option;
842
 
843
- $collected['all'][ 'option' .':~:'. $option_id ] = array(
844
  'type' => 'option',
845
  'id' => $option_id,
846
  'option' => &$option,
847
  );
848
  } else {
849
- trigger_error('Invalid option: '. $option_id, E_USER_WARNING);
850
  }
851
  }
852
- unset($option);
853
  }
854
 
855
  /**
@@ -858,7 +900,7 @@ function fw_collect_first_level_options(&$collected, &$options) {
858
  * @param array $settings
859
  * @param array $_recursion_data (private) for internal use
860
  */
861
- function fw_collect_options(&$result, &$options, $settings = array(), $_recursion_data = array()) {
862
  static $default_settings = array(
863
  /**
864
  * @type bool Wrap the result/collected options in arrays will useful info
@@ -879,15 +921,15 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
879
  * // Warning: There can be options and containers with the same id (array key will be replaced)
880
  * )
881
  */
882
- 'info_wrapper' => false,
883
  /**
884
  * @type int Nested options level limit. For e.g. use 1 to collect only first level. 0 is for unlimited.
885
  */
886
- 'limit_level' => 0,
887
  /**
888
  * @type false|array('option-type', ...) Empty array will skip all types
889
  */
890
- 'limit_option_types' => false,
891
  /**
892
  * @type false|array('container-type', ...) Empty array will skip all types
893
  */
@@ -895,40 +937,41 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
895
  /**
896
  * @type int Limit the number of options that will be collected
897
  */
898
- 'limit' => 0,
899
  /**
900
  * @type callable Executed on each collected option
901
  * @since 2.6.0
902
  */
903
- 'callback' => null,
904
  );
905
 
906
  static $access_key = null;
907
 
908
- if (empty($options)) {
909
  return;
910
  }
911
 
912
- if (empty($_recursion_data)) {
913
- if (is_null($access_key)) {
914
- $access_key = new FW_Access_Key('fw_collect_options');
915
  }
916
 
917
- $settings = array_merge($default_settings, $settings);
918
 
919
  $_recursion_data = array(
920
- 'level' => 1,
921
  'access_key' => $access_key,
922
  // todo: maybe add 'parent' => array('id' => '{id}', 'type' => 'container|option') ?
923
  );
924
- } elseif (!(
925
- isset($_recursion_data['access_key'])
926
  &&
927
- ($_recursion_data['access_key'] instanceof FW_Access_Key)
928
  &&
929
- ($_recursion_data['access_key']->get_key() === 'fw_collect_options')
930
- )) {
931
- trigger_error('Call not allowed', E_USER_ERROR);
 
932
  }
933
 
934
  if (
@@ -939,17 +982,17 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
939
  return;
940
  }
941
 
942
- foreach ($options as $option_id => &$option) {
943
- if (isset($option['options'])) { // this is a container
944
  do {
945
  if (
946
- is_array($settings['limit_container_types'])
947
  &&
948
  (
949
  // Customizer options can contain options with not existing or empty $option['type']
950
- empty($option['type'])
951
  ||
952
- !in_array($option['type'], $settings['limit_container_types'])
953
  )
954
  ) {
955
  break;
@@ -958,47 +1001,50 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
958
  if (
959
  $settings['limit']
960
  &&
961
- count($result) >= $settings['limit']
962
  ) {
963
  return;
964
  }
965
 
966
- if ($settings['info_wrapper']) {
967
- $result['container:'. $option_id] = array(
968
  'group' => 'container',
969
  'id' => $option_id,
970
  'option' => &$option,
971
  'level' => $_recursion_data['level'],
972
  );
973
  } else {
974
- $result[$option_id] = &$option;
975
  }
976
 
977
- if ($settings['callback']) {
978
- call_user_func_array($settings['callback'], array(array(
979
- 'group' => 'container',
980
- 'id' => $option_id,
981
- 'option' => &$option,
982
- )));
 
 
 
983
  }
984
- } while(false);
985
 
986
  fw_collect_options(
987
  $result,
988
  $option['options'],
989
  $settings,
990
- array_merge($_recursion_data, array('level' => $_recursion_data['level'] + 1))
991
  );
992
  } elseif (
993
- is_int($option_id)
994
  &&
995
- is_array($option)
996
  &&
997
  /**
998
  * make sure the array key was generated automatically
999
  * and it's not an associative array with numeric keys created like this: $options[1] = array();
1000
  */
1001
- isset($options[0])
1002
  ) {
1003
  /**
1004
  * Array "without key" containing options.
@@ -1024,12 +1070,12 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
1024
  * ),
1025
  * )
1026
  */
1027
- fw_collect_options($result, $option, $settings, $_recursion_data);
1028
- } elseif (isset($option['type'])) { // option
1029
  if (
1030
- is_array($settings['limit_option_types'])
1031
  &&
1032
- !in_array($option['type'], $settings['limit_option_types'])
1033
  ) {
1034
  continue;
1035
  }
@@ -1037,31 +1083,34 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
1037
  if (
1038
  $settings['limit']
1039
  &&
1040
- count($result) >= $settings['limit']
1041
  ) {
1042
  return;
1043
  }
1044
 
1045
- if ($settings['info_wrapper']) {
1046
- $result['option:'. $option_id] = array(
1047
  'group' => 'option',
1048
  'id' => $option_id,
1049
  'option' => &$option,
1050
  'level' => $_recursion_data['level'],
1051
  );
1052
  } else {
1053
- $result[$option_id] = &$option;
1054
  }
1055
 
1056
- if ($settings['callback']) {
1057
- call_user_func_array($settings['callback'], array(array(
1058
- 'group' => 'option',
1059
- 'id' => $option_id,
1060
- 'option' => &$option,
1061
- )));
 
 
 
1062
  }
1063
  } else {
1064
- trigger_error('Invalid option: '. $option_id, E_USER_WARNING);
1065
  }
1066
  }
1067
  }
@@ -1069,26 +1118,28 @@ function fw_collect_options(&$result, &$options, $settings = array(), $_recursio
1069
  /**
1070
  * Get correct values from input (POST) for given options
1071
  * This values can be saved in db then replaced with $option['value'] for each option
 
1072
  * @param array $options
1073
  * @param array $input_array
 
1074
  * @return array Values
1075
  */
1076
- function fw_get_options_values_from_input(array $options, $input_array = null) {
1077
- if (!is_array($input_array)) {
1078
- $input_array = FW_Request::POST(fw()->backend->get_options_name_attr_prefix());
1079
  }
1080
 
1081
  $values = array();
1082
 
1083
- foreach (fw_extract_only_options($options) as $id => $option) {
1084
- $values[$id] = fw()->backend->option_type($option['type'])->get_value_from_input(
1085
  $option,
1086
- isset($input_array[$id]) ? $input_array[$id] : null
1087
  );
1088
 
1089
- if (is_null($values[$id])) {
1090
  // do not save null values
1091
- unset($values[$id]);
1092
  }
1093
  }
1094
 
@@ -1098,28 +1149,29 @@ function fw_get_options_values_from_input(array $options, $input_array = null) {
1098
  /**
1099
  * @param $attr_name
1100
  * @param bool $set_mode
 
1101
  * @return mixed
1102
  */
1103
- function fw_html_attr_name_to_array_multi_key($attr_name, $set_mode = false) {
1104
- if ($set_mode) {
1105
  /**
1106
  * The key will be used to set value in array
1107
  * 'hello[world][]' -> 'hello/world/'
1108
  * $array['hello']['world'][] = $value;
1109
  */
1110
- $attr_name = str_replace('[]', '/', $attr_name);
1111
  } else {
1112
  /**
1113
  * The key will be used to get value from array
1114
  * 'hello[world][]' -> 'hello/world'
1115
  * $value = $array['hello']['world'];
1116
  */
1117
- $attr_name = str_replace('[]', '', $attr_name);
1118
  }
1119
 
1120
- $attr_name = str_replace('][', '/', $attr_name);
1121
- $attr_name = str_replace('[', '/', $attr_name);
1122
- $attr_name = str_replace(']', '', $attr_name);
1123
 
1124
  return $attr_name;
1125
  }
@@ -1128,16 +1180,18 @@ function fw_html_attr_name_to_array_multi_key($attr_name, $set_mode = false) {
1128
  * Used when getting some option value from serialized array saved in a custom place
1129
  * and that option is unreachable for standard WordPress filters by other plugins
1130
  * For e.g. that option cannot be translated by plugins, so we pass its value through this function and do the fixes
 
1131
  * @param $value
 
1132
  * @return array
1133
  */
1134
- function fw_prepare_option_value($value) {
1135
- if (empty($value)) {
1136
  return $value;
1137
  }
1138
 
1139
- if (function_exists('qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage')) {
1140
- $value = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage($value);
1141
  }
1142
 
1143
  return $value;
@@ -1150,6 +1204,7 @@ function fw_prepare_option_value($value) {
1150
  * not a revision, auto-save or something else
1151
  *
1152
  * @param $post_id
 
1153
  * @return bool
1154
  *
1155
  * @deprecated
@@ -1158,14 +1213,14 @@ function fw_prepare_option_value($value) {
1158
  * the developers should study and understand better how it works
1159
  * and handle different save cases in their scripts using wp functions
1160
  */
1161
- function fw_is_real_post_save($post_id) {
1162
- return !(
1163
- wp_is_post_revision($post_id)
1164
- || wp_is_post_autosave($post_id)
1165
- || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
1166
- || (defined('DOING_AJAX') && DOING_AJAX)
1167
- || empty($_POST)
1168
- || empty($_POST['post_ID'])
1169
  || $_POST['post_ID'] != $post_id
1170
  );
1171
  }
@@ -1177,25 +1232,25 @@ function fw_get_google_fonts() {
1177
  $cache_key = 'fw_google_fonts';
1178
 
1179
  try {
1180
- return FW_Cache::get($cache_key);
1181
- } catch (FW_Cache_Not_Found_Exception $e) {
1182
- $g_fonts = json_decode(fw_get_google_fonts_v2(), true);
1183
- $old_fonts = include(dirname(__FILE__) .'/fw-google-fonts.json.php');
1184
- $fonts = array();
1185
 
1186
  foreach ( $g_fonts['items'] as $font ) {
1187
  $fonts[ $font['family'] ] = array(
1188
- 'family' => $font['family'],
1189
  'variants' => $font['variants'],
1190
- 'position' => isset($old_fonts[$font['family']])
1191
- ? $old_fonts[$font['family']]['position']
1192
  : 99999
1193
  );
1194
  }
1195
 
1196
- $fonts = apply_filters('fw_google_fonts', $fonts);
1197
 
1198
- FW_Cache::set($cache_key, $fonts);
1199
 
1200
  return $fonts;
1201
  }
@@ -1206,14 +1261,15 @@ function fw_get_google_fonts() {
1206
  */
1207
  function fw_get_google_fonts_v2() {
1208
  $saved_data = get_option( 'fw_google_fonts', false );
1209
- $ttl = 7 * DAY_IN_SECONDS;
1210
 
1211
  if (
1212
  false === $saved_data
1213
  ||
1214
  ( $saved_data['last_update'] + $ttl < time() )
1215
  ) {
1216
- $response = wp_remote_get( apply_filters( 'fw_googleapis_webfonts_url', 'http://google-webfonts-cache.unyson.io/v1/webfonts' ) );
 
1217
  $body = wp_remote_retrieve_body( $response );
1218
 
1219
  if (
@@ -1221,10 +1277,12 @@ function fw_get_google_fonts_v2() {
1221
  &&
1222
  ! is_wp_error( $body ) && ! empty( $body )
1223
  ) {
1224
- update_option( 'fw_google_fonts', array(
1225
- 'last_update' => time(),
1226
- 'fonts' => $body
1227
- ), false );
 
 
1228
 
1229
  return $body;
1230
  } else {
@@ -1232,10 +1290,12 @@ function fw_get_google_fonts_v2() {
1232
  $saved_data['fonts'] = json_encode( array( 'items' => array() ) );
1233
  }
1234
 
1235
- update_option( 'fw_google_fonts', array(
1236
- 'last_update' => time() - $ttl + MINUTE_IN_SECONDS,
1237
- 'fonts' => $saved_data['fonts']
1238
- ), false );
 
 
1239
  }
1240
  }
1241
 
@@ -1248,31 +1308,31 @@ function fw_get_google_fonts_v2() {
1248
  function fw_current_url() {
1249
  static $url = null;
1250
 
1251
- if ($url === null) {
1252
  $url = 'http://';
1253
 
1254
- if ($_SERVER['SERVER_NAME'] === '_') { // https://github.com/ThemeFuse/Unyson/issues/126
1255
  $url .= $_SERVER['HTTP_HOST'];
1256
  } else {
1257
  $url .= $_SERVER['SERVER_NAME'];
1258
  }
1259
 
1260
- if (!in_array(intval($_SERVER['SERVER_PORT']), array(80, 443))) {
1261
- $url .= ':'. $_SERVER['SERVER_PORT'];
1262
  }
1263
 
1264
  $url .= $_SERVER['REQUEST_URI'];
1265
 
1266
- $url = set_url_scheme($url); // https fix
1267
 
1268
  if ( is_multisite() ) {
1269
  if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
1270
- $site_url = parse_url($url);
1271
 
1272
- if ( isset($site_url['query']) ) {
1273
- $url = home_url($site_url['path'] . '?' . $site_url['query']);
1274
  } else {
1275
- $url = home_url($site_url['path']);
1276
  }
1277
  }
1278
  }
@@ -1281,10 +1341,10 @@ function fw_current_url() {
1281
  return $url;
1282
  }
1283
 
1284
- function fw_is_valid_domain_name($domain_name) {
1285
- return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) // valid chars check
1286
- && preg_match("/^.{1,253}$/", $domain_name) // overall length check
1287
- && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); // length of each label
1288
  }
1289
 
1290
  /**
@@ -1292,10 +1352,11 @@ function fw_is_valid_domain_name($domain_name) {
1292
  * Info: Cannot use default parameters because in php 5.2 encoding is not UTF-8 by default
1293
  *
1294
  * @param string $string
 
1295
  * @return string
1296
  */
1297
- function fw_htmlspecialchars($string) {
1298
- return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
1299
  }
1300
 
1301
  /**
@@ -1303,21 +1364,22 @@ function fw_htmlspecialchars($string) {
1303
  *
1304
  * @param array $capabilities list of capabilities to check
1305
  * @param mixed $default_value
 
1306
  * @return string|bool|mixed
1307
  * Return first capability that user can.
1308
  * Else, return default value if it is not null, else return first capability from list.
1309
  * Use default value false to check if user can some of the capabilities
1310
  */
1311
- function fw_current_user_can($capabilities, $default_value = null)
1312
- {
1313
- if (is_user_logged_in()) {
1314
- foreach ($capabilities as $capability) {
1315
- if (current_user_can($capability))
1316
  return $capability;
 
1317
  }
1318
  }
1319
 
1320
- return ($default_value !== null ? $default_value : array_shift($capabilities));
1321
  }
1322
 
1323
  /**
@@ -1327,52 +1389,54 @@ function fw_current_user_can($capabilities, $default_value = null)
1327
  * then you can use this string how you want, for e.g. append ' ago' => '2 minutes ago'
1328
  *
1329
  * @param int $seconds
 
1330
  * @return string
1331
  */
1332
- function fw_human_time($seconds) {
1333
  static $translations = null;
1334
- if ($translations === null) {
1335
  $translations = array(
1336
- 'year' => __('year', 'fw'),
1337
- 'years' => __('years', 'fw'),
1338
 
1339
- 'month' => __('month', 'fw'),
1340
- 'months' => __('months', 'fw'),
1341
 
1342
- 'week' => __('week', 'fw'),
1343
- 'weeks' => __('weeks', 'fw'),
1344
 
1345
- 'day' => __('day', 'fw'),
1346
- 'days' => __('days', 'fw'),
1347
 
1348
- 'hour' => __('hour', 'fw'),
1349
- 'hours' => __('hours', 'fw'),
1350
 
1351
- 'minute' => __('minute', 'fw'),
1352
- 'minutes' => __('minutes', 'fw'),
1353
 
1354
- 'second' => __('second', 'fw'),
1355
- 'seconds' => __('seconds', 'fw'),
1356
  );
1357
  }
1358
 
1359
- $tokens = array (
1360
  31536000 => 'year',
1361
- 2592000 => 'month',
1362
- 604800 => 'week',
1363
- 86400 => 'day',
1364
- 3600 => 'hour',
1365
- 60 => 'minute',
1366
- 1 => 'second'
1367
  );
1368
 
1369
- foreach ($tokens as $unit => $translation_key) {
1370
- if ($seconds < $unit)
1371
  continue;
 
1372
 
1373
- $number_of_units = floor($seconds / $unit);
1374
 
1375
- return $number_of_units .' '. $translations[ $translation_key . ($number_of_units != 1 ? 's' : '') ];
1376
  }
1377
  }
1378
 
@@ -1381,39 +1445,40 @@ function fw_human_time($seconds) {
1381
  *
1382
  * @param integer $bytes Size in bytes to convert
1383
  * @param integer $precision
 
1384
  * @return string
1385
  * @since 2.4.17
1386
  */
1387
- function fw_human_bytes($bytes, $precision = 2) {
1388
  $kilobyte = 1024;
1389
  $megabyte = $kilobyte * 1024;
1390
  $gigabyte = $megabyte * 1024;
1391
  $terabyte = $gigabyte * 1024;
1392
 
1393
- if (($bytes >= 0) && ($bytes < $kilobyte)) {
1394
  return $bytes . ' B';
1395
 
1396
- } elseif (($bytes >= $kilobyte) && ($bytes < $megabyte)) {
1397
- return round($bytes / $kilobyte, $precision) . ' KB';
1398
 
1399
- } elseif (($bytes >= $megabyte) && ($bytes < $gigabyte)) {
1400
- return round($bytes / $megabyte, $precision) . ' MB';
1401
 
1402
- } elseif (($bytes >= $gigabyte) && ($bytes < $terabyte)) {
1403
- return round($bytes / $gigabyte, $precision) . ' GB';
1404
 
1405
- } elseif ($bytes >= $terabyte) {
1406
- return round($bytes / $terabyte, $precision) . ' TB';
1407
  } else {
1408
  return $bytes . ' B';
1409
  }
1410
  }
1411
 
1412
- function fw_strlen($string) {
1413
- if (function_exists('mb_strlen')) {
1414
- return mb_strlen($string, 'UTF-8');
1415
  } else {
1416
- return strlen($string);
1417
  }
1418
  }
1419
 
@@ -1424,31 +1489,31 @@ function fw_strlen($string) {
1424
  function fw_is_post_edit() {
1425
  static $result = null;
1426
 
1427
- if ($result === null) {
1428
  $result = false;
1429
 
1430
- if (is_admin()) {
1431
  if (
1432
- empty($_POST)
1433
  &&
1434
- isset($_GET['action'])
1435
  &&
1436
  $_GET['action'] === 'edit'
1437
  &&
1438
- isset($_GET['post'])
1439
  ) {
1440
  // Display Edit Post page
1441
  $result = true;
1442
  } elseif (
1443
- isset($_POST['action'])
1444
  &&
1445
  $_POST['action'] === 'editpost'
1446
  &&
1447
- isset($_POST['post_type'])
1448
  &&
1449
- isset($_POST['post_ID'])
1450
  &&
1451
- strpos(wp_get_referer(), 'action=edit') !== false
1452
  ) {
1453
  // Submit Edit Post page
1454
  $result = true;
@@ -1461,12 +1526,13 @@ function fw_is_post_edit() {
1461
 
1462
  /**
1463
  * @param string $dirname 'foo-bar'
 
1464
  * @return string 'Foo_Bar'
1465
  */
1466
- function fw_dirname_to_classname($dirname) {
1467
- $class_name = explode('-', $dirname);
1468
- $class_name = array_map('ucfirst', $class_name);
1469
- $class_name = implode('_', $class_name);
1470
 
1471
  return $class_name;
1472
  }
@@ -1476,20 +1542,21 @@ function fw_dirname_to_classname($dirname) {
1476
  *
1477
  * @param $url
1478
  * @param array $args
 
1479
  * @return bool|string
1480
  */
1481
- function fw_oembed_get($url, $args = array()) {
1482
- $html = wp_oembed_get($url, $args);
1483
 
1484
- if (!empty($args['width']) and !empty($args['height']) and class_exists('DOMDocument') and !empty($html)) {
1485
  $dom_element = new DOMDocument();
1486
- @$dom_element->loadHTML($html);
1487
 
1488
- if ($obj = $dom_element->getElementsByTagName('iframe')->item(0)) {
1489
- $obj->setAttribute('width', $args['width']);
1490
- $obj->setAttribute('height', $args['height']);
1491
  //saveXml instead of SaveHTML for php version compatibility
1492
- $html = $dom_element->saveXML($obj, LIBXML_NOEMPTYTAG);
1493
  }
1494
  }
1495
 
@@ -1506,32 +1573,32 @@ function fw_oembed_get($url, $args = array()) {
1506
  * http://www.zimuel.it/en/strong-cryptography-in-php/
1507
  * > Don't use rand() or mt_rand()
1508
  */
1509
- function fw_secure_rand($length) {
1510
- if (function_exists('openssl_random_pseudo_bytes')) {
1511
- $rnd = openssl_random_pseudo_bytes($length, $strong);
1512
- if ($strong) {
1513
  return $rnd;
1514
  }
1515
  }
1516
 
1517
- $sha ='';
1518
- $rnd ='';
1519
 
1520
- if (file_exists('/dev/urandom')) {
1521
- $fp = fopen('/dev/urandom', 'rb');
1522
- if ($fp) {
1523
- if (function_exists('stream_set_read_buffer')) {
1524
- stream_set_read_buffer($fp, 0);
1525
  }
1526
- $sha = fread($fp, $length);
1527
- fclose($fp);
1528
  }
1529
  }
1530
 
1531
- for ($i = 0; $i < $length; $i++) {
1532
- $sha = hash('sha256', $sha.mt_rand());
1533
- $char = mt_rand(0, 62);
1534
- $rnd .= chr(hexdec($sha[$char].$sha[$char+1]));
1535
  }
1536
 
1537
  return $rnd;
@@ -1539,27 +1606,34 @@ function fw_secure_rand($length) {
1539
 
1540
  /**
1541
  * Try to make user friendly title from an id
 
1542
  * @param string $id 'hello-world'
 
1543
  * @return string 'Hello world'
1544
  */
1545
- function fw_id_to_title($id) {
1546
  // mb_ucfirst()
1547
- if (function_exists('mb_strtoupper') && function_exists('mb_substr') && function_exists('mb_strlen')) {
1548
- $id = mb_strtoupper(mb_substr($id, 0, 1, 'UTF-8'), 'UTF-8') . mb_substr($id, 1, mb_strlen($id, 'UTF-8'), 'UTF-8');
 
 
 
1549
  } else {
1550
- $id = strtoupper(substr($id, 0, 1)) . substr($id, 1, strlen($id));
1551
  }
1552
 
1553
- return str_replace(array('_', '-'), ' ', $id);
1554
  }
1555
 
1556
  /**
1557
  * Alias
 
1558
  * @param string $extension_name
 
1559
  * @return FW_Extension|null
1560
  */
1561
- function fw_ext($extension_name) {
1562
- return fw()->extensions->get($extension_name);
1563
  }
1564
 
1565
  /*
@@ -1571,33 +1645,35 @@ function fw_get_url_without_scheme( $url ) {
1571
 
1572
  /**
1573
  * Try to find file path by its uri and read the file contents
 
1574
  * @param string $file_uri
 
1575
  * @return bool|string false or string - the file contents
1576
  */
1577
- function fw_read_file_by_uri($file_uri) {
1578
  static $base = null;
1579
 
1580
- if ($base === null) {
1581
- $base = array();
1582
- $base['dir'] = WP_CONTENT_DIR;
1583
- $base['uri'] = ltrim(content_url(), '/');
1584
- $base['uri_prefix_regex'] = '/^'. preg_quote($base['uri'], '/') .'/';
1585
  }
1586
 
1587
- $file_rel_path = preg_replace($base['uri_prefix_regex'], '', $file_uri);
1588
 
1589
- if ($base['uri'] === $file_rel_path) {
1590
  // the file is not inside base dir
1591
  return false;
1592
  }
1593
 
1594
- $file_path = $base['dir'] .'/'. $file_rel_path;
1595
 
1596
- if (!file_exists($file_path)) {
1597
  return false;
1598
  }
1599
 
1600
- return file_get_contents($file_path);
1601
  }
1602
 
1603
  /**
@@ -1606,18 +1682,19 @@ function fw_read_file_by_uri($file_uri) {
1606
  *
1607
  * @param string $href 'http://.../style.css'
1608
  * @param null|string $contents If not specified, will try to read from $href
 
1609
  * @return bool|string false - on failure; string - stylesheet contents
1610
  */
1611
- function fw_make_stylesheet_portable($href, $contents = null) {
1612
- if (is_null($contents)) {
1613
- $contents = fw_read_file_by_uri($href);
1614
 
1615
- if ($contents === false) {
1616
  return false;
1617
  }
1618
  }
1619
 
1620
- $dir_uri = dirname($href);
1621
 
1622
  /**
1623
  * Replace relative 'url(img/bg.png)'
@@ -1632,7 +1709,7 @@ function fw_make_stylesheet_portable($href, $contents = null) {
1632
  */
1633
  $contents = preg_replace(
1634
  '/url\s*\((?!\s*[\'"]?(?:\/|data\:|\#|(?:https?:)?\/\/))\s*([\'"])?/',
1635
- 'url($1'. $dir_uri .'/',
1636
  $contents
1637
  );
1638
 
@@ -1643,6 +1720,7 @@ function fw_make_stylesheet_portable($href, $contents = null) {
1643
  * Return all images sizes register by add_image_size() merged with
1644
  * WordPress default image sizes.
1645
  * @link https://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
 
1646
  * @param string $size
1647
  *
1648
  * @return array|bool
@@ -1650,27 +1728,27 @@ function fw_make_stylesheet_portable($href, $contents = null) {
1650
  function fw_get_image_sizes( $size = '' ) {
1651
  global $_wp_additional_image_sizes;
1652
 
1653
- $sizes = array();
1654
  $get_intermediate_image_sizes = get_intermediate_image_sizes();
1655
 
1656
  // Create the full array with sizes and crop info
1657
- foreach( $get_intermediate_image_sizes as $_size ) {
1658
  if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
1659
- $sizes[ $_size ]['width'] = get_option( $_size . '_size_w' );
1660
  $sizes[ $_size ]['height'] = get_option( $_size . '_size_h' );
1661
- $sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' );
1662
  } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
1663
  $sizes[ $_size ] = array(
1664
- 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
1665
  'height' => $_wp_additional_image_sizes[ $_size ]['height'],
1666
- 'crop' => $_wp_additional_image_sizes[ $_size ]['crop']
1667
  );
1668
  }
1669
  }
1670
 
1671
  // Get only 1 size if found
1672
  if ( $size ) {
1673
- if( isset( $sizes[ $size ] ) ) {
1674
  return $sizes[ $size ];
1675
  } else {
1676
  return false;
@@ -1683,44 +1761,45 @@ function fw_get_image_sizes( $size = '' ) {
1683
  /**
1684
  * @param string $icon A string that is meant to be an icon (an image, a font icon class, or something else)
1685
  * @param array Additional attributes
 
1686
  * @return string
1687
  */
1688
- function fw_string_to_icon_html($icon, array $attributes = array()) {
1689
  if (
1690
- preg_match('/\.(png|jpg|jpeg|gif|svg|webp)$/', $icon)
1691
  ||
1692
- preg_match('/^data:image\//', $icon)
1693
  ) {
1694
  // http://.../image.png
1695
- $tag = 'img';
1696
  $attr = array(
1697
  'src' => $icon,
1698
  'alt' => 'icon',
1699
  );
1700
- } elseif (preg_match('/^[a-zA-Z0-9\-_ ]+$/', $icon)) {
1701
  // 'font-icon font-icon-class'
1702
- $tag = 'span';
1703
  $attr = array(
1704
- 'class' => trim($icon),
1705
  );
1706
  } else {
1707
  // can't detect. maybe it's raw html '<span ...'
1708
  return $icon;
1709
  }
1710
 
1711
- foreach ($attributes as $attr_name => $attr_val) {
1712
- if (isset($attr[$attr_name])) {
1713
- if ($attr_name === 'class') {
1714
- $attr[$attr_name] .= ' '. $attr_val;
1715
  } else {
1716
  // ignore. do not overwrite already set attributes
1717
  }
1718
  } else {
1719
- $attr[$attr_name] = (string)$attr_val;
1720
  }
1721
  }
1722
 
1723
- return fw_html_tag($tag, $attr);
1724
  }
1725
 
1726
  /**
@@ -1728,27 +1807,27 @@ function fw_string_to_icon_html($icon, array $attributes = array()) {
1728
  * @since 2.4.10
1729
  */
1730
  function fw_get_json_last_error_message() {
1731
- switch (function_exists('json_last_error') ? json_last_error() : -1) {
1732
  case JSON_ERROR_NONE:
1733
  return null; // __('No errors', 'fw');
1734
  break;
1735
  case JSON_ERROR_DEPTH:
1736
- return __('Maximum stack depth exceeded', 'fw');
1737
  break;
1738
  case JSON_ERROR_STATE_MISMATCH:
1739
- return __('Underflow or the modes mismatch', 'fw');
1740
  break;
1741
  case JSON_ERROR_CTRL_CHAR:
1742
- return __('Unexpected control character found', 'fw');
1743
  break;
1744
  case JSON_ERROR_SYNTAX:
1745
- return __('Syntax error, malformed JSON', 'fw');
1746
  break;
1747
  case JSON_ERROR_UTF8:
1748
- return __('Malformed UTF-8 characters, possibly incorrectly encoded', 'fw');
1749
  break;
1750
  default:
1751
- return __('Unknown error', 'fw');
1752
  break;
1753
  }
1754
  }
@@ -1807,36 +1886,71 @@ if ( ! function_exists( 'fw_resize' ) ) {
1807
 
1808
  /**
1809
  * fw_get_path_url( dirname(__FILE__) .'/test.css' ) --> http://site.url/path/to/test.css
 
1810
  * @param string $path
 
1811
  * @return string|null
1812
  * @since 2.6.11
1813
  */
1814
- function fw_get_path_url($path) {
1815
  try {
1816
- $paths_to_urls = FW_Cache::get($cache_key = 'fw:paths_to_urls');
1817
- } catch (FW_Cache_Not_Found_Exception $e) {
1818
  $wp_upload_dir = wp_upload_dir();
1819
 
1820
  $paths_to_urls = array(
1821
- fw_fix_path(WP_PLUGIN_DIR) => plugins_url(),
1822
- fw_fix_path(get_theme_root()) => get_theme_root_uri(),
1823
- fw_fix_path($wp_upload_dir['basedir']) => $wp_upload_dir['baseurl'],
1824
  );
1825
 
1826
- if (is_multisite() && WPMU_PLUGIN_DIR) {
1827
- $paths_to_urls[ fw_fix_path(WPMU_PLUGIN_DIR) ] = WPMU_PLUGIN_URL;
1828
  }
1829
 
1830
- FW_Cache::set($cache_key, $paths_to_urls);
1831
  }
1832
 
1833
- $path = fw_fix_path($path);
1834
 
1835
- foreach ($paths_to_urls as $_path => $_url) {
1836
- if (preg_match($regex = '/^'. preg_quote($_path, '/') .'($|\/)/', $path)) {
1837
- return $_url .'/'. preg_replace($regex, '', $path);
1838
  }
1839
  }
1840
 
1841
  return null;
1842
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'FW' ) ) {
2
+ die( 'Forbidden' );
3
+ }
4
  // Useful functions
5
 
6
  /**
7
  * Convert to Unix style directory separators
8
  */
9
+ function fw_fix_path( $path ) {
10
+ $windows_network_path = isset( $_SERVER['windir'] ) && in_array( substr( $path, 0, 2 ),
11
+ array( '//', '\\\\' ),
12
+ true );
13
+ $fixed_path = untrailingslashit( str_replace( array( '//', '\\' ), array( '/', '/' ), $path ) );
14
 
15
+ if ( empty( $fixed_path ) && ! empty( $path ) ) {
16
  $fixed_path = '/';
17
  }
18
 
19
+ if ( $windows_network_path ) {
20
+ $fixed_path = '//' . ltrim( $fixed_path, '/' );
21
  }
22
 
23
  return $fixed_path;
25
 
26
  /**
27
  * Relative path of the framework customizations directory
28
+ *
29
  * @param string $append
30
+ *
31
  * @return string
32
  */
33
+ function fw_get_framework_customizations_dir_rel_path( $append = '' ) {
34
  try {
35
+ $dir = FW_Cache::get( $cache_key = 'fw_customizations_dir_rel_path' );
36
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
37
  FW_Cache::set(
38
  $cache_key,
39
+ $dir = apply_filters( 'fw_framework_customizations_dir_rel_path', '/framework-customizations' )
40
  );
41
  }
42
 
47
  {
48
  /**
49
  * Full path to the child-theme framework customizations directory
50
+ *
51
  * @param string $rel_path
52
+ *
53
  * @return null|string
54
  */
55
+ function fw_get_stylesheet_customizations_directory( $rel_path = '' ) {
56
+ if ( is_child_theme() ) {
57
+ return get_stylesheet_directory() . fw_get_framework_customizations_dir_rel_path( $rel_path );
58
  } else {
59
  // check is_child_theme() before using this function
60
  return null;
63
 
64
  /**
65
  * URI to the child-theme framework customizations directory
66
+ *
67
  * @param string $rel_path
68
+ *
69
  * @return null|string
70
  */
71
+ function fw_get_stylesheet_customizations_directory_uri( $rel_path = '' ) {
72
+ if ( is_child_theme() ) {
73
+ return get_stylesheet_directory_uri() . fw_get_framework_customizations_dir_rel_path( $rel_path );
74
  } else {
75
  // check is_child_theme() before using this function
76
  return null;
82
  {
83
  /**
84
  * Full path to the parent-theme framework customizations directory
85
+ *
86
  * @param string $rel_path
87
+ *
88
  * @return string
89
  */
90
+ function fw_get_template_customizations_directory( $rel_path = '' ) {
91
  try {
92
+ $dir = FW_Cache::get( $cache_key = 'fw_template_customizations_dir' );
93
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
94
  FW_Cache::set(
95
  $cache_key,
96
  $dir = get_template_directory() . fw_get_framework_customizations_dir_rel_path()
102
 
103
  /**
104
  * URI to the parent-theme framework customizations directory
105
+ *
106
  * @param string $rel_path
107
+ *
108
  * @return string
109
  */
110
+ function fw_get_template_customizations_directory_uri( $rel_path = '' ) {
111
  try {
112
+ $dir = FW_Cache::get( $cache_key = 'fw_template_customizations_dir_uri' );
113
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
114
  FW_Cache::set(
115
  $cache_key,
116
  $dir = get_template_directory_uri() . fw_get_framework_customizations_dir_rel_path()
125
  {
126
  /**
127
  * Full path to the parent-theme/framework directory
128
+ *
129
  * @param string $rel_path
130
+ *
131
  * @return string
132
  */
133
+ function fw_get_framework_directory( $rel_path = '' ) {
134
  try {
135
+ $dir = FW_Cache::get( $cache_key = 'fw_framework_dir' );
136
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
137
  FW_Cache::set(
138
  $cache_key,
139
  $dir = apply_filters(
140
  'fw_framework_directory',
141
+ fw_fix_path( dirname( dirname( __FILE__ ) ) ) // double dirname() to remove '/helpers', use parent dir
142
  )
143
  );
144
  }
148
 
149
  /**
150
  * URI to the parent-theme/framework directory
151
+ *
152
  * @param string $rel_path
153
+ *
154
  * @return string
155
  */
156
+ function fw_get_framework_directory_uri( $rel_path = '' ) {
157
  try {
158
+ $uri = FW_Cache::get( $cache_key = 'fw_framework_dir_uri' );
159
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
160
  FW_Cache::set(
161
  $cache_key,
162
  $uri = apply_filters(
163
  'fw_framework_directory_uri',
164
+ ( $uri = fw_get_path_url( fw_get_framework_directory() ) )
165
  ? $uri
166
  : get_template_directory_uri() . '/framework'
167
  )
179
  * @param array|object $array_or_object
180
  * @param null|mixed $default_value
181
  * @param string $keys_delimiter
182
+ *
183
  * @return null|mixed
184
  */
185
+ function fw_akg( $keys, $array_or_object, $default_value = null, $keys_delimiter = '/' ) {
186
+ if ( ! is_array( $keys ) ) {
187
  $keys = explode( $keys_delimiter, (string) $keys );
188
  }
189
 
190
+ $array_or_object = fw_call( $array_or_object );
191
+
192
+ $key_or_property = array_shift( $keys );
193
+ if ( $key_or_property === null ) {
194
  return $default_value;
195
  }
196
 
197
+ $is_object = is_object( $array_or_object );
198
 
199
+ if ( $is_object ) {
200
+ if ( ! property_exists( $array_or_object, $key_or_property ) ) {
201
  return $default_value;
202
  }
203
  } else {
204
+ if ( ! is_array( $array_or_object ) || ! array_key_exists( $key_or_property, $array_or_object ) ) {
205
  return $default_value;
206
  }
207
  }
208
 
209
+ if ( isset( $keys[0] ) ) { // not used count() for performance reasons
210
+ if ( $is_object ) {
211
+ return fw_akg( $keys, $array_or_object->{$key_or_property}, $default_value );
212
  } else {
213
+ return fw_akg( $keys, $array_or_object[ $key_or_property ], $default_value );
214
  }
215
  } else {
216
+ if ( $is_object ) {
217
  return $array_or_object->{$key_or_property};
218
  } else {
219
+ return $array_or_object[ $key_or_property ];
220
  }
221
  }
222
  }
229
  * @param array|object $array_or_object
230
  * @param string $keys_delimiter
231
  */
232
+ function fw_aks( $keys, $value, &$array_or_object, $keys_delimiter = '/' ) {
233
+ if ( ! is_array( $keys ) ) {
234
+ $keys = explode( $keys_delimiter, (string) $keys );
235
  }
236
 
237
+ $key_or_property = array_shift( $keys );
238
+ if ( $key_or_property === null ) {
239
  return;
240
  }
241
 
242
+ $is_object = is_object( $array_or_object );
243
 
244
+ if ( $is_object ) {
245
+ if ( ! property_exists( $array_or_object, $key_or_property )
246
+ || ! ( is_array( $array_or_object->{$key_or_property} ) || is_object( $array_or_object->{$key_or_property} ) )
247
  ) {
248
+ if ( $key_or_property === '' ) {
249
  // this happens when use 'empty keys' like: abc/d/e////i/j//foo/
250
+ trigger_error( 'Cannot push value to object like in array ($arr[] = $val)', E_USER_WARNING );
251
  } else {
252
  $array_or_object->{$key_or_property} = array();
253
  }
254
  }
255
  } else {
256
+ if ( ! is_array( $array_or_object ) ) {
257
  $array_or_object = array();
258
  }
259
 
260
+ if ( ! array_key_exists( $key_or_property,
261
+ $array_or_object ) || ! is_array( $array_or_object[ $key_or_property ] )
262
+ ) {
263
+ if ( $key_or_property === '' ) {
264
  // this happens when use 'empty keys' like: abc.d.e....i.j..foo.
265
  $array_or_object[] = array();
266
 
267
  // get auto created key (last)
268
+ end( $array_or_object );
269
+ $key_or_property = key( $array_or_object );
270
  } else {
271
+ $array_or_object[ $key_or_property ] = array();
272
  }
273
  }
274
  }
275
 
276
+ if ( isset( $keys[0] ) ) { // not used count() for performance reasons
277
+ if ( $is_object ) {
278
+ fw_aks( $keys, $value, $array_or_object->{$key_or_property} );
279
  } else {
280
+ fw_aks( $keys, $value, $array_or_object[ $key_or_property ] );
281
  }
282
  } else {
283
+ if ( $is_object ) {
284
  $array_or_object->{$key_or_property} = $value;
285
  } else {
286
+ $array_or_object[ $key_or_property ] = $value;
287
  }
288
  }
289
  }
295
  * @param array|object $array_or_object
296
  * @param string $keys_delimiter
297
  */
298
+ function fw_aku( $keys, &$array_or_object, $keys_delimiter = '/' ) {
299
+ if ( ! is_array( $keys ) ) {
300
+ $keys = explode( $keys_delimiter, (string) $keys );
301
  }
302
 
303
+ $key_or_property = array_shift( $keys );
304
+ if ( $key_or_property === null || $key_or_property === '' ) {
305
  return;
306
  }
307
 
308
+ $is_object = is_object( $array_or_object );
309
 
310
+ if ( $is_object ) {
311
+ if ( ! property_exists( $array_or_object, $key_or_property ) ) {
312
  return;
313
  }
314
  } else {
315
+ if ( ! is_array( $array_or_object ) || ! array_key_exists( $key_or_property, $array_or_object ) ) {
316
  return;
317
  }
318
  }
319
 
320
+ if ( isset( $keys[0] ) ) { // not used count() for performance reasons
321
+ if ( $is_object ) {
322
+ fw_aku( $keys, $array_or_object->{$key_or_property} );
323
  } else {
324
+ fw_aku( $keys, $array_or_object[ $key_or_property ] );
325
  }
326
  } else {
327
+ if ( $is_object ) {
328
+ unset( $array_or_object->{$key_or_property} );
329
  } else {
330
+ unset( $array_or_object[ $key_or_property ] );
331
  }
332
 
333
  return;
338
  * Generate random unique md5
339
  */
340
  function fw_rand_md5() {
341
+ return md5( time() . '-' . uniqid( rand(), true ) . '-' . mt_rand( 1, 1000 ) );
342
  }
343
 
344
  function fw_unique_increment() {
345
  static $i = 0;
346
+
347
+ return ++ $i;
348
  }
349
 
350
  /**
352
  *
353
  * @param mixed $value Value to debug
354
  */
355
+ function fw_print( $value ) {
356
  static $first_time = true;
357
 
358
+ if ( $first_time ) {
359
  ob_start();
360
  echo '<style type="text/css">
361
  div.fw_print_r {
396
  border-width: 0;
397
  }
398
  </style>';
399
+ echo str_replace( array( ' ', "\n" ), '', ob_get_clean() );
400
 
401
  $first_time = false;
402
  }
403
 
404
+ if ( func_num_args() == 1 ) {
405
  echo '<div class="fw_print_r"><pre>';
406
+ echo fw_htmlspecialchars( FW_Dumper::dump( $value ) );
407
  echo '</pre></div>';
408
  } else {
409
  echo '<div class="fw_print_r_group">';
410
+ foreach ( func_get_args() as $param ) {
411
+ fw_print( $param );
412
  }
413
  echo '</div>';
414
  }
420
  * @param string $tag Tag name
421
  * @param array $attr Tag attributes
422
  * @param bool|string $end Append closing tag. Also accepts body content
423
+ *
424
  * @return string The tag's html
425
  */
426
+ function fw_html_tag( $tag, $attr = array(), $end = false ) {
427
+ $html = '<' . $tag . ' ' . fw_attr_to_html( $attr );
428
 
429
+ if ( $end === true ) {
430
  # <script></script>
431
+ $html .= '></' . $tag . '>';
432
+ } else if ( $end === false ) {
433
  # <br/>
434
  $html .= '/>';
435
  } else {
436
  # <div>content</div>
437
+ $html .= '>' . $end . '</' . $tag . '>';
438
  }
439
 
440
  return $html;
442
 
443
  /**
444
  * Generate attributes string for html tag
445
+ *
446
  * @param array $attr_array array('href' => '/', 'title' => 'Test')
447
+ *
448
  * @return string 'href="/" title="Test"'
449
  */
450
+ function fw_attr_to_html( array $attr_array ) {
451
  $html_attr = '';
452
 
453
+ foreach ( $attr_array as $attr_name => $attr_val ) {
454
+ if ( $attr_val === false ) {
455
  continue;
456
  }
457
 
458
+ $html_attr .= $attr_name . '="' . fw_htmlspecialchars( $attr_val ) . '" ';
459
  }
460
 
461
  return $html_attr;
464
  /**
465
  * Strip slashes from values, and from keys if magic_quotes_gpc = On
466
  */
467
+ function fw_stripslashes_deep_keys( $value ) {
468
  static $magic_quotes = null;
469
+ if ( $magic_quotes === null ) {
470
  $magic_quotes = get_magic_quotes_gpc();
471
  }
472
 
473
+ if ( is_array( $value ) ) {
474
+ if ( $magic_quotes ) {
475
  $new_value = array();
476
+ foreach ( $value as $key => $val ) {
477
+ $new_value[ is_string( $key ) ? stripslashes( $key ) : $key ] = fw_stripslashes_deep_keys( $val );
478
  }
479
  $value = $new_value;
480
+ unset( $new_value );
481
  } else {
482
+ $value = array_map( 'fw_stripslashes_deep_keys', $value );
483
  }
484
+ } elseif ( is_object( $value ) ) {
485
+ $vars = get_object_vars( $value );
486
+ foreach ( $vars as $key => $data ) {
487
+ $value->{$key} = fw_stripslashes_deep_keys( $data );
488
  }
489
+ } elseif ( is_string( $value ) ) {
490
+ $value = stripslashes( $value );
491
  }
492
 
493
  return $value;
496
  /**
497
  * Add slashes to values, and to keys if magic_quotes_gpc = On
498
  */
499
+ function fw_addslashes_deep_keys( $value ) {
500
  static $magic_quotes = null;
501
+ if ( $magic_quotes === null ) {
502
  $magic_quotes = get_magic_quotes_gpc();
503
  }
504
 
505
+ if ( is_array( $value ) ) {
506
+ if ( $magic_quotes ) {
507
  $new_value = array();
508
+ foreach ( $value as $key => $value ) {
509
+ $new_value[ is_string( $key ) ? addslashes( $key ) : $key ] = fw_addslashes_deep_keys( $value );
510
  }
511
  $value = $new_value;
512
+ unset( $new_value );
513
  } else {
514
+ $value = array_map( 'fw_addslashes_deep_keys', $value );
515
  }
516
+ } elseif ( is_object( $value ) ) {
517
+ $vars = get_object_vars( $value );
518
+ foreach ( $vars as $key => $data ) {
519
+ $value->{$key} = fw_addslashes_deep_keys( $data );
520
  }
521
+ } elseif ( is_string( $value ) ) {
522
+ $value = addslashes( $value );
523
  }
524
 
525
  return $value;
527
 
528
  /**
529
  * Check if current screen pass/match give rules
530
+ *
531
  * @param array $rules Rules for current screen
532
+ *
533
  * @return bool
534
  */
535
+ function fw_current_screen_match( array $rules ) {
536
  $available_options = array(
537
  'action' => true,
538
  'base' => true,
545
  'taxonomy' => true,
546
  );
547
 
548
+ if ( empty( $rules ) ) {
549
  return true;
550
  }
551
 
557
  $rules
558
  );
559
 
560
+ if ( empty( $rules['exclude'] ) && empty( $rules['only'] ) ) {
561
  return true;
562
  }
563
 
564
  global $current_screen;
565
 
566
+ if ( gettype( $current_screen ) != 'object' ) {
567
  return false;
568
  }
569
 
571
  do {
572
  $only = $rules['only'];
573
 
574
+ if ( empty( $only ) ) {
575
  break;
576
  }
577
 
578
+ if ( ! isset( $only[0] ) ) { // if not array of arrays
579
+ $only = array( $only );
580
  }
581
 
582
  $found_one = false;
583
+ $counter = 0;
584
+ foreach ( $only as $rule ) {
585
+ if ( ! count( $rule ) ) {
586
  continue;
587
  }
588
 
589
  $match = true;
590
 
591
+ foreach ( $rule as $r_key => $r_val ) {
592
+ if ( ! isset( $available_options[ $r_key ] ) ) {
593
  continue;
594
  }
595
 
596
+ if ( gettype( $r_val ) != 'array' ) {
597
+ $r_val = array( $r_val );
598
  }
599
 
600
+ $counter ++;
601
 
602
+ if ( ! in_array( $current_screen->{$r_key}, $r_val ) ) {
603
  $match = false;
604
  break;
605
  }
606
  }
607
 
608
+ if ( $match ) {
609
  $found_one = true;
610
  break;
611
  }
612
  }
613
 
614
+ if ( ! $found_one && $counter ) {
615
  return false;
616
  }
617
+ } while ( false );
618
 
619
  // check if current screen passes the "exclude" rules
620
  do {
621
  $exclude = $rules['exclude'];
622
 
623
+ if ( empty( $exclude ) ) {
624
  break;
625
  }
626
 
627
+ if ( ! isset( $exclude[0] ) ) { // if not array of arrays
628
+ $exclude = array( $exclude );
629
  }
630
 
631
+ foreach ( $exclude as $rule ) {
632
+ if ( ! count( $rule ) ) {
633
  continue;
634
  }
635
 
636
  $match = true;
637
  $counter = 0;
638
 
639
+ foreach ( $rule as $r_key => $r_val ) {
640
+ if ( ! isset( $available_options[ $r_key ] ) ) {
641
  continue;
642
  }
643
 
644
+ if ( gettype( $r_val ) != 'array' ) {
645
+ $r_val = array( $r_val );
646
  }
647
 
648
+ $counter ++;
649
 
650
+ if ( ! in_array( $current_screen->{$r_key}, $r_val ) ) {
651
  $match = false;
652
  break;
653
  }
654
  }
655
 
656
+ if ( $match && $counter ) {
657
  return false;
658
  }
659
  }
660
+ } while ( false );
661
 
662
  return true;
663
  }
666
  * Search relative path in child then in parent theme directory and return URI
667
  *
668
  * @param string $rel_path '/some/path_to_dir' or '/some/path_to_file.php'
669
+ *
670
  * @return string URI
671
  */
672
+ function fw_locate_theme_path_uri( $rel_path ) {
673
+ if ( is_child_theme() && file_exists( get_stylesheet_directory() . $rel_path ) ) {
674
  return get_stylesheet_directory_uri() . $rel_path;
675
+ } elseif ( file_exists( get_template_directory() . $rel_path ) ) {
676
  return get_template_directory_uri() . $rel_path;
677
  } else {
678
+ return 'about:blank#theme-file-not-found:' . $rel_path;
679
  }
680
  }
681
 
683
  * Search relative path in child then in parent theme directory and return full path
684
  *
685
  * @param string $rel_path '/some/path_to_dir' or '/some/path_to_file.php'
686
+ *
687
  * @return string URI
688
  */
689
+ function fw_locate_theme_path( $rel_path ) {
690
+ if ( is_child_theme() && file_exists( get_stylesheet_directory() . $rel_path ) ) {
691
  return get_stylesheet_directory() . $rel_path;
692
+ } elseif ( file_exists( get_template_directory() . $rel_path ) ) {
693
  return get_template_directory() . $rel_path;
694
  } else {
695
  return false;
702
  * https://github.com/ThemeFuse/Unyson/commit/07be8b1f4b50eaf0f1f7e85ea1c6912a0415d241#diff-cf866bf08b8f747e3120221a6b1b07cfR48
703
  * it throws fatal error because this function here is defined after that
704
  */
705
+ if ( ! function_exists( 'fw_render_view' ) ):
706
+ /**
707
+ * Safe render a view and return html
708
+ * In view will be accessible only passed variables
709
+ * Use this function to not include files directly and to not give access to current context variables (like $this)
710
+ *
711
+ * @param string $file_path
712
+ * @param array $view_variables
713
+ * @param bool $return In some cases, for memory saving reasons, you can disable the use of output buffering
714
+ *
715
+ * @return string HTML
716
+ */
717
+ function fw_render_view( $file_path, $view_variables = array(), $return = true ) {
718
+ extract( $view_variables, EXTR_REFS );
719
+ unset( $view_variables );
720
 
721
+ if ( $return ) {
722
+ ob_start();
723
+ require $file_path;
724
+
725
+ return ob_get_clean();
726
+ } else {
727
+ require $file_path;
728
+ }
729
  }
 
730
  endif;
731
 
732
  /**
733
  * Safe load variables from an file
734
  * Use this function to not include files directly and to not give access to current context variables (like $this)
735
+ *
736
  * @param string $file_path
737
  * @param array $_extract_variables Extract these from file array('variable_name' => 'default_value')
738
  * @param array $_set_variables Set these to be available in file (like variables in view)
739
+ *
740
  * @return array
741
  */
742
+ function fw_get_variables_from_file( $file_path, array $_extract_variables, array $_set_variables = array() ) {
743
+ extract( $_set_variables, EXTR_REFS );
744
+ unset( $_set_variables );
745
 
746
  require $file_path;
747
 
748
+ foreach ( $_extract_variables as $variable_name => $default_value ) {
749
+ if ( isset( $$variable_name ) ) {
750
+ $_extract_variables[ $variable_name ] = $$variable_name;
751
  }
752
  }
753
 
756
 
757
  /**
758
  * Use this function to not include files directly and to not give access to current context variables (like $this)
759
+ *
760
  * @param string $file_path
761
  * @param bool $once
762
+ *
763
  * @return bool If was included or not
764
  */
765
+ function fw_include_file_isolated( $file_path, $once = false ) {
766
+ if ( file_exists( $file_path ) ) {
767
  if ( (bool) $once ) {
768
  include_once $file_path;
769
  } else {
770
  include $file_path;
771
  }
772
+
773
  return true;
774
  } else {
775
  return false;
778
 
779
  /**
780
  * Extract only input options (without containers)
781
+ *
782
  * @param array $options
783
+ *
784
  * @return array {option_id => option}
785
  */
786
+ function fw_extract_only_options( array $options ) {
787
  $collected = array();
788
 
789
+ fw_collect_options( $collected, $options );
790
 
791
  return $collected;
792
  }
793
 
794
  /**
795
  * Collect correct options from the first level of the array and group them
796
+ *
797
  * @param array $collected Will be filled with found correct options
798
  * @param array $options
799
  *
802
  * but this function is hardcoded only for tab,box,group.
803
  * Use fw_collect_options()
804
  */
805
+ function fw_collect_first_level_options( &$collected, &$options ) {
806
+ if ( empty( $options ) ) {
807
  return;
808
  }
809
 
810
+ if ( empty( $collected ) ) {
811
  $collected['tabs'] =
812
  $collected['boxes'] =
813
  $collected['groups'] =
816
  $collected['all'] = array();
817
  }
818
 
819
+ foreach ( $options as $option_id => &$option ) {
820
+ if ( isset( $option['options'] ) ) {
821
  // this is container for other options
822
 
823
+ switch ( $option['type'] ) {
824
  case 'tab':
825
+ $collected['tabs'][ $option_id ] =& $option;
826
  break;
827
  case 'box':
828
+ $collected['boxes'][ $option_id ] =& $option;
829
  break;
830
  case 'group':
831
+ $collected['groups'][ $option_id ] =& $option;
832
+ $collected['groups_and_options'][ $option_id ] =& $option;
833
  break;
834
  default:
835
+ trigger_error( 'Invalid option container type: ' . $option['type'], E_USER_WARNING );
836
  continue 2;
837
  }
838
 
839
+ $collected['all'][ $option['type'] . ':~:' . $option_id ] = array(
840
  'type' => $option['type'],
841
  'id' => $option_id,
842
  'option' => &$option,
843
  );
844
  } elseif (
845
+ is_int( $option_id )
846
  &&
847
+ is_array( $option )
848
  &&
849
  /**
850
  * make sure the array key was generated automatically
851
  * and it's not an associative array with numeric keys created like this: $options[1] = array();
852
  */
853
+ isset( $options[0] )
854
  ) {
855
  /**
856
  * Array "without key" containing options.
876
  * ),
877
  * )
878
  */
879
+ fw_collect_first_level_options( $collected, $option );
880
+ } elseif ( isset( $option['type'] ) ) {
881
  // simple option, last possible level in options array
882
+ $collected['options'][ $option_id ] =& $option;
883
+ $collected['groups_and_options'][ $option_id ] =& $option;
884
 
885
+ $collected['all'][ 'option' . ':~:' . $option_id ] = array(
886
  'type' => 'option',
887
  'id' => $option_id,
888
  'option' => &$option,
889
  );
890
  } else {
891
+ trigger_error( 'Invalid option: ' . $option_id, E_USER_WARNING );
892
  }
893
  }
894
+ unset( $option );
895
  }
896
 
897
  /**
900
  * @param array $settings
901
  * @param array $_recursion_data (private) for internal use
902
  */
903
+ function fw_collect_options( &$result, &$options, $settings = array(), $_recursion_data = array() ) {
904
  static $default_settings = array(
905
  /**
906
  * @type bool Wrap the result/collected options in arrays will useful info
921
  * // Warning: There can be options and containers with the same id (array key will be replaced)
922
  * )
923
  */
924
+ 'info_wrapper' => false,
925
  /**
926
  * @type int Nested options level limit. For e.g. use 1 to collect only first level. 0 is for unlimited.
927
  */
928
+ 'limit_level' => 0,
929
  /**
930
  * @type false|array('option-type', ...) Empty array will skip all types
931
  */
932
+ 'limit_option_types' => false,
933
  /**
934
  * @type false|array('container-type', ...) Empty array will skip all types
935
  */
937
  /**
938
  * @type int Limit the number of options that will be collected
939
  */
940
+ 'limit' => 0,
941
  /**
942
  * @type callable Executed on each collected option
943
  * @since 2.6.0
944
  */
945
+ 'callback' => null,
946
  );
947
 
948
  static $access_key = null;
949
 
950
+ if ( empty( $options ) ) {
951
  return;
952
  }
953
 
954
+ if ( empty( $_recursion_data ) ) {
955
+ if ( is_null( $access_key ) ) {
956
+ $access_key = new FW_Access_Key( 'fw_collect_options' );
957
  }
958
 
959
+ $settings = array_merge( $default_settings, $settings );
960
 
961
  $_recursion_data = array(
962
+ 'level' => 1,
963
  'access_key' => $access_key,
964
  // todo: maybe add 'parent' => array('id' => '{id}', 'type' => 'container|option') ?
965
  );
966
+ } elseif ( ! (
967
+ isset( $_recursion_data['access_key'] )
968
  &&
969
+ ( $_recursion_data['access_key'] instanceof FW_Access_Key )
970
  &&
971
+ ( $_recursion_data['access_key']->get_key() === 'fw_collect_options' )
972
+ )
973
+ ) {
974
+ trigger_error( 'Call not allowed', E_USER_ERROR );
975
  }
976
 
977
  if (
982
  return;
983
  }
984
 
985
+ foreach ( $options as $option_id => &$option ) {
986
+ if ( isset( $option['options'] ) ) { // this is a container
987
  do {
988
  if (
989
+ is_array( $settings['limit_container_types'] )
990
  &&
991
  (
992
  // Customizer options can contain options with not existing or empty $option['type']
993
+ empty( $option['type'] )
994
  ||
995
+ ! in_array( $option['type'], $settings['limit_container_types'] )
996
  )
997
  ) {
998
  break;
1001
  if (
1002
  $settings['limit']
1003
  &&
1004
+ count( $result ) >= $settings['limit']
1005
  ) {
1006
  return;
1007
  }
1008
 
1009
+ if ( $settings['info_wrapper'] ) {
1010
+ $result[ 'container:' . $option_id ] = array(
1011
  'group' => 'container',
1012
  'id' => $option_id,
1013
  'option' => &$option,
1014
  'level' => $_recursion_data['level'],
1015
  );
1016
  } else {
1017
+ $result[ $option_id ] = &$option;
1018
  }
1019
 
1020
+ if ( $settings['callback'] ) {
1021
+ call_user_func_array( $settings['callback'],
1022
+ array(
1023
+ array(
1024
+ 'group' => 'container',
1025
+ 'id' => $option_id,
1026
+ 'option' => &$option,
1027
+ )
1028
+ ) );
1029
  }
1030
+ } while ( false );
1031
 
1032
  fw_collect_options(
1033
  $result,
1034
  $option['options'],
1035
  $settings,
1036
+ array_merge( $_recursion_data, array( 'level' => $_recursion_data['level'] + 1 ) )
1037
  );
1038
  } elseif (
1039
+ is_int( $option_id )
1040
  &&
1041
+ is_array( $option )
1042
  &&
1043
  /**
1044
  * make sure the array key was generated automatically
1045
  * and it's not an associative array with numeric keys created like this: $options[1] = array();
1046
  */
1047
+ isset( $options[0] )
1048
  ) {
1049
  /**
1050
  * Array "without key" containing options.
1070
  * ),
1071
  * )
1072
  */
1073
+ fw_collect_options( $result, $option, $settings, $_recursion_data );
1074
+ } elseif ( isset( $option['type'] ) ) { // option
1075
  if (
1076
+ is_array( $settings['limit_option_types'] )
1077
  &&
1078
+ ! in_array( $option['type'], $settings['limit_option_types'] )
1079
  ) {
1080
  continue;
1081
  }
1083
  if (
1084
  $settings['limit']
1085
  &&
1086
+ count( $result ) >= $settings['limit']
1087
  ) {
1088
  return;
1089
  }
1090
 
1091
+ if ( $settings['info_wrapper'] ) {
1092
+ $result[ 'option:' . $option_id ] = array(
1093
  'group' => 'option',
1094
  'id' => $option_id,
1095
  'option' => &$option,
1096
  'level' => $_recursion_data['level'],
1097
  );
1098
  } else {
1099
+ $result[ $option_id ] = &$option;
1100
  }
1101
 
1102
+ if ( $settings['callback'] ) {
1103
+ call_user_func_array( $settings['callback'],
1104
+ array(
1105
+ array(
1106
+ 'group' => 'option',
1107
+ 'id' => $option_id,
1108
+ 'option' => &$option,
1109
+ )
1110
+ ) );
1111
  }
1112
  } else {
1113
+ trigger_error( 'Invalid option: ' . $option_id, E_USER_WARNING );
1114
  }
1115
  }
1116
  }
1118
  /**
1119
  * Get correct values from input (POST) for given options
1120
  * This values can be saved in db then replaced with $option['value'] for each option
1121
+ *
1122
  * @param array $options
1123
  * @param array $input_array
1124
+ *
1125
  * @return array Values
1126
  */
1127
+ function fw_get_options_values_from_input( array $options, $input_array = null ) {
1128
+ if ( ! is_array( $input_array ) ) {
1129
+ $input_array = FW_Request::POST( fw()->backend->get_options_name_attr_prefix() );
1130
  }
1131
 
1132
  $values = array();
1133
 
1134
+ foreach ( fw_extract_only_options( $options ) as $id => $option ) {
1135
+ $values[ $id ] = fw()->backend->option_type( $option['type'] )->get_value_from_input(
1136
  $option,
1137
+ isset( $input_array[ $id ] ) ? $input_array[ $id ] : null
1138
  );
1139
 
1140
+ if ( is_null( $values[ $id ] ) ) {
1141
  // do not save null values
1142
+ unset( $values[ $id ] );
1143
  }
1144
  }
1145
 
1149
  /**
1150
  * @param $attr_name
1151
  * @param bool $set_mode
1152
+ *
1153
  * @return mixed
1154
  */
1155
+ function fw_html_attr_name_to_array_multi_key( $attr_name, $set_mode = false ) {
1156
+ if ( $set_mode ) {
1157
  /**
1158
  * The key will be used to set value in array
1159
  * 'hello[world][]' -> 'hello/world/'
1160
  * $array['hello']['world'][] = $value;
1161
  */
1162
+ $attr_name = str_replace( '[]', '/', $attr_name );
1163
  } else {
1164
  /**
1165
  * The key will be used to get value from array
1166
  * 'hello[world][]' -> 'hello/world'
1167
  * $value = $array['hello']['world'];
1168
  */
1169
+ $attr_name = str_replace( '[]', '', $attr_name );
1170
  }
1171
 
1172
+ $attr_name = str_replace( '][', '/', $attr_name );
1173
+ $attr_name = str_replace( '[', '/', $attr_name );
1174
+ $attr_name = str_replace( ']', '', $attr_name );
1175
 
1176
  return $attr_name;
1177
  }
1180
  * Used when getting some option value from serialized array saved in a custom place
1181
  * and that option is unreachable for standard WordPress filters by other plugins
1182
  * For e.g. that option cannot be translated by plugins, so we pass its value through this function and do the fixes
1183
+ *
1184
  * @param $value
1185
+ *
1186
  * @return array
1187
  */
1188
+ function fw_prepare_option_value( $value ) {
1189
+ if ( empty( $value ) ) {
1190
  return $value;
1191
  }
1192
 
1193
+ if ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
1194
+ $value = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $value );
1195
  }
1196
 
1197
  return $value;
1204
  * not a revision, auto-save or something else
1205
  *
1206
  * @param $post_id
1207
+ *
1208
  * @return bool
1209
  *
1210
  * @deprecated
1213
  * the developers should study and understand better how it works
1214
  * and handle different save cases in their scripts using wp functions
1215
  */
1216
+ function fw_is_real_post_save( $post_id ) {
1217
+ return ! (
1218
+ wp_is_post_revision( $post_id )
1219
+ || wp_is_post_autosave( $post_id )
1220
+ || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
1221
+ || ( defined( 'DOING_AJAX' ) && DOING_AJAX )
1222
+ || empty( $_POST )
1223
+ || empty( $_POST['post_ID'] )
1224
  || $_POST['post_ID'] != $post_id
1225
  );
1226
  }
1232
  $cache_key = 'fw_google_fonts';
1233
 
1234
  try {
1235
+ return FW_Cache::get( $cache_key );
1236
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
1237
+ $g_fonts = json_decode( fw_get_google_fonts_v2(), true );
1238
+ $old_fonts = include( dirname( __FILE__ ) . '/fw-google-fonts.json.php' );
1239
+ $fonts = array();
1240
 
1241
  foreach ( $g_fonts['items'] as $font ) {
1242
  $fonts[ $font['family'] ] = array(
1243
+ 'family' => $font['family'],
1244
  'variants' => $font['variants'],
1245
+ 'position' => isset( $old_fonts[ $font['family'] ] )
1246
+ ? $old_fonts[ $font['family'] ]['position']
1247
  : 99999
1248
  );
1249
  }
1250
 
1251
+ $fonts = apply_filters( 'fw_google_fonts', $fonts );
1252
 
1253
+ FW_Cache::set( $cache_key, $fonts );
1254
 
1255
  return $fonts;
1256
  }
1261
  */
1262
  function fw_get_google_fonts_v2() {
1263
  $saved_data = get_option( 'fw_google_fonts', false );
1264
+ $ttl = 7 * DAY_IN_SECONDS;
1265
 
1266
  if (
1267
  false === $saved_data
1268
  ||
1269
  ( $saved_data['last_update'] + $ttl < time() )
1270
  ) {
1271
+ $response = wp_remote_get( apply_filters( 'fw_googleapis_webfonts_url',
1272
+ 'http://google-webfonts-cache.unyson.io/v1/webfonts' ) );
1273
  $body = wp_remote_retrieve_body( $response );
1274
 
1275
  if (
1277
  &&
1278
  ! is_wp_error( $body ) && ! empty( $body )
1279
  ) {
1280
+ update_option( 'fw_google_fonts',
1281
+ array(
1282
+ 'last_update' => time(),
1283
+ 'fonts' => $body
1284
+ ),
1285
+ false );
1286
 
1287
  return $body;
1288
  } else {
1290
  $saved_data['fonts'] = json_encode( array( 'items' => array() ) );
1291
  }
1292
 
1293
+ update_option( 'fw_google_fonts',
1294
+ array(
1295
+ 'last_update' => time() - $ttl + MINUTE_IN_SECONDS,
1296
+ 'fonts' => $saved_data['fonts']
1297
+ ),
1298
+ false );
1299
  }
1300
  }
1301
 
1308
  function fw_current_url() {
1309
  static $url = null;
1310
 
1311
+ if ( $url === null ) {
1312
  $url = 'http://';
1313
 
1314
+ if ( $_SERVER['SERVER_NAME'] === '_' ) { // https://github.com/ThemeFuse/Unyson/issues/126
1315
  $url .= $_SERVER['HTTP_HOST'];
1316
  } else {
1317
  $url .= $_SERVER['SERVER_NAME'];
1318
  }
1319
 
1320
+ if ( ! in_array( intval( $_SERVER['SERVER_PORT'] ), array( 80, 443 ) ) ) {
1321
+ $url .= ':' . $_SERVER['SERVER_PORT'];
1322
  }
1323
 
1324
  $url .= $_SERVER['REQUEST_URI'];
1325
 
1326
+ $url = set_url_scheme( $url ); // https fix
1327
 
1328
  if ( is_multisite() ) {
1329
  if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
1330
+ $site_url = parse_url( $url );
1331
 
1332
+ if ( isset( $site_url['query'] ) ) {
1333
+ $url = home_url( $site_url['path'] . '?' . $site_url['query'] );
1334
  } else {
1335
+ $url = home_url( $site_url['path'] );
1336
  }
1337
  }
1338
  }
1341
  return $url;
1342
  }
1343
 
1344
+ function fw_is_valid_domain_name( $domain_name ) {
1345
+ return ( preg_match( "/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name ) // valid chars check
1346
+ && preg_match( "/^.{1,253}$/", $domain_name ) // overall length check
1347
+ && preg_match( "/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name ) ); // length of each label
1348
  }
1349
 
1350
  /**
1352
  * Info: Cannot use default parameters because in php 5.2 encoding is not UTF-8 by default
1353
  *
1354
  * @param string $string
1355
+ *
1356
  * @return string
1357
  */
1358
+ function fw_htmlspecialchars( $string ) {
1359
+ return htmlspecialchars( $string, ENT_QUOTES, 'UTF-8' );
1360
  }
1361
 
1362
  /**
1364
  *
1365
  * @param array $capabilities list of capabilities to check
1366
  * @param mixed $default_value
1367
+ *
1368
  * @return string|bool|mixed
1369
  * Return first capability that user can.
1370
  * Else, return default value if it is not null, else return first capability from list.
1371
  * Use default value false to check if user can some of the capabilities
1372
  */
1373
+ function fw_current_user_can( $capabilities, $default_value = null ) {
1374
+ if ( is_user_logged_in() ) {
1375
+ foreach ( $capabilities as $capability ) {
1376
+ if ( current_user_can( $capability ) ) {
 
1377
  return $capability;
1378
+ }
1379
  }
1380
  }
1381
 
1382
+ return ( $default_value !== null ? $default_value : array_shift( $capabilities ) );
1383
  }
1384
 
1385
  /**
1389
  * then you can use this string how you want, for e.g. append ' ago' => '2 minutes ago'
1390
  *
1391
  * @param int $seconds
1392
+ *
1393
  * @return string
1394
  */
1395
+ function fw_human_time( $seconds ) {
1396
  static $translations = null;
1397
+ if ( $translations === null ) {
1398
  $translations = array(
1399
+ 'year' => __( 'year', 'fw' ),
1400
+ 'years' => __( 'years', 'fw' ),
1401
 
1402
+ 'month' => __( 'month', 'fw' ),
1403
+ 'months' => __( 'months', 'fw' ),
1404
 
1405
+ 'week' => __( 'week', 'fw' ),
1406
+ 'weeks' => __( 'weeks', 'fw' ),
1407
 
1408
+ 'day' => __( 'day', 'fw' ),
1409
+ 'days' => __( 'days', 'fw' ),
1410
 
1411
+ 'hour' => __( 'hour', 'fw' ),
1412
+ 'hours' => __( 'hours', 'fw' ),
1413
 
1414
+ 'minute' => __( 'minute', 'fw' ),
1415
+ 'minutes' => __( 'minutes', 'fw' ),
1416
 
1417
+ 'second' => __( 'second', 'fw' ),
1418
+ 'seconds' => __( 'seconds', 'fw' ),
1419
  );
1420
  }
1421
 
1422
+ $tokens = array(
1423
  31536000 => 'year',
1424
+ 2592000 => 'month',
1425
+ 604800 => 'week',
1426
+ 86400 => 'day',
1427
+ 3600 => 'hour',
1428
+ 60 => 'minute',
1429
+ 1 => 'second'
1430
  );
1431
 
1432
+ foreach ( $tokens as $unit => $translation_key ) {
1433
+ if ( $seconds < $unit ) {
1434
  continue;
1435
+ }
1436
 
1437
+ $number_of_units = floor( $seconds / $unit );
1438
 
1439
+ return $number_of_units . ' ' . $translations[ $translation_key . ( $number_of_units != 1 ? 's' : '' ) ];
1440
  }
1441
  }
1442
 
1445
  *
1446
  * @param integer $bytes Size in bytes to convert
1447
  * @param integer $precision
1448
+ *
1449
  * @return string
1450
  * @since 2.4.17
1451
  */
1452
+ function fw_human_bytes( $bytes, $precision = 2 ) {
1453
  $kilobyte = 1024;
1454
  $megabyte = $kilobyte * 1024;
1455
  $gigabyte = $megabyte * 1024;
1456
  $terabyte = $gigabyte * 1024;
1457
 
1458
+ if ( ( $bytes >= 0 ) && ( $bytes < $kilobyte ) ) {
1459
  return $bytes . ' B';
1460
 
1461
+ } elseif ( ( $bytes >= $kilobyte ) && ( $bytes < $megabyte ) ) {
1462
+ return round( $bytes / $kilobyte, $precision ) . ' KB';
1463
 
1464
+ } elseif ( ( $bytes >= $megabyte ) && ( $bytes < $gigabyte ) ) {
1465
+ return round( $bytes / $megabyte, $precision ) . ' MB';
1466
 
1467
+ } elseif ( ( $bytes >= $gigabyte ) && ( $bytes < $terabyte ) ) {
1468
+ return round( $bytes / $gigabyte, $precision ) . ' GB';
1469
 
1470
+ } elseif ( $bytes >= $terabyte ) {
1471
+ return round( $bytes / $terabyte, $precision ) . ' TB';
1472
  } else {
1473
  return $bytes . ' B';
1474
  }
1475
  }
1476
 
1477
+ function fw_strlen( $string ) {
1478
+ if ( function_exists( 'mb_strlen' ) ) {
1479
+ return mb_strlen( $string, 'UTF-8' );
1480
  } else {
1481
+ return strlen( $string );
1482
  }
1483
  }
1484
 
1489
  function fw_is_post_edit() {
1490
  static $result = null;
1491
 
1492
+ if ( $result === null ) {
1493
  $result = false;
1494
 
1495
+ if ( is_admin() ) {
1496
  if (
1497
+ empty( $_POST )
1498
  &&
1499
+ isset( $_GET['action'] )
1500
  &&
1501
  $_GET['action'] === 'edit'
1502
  &&
1503
+ isset( $_GET['post'] )
1504
  ) {
1505
  // Display Edit Post page
1506
  $result = true;
1507
  } elseif (
1508
+ isset( $_POST['action'] )
1509
  &&
1510
  $_POST['action'] === 'editpost'
1511
  &&
1512
+ isset( $_POST['post_type'] )
1513
  &&
1514
+ isset( $_POST['post_ID'] )
1515
  &&
1516
+ strpos( wp_get_referer(), 'action=edit' ) !== false
1517
  ) {
1518
  // Submit Edit Post page
1519
  $result = true;
1526
 
1527
  /**
1528
  * @param string $dirname 'foo-bar'
1529
+ *
1530
  * @return string 'Foo_Bar'
1531
  */
1532
+ function fw_dirname_to_classname( $dirname ) {
1533
+ $class_name = explode( '-', $dirname );
1534
+ $class_name = array_map( 'ucfirst', $class_name );
1535
+ $class_name = implode( '_', $class_name );
1536
 
1537
  return $class_name;
1538
  }
1542
  *
1543
  * @param $url
1544
  * @param array $args
1545
+ *
1546
  * @return bool|string
1547
  */
1548
+ function fw_oembed_get( $url, $args = array() ) {
1549
+ $html = wp_oembed_get( $url, $args );
1550
 
1551
+ if ( ! empty( $args['width'] ) and ! empty( $args['height'] ) and class_exists( 'DOMDocument' ) and ! empty( $html ) ) {
1552
  $dom_element = new DOMDocument();
1553
+ @$dom_element->loadHTML( $html );
1554
 
1555
+ if ( $obj = $dom_element->getElementsByTagName( 'iframe' )->item( 0 ) ) {
1556
+ $obj->setAttribute( 'width', $args['width'] );
1557
+ $obj->setAttribute( 'height', $args['height'] );
1558
  //saveXml instead of SaveHTML for php version compatibility
1559
+ $html = $dom_element->saveXML( $obj, LIBXML_NOEMPTYTAG );
1560
  }
1561
  }
1562
 
1573
  * http://www.zimuel.it/en/strong-cryptography-in-php/
1574
  * > Don't use rand() or mt_rand()
1575
  */
1576
+ function fw_secure_rand( $length ) {
1577
+ if ( function_exists( 'openssl_random_pseudo_bytes' ) ) {
1578
+ $rnd = openssl_random_pseudo_bytes( $length, $strong );
1579
+ if ( $strong ) {
1580
  return $rnd;
1581
  }
1582
  }
1583
 
1584
+ $sha = '';
1585
+ $rnd = '';
1586
 
1587
+ if ( file_exists( '/dev/urandom' ) ) {
1588
+ $fp = fopen( '/dev/urandom', 'rb' );
1589
+ if ( $fp ) {
1590
+ if ( function_exists( 'stream_set_read_buffer' ) ) {
1591
+ stream_set_read_buffer( $fp, 0 );
1592
  }
1593
+ $sha = fread( $fp, $length );
1594
+ fclose( $fp );
1595
  }
1596
  }
1597
 
1598
+ for ( $i = 0; $i < $length; $i ++ ) {
1599
+ $sha = hash( 'sha256', $sha . mt_rand() );
1600
+ $char = mt_rand( 0, 62 );
1601
+ $rnd .= chr( hexdec( $sha[ $char ] . $sha[ $char + 1 ] ) );
1602
  }
1603
 
1604
  return $rnd;
1606
 
1607
  /**
1608
  * Try to make user friendly title from an id
1609
+ *
1610
  * @param string $id 'hello-world'
1611
+ *
1612
  * @return string 'Hello world'
1613
  */
1614
+ function fw_id_to_title( $id ) {
1615
  // mb_ucfirst()
1616
+ if ( function_exists( 'mb_strtoupper' ) && function_exists( 'mb_substr' ) && function_exists( 'mb_strlen' ) ) {
1617
+ $id = mb_strtoupper( mb_substr( $id, 0, 1, 'UTF-8' ), 'UTF-8' ) . mb_substr( $id,
1618
+ 1,
1619
+ mb_strlen( $id, 'UTF-8' ),
1620
+ 'UTF-8' );
1621
  } else {
1622
+ $id = strtoupper( substr( $id, 0, 1 ) ) . substr( $id, 1, strlen( $id ) );
1623
  }
1624
 
1625
+ return str_replace( array( '_', '-' ), ' ', $id );
1626
  }
1627
 
1628
  /**
1629
  * Alias
1630
+ *
1631
  * @param string $extension_name
1632
+ *
1633
  * @return FW_Extension|null
1634
  */
1635
+ function fw_ext( $extension_name ) {
1636
+ return fw()->extensions->get( $extension_name );
1637
  }
1638
 
1639
  /*
1645
 
1646
  /**
1647
  * Try to find file path by its uri and read the file contents
1648
+ *
1649
  * @param string $file_uri
1650
+ *
1651
  * @return bool|string false or string - the file contents
1652
  */
1653
+ function fw_read_file_by_uri( $file_uri ) {
1654
  static $base = null;
1655
 
1656
+ if ( $base === null ) {
1657
+ $base = array();
1658
+ $base['dir'] = WP_CONTENT_DIR;
1659
+ $base['uri'] = ltrim( content_url(), '/' );
1660
+ $base['uri_prefix_regex'] = '/^' . preg_quote( $base['uri'], '/' ) . '/';
1661
  }
1662
 
1663
+ $file_rel_path = preg_replace( $base['uri_prefix_regex'], '', $file_uri );
1664
 
1665
+ if ( $base['uri'] === $file_rel_path ) {
1666
  // the file is not inside base dir
1667
  return false;
1668
  }
1669
 
1670
+ $file_path = $base['dir'] . '/' . $file_rel_path;
1671
 
1672
+ if ( ! file_exists( $file_path ) ) {
1673
  return false;
1674
  }
1675
 
1676
+ return file_get_contents( $file_path );
1677
  }
1678
 
1679
  /**
1682
  *
1683
  * @param string $href 'http://.../style.css'
1684
  * @param null|string $contents If not specified, will try to read from $href
1685
+ *
1686
  * @return bool|string false - on failure; string - stylesheet contents
1687
  */
1688
+ function fw_make_stylesheet_portable( $href, $contents = null ) {
1689
+ if ( is_null( $contents ) ) {
1690
+ $contents = fw_read_file_by_uri( $href );
1691
 
1692
+ if ( $contents === false ) {
1693
  return false;
1694
  }
1695
  }
1696
 
1697
+ $dir_uri = dirname( $href );
1698
 
1699
  /**
1700
  * Replace relative 'url(img/bg.png)'
1709
  */
1710
  $contents = preg_replace(
1711
  '/url\s*\((?!\s*[\'"]?(?:\/|data\:|\#|(?:https?:)?\/\/))\s*([\'"])?/',
1712
+ 'url($1' . $dir_uri . '/',
1713
  $contents
1714
  );
1715
 
1720
  * Return all images sizes register by add_image_size() merged with
1721
  * WordPress default image sizes.
1722
  * @link https://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
1723
+ *
1724
  * @param string $size
1725
  *
1726
  * @return array|bool
1728
  function fw_get_image_sizes( $size = '' ) {
1729
  global $_wp_additional_image_sizes;
1730
 
1731
+ $sizes = array();
1732
  $get_intermediate_image_sizes = get_intermediate_image_sizes();
1733
 
1734
  // Create the full array with sizes and crop info
1735
+ foreach ( $get_intermediate_image_sizes as $_size ) {
1736
  if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
1737
+ $sizes[ $_size ]['width'] = get_option( $_size . '_size_w' );
1738
  $sizes[ $_size ]['height'] = get_option( $_size . '_size_h' );
1739
+ $sizes[ $_size ]['crop'] = (bool) get_option( $_size . '_crop' );
1740
  } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
1741
  $sizes[ $_size ] = array(
1742
+ 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
1743
  'height' => $_wp_additional_image_sizes[ $_size ]['height'],
1744
+ 'crop' => $_wp_additional_image_sizes[ $_size ]['crop']
1745
  );
1746
  }
1747
  }
1748
 
1749
  // Get only 1 size if found
1750
  if ( $size ) {
1751
+ if ( isset( $sizes[ $size ] ) ) {
1752
  return $sizes[ $size ];
1753
  } else {
1754
  return false;
1761
  /**
1762
  * @param string $icon A string that is meant to be an icon (an image, a font icon class, or something else)
1763
  * @param array Additional attributes
1764
+ *
1765
  * @return string
1766
  */
1767
+ function fw_string_to_icon_html( $icon, array $attributes = array() ) {
1768
  if (
1769
+ preg_match( '/\.(png|jpg|jpeg|gif|svg|webp)$/', $icon )
1770
  ||
1771
+ preg_match( '/^data:image\//', $icon )
1772
  ) {
1773
  // http://.../image.png
1774
+ $tag = 'img';
1775
  $attr = array(
1776
  'src' => $icon,
1777
  'alt' => 'icon',
1778
  );
1779
+ } elseif ( preg_match( '/^[a-zA-Z0-9\-_ ]+$/', $icon ) ) {
1780
  // 'font-icon font-icon-class'
1781
+ $tag = 'span';
1782
  $attr = array(
1783
+ 'class' => trim( $icon ),
1784
  );
1785
  } else {
1786
  // can't detect. maybe it's raw html '<span ...'
1787
  return $icon;
1788
  }
1789
 
1790
+ foreach ( $attributes as $attr_name => $attr_val ) {
1791
+ if ( isset( $attr[ $attr_name ] ) ) {
1792
+ if ( $attr_name === 'class' ) {
1793
+ $attr[ $attr_name ] .= ' ' . $attr_val;
1794
  } else {
1795
  // ignore. do not overwrite already set attributes
1796
  }
1797
  } else {
1798
+ $attr[ $attr_name ] = (string) $attr_val;
1799
  }
1800
  }
1801
 
1802
+ return fw_html_tag( $tag, $attr );
1803
  }
1804
 
1805
  /**
1807
  * @since 2.4.10
1808
  */
1809
  function fw_get_json_last_error_message() {
1810
+ switch ( function_exists( 'json_last_error' ) ? json_last_error() : - 1 ) {
1811
  case JSON_ERROR_NONE:
1812
  return null; // __('No errors', 'fw');
1813
  break;
1814
  case JSON_ERROR_DEPTH:
1815
+ return __( 'Maximum stack depth exceeded', 'fw' );
1816
  break;
1817
  case JSON_ERROR_STATE_MISMATCH:
1818
+ return __( 'Underflow or the modes mismatch', 'fw' );
1819
  break;
1820
  case JSON_ERROR_CTRL_CHAR:
1821
+ return __( 'Unexpected control character found', 'fw' );
1822
  break;
1823
  case JSON_ERROR_SYNTAX:
1824
+ return __( 'Syntax error, malformed JSON', 'fw' );
1825
  break;
1826
  case JSON_ERROR_UTF8:
1827
+ return __( 'Malformed UTF-8 characters, possibly incorrectly encoded', 'fw' );
1828
  break;
1829
  default:
1830
+ return __( 'Unknown error', 'fw' );
1831
  break;
1832
  }
1833
  }
1886
 
1887
  /**
1888
  * fw_get_path_url( dirname(__FILE__) .'/test.css' ) --> http://site.url/path/to/test.css
1889
+ *
1890
  * @param string $path
1891
+ *
1892
  * @return string|null
1893
  * @since 2.6.11
1894
  */
1895
+ function fw_get_path_url( $path ) {
1896
  try {
1897
+ $paths_to_urls = FW_Cache::get( $cache_key = 'fw:paths_to_urls' );
1898
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
1899
  $wp_upload_dir = wp_upload_dir();
1900
 
1901
  $paths_to_urls = array(
1902
+ fw_fix_path( WP_PLUGIN_DIR ) => plugins_url(),
1903
+ fw_fix_path( get_theme_root() ) => get_theme_root_uri(),
1904
+ fw_fix_path( $wp_upload_dir['basedir'] ) => $wp_upload_dir['baseurl'],
1905
  );
1906
 
1907
+ if ( is_multisite() && WPMU_PLUGIN_DIR ) {
1908
+ $paths_to_urls[ fw_fix_path( WPMU_PLUGIN_DIR ) ] = WPMU_PLUGIN_URL;
1909
  }
1910
 
1911
+ FW_Cache::set( $cache_key, $paths_to_urls );
1912
  }
1913
 
1914
+ $path = fw_fix_path( $path );
1915
 
1916
+ foreach ( $paths_to_urls as $_path => $_url ) {
1917
+ if ( preg_match( $regex = '/^' . preg_quote( $_path, '/' ) . '($|\/)/', $path ) ) {
1918
+ return $_url . '/' . preg_replace( $regex, '', $path );
1919
  }
1920
  }
1921
 
1922
  return null;
1923
  }
1924
+
1925
+ /**
1926
+ * @param string|array $callback Callback function
1927
+ * @param array $args Callback arguments
1928
+ * @param bool $cache Whenever you want to cache the function value after it's first call or not
1929
+ * Recommend when the function call may require many resources or time (database requests) , or the value is small
1930
+ * Not recommended using on very large values
1931
+ *
1932
+ * @return FW_Callback
1933
+ *
1934
+ * @since 2.6.14
1935
+ */
1936
+ function fw_callback( $callback, array $args = array(), $cache = true ) {
1937
+ return new FW_Callback( $callback, $args, $cache );
1938
+ }
1939
+
1940
+ /**
1941
+ * In the value is instance of FW_Callback class then it is executed and returns the callback value
1942
+ * In other case function returns the provided value
1943
+ *
1944
+ * @param mixed|FW_Callback $value
1945
+ *
1946
+ * @return mixed
1947
+ *
1948
+ * @since 2.6.14
1949
+ */
1950
+ function fw_call( $value ) {
1951
+ if ( $value instanceof FW_Callback ) {
1952
+ return $value->execute();
1953
+ }
1954
+
1955
+ return $value;
1956
+ }
framework/includes/option-storage/type/class-fw-option-storage-type-wp-option.php CHANGED
@@ -25,7 +25,12 @@ class FW_Option_Storage_Type_WP_Option extends FW_Option_Storage_Type {
25
 
26
  unset($wp_option_value);
27
  } else {
28
- update_option($wp_option, $value, false);
 
 
 
 
 
29
  }
30
 
31
  return fw()->backend->option_type($option['type'])->get_value_from_input(
25
 
26
  unset($wp_option_value);
27
  } else {
28
+ if (empty($value)) {
29
+ delete_option($wp_option);
30
+ return $value; // Preserve value (don't return default below) because it can be false|0|array()
31
+ } else {
32
+ update_option($wp_option, $value, false);
33
+ }
34
  }
35
 
36
  return fw()->backend->option_type($option['type'])->get_value_from_input(
framework/includes/option-types/simple.php CHANGED
@@ -791,7 +791,7 @@ class FW_Option_Type_Select extends FW_Option_Type {
791
  return $result;
792
  }
793
 
794
- protected function render_choices( &$choices, &$value ) {
795
  if ( empty( $choices ) || ! is_array( $choices ) ) {
796
  return '';
797
  }
@@ -946,7 +946,7 @@ class FW_Option_Type_Select_Multiple extends FW_Option_Type_Select {
946
  return $input_value;
947
  }
948
 
949
- protected function render_choices( &$choices, &$value ) {
950
  if ( empty( $choices ) || ! is_array( $choices ) ) {
951
  return '';
952
  }
791
  return $result;
792
  }
793
 
794
+ protected function render_choices( $choices, $value ) {
795
  if ( empty( $choices ) || ! is_array( $choices ) ) {
796
  return '';
797
  }
946
  return $input_value;
947
  }
948
 
949
+ protected function render_choices( $choices, $value ) {
950
  if ( empty( $choices ) || ! is_array( $choices ) ) {
951
  return '';
952
  }
framework/includes/option-types/typography-v2/class-fw-option-type-typography-v2.php CHANGED
@@ -173,7 +173,10 @@ class FW_Option_Type_Typography_v2 extends FW_Option_Type {
173
  'size' => true,
174
  'line-height' => true,
175
  'letter-spacing' => true,
176
- 'color' => true
 
 
 
177
  )
178
  );
179
  }
173
  'size' => true,
174
  'line-height' => true,
175
  'letter-spacing' => true,
176
+ 'color' => true,
177
+ 'weight' => true,
178
+ 'style' => true,
179
+ 'variation' => true,
180
  )
181
  );
182
  }
framework/includes/option-types/typography-v2/static/css/styles.css CHANGED
@@ -68,6 +68,7 @@ body.rtl .fw-force-xs .fw-option-type-typography-v2 .fw-option-typography-v2-opt
68
  display: inline-block;
69
  margin: 0;
70
  width: 100%;
 
71
  }
72
 
73
 
68
  display: inline-block;
69
  margin: 0;
70
  width: 100%;
71
+ padding: 5px 4px;
72
  }
73
 
74
 
framework/includes/option-types/typography-v2/static/js/scripts.js CHANGED
@@ -118,18 +118,14 @@
118
  .html(getFontsOptionHTML($fontFamilySelect.attr('data-value')))
119
  .selectize({
120
  onChange: function (selected) {
121
- var results = $.grep(googleFonts['items'], function (font) {
122
- return font['family'] === selected;
123
- });
124
- var $variations = this.$dropdown.closest(optionTypeClass).find('.fw-option-typography-v2-option-variation');
125
- var $subsets = this.$dropdown.closest(optionTypeClass).find('.fw-option-typography-v2-option-subset');
126
-
127
- var $style = this.$dropdown.closest(optionTypeClass).find('.fw-option-typography-v2-option-style');
128
- var $weight = this.$dropdown.closest(optionTypeClass).find('.fw-option-typography-v2-option-weight');
129
 
130
  if (results.length === 1) {
131
- var variations = '';
132
- var subsets = '';
133
  _.each(results[0]['variants'], function (variation) {
134
  variations += '<option value="' + variation + '">' + variation + '</option>';
135
  });
118
  .html(getFontsOptionHTML($fontFamilySelect.attr('data-value')))
119
  .selectize({
120
  onChange: function (selected) {
121
+ var results = $.grep(googleFonts['items'], function (font) { return font['family'] === selected; }),
122
+ $variations = $option.find('.fw-option-typography-v2-option-variation'),
123
+ $subsets = $option.find('.fw-option-typography-v2-option-subset'),
124
+ $style = $option.find('.fw-option-typography-v2-option-style'),
125
+ $weight = $option.find('.fw-option-typography-v2-option-weight');
 
 
 
126
 
127
  if (results.length === 1) {
128
+ var variations = '', subsets = '';
 
129
  _.each(results[0]['variants'], function (variation) {
130
  variations += '<option value="' + variation + '">' + variation + '</option>';
131
  });
framework/includes/option-types/typography-v2/view.php CHANGED
@@ -25,7 +25,9 @@
25
 
26
  }
27
 
28
- $components = (isset($option['components']) && is_array($option['components'])) ? array_merge($defaults['components'], $option['components']) : $defaults['components'];
 
 
29
  ?>
30
  <div <?php echo fw_attr_to_html( $wrapper_attr ) ?>>
31
  <?php if ( $components['family'] ) : ?>
@@ -38,6 +40,7 @@ $components = (isset($option['components']) && is_array($option['components']))
38
  <div class="fw-inner"><?php _e('Font face', 'fw'); ?></div>
39
  </div>
40
 
 
41
  <div class="fw-option-typography-v2-option fw-option-typography-v2-option-style fw-border-box-sizing fw-col-sm-3"
42
  style="display: <?php echo ( $google_font ) ? 'none' : 'inline-block'; ?>;">
43
  <select data-type="style" name="<?php echo esc_attr( $option['attr']['name'] ) ?>[style]"
@@ -57,7 +60,9 @@ $components = (isset($option['components']) && is_array($option['components']))
57
 
58
  <div class="fw-inner"><?php _e( 'Style', 'fw' ); ?></div>
59
  </div>
 
60
 
 
61
  <div class="fw-option-typography-v2-option fw-option-typography-v2-option-weight fw-border-box-sizing fw-col-sm-3"
62
  style="display: <?php echo ( $google_font ) ? 'none' : 'inline-block'; ?>;">
63
  <select data-type="weight" name="<?php echo esc_attr( $option['attr']['name'] ) ?>[weight]"
@@ -83,6 +88,7 @@ $components = (isset($option['components']) && is_array($option['components']))
83
 
84
  <div class="fw-inner"><?php _e( 'Weight', 'fw' ); ?></div>
85
  </div>
 
86
 
87
  <div class="fw-option-typography-v2-option fw-option-typography-v2-option-subset fw-border-box-sizing fw-col-sm-2"
88
  style="display: <?php echo ( $google_font ) ? 'inline-block' : 'none'; ?>;">
@@ -100,6 +106,8 @@ $components = (isset($option['components']) && is_array($option['components']))
100
  <div class="fw-inner"><?php _e( 'Script', 'fw' ); ?></div>
101
  </div>
102
 
 
 
103
  <div
104
  class="fw-option-typography-v2-option fw-option-typography-v2-option-variation fw-border-box-sizing fw-col-sm-2"
105
  style="display: <?php echo ( $google_font ) ? 'inline-block' : 'none'; ?>;">
@@ -114,8 +122,9 @@ $components = (isset($option['components']) && is_array($option['components']))
114
  ?>
115
  </select>
116
 
117
- <div class="fw-inner"><?php _e( 'Style', 'fw' ); ?></div>
118
  </div>
 
119
  <?php endif; ?>
120
 
121
  <?php if ( $components['size'] ) : ?>
@@ -124,7 +133,7 @@ $components = (isset($option['components']) && is_array($option['components']))
124
  class="fw-option-typography-v2-option-size-input" type="text"
125
  value="<?php echo esc_attr($data['value']['size']); ?>">
126
 
127
- <div class="fw-inner"><?php _e( 'Size', 'fw' ); ?></div>
128
  </div>
129
  <?php endif; ?>
130
 
@@ -135,7 +144,7 @@ $components = (isset($option['components']) && is_array($option['components']))
135
  value="<?php echo esc_attr($data['value']['line-height']); ?>"
136
  class="fw-option-typography-v2-option-line-height-input" type="text">
137
 
138
- <div class="fw-inner"><?php _e( 'Line height', 'fw' ); ?></div>
139
  </div>
140
  <?php endif; ?>
141
 
@@ -146,7 +155,7 @@ $components = (isset($option['components']) && is_array($option['components']))
146
  value="<?php echo esc_attr($data['value']['letter-spacing']); ?>"
147
  class="fw-option-typography-v2-option-letter-spacing-input" type="text">
148
 
149
- <div class="fw-inner"><?php _e( 'Letter spacing', 'fw' ); ?></div>
150
  </div>
151
  <?php endif; ?>
152
 
@@ -169,7 +178,7 @@ $components = (isset($option['components']) && is_array($option['components']))
169
  )
170
  )
171
  ?>
172
- <div class="fw-inner"><?php _e( 'Color', 'fw' ); ?></div>
173
  </div>
174
  <?php endif; ?>
175
 
25
 
26
  }
27
 
28
+ $components = (isset($option['components']) && is_array($option['components']))
29
+ ? array_merge($defaults['components'], $option['components'])
30
+ : $defaults['components'];
31
  ?>
32
  <div <?php echo fw_attr_to_html( $wrapper_attr ) ?>>
33
  <?php if ( $components['family'] ) : ?>
40
  <div class="fw-inner"><?php _e('Font face', 'fw'); ?></div>
41
  </div>
42
 
43
+ <?php if ( $components['style'] ) : ?>
44
  <div class="fw-option-typography-v2-option fw-option-typography-v2-option-style fw-border-box-sizing fw-col-sm-3"
45
  style="display: <?php echo ( $google_font ) ? 'none' : 'inline-block'; ?>;">
46
  <select data-type="style" name="<?php echo esc_attr( $option['attr']['name'] ) ?>[style]"
60
 
61
  <div class="fw-inner"><?php _e( 'Style', 'fw' ); ?></div>
62
  </div>
63
+ <?php endif; ?>
64
 
65
+ <?php if ( $components['weight'] ) : ?>
66
  <div class="fw-option-typography-v2-option fw-option-typography-v2-option-weight fw-border-box-sizing fw-col-sm-3"
67
  style="display: <?php echo ( $google_font ) ? 'none' : 'inline-block'; ?>;">
68
  <select data-type="weight" name="<?php echo esc_attr( $option['attr']['name'] ) ?>[weight]"
88
 
89
  <div class="fw-inner"><?php _e( 'Weight', 'fw' ); ?></div>
90
  </div>
91
+ <?php endif; ?>
92
 
93
  <div class="fw-option-typography-v2-option fw-option-typography-v2-option-subset fw-border-box-sizing fw-col-sm-2"
94
  style="display: <?php echo ( $google_font ) ? 'inline-block' : 'none'; ?>;">
106
  <div class="fw-inner"><?php _e( 'Script', 'fw' ); ?></div>
107
  </div>
108
 
109
+
110
+ <?php if ( $components['variation'] ) : ?>
111
  <div
112
  class="fw-option-typography-v2-option fw-option-typography-v2-option-variation fw-border-box-sizing fw-col-sm-2"
113
  style="display: <?php echo ( $google_font ) ? 'inline-block' : 'none'; ?>;">
122
  ?>
123
  </select>
124
 
125
+ <div class="fw-inner"><?php esc_html_e( 'Style', 'fw' ); ?></div>
126
  </div>
127
+ <?php endif; ?>
128
  <?php endif; ?>
129
 
130
  <?php if ( $components['size'] ) : ?>
133
  class="fw-option-typography-v2-option-size-input" type="text"
134
  value="<?php echo esc_attr($data['value']['size']); ?>">
135
 
136
+ <div class="fw-inner"><?php esc_html_e( 'Size', 'fw' ); ?></div>
137
  </div>
138
  <?php endif; ?>
139
 
144
  value="<?php echo esc_attr($data['value']['line-height']); ?>"
145
  class="fw-option-typography-v2-option-line-height-input" type="text">
146
 
147
+ <div class="fw-inner"><?php esc_html_e( 'Line height', 'fw' ); ?></div>
148
  </div>
149
  <?php endif; ?>
150
 
155
  value="<?php echo esc_attr($data['value']['letter-spacing']); ?>"
156
  class="fw-option-typography-v2-option-letter-spacing-input" type="text">
157
 
158
+ <div class="fw-inner"><?php esc_html_e( 'Spacing', 'fw' ); ?></div>
159
  </div>
160
  <?php endif; ?>
161
 
178
  )
179
  )
180
  ?>
181
+ <div class="fw-inner"><?php esc_html_e( 'Color', 'fw' ); ?></div>
182
  </div>
183
  <?php endif; ?>
184
 
framework/manifest.php CHANGED
@@ -4,4 +4,4 @@ $manifest = array();
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
- $manifest['version'] = '2.6.13';
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
+ $manifest['version'] = '2.6.14';
framework/static/js/fw.js CHANGED
@@ -730,6 +730,8 @@ fw.getQueryString = function(name) {
730
  uploader: false
731
  });
732
 
 
 
733
  var modal = this;
734
 
735
  this.frame.once('ready', function(){
@@ -977,6 +979,65 @@ fw.getQueryString = function(name) {
977
  $frame.css('overflow-y', 'hidden');
978
  }
979
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
980
  })();
981
 
982
  /**
@@ -1196,6 +1257,18 @@ fw.getValuesFromServer = function (data) {
1196
  silentReceiveOfDefaultValues: true
1197
  }
1198
  ),
 
 
 
 
 
 
 
 
 
 
 
 
1199
  initializeFrame: function(settings) {
1200
  fw.Modal.prototype.initializeFrame.call(this, settings);
1201
 
@@ -1205,7 +1278,7 @@ fw.getValuesFromServer = function (data) {
1205
  buttons = [
1206
  {
1207
  style: 'primary',
1208
- text: settings.saveText || _fw_localized.l10n.save,
1209
  priority: 40,
1210
  click: function () {
1211
  if (settings.shouldSaveWithoutClose) {
730
  uploader: false
731
  });
732
 
733
+ patchMediaFramesModalToDoTheFocusCorrectly( this.frame );
734
+
735
  var modal = this;
736
 
737
  this.frame.once('ready', function(){
979
  $frame.css('overflow-y', 'hidden');
980
  }
981
  });
982
+
983
+ // https://github.com/WordPress/WordPress/blob/4ca4ff999aba05892c05d26cddf3af540f47b93b/wp-includes/js/media-views.js#L6800
984
+ // When you execute frame.modal.open() - the modal tries to switch focus
985
+ // to its $el. Actually, this switches the scrollTop property for window.
986
+ //
987
+ // In order to prevent that we'll have to monkey patch the frame.modal.open
988
+ // method and do the focus properly - that's what this function does
989
+ function patchMediaFramesModalToDoTheFocusCorrectly (frame) {
990
+ if (! frame.modal) return;
991
+
992
+ frame.modal.open = function () {
993
+ var $el = this.$el,
994
+ options = this.options,
995
+ mceEditor;
996
+
997
+ if ( $el.is(':visible') ) {
998
+ return this;
999
+ }
1000
+
1001
+ this.clickedOpenerEl = document.activeElement;
1002
+
1003
+ if ( ! this.views.attached ) {
1004
+ this.attach();
1005
+ }
1006
+
1007
+ // If the `freeze` option is set, record the window's scroll position.
1008
+ if ( options.freeze ) {
1009
+ this._freeze = {
1010
+ scrollTop: jQuery( window ).scrollTop()
1011
+ };
1012
+ }
1013
+
1014
+ // Disable page scrolling.
1015
+ jQuery( 'body' ).addClass( 'modal-open' );
1016
+
1017
+ $el.show();
1018
+
1019
+ // Try to close the onscreen keyboard
1020
+ if ( 'ontouchend' in document ) {
1021
+ if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
1022
+ mceEditor.iframeElement.focus();
1023
+ mceEditor.iframeElement.blur();
1024
+
1025
+ setTimeout( function() {
1026
+ mceEditor.iframeElement.blur();
1027
+ }, 100 );
1028
+ }
1029
+ }
1030
+
1031
+ // this part is changed from the original method
1032
+ // this.$el.focus();
1033
+ // http://stackoverflow.com/a/11676673/3220977
1034
+ var initialX = window.scrollX, initialY = window.scrollY;
1035
+ this.$el.focus();
1036
+ window.scrollTo(initialX, initialY);
1037
+
1038
+ return this.propagate('open');
1039
+ }
1040
+ }
1041
  })();
1042
 
1043
  /**
1257
  silentReceiveOfDefaultValues: true
1258
  }
1259
  ),
1260
+ initialize: function () {
1261
+ fw.Modal.prototype.initialize.call(this);
1262
+
1263
+ // Forward events to fwEvents
1264
+ {
1265
+ /** @since 2.6.14 */
1266
+ this.on('open', function () { fwEvents.trigger('fw:options-modal:open', {modal: this}); });
1267
+
1268
+ /** @since 2.6.14 */
1269
+ this.on('close', function () { fwEvents.trigger('fw:options-modal:close', {modal: this}); });
1270
+ }
1271
+ },
1272
  initializeFrame: function(settings) {
1273
  fw.Modal.prototype.initializeFrame.call(this, settings);
1274
 
1278
  buttons = [
1279
  {
1280
  style: 'primary',
1281
+ text: settings.saveText || _fw_localized.l10n.modal_save_btn,
1282
  priority: 40,
1283
  click: function () {
1284
  if (settings.shouldSaveWithoutClose) {
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: unyson
3
  Tags: page builder, shortcodes, backup, seo, breadcrumbs, portfolio, framework
4
  Requires at least: 4.4
5
  Tested up to: 4.7
6
- Stable tag: 2.6.13
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -85,6 +85,10 @@ Yes; Unyson will work with any theme.
85
 
86
  == Changelog ==
87
 
 
 
 
 
88
  = 2.6.13 =
89
  * Fixed [#2310](https://github.com/ThemeFuse/Unyson/issues/2310), [#2308](https://github.com/ThemeFuse/Unyson/issues/2308), [#961](https://github.com/ThemeFuse/Unyson/issues/961), [#2073](https://github.com/ThemeFuse/Unyson/issues/2073)
90
 
3
  Tags: page builder, shortcodes, backup, seo, breadcrumbs, portfolio, framework
4
  Requires at least: 4.4
5
  Tested up to: 4.7
6
+ Stable tag: 2.6.14
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
85
 
86
  == Changelog ==
87
 
88
+ = 2.6.14 =
89
+ * Fixed infinite loop when php memory limit is `-1`
90
+ * Minor changes
91
+
92
  = 2.6.13 =
93
  * Fixed [#2310](https://github.com/ThemeFuse/Unyson/issues/2310), [#2308](https://github.com/ThemeFuse/Unyson/issues/2308), [#961](https://github.com/ThemeFuse/Unyson/issues/961), [#2073](https://github.com/ThemeFuse/Unyson/issues/2073)
94
 
unyson.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Unyson
4
  * Plugin URI: http://unyson.io/
5
  * Description: A free drag & drop framework that comes with a bunch of built in extensions that will help you develop premium themes fast & easy.
6
- * Version: 2.6.13
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+
3
  * Plugin Name: Unyson
4
  * Plugin URI: http://unyson.io/
5
  * Description: A free drag & drop framework that comes with a bunch of built in extensions that will help you develop premium themes fast & easy.
6
+ * Version: 2.6.14
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+