Unyson - Version 2.7.9

Version Description

  • Fixed #1351,#2716,#2833,#2736,#2718,#2953,#2888,#2855,#2906
Download this release

Release Info

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

Code changes from version 2.7.8 to 2.7.9

framework/core/components/backend.php CHANGED
@@ -624,7 +624,7 @@ final class _FW_Component_Backend {
624
  * @param WP_Post $post
625
  */
626
  public function _action_create_post_meta_boxes( $post_type, $post ) {
627
- if ('comment' === $post_type) {
628
  /**
629
  * This is wrong, comment is not a post(type)
630
  * it is stored in a separate db table and has a separate meta (wp_comments and wp_commentmeta)
@@ -641,50 +641,34 @@ final class _FW_Component_Backend {
641
  $collected = array();
642
 
643
  fw_collect_options( $collected, $options, array(
644
- 'limit_option_types' => false,
645
  'limit_container_types' => false,
646
- 'limit_level' => 1,
647
  ) );
648
 
649
- if (empty($collected)) {
650
  return;
651
  }
652
 
653
  $values = fw_get_db_post_option( $post->ID );
654
 
655
- foreach ( $collected as $id => &$option ) {
656
- if (
657
- isset($option['options']) // container
658
- &&
659
- $option['type'] === 'box'
660
- ) { // this is a box, add it as a metabox
661
- $context = isset( $option['context'] )
662
- ? $option['context']
663
- : 'normal';
664
- $priority = isset( $option['priority'] )
665
- ? $option['priority']
666
- : 'default';
667
-
668
- add_meta_box(
669
- 'fw-options-box-' . $id,
670
- empty( $option['title'] ) ? ' ' : $option['title'],
671
- $this->print_meta_box_content_callback,
672
- $post_type,
673
- $context,
674
- $priority,
675
- $this->render_options( $option['options'], $values )
676
- );
677
- } else { // this is not a box, wrap it in auto-generated box
678
- add_meta_box(
679
- 'fw-options-box:auto-generated:'. time() .':'. fw_unique_increment(),
680
- ' ',
681
- $this->print_meta_box_content_callback,
682
- $post_type,
683
- 'normal',
684
- 'default',
685
- $this->render_options( array($id => $option), $values )
686
- );
687
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
688
  }
689
  }
690
 
624
  * @param WP_Post $post
625
  */
626
  public function _action_create_post_meta_boxes( $post_type, $post ) {
627
+ if ( 'comment' === $post_type ) {
628
  /**
629
  * This is wrong, comment is not a post(type)
630
  * it is stored in a separate db table and has a separate meta (wp_comments and wp_commentmeta)
641
  $collected = array();
642
 
643
  fw_collect_options( $collected, $options, array(
644
+ 'limit_option_types' => false,
645
  'limit_container_types' => false,
646
+ 'limit_level' => 1,
647
  ) );
648
 
649
+ if ( empty( $collected ) ) {
650
  return;
651
  }
652
 
653
  $values = fw_get_db_post_option( $post->ID );
654
 
655
+ foreach ( $collected as $id => $option ) {
656
+ if ( ! isset( $option['options'] ) || ! in_array( $option['type'], array( 'box', 'group' ) ) ) {
657
+ continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  }
659
+
660
+ $context = isset( $option['context'] ) ? $option['context'] : 'normal';
661
+ $priority = isset( $option['priority'] ) ? $option['priority'] : 'default';
662
+
663
+ add_meta_box(
664
+ "fw-options-{$option['type']}-{$id}",
665
+ empty( $option['title'] ) ? ' ' : $option['title'],
666
+ $this->print_meta_box_content_callback,
667
+ $post_type,
668
+ $context,
669
+ $priority,
670
+ $this->render_options( $option['options'], $values )
671
+ );
672
  }
673
  }
674
 
framework/core/components/theme.php CHANGED
@@ -1,11 +1,10 @@
1
- <?php if (!defined('FW')) die('Forbidden');
2
 
3
  /**
4
  * Theme Component
5
  * Works with framework customizations / theme directory
6
  */
7
- final class _FW_Component_Theme
8
- {
9
  private static $cache_key = 'fw_theme';
10
 
11
  /**
@@ -26,29 +25,28 @@ final class _FW_Component_Theme
26
  /**
27
  * @internal
28
  */
29
- public function _init()
30
- {
31
- add_action('admin_notices', array($this, '_action_admin_notices'));
32
  }
33
 
34
  /**
35
  * @internal
36
  */
37
- public function _after_components_init()
38
- {
39
  }
40
 
41
  /**
42
  * Search relative path in: child theme -> parent "theme" directory and return full path
 
43
  * @param string $rel_path
 
44
  * @return false|string
45
  */
46
- public function locate_path($rel_path)
47
- {
48
- if (is_child_theme() && file_exists(fw_get_stylesheet_customizations_directory('/theme'. $rel_path))) {
49
- return fw_get_stylesheet_customizations_directory('/theme'. $rel_path);
50
- } elseif (file_exists(fw_get_template_customizations_directory('/theme'. $rel_path))) {
51
- return fw_get_template_customizations_directory('/theme'. $rel_path);
52
  } else {
53
  return false;
54
  }
@@ -56,86 +54,83 @@ final class _FW_Component_Theme
56
 
57
  /**
58
  * Return array with options from specified name/path
 
59
  * @param string $name '{theme}/framework-customizations/theme/options/{$name}.php'
60
  * @param array $variables These will be available in options file (like variables for view)
 
61
  * @return array
62
  */
63
- public function get_options($name, array $variables = array())
64
- {
65
- $path = $this->locate_path('/options/'. $name .'.php');
66
 
67
- if (!$path) {
68
  return array();
69
  }
70
 
71
- $variables = fw_get_variables_from_file($path, array('options' => array()), $variables);
72
 
73
  return $variables['options'];
74
  }
75
 
76
- public function get_settings_options()
77
- {
78
- $cache_key = self::$cache_key .'/options/settings';
79
 
80
  try {
81
- return FW_Cache::get($cache_key);
82
- } catch (FW_Cache_Not_Found_Exception $e) {
83
- $options = apply_filters('fw_settings_options', $this->get_options('settings'));
84
 
85
- FW_Cache::set($cache_key, $options);
86
 
87
  return $options;
88
  }
89
  }
90
 
91
- public function get_customizer_options()
92
- {
93
- $cache_key = self::$cache_key .'/options/customizer';
94
 
95
  try {
96
- return FW_Cache::get($cache_key);
97
- } catch (FW_Cache_Not_Found_Exception $e) {
98
- $options = apply_filters('fw_customizer_options', $this->get_options('customizer'));
99
 
100
- FW_Cache::set($cache_key, $options);
101
 
102
  return $options;
103
  }
104
  }
105
 
106
- public function get_post_options($post_type)
107
- {
108
- $cache_key = self::$cache_key .'/options/posts/'. $post_type;
109
 
110
  try {
111
- return FW_Cache::get($cache_key);
112
- } catch (FW_Cache_Not_Found_Exception $e) {
113
  $options = apply_filters(
114
  'fw_post_options',
115
  apply_filters( "fw_post_options:$post_type", $this->get_options( 'posts/' . $post_type ) ),
116
  $post_type
117
  );
118
 
119
- FW_Cache::set($cache_key, $options);
120
 
121
  return $options;
122
  }
123
  }
124
 
125
- public function get_taxonomy_options($taxonomy)
126
- {
127
- $cache_key = self::$cache_key .'/options/taxonomies/'. $taxonomy;
128
 
129
  try {
130
- return FW_Cache::get($cache_key);
131
- } catch (FW_Cache_Not_Found_Exception $e) {
132
  $options = apply_filters(
133
  'fw_taxonomy_options',
134
  apply_filters( "fw_taxonomy_options:$taxonomy", $this->get_options( 'taxonomies/' . $taxonomy ) ),
135
  $taxonomy
136
  );
137
 
138
- FW_Cache::set($cache_key, $options);
139
 
140
  return $options;
141
  }
@@ -144,62 +139,65 @@ final class _FW_Component_Theme
144
  /**
145
  * Return config key value, or entire config array
146
  * Config array is merged from child configs
 
147
  * @param string|null $key Multi key format accepted: 'a/b/c'
148
  * @param mixed $default_value
 
149
  * @return mixed|null
150
  */
151
- final public function get_config($key = null, $default_value = null)
152
- {
153
- $cache_key = self::$cache_key .'/config';
154
 
155
  try {
156
- $config = FW_Cache::get($cache_key);
157
- } catch (FW_Cache_Not_Found_Exception $e) {
158
  // default values
159
  $config = array(
160
  /** Toggle Theme Settings form ajax submit */
161
  'settings_form_ajax_submit' => true,
162
  /** Toggle Theme Settings side tabs */
163
- 'settings_form_side_tabs' => false,
164
  /** Toggle Tabs rendered all at once, or initialized only on open/display */
165
- 'lazy_tabs' => true,
166
  );
167
 
168
- if (file_exists(fw_get_template_customizations_directory('/theme/config.php'))) {
169
- $variables = fw_get_variables_from_file(fw_get_template_customizations_directory('/theme/config.php'), array('cfg' => null));
170
 
171
- if (!empty($variables['cfg'])) {
172
- $config = array_merge($config, $variables['cfg']);
173
- unset($variables);
174
  }
175
  }
176
 
177
- if (is_child_theme() && file_exists(fw_get_stylesheet_customizations_directory('/theme/config.php'))) {
178
- $variables = fw_get_variables_from_file(fw_get_stylesheet_customizations_directory('/theme/config.php'), array('cfg' => null));
179
 
180
- if (!empty($variables['cfg'])) {
181
- $config = array_merge($config, $variables['cfg']);
182
- unset($variables);
183
  }
184
  }
185
 
186
- unset($path);
187
 
188
- FW_Cache::set($cache_key, $config);
189
  }
190
 
191
- return $key === null ? $config : fw_akg($key, $config, $default_value);
192
  }
193
 
194
  /**
195
  * @internal
196
  */
197
- public function _action_admin_notices()
198
- {
199
- if ( is_admin() && !fw()->theme->manifest->check_requirements() && current_user_can('manage_options') ) {
200
- echo '<div class="notice notice-warning"><p>';
201
- echo __('Theme requirements not met:', 'fw') .' '. fw()->theme->manifest->get_not_met_requirement_text();
202
- echo '</p></div>';
 
 
203
  }
204
  }
205
  }
1
+ <?php defined( 'FW' ) or die();
2
 
3
  /**
4
  * Theme Component
5
  * Works with framework customizations / theme directory
6
  */
7
+ final class _FW_Component_Theme {
 
8
  private static $cache_key = 'fw_theme';
9
 
10
  /**
25
  /**
26
  * @internal
27
  */
28
+ public function _init() {
29
+ add_action( 'admin_notices', array( $this, '_action_admin_notices' ) );
 
30
  }
31
 
32
  /**
33
  * @internal
34
  */
35
+ public function _after_components_init() {
 
36
  }
37
 
38
  /**
39
  * Search relative path in: child theme -> parent "theme" directory and return full path
40
+ *
41
  * @param string $rel_path
42
+ *
43
  * @return false|string
44
  */
45
+ public function locate_path( $rel_path ) {
46
+ if ( is_child_theme() && file_exists( fw_get_stylesheet_customizations_directory( '/theme' . $rel_path ) ) ) {
47
+ return fw_get_stylesheet_customizations_directory( '/theme' . $rel_path );
48
+ } elseif ( file_exists( fw_get_template_customizations_directory( '/theme' . $rel_path ) ) ) {
49
+ return fw_get_template_customizations_directory( '/theme' . $rel_path );
 
50
  } else {
51
  return false;
52
  }
54
 
55
  /**
56
  * Return array with options from specified name/path
57
+ *
58
  * @param string $name '{theme}/framework-customizations/theme/options/{$name}.php'
59
  * @param array $variables These will be available in options file (like variables for view)
60
+ *
61
  * @return array
62
  */
63
+ public function get_options( $name, array $variables = array() ) {
64
+ $path = $this->locate_path( '/options/' . $name . '.php' );
 
65
 
66
+ if ( ! $path ) {
67
  return array();
68
  }
69
 
70
+ $variables = fw_get_variables_from_file( $path, array( 'options' => array() ), $variables );
71
 
72
  return $variables['options'];
73
  }
74
 
75
+ public function get_settings_options() {
76
+ $cache_key = self::$cache_key . '/options/settings';
 
77
 
78
  try {
79
+ return FW_Cache::get( $cache_key );
80
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
81
+ $options = apply_filters( 'fw_settings_options', $this->get_options( 'settings' ) );
82
 
83
+ FW_Cache::set( $cache_key, $options );
84
 
85
  return $options;
86
  }
87
  }
88
 
89
+ public function get_customizer_options() {
90
+ $cache_key = self::$cache_key . '/options/customizer';
 
91
 
92
  try {
93
+ return FW_Cache::get( $cache_key );
94
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
95
+ $options = apply_filters( 'fw_customizer_options', $this->get_options( 'customizer' ) );
96
 
97
+ FW_Cache::set( $cache_key, $options );
98
 
99
  return $options;
100
  }
101
  }
102
 
103
+ public function get_post_options( $post_type ) {
104
+ $cache_key = self::$cache_key . '/options/posts/' . $post_type;
 
105
 
106
  try {
107
+ return FW_Cache::get( $cache_key );
108
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
109
  $options = apply_filters(
110
  'fw_post_options',
111
  apply_filters( "fw_post_options:$post_type", $this->get_options( 'posts/' . $post_type ) ),
112
  $post_type
113
  );
114
 
115
+ FW_Cache::set( $cache_key, $options );
116
 
117
  return $options;
118
  }
119
  }
120
 
121
+ public function get_taxonomy_options( $taxonomy ) {
122
+ $cache_key = self::$cache_key . '/options/taxonomies/' . $taxonomy;
 
123
 
124
  try {
125
+ return FW_Cache::get( $cache_key );
126
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
127
  $options = apply_filters(
128
  'fw_taxonomy_options',
129
  apply_filters( "fw_taxonomy_options:$taxonomy", $this->get_options( 'taxonomies/' . $taxonomy ) ),
130
  $taxonomy
131
  );
132
 
133
+ FW_Cache::set( $cache_key, $options );
134
 
135
  return $options;
136
  }
139
  /**
140
  * Return config key value, or entire config array
141
  * Config array is merged from child configs
142
+ *
143
  * @param string|null $key Multi key format accepted: 'a/b/c'
144
  * @param mixed $default_value
145
+ *
146
  * @return mixed|null
147
  */
148
+ final public function get_config( $key = null, $default_value = null ) {
149
+ $cache_key = self::$cache_key . '/config';
 
150
 
151
  try {
152
+ $config = FW_Cache::get( $cache_key );
153
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
154
  // default values
155
  $config = array(
156
  /** Toggle Theme Settings form ajax submit */
157
  'settings_form_ajax_submit' => true,
158
  /** Toggle Theme Settings side tabs */
159
+ 'settings_form_side_tabs' => false,
160
  /** Toggle Tabs rendered all at once, or initialized only on open/display */
161
+ 'lazy_tabs' => true,
162
  );
163
 
164
+ if ( file_exists( fw_get_template_customizations_directory( '/theme/config.php' ) ) ) {
165
+ $variables = fw_get_variables_from_file( fw_get_template_customizations_directory( '/theme/config.php' ), array( 'cfg' => null ) );
166
 
167
+ if ( ! empty( $variables['cfg'] ) ) {
168
+ $config = array_merge( $config, $variables['cfg'] );
169
+ unset( $variables );
170
  }
171
  }
172
 
173
+ if ( is_child_theme() && file_exists( fw_get_stylesheet_customizations_directory( '/theme/config.php' ) ) ) {
174
+ $variables = fw_get_variables_from_file( fw_get_stylesheet_customizations_directory( '/theme/config.php' ), array( 'cfg' => null ) );
175
 
176
+ if ( ! empty( $variables['cfg'] ) ) {
177
+ $config = array_merge( $config, $variables['cfg'] );
178
+ unset( $variables );
179
  }
180
  }
181
 
182
+ unset( $path );
183
 
184
+ FW_Cache::set( $cache_key, $config );
185
  }
186
 
187
+ return $key === null ? $config : fw_akg( $key, $config, $default_value );
188
  }
189
 
190
  /**
191
  * @internal
192
  */
193
+ public function _action_admin_notices() {
194
+ if ( is_admin() && ! fw()->theme->manifest->check_requirements() && current_user_can( 'manage_options' ) ) {
195
+ echo
196
+ '<div class="notice notice-warning">
197
+ <p>' .
198
+ __( 'Theme requirements not met:', 'fw' ) . ' ' . fw()->theme->manifest->get_not_met_requirement_text() .
199
+ '</p>
200
+ </div>';
201
  }
202
  }
203
  }
framework/extensions/update/class-fw-extension-update.php CHANGED
@@ -1,135 +1,121 @@
1
- <?php if (!defined('FW')) die('Forbidden');
2
 
3
- require dirname(__FILE__) .'/includes/extends/class-fw-ext-update-service.php';
4
 
5
- class FW_Extension_Update extends FW_Extension
6
- {
7
  /**
8
  * {@inheritdoc}
9
  */
10
- public function _child_extension_is_valid($child_extension_instance)
11
- {
12
- return is_subclass_of($child_extension_instance, 'FW_Ext_Update_Service');
13
  }
14
 
15
  /**
16
  * File names to skip (do not delete or change) during the update process
17
  * @var array
18
  */
19
- private $skip_file_names = array('.git');
20
 
21
  /**
22
  * @internal
23
  */
24
- protected function _init()
25
- {
26
- {
27
- $has_access = (current_user_can('update_themes') || current_user_can('update_plugins'));
28
-
29
- if ($has_access) {
30
- if (is_multisite() && !is_network_admin()) {
31
- // only network admin can change files that affects the entire network
32
- $has_access = false;
33
- }
34
- }
35
 
36
- if (!$has_access) {
37
- return false; // prevent child extensions activation
38
- }
39
  }
40
 
41
  $this->add_actions();
42
  $this->add_filters();
 
 
43
  }
44
 
45
- private function add_actions()
46
- {
47
- add_action('core_upgrade_preamble', array($this, '_action_updates_page_footer'));
48
 
49
- add_action('update-core-custom_'. 'fw-update-framework', array($this, '_action_update_framework'));
50
- add_action('update-core-custom_'. 'fw-update-theme', array($this, '_action_update_theme'));
51
- add_action('update-core-custom_'. 'fw-update-extensions', array($this, '_action_update_extensions'));
52
 
53
- add_action('admin_notices', array($this, '_action_admin_notices'));
 
 
 
54
  }
55
 
56
- private function add_filters()
57
- {
58
- add_filter('wp_get_update_data', array($this, '_filter_update_data'), 10, 2);
59
  }
60
 
61
- private function get_fixed_version($version)
62
- {
63
  // remove from the beginning everything that is not a number: 'v1.2.3' -> '1.2.3', 'ver1.0.0' -> '1.0.0'
64
- return preg_replace('/^[^0-9]+/i', '', $version);;
65
  }
66
 
67
- private function get_wp_fs_tmp_dir()
68
- {
69
  return FW_WP_Filesystem::real_path_to_filesystem_path(
70
- apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) .'/tmp')
71
  );
72
  }
73
 
74
  /**
75
  * @internal
76
  */
77
- public function _action_updates_page_footer()
78
- {
79
- echo $this->render_view('updates-list', array(
80
- 'updates' => $this->get_updates(!empty($_GET['force-check']))
81
- ));
82
  }
83
 
84
  /**
85
  * @internal
86
  */
87
- public function _filter_update_data($data, $titles)
88
- {
89
- $updates = $this->get_updates(!empty($_GET['force-check']));
90
 
91
- if ($updates['framework'] && !is_wp_error($updates['framework'])) {
92
- ++$data['counts']['total'];
93
  }
94
 
95
- if ($updates['theme'] && !is_wp_error($updates['theme'])) {
96
- ++$data['counts']['total'];
97
  }
98
 
99
- if (!empty($updates['extensions'])) {
100
  foreach ( $updates['extensions'] as $ext_name => $ext_update ) {
101
  if ( is_wp_error( $ext_update ) ) {
102
  continue;
103
  }
104
 
105
- ++$data['counts']['total'];
106
 
107
- if ($this->get_config('extensions_as_one_update')) {
108
  // no matter how many extensions, display as one update
109
  break;
110
  }
111
  }
 
 
 
 
112
  }
113
 
114
  return $data;
115
  }
116
 
117
- private function get_updates($force_check = false)
118
- {
119
  $cache_key = 'fw_ext_update/updates';
120
 
121
  // use cache because this method may be called multiple times (to prevent useless requests to update servers)
122
-
123
  try {
124
- return FW_Cache::get($cache_key);
125
- } catch (FW_Cache_Not_Found_Exception $e) {
126
  $updates = array(
127
- 'framework' => $this->get_framework_update($force_check),
128
- 'theme' => $this->get_theme_update($force_check),
129
- 'extensions' => $this->get_extensions_with_updates($force_check)
130
  );
131
 
132
- FW_Cache::set($cache_key, $updates);
133
 
134
  return $updates;
135
  }
@@ -137,56 +123,57 @@ class FW_Extension_Update extends FW_Extension
137
 
138
  /**
139
  * Collect extensions that has new versions available
 
140
  * @param bool $force_check
 
141
  * @return array {ext_name => update_data}
142
  */
143
- private function get_extensions_with_updates($force_check = false)
144
- {
145
- $updates = array();
146
- $services = $this->get_children();
147
- $theme_ext_requirements = fw()->theme->manifest->get('requirements/extensions');
148
 
149
- foreach (fw()->extensions->get_all() as $ext_name => $extension) {
150
  /** @var FW_Extension $extension */
151
 
152
  /**
153
  * Ask each service if it knows how to update the extension
154
  */
155
- foreach ($services as $service_name => $service) {
156
  /** @var $service FW_Ext_Update_Service */
157
 
158
- $latest_version = $service->_get_extension_latest_version($extension, $force_check);
159
 
160
- if ($latest_version === false) {
161
  // It said that it doesn't know how to update it
162
  continue;
163
  }
164
 
165
- if (is_wp_error($latest_version)) {
166
- $updates[$ext_name] = $latest_version;
167
  break;
168
  }
169
 
170
- $fixed_latest_version = $this->get_fixed_version($latest_version);
171
 
172
- if (!version_compare($fixed_latest_version, $extension->manifest->get_version(), '>')) {
173
  // we already have latest version
174
  continue;
175
  }
176
 
177
  if (
178
- isset($theme_ext_requirements[$ext_name])
179
  &&
180
- isset($theme_ext_requirements[$ext_name]['max_version'])
181
  &&
182
- version_compare($fixed_latest_version, $theme_ext_requirements[$ext_name]['max_version'], '>')
183
  ) {
184
  continue; // do not allow update if it exceeds max_version
185
  }
186
 
187
- $updates[$ext_name] = array(
188
- 'service' => $service_name,
189
- 'latest_version' => $latest_version,
190
  'fixed_latest_version' => $fixed_latest_version
191
  );
192
 
@@ -199,37 +186,37 @@ class FW_Extension_Update extends FW_Extension
199
 
200
  /**
201
  * @param bool $force_check
 
202
  * @return array|false|WP_Error
203
  */
204
- private function get_framework_update($force_check = false)
205
- {
206
  /**
207
  * Ask each service if it knows how to update the framework
208
  */
209
- foreach ($this->get_children() as $service_name => $service) {
210
  /** @var $service FW_Ext_Update_Service */
211
 
212
- $latest_version = $service->_get_framework_latest_version($force_check);
213
 
214
- if ($latest_version === false) {
215
  // It said that it doesn't know how to update it
216
  continue;
217
  }
218
 
219
- if (is_wp_error($latest_version)) {
220
  return $latest_version;
221
  }
222
 
223
- $fixed_latest_version = $this->get_fixed_version($latest_version);
224
 
225
- if (!version_compare($fixed_latest_version, fw()->manifest->get_version(), '>')) {
226
  // we already have latest version
227
  continue;
228
  }
229
 
230
  return array(
231
- 'service' => $service_name,
232
- 'latest_version' => $latest_version,
233
  'fixed_latest_version' => $fixed_latest_version
234
  );
235
  }
@@ -239,37 +226,37 @@ class FW_Extension_Update extends FW_Extension
239
 
240
  /**
241
  * @param bool $force_check
 
242
  * @return array|false|WP_Error
243
  */
244
- private function get_theme_update($force_check = false)
245
- {
246
  /**
247
  * Ask each service if it knows how to update the theme
248
  */
249
- foreach ($this->get_children() as $service_name => $service) {
250
  /** @var $service FW_Ext_Update_Service */
251
 
252
- $latest_version = $service->_get_theme_latest_version($force_check);
253
 
254
- if ($latest_version === false) {
255
  // It said that it doesn't know how to update it
256
  continue;
257
  }
258
 
259
- if (is_wp_error($latest_version)) {
260
  return $latest_version;
261
  }
262
 
263
- $fixed_latest_version = $this->get_fixed_version($latest_version);
264
 
265
- if (!version_compare($fixed_latest_version, fw()->theme->manifest->get_version(), '>')) {
266
  // we already have latest version
267
  continue;
268
  }
269
 
270
  return array(
271
- 'service' => $service_name,
272
- 'latest_version' => $latest_version,
273
  'fixed_latest_version' => $fixed_latest_version
274
  );
275
  }
@@ -279,29 +266,29 @@ class FW_Extension_Update extends FW_Extension
279
 
280
  /**
281
  * Turn on/off the maintenance mode
 
282
  * @param bool $enable
283
  */
284
- private function maintenance_mode($enable = false)
285
- {
286
  /** @var WP_Filesystem_Base $wp_filesystem */
287
  global $wp_filesystem;
288
 
289
- if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
290
  return;
291
  }
292
 
293
  $file_path = $wp_filesystem->abspath() . '.maintenance';
294
 
295
- if ($wp_filesystem->exists($file_path)) {
296
- if (!$wp_filesystem->delete($file_path)) {
297
- trigger_error(__('Cannot delete: ', 'fw') . $file_path, E_USER_WARNING);
298
  }
299
  }
300
 
301
- if ($enable) {
302
  // Create maintenance file to signal that we are upgrading
303
- if (!$wp_filesystem->put_contents($file_path, '<?php $upgrading = ' . time() . '; ?>', FS_CHMOD_FILE)) {
304
- trigger_error(__('Cannot create: ', 'fw') . $file_path, E_USER_WARNING);
305
  }
306
  }
307
  }
@@ -314,10 +301,10 @@ class FW_Extension_Update extends FW_Extension
314
  * @param array $data
315
  * @param bool $merge_extensions The extensions/ directory will not be replaced entirely,
316
  * only extensions that comes with the update will be replaced
 
317
  * @return null|WP_Error
318
  */
319
- private function update($data, $merge_extensions = false)
320
- {
321
  $required_data_keys = array(
322
  'wp_fs_destination_dir' => true,
323
  'download_callback' => true,
@@ -326,8 +313,8 @@ class FW_Extension_Update extends FW_Extension
326
  'title' => true,
327
  );
328
 
329
- if (count($required_data_keys) > count(array_intersect_key($required_data_keys, $data))) {
330
- trigger_error('Some required keys are not present', E_USER_ERROR);
331
  }
332
 
333
  // move manually every key to variable, so IDE will understand better them
@@ -360,7 +347,7 @@ class FW_Extension_Update extends FW_Extension
360
  */
361
  $title = $data['title'];
362
 
363
- unset($data);
364
  }
365
 
366
  /**
@@ -376,61 +363,61 @@ class FW_Extension_Update extends FW_Extension
376
 
377
  // create temporary directory
378
  {
379
- if ($wp_filesystem->exists($tmp_download_dir)) {
380
  // just in case it already exists, clear everything, it may contain old files
381
- if (!$wp_filesystem->rmdir($tmp_download_dir, true)) {
382
- $error = __('Cannot remove old temporary directory: ', 'fw') . $tmp_download_dir;
383
  break;
384
  }
385
  }
386
 
387
- if (!FW_WP_Filesystem::mkdir_recursive($tmp_download_dir)) {
388
- $error = __('Cannot create directory: ', 'fw') . $tmp_download_dir;
389
  break;
390
  }
391
  }
392
 
393
- $skin->feedback(sprintf(__('Downloading the %s...', 'fw'), $title));
394
  {
395
- $downloaded_dir = call_user_func_array($download_callback, $download_callback_args);
396
 
397
- if (!$downloaded_dir) {
398
- $error = sprintf(__('Cannot download the %s.', 'fw'), $title);
399
  break;
400
- } elseif (is_wp_error($downloaded_dir)) {
401
  $error = $downloaded_dir;
402
  break;
403
  }
404
  }
405
 
406
- $this->maintenance_mode(true);
407
 
408
- $skin->feedback(sprintf(__('Installing the %s...', 'fw'), $title));
409
  {
410
  // remove all files from destination directory
411
  {
412
- $dir_files = $wp_filesystem->dirlist($wp_fs_destination_dir, true);
413
- if ($dir_files === false) {
414
- $error =__('Cannot access directory: ', 'fw') . $wp_fs_destination_dir;
415
  break;
416
  }
417
 
418
- foreach ($dir_files as $file) {
419
- if (in_array($file['name'], $this->skip_file_names)) {
420
  continue;
421
  }
422
 
423
- if ($merge_extensions) {
424
- if ($file['name'] === 'extensions' && $file['type'] === 'd') {
425
  // do not remove extensions, will be merged later
426
  continue;
427
  }
428
  }
429
 
430
- $file_path = $wp_fs_destination_dir .'/'. $file['name'];
431
 
432
- if (!$wp_filesystem->delete($file_path, true, $file['type'])) {
433
- $error = __('Cannot remove: ', 'fw') . $file_path;
434
  break 2;
435
  }
436
  }
@@ -438,54 +425,54 @@ class FW_Extension_Update extends FW_Extension
438
 
439
  // move all files from the temporary directory to the destination directory
440
  {
441
- $dir_files = $wp_filesystem->dirlist($downloaded_dir, true);
442
- if ($dir_files === false) {
443
- $error = __('Cannot access directory: ', 'fw') . $downloaded_dir;
444
  break;
445
  }
446
 
447
- foreach ($dir_files as $file) {
448
- if (in_array($file['name'], $this->skip_file_names)) {
449
  continue;
450
  }
451
 
452
- $downloaded_file_path = $downloaded_dir .'/'. $file['name'];
453
- $destination_file_path = $wp_fs_destination_dir .'/'. $file['name'];
454
 
455
- if ($merge_extensions) {
456
- if ($file['name'] === 'extensions' && $file['type'] === 'd') {
457
  // merge extensions/ after all other files was moved
458
  $merge_extensions_data = array(
459
- 'source' => $downloaded_file_path,
460
  'destination' => $destination_file_path,
461
  );
462
  continue;
463
  }
464
  }
465
 
466
- if (!$wp_filesystem->move($downloaded_file_path, $destination_file_path)) {
467
  $error = sprintf(
468
- __('Cannot move "%s" to "%s"', 'fw'),
469
  $downloaded_file_path, $destination_file_path
470
  );
471
  break 2;
472
  }
473
  }
474
 
475
- if ($merge_extensions) {
476
- if (!empty($merge_extensions_data)) {
477
  $merge_result = $this->merge_extensions(
478
  $merge_extensions_data['source'],
479
  $merge_extensions_data['destination']
480
  );
481
 
482
- if ($merge_result === false) {
483
  $error = sprintf(
484
- __('Cannot merge "%s" with "%s"', 'fw'),
485
  $downloaded_file_path, $destination_file_path
486
  );
487
  break;
488
- } elseif (is_wp_error($merge_result)) {
489
  $error = $merge_result;
490
  break;
491
  }
@@ -494,20 +481,20 @@ class FW_Extension_Update extends FW_Extension
494
  }
495
  }
496
 
497
- $skin->feedback(sprintf(__('The %s has been successfully updated.', 'fw'), $title));
498
- } while(false);
499
 
500
- $this->maintenance_mode(false);
501
 
502
- if ($wp_filesystem->exists($tmp_download_dir)) {
503
  if ( ! $wp_filesystem->delete( $tmp_download_dir, true, 'd' ) ) {
504
  $error = sprintf( __( 'Cannot remove temporary directory "%s".', 'fw' ), $tmp_download_dir );
505
  }
506
  }
507
 
508
- if ($error) {
509
- if (!is_wp_error($error)) {
510
- $error = new WP_Error( 'fw_ext_update_failed', (string)$error );
511
  }
512
 
513
  return $error;
@@ -516,56 +503,58 @@ class FW_Extension_Update extends FW_Extension
516
 
517
  /**
518
  * Merge two extensions/ directories
 
519
  * @param string $source_dir WP_Filesystem dir '/a/b/c/extensions'
520
  * @param string $destination_dir WP_Filesystem dir '/a/b/d/extensions'
 
521
  * @return bool|WP_Error
522
  */
523
- private function merge_extensions($source_dir, $destination_dir)
524
- {
525
  /** @var WP_Filesystem_Base $wp_filesystem */
526
  global $wp_filesystem;
527
 
528
  $wp_error_id = 'fw_ext_update_merge_extensions';
529
 
530
- if (!$wp_filesystem->exists($destination_dir)) {
531
  // do a simple move if destination does not exist
532
- if (!$wp_filesystem->move($source_dir, $destination_dir)) {
533
- return new WP_Error($wp_error_id,
534
- sprintf(__('Cannot move "%s" to "%s"', 'fw'), $source_dir, $destination_dir)
535
  );
536
  }
 
537
  return true;
538
  }
539
 
540
- $source_ext_dirs = $wp_filesystem->dirlist($source_dir, true);
541
- if ($source_ext_dirs === false) {
542
- return new WP_Error($wp_error_id,
543
- __('Cannot access directory: ', 'fw') . $source_dir
544
  );
545
  }
546
 
547
- foreach ($source_ext_dirs as $ext_dir) {
548
- if (in_array($ext_dir['name'], $this->skip_file_names)) {
549
  continue;
550
  }
551
 
552
- if ($ext_dir['type'] !== 'd') {
553
  // process only directories from the extensions/ directory
554
  continue;
555
  }
556
 
557
- $source_extension_dir = $source_dir .'/'. $ext_dir['name'];
558
- $destination_extension_dir = $destination_dir .'/'. $ext_dir['name'];
559
 
560
  {
561
- $source_ext_files = $wp_filesystem->dirlist($source_extension_dir, true);
562
- if ($source_ext_files === false) {
563
- return new WP_Error($wp_error_id,
564
- __('Cannot access directory: ', 'fw') . $source_extension_dir
565
  );
566
  }
567
 
568
- if (empty($source_ext_files)) {
569
  /**
570
  * Source extension directory is empty, do nothing.
571
  * This happens when the extension is a git submodule in repository
@@ -578,40 +567,40 @@ class FW_Extension_Update extends FW_Extension
578
  // prepare destination
579
  {
580
  // create if not exists
581
- if (!$wp_filesystem->exists($destination_extension_dir)) {
582
- if (!FW_WP_Filesystem::mkdir_recursive($destination_extension_dir)) {
583
- return new WP_Error($wp_error_id,
584
- __('Cannot create directory: ', 'fw') . $destination_extension_dir
585
  );
586
  }
587
  }
588
 
589
  // remove everything except the extensions/ dir
590
  {
591
- $dest_ext_files = $wp_filesystem->dirlist($destination_extension_dir, true);
592
- if ($dest_ext_files === false) {
593
- return new WP_Error($wp_error_id,
594
- __('Cannot access directory: ', 'fw') . $destination_extension_dir
595
  );
596
  }
597
 
598
  $destination_has_extensions_dir = false;
599
 
600
- foreach ($dest_ext_files as $dest_ext_file) {
601
- if (in_array($dest_ext_file['name'], $this->skip_file_names)) {
602
  continue;
603
  }
604
 
605
- if ($dest_ext_file['name'] === 'extensions' && $dest_ext_file['type'] === 'd') {
606
  $destination_has_extensions_dir = true;
607
  continue;
608
  }
609
 
610
- $dest_ext_file_path = $destination_extension_dir .'/'. $dest_ext_file['name'];
611
 
612
- if (!$wp_filesystem->delete($dest_ext_file_path, true, $dest_ext_file['type'])) {
613
- return new WP_Error($wp_error_id,
614
- __('Cannot delete: ', 'fw') . $dest_ext_file_path
615
  );
616
  }
617
  }
@@ -622,22 +611,22 @@ class FW_Extension_Update extends FW_Extension
622
  {
623
  $source_has_extensions_dir = false;
624
 
625
- foreach ($source_ext_files as $source_ext_file) {
626
- if (in_array($source_ext_file['name'], $this->skip_file_names)) {
627
  continue;
628
  }
629
 
630
- if ($source_ext_file['name'] === 'extensions' && $source_ext_file['type'] === 'd') {
631
  $source_has_extensions_dir = true;
632
  continue;
633
  }
634
 
635
- $source_ext_file_path = $source_extension_dir .'/'. $source_ext_file['name'];
636
- $dest_ext_file_path = $destination_extension_dir .'/'. $source_ext_file['name'];
637
 
638
- if (!$wp_filesystem->move($source_ext_file_path, $dest_ext_file_path)) {
639
- return new WP_Error($wp_error_id,
640
- sprintf(__('Cannot move "%s" to "%s"', 'fw'),
641
  $source_ext_file_path, $dest_ext_file_path
642
  )
643
  );
@@ -645,25 +634,25 @@ class FW_Extension_Update extends FW_Extension
645
  }
646
  }
647
 
648
- if ($source_has_extensions_dir) {
649
- if ($destination_has_extensions_dir) {
650
  $merge_result = $this->merge_extensions(
651
- $source_extension_dir .'/extensions',
652
- $destination_extension_dir .'/extensions'
653
  );
654
 
655
- if ($merge_result !== true) {
656
  return $merge_result;
657
  }
658
  } else {
659
- if (!$wp_filesystem->move(
660
- $source_extension_dir .'/extensions',
661
- $destination_extension_dir .'/extensions'
662
- )) {
663
- return new WP_Error($wp_error_id,
664
- sprintf(__('Cannot move "%s" to "%s"', 'fw'),
665
- $source_extension_dir .'/extensions',
666
- $destination_extension_dir .'/extensions'
667
  )
668
  );
669
  }
@@ -677,184 +666,181 @@ class FW_Extension_Update extends FW_Extension
677
  /**
678
  * @internal
679
  */
680
- public function _action_update_framework()
681
- {
682
  $nonce_name = '_nonce_fw_ext_update_framework';
683
- if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
684
- wp_die(__('Invalid nonce.', 'fw'));
685
  }
686
 
687
  {
688
- if (!class_exists('_FW_Ext_Update_Framework_Upgrader_Skin')) {
689
  fw_include_file_isolated(
690
- $this->get_declared_path('/includes/classes/class--fw-ext-update-framework-upgrader-skin.php')
691
  );
692
  }
693
 
694
- $skin = new _FW_Ext_Update_Framework_Upgrader_Skin(array(
695
- 'title' => __('Framework Update', 'fw'),
696
- ));
697
  }
698
 
699
- require_once ABSPATH .'wp-admin/admin-header.php';
700
 
701
  $skin->header();
702
 
703
  do {
704
- if (!FW_WP_Filesystem::request_access(fw_get_framework_directory(), fw_current_url(), array($nonce_name))) {
705
  break;
706
  }
707
 
708
  $update = $this->get_framework_update();
709
 
710
- if ($update === false) {
711
- $skin->error(__('Failed to get framework latest version.', 'fw'));
712
  break;
713
- } elseif (is_wp_error($update)) {
714
- $skin->error($update);
715
  break;
716
  }
717
 
718
  /** @var FW_Ext_Update_Service $service */
719
- $service = $this->get_child($update['service']);
720
 
721
- $update_result = $this->update(array(
722
- 'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
723
  fw_get_framework_directory()
724
  ),
725
- 'download_callback' => array($service, '_download_framework'),
726
- 'download_callback_args' => array($update['latest_version'], $this->get_wp_fs_tmp_dir()),
727
- 'skin' => $skin,
728
- 'title' => __('Framework', 'fw'),
729
- ));
730
-
731
- if (is_wp_error($update_result)) {
732
- $skin->error($update_result);
733
  break;
734
  }
735
 
736
- $skin->set_result(true);
737
  $skin->after();
738
- } while(false);
739
 
740
  $skin->footer();
741
 
742
- require_once(ABSPATH . 'wp-admin/admin-footer.php');
743
  }
744
 
745
  /**
746
  * @internal
747
  */
748
- public function _action_update_theme()
749
- {
750
  $nonce_name = '_nonce_fw_ext_update_theme';
751
- if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
752
- wp_die(__('Invalid nonce.', 'fw'));
753
  }
754
 
755
  {
756
- if (!class_exists('_FW_Ext_Update_Theme_Upgrader_Skin')) {
757
  fw_include_file_isolated(
758
- $this->get_declared_path('/includes/classes/class--fw-ext-update-theme-upgrader-skin.php')
759
  );
760
  }
761
 
762
- $skin = new _FW_Ext_Update_Theme_Upgrader_Skin(array(
763
- 'title' => __('Theme Update', 'fw'),
764
- ));
765
  }
766
 
767
- require_once(ABSPATH . 'wp-admin/admin-header.php');
768
 
769
  $skin->header();
770
 
771
  do {
772
- if (!FW_WP_Filesystem::request_access(get_template_directory(), fw_current_url(), array($nonce_name))) {
773
  break;
774
  }
775
 
776
  $update = $this->get_theme_update();
777
 
778
- if ($update === false) {
779
- $skin->error(__('Failed to get theme latest version.', 'fw'));
780
  break;
781
- } elseif (is_wp_error($update)) {
782
- $skin->error($update);
783
  break;
784
  }
785
 
786
  /** @var FW_Ext_Update_Service $service */
787
- $service = $this->get_child($update['service']);
788
 
789
- $update_result = $this->update(array(
790
- 'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
791
  get_template_directory()
792
  ),
793
- 'download_callback' => array($service, '_download_theme'),
794
- 'download_callback_args' => array($update['latest_version'], $this->get_wp_fs_tmp_dir()),
795
- 'skin' => $skin,
796
- 'title' => __('Theme', 'fw'),
797
- ));
798
-
799
- if (is_wp_error($update_result)) {
800
- $skin->error($update_result);
801
  break;
802
  }
803
 
804
- $skin->set_result(true);
805
  $skin->after();
806
- } while(false);
807
 
808
  $skin->footer();
809
 
810
- require_once(ABSPATH . 'wp-admin/admin-footer.php');
811
  }
812
 
813
  /**
814
  * @internal
815
  */
816
- public function _action_update_extensions()
817
- {
818
  $nonce_name = '_nonce_fw_ext_update_extensions';
819
- if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
820
- wp_die(__('Invalid nonce.', 'fw'));
821
  }
822
 
823
  $form_input_name = 'extensions';
824
- $extensions_list = FW_Request::POST($form_input_name);
825
 
826
- if (empty($extensions_list)) {
827
  FW_Flash_Messages::add(
828
  'fw_ext_update',
829
- __('Please check the extensions you want to update.', 'fw'),
830
  'warning'
831
  );
832
- wp_redirect(self_admin_url('update-core.php'));
833
  exit;
834
  }
835
 
836
  // handle changes by the hack below
837
  {
838
- if (is_string($extensions_list)) {
839
- $extensions_list = json_decode($extensions_list);
840
  } else {
841
- $extensions_list = array_keys($extensions_list);
842
  }
843
  }
844
 
845
  {
846
- if (!class_exists('_FW_Ext_Update_Extensions_Upgrader_Skin')) {
847
  fw_include_file_isolated(
848
- $this->get_declared_path('/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php')
849
  );
850
  }
851
 
852
- $skin = new _FW_Ext_Update_Extensions_Upgrader_Skin(array(
853
- 'title' => __('Extensions Update', 'fw'),
854
- ));
855
  }
856
 
857
- require_once(ABSPATH . 'wp-admin/admin-header.php');
858
 
859
  $skin->header();
860
 
@@ -863,106 +849,96 @@ class FW_Extension_Update extends FW_Extension
863
  * Hack for the ftp credentials template that does not support array post values
864
  * https://github.com/WordPress/WordPress/blob/3949a8b6cc50a021ed93798287b4ef9ea8a560d9/wp-admin/includes/file.php#L1144
865
  */
866
- {
867
- $original_post_value = $_POST[$form_input_name];
868
- $_POST[$form_input_name] = wp_slash(json_encode($extensions_list));
869
- }
870
-
871
- if (!FW_WP_Filesystem::request_access(
872
- fw_get_framework_directory('/extensions'),
873
- fw_current_url(),
874
- array($nonce_name, $form_input_name))
875
- ) {
876
- { // revert hack changes
877
- $_POST[$form_input_name] = $original_post_value;
878
- unset($original_post_value);
879
- }
880
  break;
881
  }
882
 
883
- { // revert hack changes
884
- $_POST[$form_input_name] = $original_post_value;
885
- unset($original_post_value);
886
- }
887
 
888
  $updates = $this->get_extensions_with_updates();
889
 
890
- if (empty($updates)) {
891
- $skin->error(__('No extensions updates found.', 'fw'));
892
  break;
893
  }
894
 
895
- foreach ($extensions_list as $extension_name) {
896
- if (!($extension = fw()->extensions->get($extension_name))) {
897
- $skin->error(
898
- sprintf(__('Extension "%s" does not exist or is disabled.', 'fw'), $extension_name)
899
- );
900
  continue;
901
  }
902
 
903
- if (!isset($updates[$extension_name])) {
904
- $skin->error(
905
- sprintf(__('No update found for the "%s" extension.', 'fw'), $extension->manifest->get_name())
906
- );
907
  continue;
908
  }
909
 
910
- $update = $updates[$extension_name];
911
 
912
- if (is_wp_error($update)) {
913
- $skin->error($update);
914
  continue;
915
  }
916
 
917
  /** @var FW_Ext_Update_Service $service */
918
- $service = $this->get_child($update['service']);
919
 
920
- $update_result = $this->update(array(
921
- 'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
922
  $extension->get_declared_path()
923
  ),
924
- 'download_callback' => array($service, '_download_extension'),
925
- 'download_callback_args' => array($extension, $update['latest_version'], $this->get_wp_fs_tmp_dir()),
926
- 'skin' => $skin,
927
- 'title' => sprintf(__('%s extension', 'fw'), $extension->manifest->get_name()),
928
- ), true);
929
-
930
- if (is_wp_error($update_result)) {
931
- $skin->error($update_result);
 
 
 
 
932
  continue;
933
  }
934
 
935
- $skin->set_result(true);
936
 
937
- if (!$this->get_config('extensions_as_one_update')) {
938
  $skin->decrement_extension_update_count( $extension_name );
939
  }
940
  }
941
 
942
- if ($this->get_config('extensions_as_one_update')) {
943
  $skin->decrement_extension_update_count( $extension_name );
944
  }
945
 
946
  $skin->after();
947
- } while(false);
948
 
949
  $skin->footer();
950
 
951
- require_once(ABSPATH . 'wp-admin/admin-footer.php');
952
  }
953
 
954
  public function _action_admin_notices() {
955
- if (
956
- get_current_screen()->parent_base === fw()->extensions->manager->get_page_slug()
957
- &&
958
- ($updates = $this->get_updates())
959
- &&
960
- !empty($updates['extensions'])
961
- ) { /* ok */ } else {
962
  return;
963
  }
964
 
965
- foreach ($updates['extensions'] as $ext_name => $ext_update) {
966
  if ( is_wp_error( $ext_update ) ) {
967
  return;
968
  }
@@ -970,13 +946,18 @@ class FW_Extension_Update extends FW_Extension
970
  break;
971
  }
972
 
973
- echo '<div class="notice notice-warning"><p>'
974
- . sprintf(
975
- esc_html__('New extensions updates available. %s', 'fw'),
976
- fw_html_tag('a', array(
977
- 'href' => self_admin_url('update-core.php') .'#fw-ext-update-extensions',
978
- ), esc_html__('Go to Updates page', 'fw'))
979
- )
980
- . '</p></div>';
 
 
 
 
 
981
  }
982
  }
1
+ <?php defined( 'FW' ) or die( 'Forbidden' );
2
 
3
+ require dirname( __FILE__ ) . '/includes/extends/class-fw-ext-update-service.php';
4
 
5
+ class FW_Extension_Update extends FW_Extension {
 
6
  /**
7
  * {@inheritdoc}
8
  */
9
+ public function _child_extension_is_valid( $child_extension_instance ) {
10
+ return is_subclass_of( $child_extension_instance, 'FW_Ext_Update_Service' );
 
11
  }
12
 
13
  /**
14
  * File names to skip (do not delete or change) during the update process
15
  * @var array
16
  */
17
+ private $skip_file_names = array( '.git' );
18
 
19
  /**
20
  * @internal
21
  */
22
+ protected function _init() {
 
 
 
 
 
 
 
 
 
 
23
 
24
+ if ( ! current_user_can( 'update_plugins' ) ) {
25
+ return false;
 
26
  }
27
 
28
  $this->add_actions();
29
  $this->add_filters();
30
+
31
+ return true;
32
  }
33
 
34
+ private function add_actions() {
 
 
35
 
36
+ add_action( 'admin_notices', array( $this, '_action_admin_notices' ) );
37
+ add_action( 'network_admin_notices', array( $this, '_action_admin_notices' ) );
 
38
 
39
+ add_action( 'core_upgrade_preamble', array( $this, '_action_updates_page_footer' ) );
40
+ add_action( 'update-core-custom_' . 'fw-update-framework', array( $this, '_action_update_framework' ) );
41
+ add_action( 'update-core-custom_' . 'fw-update-theme', array( $this, '_action_update_theme' ) );
42
+ add_action( 'update-core-custom_' . 'fw-update-extensions', array( $this, '_action_update_extensions' ) );
43
  }
44
 
45
+ private function add_filters() {
46
+ add_filter( 'wp_get_update_data', array( $this, '_filter_update_data' ), 10, 2 );
 
47
  }
48
 
49
+ private function get_fixed_version( $version ) {
 
50
  // remove from the beginning everything that is not a number: 'v1.2.3' -> '1.2.3', 'ver1.0.0' -> '1.0.0'
51
+ return preg_replace( '/^[^0-9]+/i', '', $version );;
52
  }
53
 
54
+ private function get_wp_fs_tmp_dir() {
 
55
  return FW_WP_Filesystem::real_path_to_filesystem_path(
56
+ apply_filters( 'fw_tmp_dir', fw_fix_path( WP_CONTENT_DIR ) . '/tmp' )
57
  );
58
  }
59
 
60
  /**
61
  * @internal
62
  */
63
+ public function _action_updates_page_footer() {
64
+ echo $this->render_view( 'updates-list', array(
65
+ 'updates' => $this->get_updates( ! empty( $_GET['force-check'] ) )
66
+ ) );
 
67
  }
68
 
69
  /**
70
  * @internal
71
  */
72
+ public function _filter_update_data( $data, $titles ) {
73
+ $updates = $this->get_updates( ! empty( $_GET['force-check'] ) );
 
74
 
75
+ if ( $updates['framework'] && ! is_wp_error( $updates['framework'] ) ) {
76
+ ++ $data['counts']['total'];
77
  }
78
 
79
+ if ( $updates['theme'] && ! is_wp_error( $updates['theme'] ) ) {
80
+ ++ $data['counts']['total'];
81
  }
82
 
83
+ if ( ! empty( $updates['extensions'] ) ) {
84
  foreach ( $updates['extensions'] as $ext_name => $ext_update ) {
85
  if ( is_wp_error( $ext_update ) ) {
86
  continue;
87
  }
88
 
89
+ ++ $data['counts']['total'];
90
 
91
+ if ( $this->get_config( 'extensions_as_one_update' ) ) {
92
  // no matter how many extensions, display as one update
93
  break;
94
  }
95
  }
96
+
97
+ $title = esc_html__( 'Available extensions updates.', 'fw' );
98
+
99
+ $data['title'] = empty( $data['title'] ) ? $title : $data['title'] . ', ' . $title;
100
  }
101
 
102
  return $data;
103
  }
104
 
105
+ private function get_updates( $force_check = false ) {
 
106
  $cache_key = 'fw_ext_update/updates';
107
 
108
  // use cache because this method may be called multiple times (to prevent useless requests to update servers)
 
109
  try {
110
+ return FW_Cache::get( $cache_key );
111
+ } catch ( FW_Cache_Not_Found_Exception $e ) {
112
  $updates = array(
113
+ 'framework' => $this->get_framework_update( $force_check ),
114
+ 'theme' => $this->get_theme_update( $force_check ),
115
+ 'extensions' => $this->get_extensions_with_updates( $force_check )
116
  );
117
 
118
+ FW_Cache::set( $cache_key, $updates );
119
 
120
  return $updates;
121
  }
123
 
124
  /**
125
  * Collect extensions that has new versions available
126
+ *
127
  * @param bool $force_check
128
+ *
129
  * @return array {ext_name => update_data}
130
  */
131
+ private function get_extensions_with_updates( $force_check = false ) {
132
+ $updates = array();
133
+ $services = $this->get_children();
134
+ $theme_ext_requirements = fw()->theme->manifest->get( 'requirements/extensions' );
 
135
 
136
+ foreach ( fw()->extensions->get_all() as $ext_name => $extension ) {
137
  /** @var FW_Extension $extension */
138
 
139
  /**
140
  * Ask each service if it knows how to update the extension
141
  */
142
+ foreach ( $services as $service_name => $service ) {
143
  /** @var $service FW_Ext_Update_Service */
144
 
145
+ $latest_version = $service->_get_extension_latest_version( $extension, $force_check );
146
 
147
+ if ( $latest_version === false ) {
148
  // It said that it doesn't know how to update it
149
  continue;
150
  }
151
 
152
+ if ( is_wp_error( $latest_version ) ) {
153
+ $updates[ $ext_name ] = $latest_version;
154
  break;
155
  }
156
 
157
+ $fixed_latest_version = $this->get_fixed_version( $latest_version );
158
 
159
+ if ( ! version_compare( $fixed_latest_version, $extension->manifest->get_version(), '>' ) ) {
160
  // we already have latest version
161
  continue;
162
  }
163
 
164
  if (
165
+ isset( $theme_ext_requirements[ $ext_name ] )
166
  &&
167
+ isset( $theme_ext_requirements[ $ext_name ]['max_version'] )
168
  &&
169
+ version_compare( $fixed_latest_version, $theme_ext_requirements[ $ext_name ]['max_version'], '>' )
170
  ) {
171
  continue; // do not allow update if it exceeds max_version
172
  }
173
 
174
+ $updates[ $ext_name ] = array(
175
+ 'service' => $service_name,
176
+ 'latest_version' => $latest_version,
177
  'fixed_latest_version' => $fixed_latest_version
178
  );
179
 
186
 
187
  /**
188
  * @param bool $force_check
189
+ *
190
  * @return array|false|WP_Error
191
  */
192
+ private function get_framework_update( $force_check = false ) {
 
193
  /**
194
  * Ask each service if it knows how to update the framework
195
  */
196
+ foreach ( $this->get_children() as $service_name => $service ) {
197
  /** @var $service FW_Ext_Update_Service */
198
 
199
+ $latest_version = $service->_get_framework_latest_version( $force_check );
200
 
201
+ if ( $latest_version === false ) {
202
  // It said that it doesn't know how to update it
203
  continue;
204
  }
205
 
206
+ if ( is_wp_error( $latest_version ) ) {
207
  return $latest_version;
208
  }
209
 
210
+ $fixed_latest_version = $this->get_fixed_version( $latest_version );
211
 
212
+ if ( ! version_compare( $fixed_latest_version, fw()->manifest->get_version(), '>' ) ) {
213
  // we already have latest version
214
  continue;
215
  }
216
 
217
  return array(
218
+ 'service' => $service_name,
219
+ 'latest_version' => $latest_version,
220
  'fixed_latest_version' => $fixed_latest_version
221
  );
222
  }
226
 
227
  /**
228
  * @param bool $force_check
229
+ *
230
  * @return array|false|WP_Error
231
  */
232
+ private function get_theme_update( $force_check = false ) {
 
233
  /**
234
  * Ask each service if it knows how to update the theme
235
  */
236
+ foreach ( $this->get_children() as $service_name => $service ) {
237
  /** @var $service FW_Ext_Update_Service */
238
 
239
+ $latest_version = $service->_get_theme_latest_version( $force_check );
240
 
241
+ if ( $latest_version === false ) {
242
  // It said that it doesn't know how to update it
243
  continue;
244
  }
245
 
246
+ if ( is_wp_error( $latest_version ) ) {
247
  return $latest_version;
248
  }
249
 
250
+ $fixed_latest_version = $this->get_fixed_version( $latest_version );
251
 
252
+ if ( ! version_compare( $fixed_latest_version, fw()->theme->manifest->get_version(), '>' ) ) {
253
  // we already have latest version
254
  continue;
255
  }
256
 
257
  return array(
258
+ 'service' => $service_name,
259
+ 'latest_version' => $latest_version,
260
  'fixed_latest_version' => $fixed_latest_version
261
  );
262
  }
266
 
267
  /**
268
  * Turn on/off the maintenance mode
269
+ *
270
  * @param bool $enable
271
  */
272
+ private function maintenance_mode( $enable = false ) {
 
273
  /** @var WP_Filesystem_Base $wp_filesystem */
274
  global $wp_filesystem;
275
 
276
+ if ( ! $wp_filesystem || ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) ) {
277
  return;
278
  }
279
 
280
  $file_path = $wp_filesystem->abspath() . '.maintenance';
281
 
282
+ if ( $wp_filesystem->exists( $file_path ) ) {
283
+ if ( ! $wp_filesystem->delete( $file_path ) ) {
284
+ trigger_error( __( 'Cannot delete: ', 'fw' ) . $file_path, E_USER_WARNING );
285
  }
286
  }
287
 
288
+ if ( $enable ) {
289
  // Create maintenance file to signal that we are upgrading
290
+ if ( ! $wp_filesystem->put_contents( $file_path, '<?php $upgrading = ' . time() . '; ?>', FS_CHMOD_FILE ) ) {
291
+ trigger_error( __( 'Cannot create: ', 'fw' ) . $file_path, E_USER_WARNING );
292
  }
293
  }
294
  }
301
  * @param array $data
302
  * @param bool $merge_extensions The extensions/ directory will not be replaced entirely,
303
  * only extensions that comes with the update will be replaced
304
+ *
305
  * @return null|WP_Error
306
  */
307
+ private function update( $data, $merge_extensions = false ) {
 
308
  $required_data_keys = array(
309
  'wp_fs_destination_dir' => true,
310
  'download_callback' => true,
313
  'title' => true,
314
  );
315
 
316
+ if ( count( $required_data_keys ) > count( array_intersect_key( $required_data_keys, $data ) ) ) {
317
+ trigger_error( 'Some required keys are not present', E_USER_ERROR );
318
  }
319
 
320
  // move manually every key to variable, so IDE will understand better them
347
  */
348
  $title = $data['title'];
349
 
350
+ unset( $data );
351
  }
352
 
353
  /**
363
 
364
  // create temporary directory
365
  {
366
+ if ( $wp_filesystem->exists( $tmp_download_dir ) ) {
367
  // just in case it already exists, clear everything, it may contain old files
368
+ if ( ! $wp_filesystem->rmdir( $tmp_download_dir, true ) ) {
369
+ $error = __( 'Cannot remove old temporary directory: ', 'fw' ) . $tmp_download_dir;
370
  break;
371
  }
372
  }
373
 
374
+ if ( ! FW_WP_Filesystem::mkdir_recursive( $tmp_download_dir ) ) {
375
+ $error = __( 'Cannot create directory: ', 'fw' ) . $tmp_download_dir;
376
  break;
377
  }
378
  }
379
 
380
+ $skin->feedback( sprintf( __( 'Downloading the %s...', 'fw' ), $title ) );
381
  {
382
+ $downloaded_dir = call_user_func_array( $download_callback, $download_callback_args );
383
 
384
+ if ( ! $downloaded_dir ) {
385
+ $error = sprintf( __( 'Cannot download the %s.', 'fw' ), $title );
386
  break;
387
+ } elseif ( is_wp_error( $downloaded_dir ) ) {
388
  $error = $downloaded_dir;
389
  break;
390
  }
391
  }
392
 
393
+ $this->maintenance_mode( true );
394
 
395
+ $skin->feedback( sprintf( __( 'Installing the %s...', 'fw' ), $title ) );
396
  {
397
  // remove all files from destination directory
398
  {
399
+ $dir_files = $wp_filesystem->dirlist( $wp_fs_destination_dir, true );
400
+ if ( $dir_files === false ) {
401
+ $error = __( 'Cannot access directory: ', 'fw' ) . $wp_fs_destination_dir;
402
  break;
403
  }
404
 
405
+ foreach ( $dir_files as $file ) {
406
+ if ( in_array( $file['name'], $this->skip_file_names ) ) {
407
  continue;
408
  }
409
 
410
+ if ( $merge_extensions ) {
411
+ if ( $file['name'] === 'extensions' && $file['type'] === 'd' ) {
412
  // do not remove extensions, will be merged later
413
  continue;
414
  }
415
  }
416
 
417
+ $file_path = $wp_fs_destination_dir . '/' . $file['name'];
418
 
419
+ if ( ! $wp_filesystem->delete( $file_path, true, $file['type'] ) ) {
420
+ $error = __( 'Cannot remove: ', 'fw' ) . $file_path;
421
  break 2;
422
  }
423
  }
425
 
426
  // move all files from the temporary directory to the destination directory
427
  {
428
+ $dir_files = $wp_filesystem->dirlist( $downloaded_dir, true );
429
+ if ( $dir_files === false ) {
430
+ $error = __( 'Cannot access directory: ', 'fw' ) . $downloaded_dir;
431
  break;
432
  }
433
 
434
+ foreach ( $dir_files as $file ) {
435
+ if ( in_array( $file['name'], $this->skip_file_names ) ) {
436
  continue;
437
  }
438
 
439
+ $downloaded_file_path = $downloaded_dir . '/' . $file['name'];
440
+ $destination_file_path = $wp_fs_destination_dir . '/' . $file['name'];
441
 
442
+ if ( $merge_extensions ) {
443
+ if ( $file['name'] === 'extensions' && $file['type'] === 'd' ) {
444
  // merge extensions/ after all other files was moved
445
  $merge_extensions_data = array(
446
+ 'source' => $downloaded_file_path,
447
  'destination' => $destination_file_path,
448
  );
449
  continue;
450
  }
451
  }
452
 
453
+ if ( ! $wp_filesystem->move( $downloaded_file_path, $destination_file_path ) ) {
454
  $error = sprintf(
455
+ __( 'Cannot move "%s" to "%s"', 'fw' ),
456
  $downloaded_file_path, $destination_file_path
457
  );
458
  break 2;
459
  }
460
  }
461
 
462
+ if ( $merge_extensions ) {
463
+ if ( ! empty( $merge_extensions_data ) ) {
464
  $merge_result = $this->merge_extensions(
465
  $merge_extensions_data['source'],
466
  $merge_extensions_data['destination']
467
  );
468
 
469
+ if ( $merge_result === false ) {
470
  $error = sprintf(
471
+ __( 'Cannot merge "%s" with "%s"', 'fw' ),
472
  $downloaded_file_path, $destination_file_path
473
  );
474
  break;
475
+ } elseif ( is_wp_error( $merge_result ) ) {
476
  $error = $merge_result;
477
  break;
478
  }
481
  }
482
  }
483
 
484
+ $skin->feedback( sprintf( __( 'The %s has been successfully updated.', 'fw' ), $title ) );
485
+ } while ( false );
486
 
487
+ $this->maintenance_mode( false );
488
 
489
+ if ( $wp_filesystem->exists( $tmp_download_dir ) ) {
490
  if ( ! $wp_filesystem->delete( $tmp_download_dir, true, 'd' ) ) {
491
  $error = sprintf( __( 'Cannot remove temporary directory "%s".', 'fw' ), $tmp_download_dir );
492
  }
493
  }
494
 
495
+ if ( $error ) {
496
+ if ( ! is_wp_error( $error ) ) {
497
+ $error = new WP_Error( 'fw_ext_update_failed', (string) $error );
498
  }
499
 
500
  return $error;
503
 
504
  /**
505
  * Merge two extensions/ directories
506
+ *
507
  * @param string $source_dir WP_Filesystem dir '/a/b/c/extensions'
508
  * @param string $destination_dir WP_Filesystem dir '/a/b/d/extensions'
509
+ *
510
  * @return bool|WP_Error
511
  */
512
+ private function merge_extensions( $source_dir, $destination_dir ) {
 
513
  /** @var WP_Filesystem_Base $wp_filesystem */
514
  global $wp_filesystem;
515
 
516
  $wp_error_id = 'fw_ext_update_merge_extensions';
517
 
518
+ if ( ! $wp_filesystem->exists( $destination_dir ) ) {
519
  // do a simple move if destination does not exist
520
+ if ( ! $wp_filesystem->move( $source_dir, $destination_dir ) ) {
521
+ return new WP_Error( $wp_error_id,
522
+ sprintf( __( 'Cannot move "%s" to "%s"', 'fw' ), $source_dir, $destination_dir )
523
  );
524
  }
525
+
526
  return true;
527
  }
528
 
529
+ $source_ext_dirs = $wp_filesystem->dirlist( $source_dir, true );
530
+ if ( $source_ext_dirs === false ) {
531
+ return new WP_Error( $wp_error_id,
532
+ __( 'Cannot access directory: ', 'fw' ) . $source_dir
533
  );
534
  }
535
 
536
+ foreach ( $source_ext_dirs as $ext_dir ) {
537
+ if ( in_array( $ext_dir['name'], $this->skip_file_names ) ) {
538
  continue;
539
  }
540
 
541
+ if ( $ext_dir['type'] !== 'd' ) {
542
  // process only directories from the extensions/ directory
543
  continue;
544
  }
545
 
546
+ $source_extension_dir = $source_dir . '/' . $ext_dir['name'];
547
+ $destination_extension_dir = $destination_dir . '/' . $ext_dir['name'];
548
 
549
  {
550
+ $source_ext_files = $wp_filesystem->dirlist( $source_extension_dir, true );
551
+ if ( $source_ext_files === false ) {
552
+ return new WP_Error( $wp_error_id,
553
+ __( 'Cannot access directory: ', 'fw' ) . $source_extension_dir
554
  );
555
  }
556
 
557
+ if ( empty( $source_ext_files ) ) {
558
  /**
559
  * Source extension directory is empty, do nothing.
560
  * This happens when the extension is a git submodule in repository
567
  // prepare destination
568
  {
569
  // create if not exists
570
+ if ( ! $wp_filesystem->exists( $destination_extension_dir ) ) {
571
+ if ( ! FW_WP_Filesystem::mkdir_recursive( $destination_extension_dir ) ) {
572
+ return new WP_Error( $wp_error_id,
573
+ __( 'Cannot create directory: ', 'fw' ) . $destination_extension_dir
574
  );
575
  }
576
  }
577
 
578
  // remove everything except the extensions/ dir
579
  {
580
+ $dest_ext_files = $wp_filesystem->dirlist( $destination_extension_dir, true );
581
+ if ( $dest_ext_files === false ) {
582
+ return new WP_Error( $wp_error_id,
583
+ __( 'Cannot access directory: ', 'fw' ) . $destination_extension_dir
584
  );
585
  }
586
 
587
  $destination_has_extensions_dir = false;
588
 
589
+ foreach ( $dest_ext_files as $dest_ext_file ) {
590
+ if ( in_array( $dest_ext_file['name'], $this->skip_file_names ) ) {
591
  continue;
592
  }
593
 
594
+ if ( $dest_ext_file['name'] === 'extensions' && $dest_ext_file['type'] === 'd' ) {
595
  $destination_has_extensions_dir = true;
596
  continue;
597
  }
598
 
599
+ $dest_ext_file_path = $destination_extension_dir . '/' . $dest_ext_file['name'];
600
 
601
+ if ( ! $wp_filesystem->delete( $dest_ext_file_path, true, $dest_ext_file['type'] ) ) {
602
+ return new WP_Error( $wp_error_id,
603
+ __( 'Cannot delete: ', 'fw' ) . $dest_ext_file_path
604
  );
605
  }
606
  }
611
  {
612
  $source_has_extensions_dir = false;
613
 
614
+ foreach ( $source_ext_files as $source_ext_file ) {
615
+ if ( in_array( $source_ext_file['name'], $this->skip_file_names ) ) {
616
  continue;
617
  }
618
 
619
+ if ( $source_ext_file['name'] === 'extensions' && $source_ext_file['type'] === 'd' ) {
620
  $source_has_extensions_dir = true;
621
  continue;
622
  }
623
 
624
+ $source_ext_file_path = $source_extension_dir . '/' . $source_ext_file['name'];
625
+ $dest_ext_file_path = $destination_extension_dir . '/' . $source_ext_file['name'];
626
 
627
+ if ( ! $wp_filesystem->move( $source_ext_file_path, $dest_ext_file_path ) ) {
628
+ return new WP_Error( $wp_error_id,
629
+ sprintf( __( 'Cannot move "%s" to "%s"', 'fw' ),
630
  $source_ext_file_path, $dest_ext_file_path
631
  )
632
  );
634
  }
635
  }
636
 
637
+ if ( $source_has_extensions_dir ) {
638
+ if ( $destination_has_extensions_dir ) {
639
  $merge_result = $this->merge_extensions(
640
+ $source_extension_dir . '/extensions',
641
+ $destination_extension_dir . '/extensions'
642
  );
643
 
644
+ if ( $merge_result !== true ) {
645
  return $merge_result;
646
  }
647
  } else {
648
+ if ( ! $wp_filesystem->move(
649
+ $source_extension_dir . '/extensions',
650
+ $destination_extension_dir . '/extensions'
651
+ ) ) {
652
+ return new WP_Error( $wp_error_id,
653
+ sprintf( __( 'Cannot move "%s" to "%s"', 'fw' ),
654
+ $source_extension_dir . '/extensions',
655
+ $destination_extension_dir . '/extensions'
656
  )
657
  );
658
  }
666
  /**
667
  * @internal
668
  */
669
+ public function _action_update_framework() {
 
670
  $nonce_name = '_nonce_fw_ext_update_framework';
671
+ if ( ! isset( $_POST[ $nonce_name ] ) || ! wp_verify_nonce( $_POST[ $nonce_name ] ) ) {
672
+ wp_die( __( 'Invalid nonce.', 'fw' ) );
673
  }
674
 
675
  {
676
+ if ( ! class_exists( '_FW_Ext_Update_Framework_Upgrader_Skin' ) ) {
677
  fw_include_file_isolated(
678
+ $this->get_declared_path( '/includes/classes/class--fw-ext-update-framework-upgrader-skin.php' )
679
  );
680
  }
681
 
682
+ $skin = new _FW_Ext_Update_Framework_Upgrader_Skin( array(
683
+ 'title' => __( 'Framework Update', 'fw' ),
684
+ ) );
685
  }
686
 
687
+ require_once ABSPATH . 'wp-admin/admin-header.php';
688
 
689
  $skin->header();
690
 
691
  do {
692
+ if ( ! FW_WP_Filesystem::request_access( fw_get_framework_directory(), fw_current_url(), array( $nonce_name ) ) ) {
693
  break;
694
  }
695
 
696
  $update = $this->get_framework_update();
697
 
698
+ if ( $update === false ) {
699
+ $skin->error( __( 'Failed to get framework latest version.', 'fw' ) );
700
  break;
701
+ } elseif ( is_wp_error( $update ) ) {
702
+ $skin->error( $update );
703
  break;
704
  }
705
 
706
  /** @var FW_Ext_Update_Service $service */
707
+ $service = $this->get_child( $update['service'] );
708
 
709
+ $update_result = $this->update( array(
710
+ 'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
711
  fw_get_framework_directory()
712
  ),
713
+ 'download_callback' => array( $service, '_download_framework' ),
714
+ 'download_callback_args' => array( $update['latest_version'], $this->get_wp_fs_tmp_dir() ),
715
+ 'skin' => $skin,
716
+ 'title' => __( 'Framework', 'fw' ),
717
+ ) );
718
+
719
+ if ( is_wp_error( $update_result ) ) {
720
+ $skin->error( $update_result );
721
  break;
722
  }
723
 
724
+ $skin->set_result( true );
725
  $skin->after();
726
+ } while ( false );
727
 
728
  $skin->footer();
729
 
730
+ require_once( ABSPATH . 'wp-admin/admin-footer.php' );
731
  }
732
 
733
  /**
734
  * @internal
735
  */
736
+ public function _action_update_theme() {
 
737
  $nonce_name = '_nonce_fw_ext_update_theme';
738
+ if ( ! isset( $_POST[ $nonce_name ] ) || ! wp_verify_nonce( $_POST[ $nonce_name ] ) ) {
739
+ wp_die( __( 'Invalid nonce.', 'fw' ) );
740
  }
741
 
742
  {
743
+ if ( ! class_exists( '_FW_Ext_Update_Theme_Upgrader_Skin' ) ) {
744
  fw_include_file_isolated(
745
+ $this->get_declared_path( '/includes/classes/class--fw-ext-update-theme-upgrader-skin.php' )
746
  );
747
  }
748
 
749
+ $skin = new _FW_Ext_Update_Theme_Upgrader_Skin( array(
750
+ 'title' => __( 'Theme Update', 'fw' ),
751
+ ) );
752
  }
753
 
754
+ require_once( ABSPATH . 'wp-admin/admin-header.php' );
755
 
756
  $skin->header();
757
 
758
  do {
759
+ if ( ! FW_WP_Filesystem::request_access( get_template_directory(), fw_current_url(), array( $nonce_name ) ) ) {
760
  break;
761
  }
762
 
763
  $update = $this->get_theme_update();
764
 
765
+ if ( $update === false ) {
766
+ $skin->error( __( 'Failed to get theme latest version.', 'fw' ) );
767
  break;
768
+ } elseif ( is_wp_error( $update ) ) {
769
+ $skin->error( $update );
770
  break;
771
  }
772
 
773
  /** @var FW_Ext_Update_Service $service */
774
+ $service = $this->get_child( $update['service'] );
775
 
776
+ $update_result = $this->update( array(
777
+ 'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
778
  get_template_directory()
779
  ),
780
+ 'download_callback' => array( $service, '_download_theme' ),
781
+ 'download_callback_args' => array( $update['latest_version'], $this->get_wp_fs_tmp_dir() ),
782
+ 'skin' => $skin,
783
+ 'title' => __( 'Theme', 'fw' ),
784
+ ) );
785
+
786
+ if ( is_wp_error( $update_result ) ) {
787
+ $skin->error( $update_result );
788
  break;
789
  }
790
 
791
+ $skin->set_result( true );
792
  $skin->after();
793
+ } while ( false );
794
 
795
  $skin->footer();
796
 
797
+ require_once( ABSPATH . 'wp-admin/admin-footer.php' );
798
  }
799
 
800
  /**
801
  * @internal
802
  */
803
+ public function _action_update_extensions() {
 
804
  $nonce_name = '_nonce_fw_ext_update_extensions';
805
+ if ( ! isset( $_POST[ $nonce_name ] ) || ! wp_verify_nonce( $_POST[ $nonce_name ] ) ) {
806
+ wp_die( __( 'Invalid nonce.', 'fw' ) );
807
  }
808
 
809
  $form_input_name = 'extensions';
810
+ $extensions_list = FW_Request::POST( $form_input_name );
811
 
812
+ if ( empty( $extensions_list ) ) {
813
  FW_Flash_Messages::add(
814
  'fw_ext_update',
815
+ __( 'Please check the extensions you want to update.', 'fw' ),
816
  'warning'
817
  );
818
+ wp_redirect( self_admin_url( 'update-core.php' ) );
819
  exit;
820
  }
821
 
822
  // handle changes by the hack below
823
  {
824
+ if ( is_string( $extensions_list ) ) {
825
+ $extensions_list = json_decode( $extensions_list );
826
  } else {
827
+ $extensions_list = array_keys( $extensions_list );
828
  }
829
  }
830
 
831
  {
832
+ if ( ! class_exists( '_FW_Ext_Update_Extensions_Upgrader_Skin' ) ) {
833
  fw_include_file_isolated(
834
+ $this->get_declared_path( '/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php' )
835
  );
836
  }
837
 
838
+ $skin = new _FW_Ext_Update_Extensions_Upgrader_Skin( array(
839
+ 'title' => __( 'Extensions Update', 'fw' ),
840
+ ) );
841
  }
842
 
843
+ require_once( ABSPATH . 'wp-admin/admin-header.php' );
844
 
845
  $skin->header();
846
 
849
  * Hack for the ftp credentials template that does not support array post values
850
  * https://github.com/WordPress/WordPress/blob/3949a8b6cc50a021ed93798287b4ef9ea8a560d9/wp-admin/includes/file.php#L1144
851
  */
852
+ $original_post_value = $_POST[ $form_input_name ];
853
+ $_POST[ $form_input_name ] = wp_slash( json_encode( $extensions_list ) );
854
+
855
+ if ( ! FW_WP_Filesystem::request_access( fw_get_framework_directory( '/extensions' ), fw_current_url(), array( $nonce_name, $form_input_name ) ) ) {
856
+ // revert hack changes
857
+ $_POST[ $form_input_name ] = $original_post_value;
858
+ unset( $original_post_value );
859
+
 
 
 
 
 
 
860
  break;
861
  }
862
 
863
+ // revert hack changes
864
+ $_POST[ $form_input_name ] = $original_post_value;
865
+ unset( $original_post_value );
 
866
 
867
  $updates = $this->get_extensions_with_updates();
868
 
869
+ if ( empty( $updates ) ) {
870
+ $skin->error( __( 'No extensions updates found.', 'fw' ) );
871
  break;
872
  }
873
 
874
+ foreach ( $extensions_list as $extension_name ) {
875
+ if ( ! ( $extension = fw()->extensions->get( $extension_name ) ) ) {
876
+ $skin->error( sprintf( __( 'Extension "%s" does not exist or is disabled.', 'fw' ), $extension_name ) );
 
 
877
  continue;
878
  }
879
 
880
+ if ( ! isset( $updates[ $extension_name ] ) ) {
881
+ $skin->error( sprintf( __( 'No update found for the "%s" extension.', 'fw' ), $extension->manifest->get_name() ) );
 
 
882
  continue;
883
  }
884
 
885
+ $update = $updates[ $extension_name ];
886
 
887
+ if ( is_wp_error( $update ) ) {
888
+ $skin->error( $update );
889
  continue;
890
  }
891
 
892
  /** @var FW_Ext_Update_Service $service */
893
+ $service = $this->get_child( $update['service'] );
894
 
895
+ $update_result = $this->update( array(
896
+ 'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
897
  $extension->get_declared_path()
898
  ),
899
+ 'download_callback' => array( $service, '_download_extension' ),
900
+ 'download_callback_args' => array(
901
+ $extension,
902
+ $update['latest_version'],
903
+ $this->get_wp_fs_tmp_dir()
904
+ ),
905
+ 'skin' => $skin,
906
+ 'title' => sprintf( __( '%s extension', 'fw' ), $extension->manifest->get_name() ),
907
+ ), true );
908
+
909
+ if ( is_wp_error( $update_result ) ) {
910
+ $skin->error( $update_result );
911
  continue;
912
  }
913
 
914
+ $skin->set_result( true );
915
 
916
+ if ( ! $this->get_config( 'extensions_as_one_update' ) ) {
917
  $skin->decrement_extension_update_count( $extension_name );
918
  }
919
  }
920
 
921
+ if ( $this->get_config( 'extensions_as_one_update' ) ) {
922
  $skin->decrement_extension_update_count( $extension_name );
923
  }
924
 
925
  $skin->after();
926
+ } while ( false );
927
 
928
  $skin->footer();
929
 
930
+ require_once( ABSPATH . 'wp-admin/admin-footer.php' );
931
  }
932
 
933
  public function _action_admin_notices() {
934
+
935
+ $updates = $this->get_updates();
936
+
937
+ if ( empty( $updates['extensions'] ) || strpos( get_current_screen()->id, 'update-core' ) !== false ) {
 
 
 
938
  return;
939
  }
940
 
941
+ foreach ( $updates['extensions'] as $ext_name => $ext_update ) {
942
  if ( is_wp_error( $ext_update ) ) {
943
  return;
944
  }
946
  break;
947
  }
948
 
949
+ echo
950
+ '<div class="notice notice-warning">
951
+ <p>' .
952
+ sprintf(
953
+ esc_html__( 'New extensions updates available. %s', 'fw' ),
954
+ fw_html_tag(
955
+ 'a',
956
+ array( 'href' => self_admin_url( 'update-core.php' ) . '#fw-ext-update-extensions' ),
957
+ esc_html__( 'Go to Updates page', 'fw' )
958
+ )
959
+ ) .
960
+ '</p>
961
+ </div>';
962
  }
963
  }
framework/helpers/class-fw-form.php CHANGED
@@ -462,7 +462,7 @@ class FW_Form {
462
  $errors = array();
463
 
464
  if ( ! $this->check_nonce( $this->get_nonce() ) ) {
465
- $errors[ $this->get_nonce_name() ] = __( 'Nonce verification failed', 'fw' );
466
  }
467
 
468
  /**
462
  $errors = array();
463
 
464
  if ( ! $this->check_nonce( $this->get_nonce() ) ) {
465
+ $errors[ $this->get_nonce_name() ] = esc_html__( 'Your session expired. Please refresh page and try again.', 'fw' );
466
  }
467
 
468
  /**
framework/helpers/general.php CHANGED
@@ -732,6 +732,11 @@ if ( ! function_exists( 'fw_render_view' ) ):
732
  * @return string HTML
733
  */
734
  function fw_render_view( $file_path, $view_variables = array(), $return = true ) {
 
 
 
 
 
735
  extract( $view_variables, EXTR_REFS );
736
  unset( $view_variables );
737
 
@@ -743,6 +748,8 @@ if ( ! function_exists( 'fw_render_view' ) ):
743
  } else {
744
  require $file_path;
745
  }
 
 
746
  }
747
  endif;
748
 
732
  * @return string HTML
733
  */
734
  function fw_render_view( $file_path, $view_variables = array(), $return = true ) {
735
+
736
+ if ( ! is_file( $file_path ) ) {
737
+ return '';
738
+ }
739
+
740
  extract( $view_variables, EXTR_REFS );
741
  unset( $view_variables );
742
 
748
  } else {
749
  require $file_path;
750
  }
751
+
752
+ return '';
753
  }
754
  endif;
755
 
framework/includes/option-types/color-picker/static/js/scripts.js CHANGED
@@ -27,17 +27,11 @@ jQuery(document).ready(function($){
27
  getInstance: function ($iris) {
28
  return $iris.data('a8cIris');
29
  },
30
- updatePreview: function ($input, color) {
31
- if (this.isColorValid(color)) {
32
- $input.css({
33
- 'background-color': color,
34
- 'color': this.isColorDark(color) ? '#FFFFFF' : '#000000'
35
- });
36
  } else {
37
- $input.css({
38
- 'background-color': '',
39
- 'color': ''
40
- });
41
  }
42
  },
43
  increment: 0
27
  getInstance: function ($iris) {
28
  return $iris.data('a8cIris');
29
  },
30
+ updatePreview: function ( $input, color ) {
31
+ if ( this.isColorValid( color ) ) {
32
+ $input.attr( 'style', 'background-color:' + color + ' !important; color:' + ( this.isColorDark( color ) ? '#FFFFFF' : '#000000' ) + ' !important;' );
 
 
 
33
  } else {
34
+ $input.css( {'background-color': '', 'color': ''} );
 
 
 
35
  }
36
  },
37
  increment: 0
framework/includes/option-types/multi-select/class-fw-option-type-multi-select.php CHANGED
@@ -342,9 +342,9 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
342
  switch ( $option['population'] ) {
343
  case 'array' :
344
  if ( isset( $option['choices'] ) && is_array( $option['choices'] ) ) {
345
- foreach ($option['choices'] as $c_key => $c_val) {
346
  $items[] = array(
347
- 'val' => $c_key,
348
  'title' => $c_val,
349
  );
350
  }
@@ -352,27 +352,20 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
352
  break;
353
  case 'posts' :
354
  if ( isset( $option['source'] ) ) {
355
- $source = is_array( $option['source'] ) ? $option['source'] : array( $option['source'] );
356
 
357
- $items = self::get_posts( $data['value'] );
358
-
359
- if ( count( $items ) < $option['prepopulate'] ) {
360
- foreach (
361
- self::query_posts( array(
362
- 'type' => array_fill_keys( $source, true ),
363
- 'limit' => $option['prepopulate'],
364
- ) ) as $item
365
- ) {
366
- if ( ! in_array( $item, $items ) ) {
367
- array_push( $items, $item );
368
- }
369
-
370
- if ( count( $items ) == $option['prepopulate'] ) {
371
- break;
372
- }
373
- }
374
- }
375
  }
 
376
  $items = array_map(
377
  array( $this, 'build_post' ),
378
  $items,
@@ -385,22 +378,18 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
385
 
386
  $items = self::get_terms( $data['value'], $source );
387
 
388
- if ( count( $items ) < $option['prepopulate'] ) {
389
- foreach (
390
- self::query_terms( array(
391
- 'taxonomy' => array_fill_keys( $source, true ),
392
- 'limit' => $option['prepopulate'],
393
- ) ) as $item
394
- ) {
395
- if ( ! in_array( $item, $items ) ) {
396
- array_push( $items, $item );
397
- }
398
-
399
- if ( count( $items ) == $option['prepopulate'] ) {
400
- break;
401
- }
402
- }
403
  }
 
404
  }
405
 
406
  $items = array_map(
@@ -410,27 +399,39 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
410
  );
411
  break;
412
  case 'users' :
413
- if ( isset( $option['source'] ) && ! empty( $option['source'] ) ) {
414
- $source = is_array( $option['source'] ) ? $option['source'] : array( $option['source'] );
415
- } else {
416
- $source = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  }
418
 
419
- $items = self::query_users(array(
420
- 'role' => array_fill_keys($source, true),
421
- 'id' => $data['value'],
422
- 'limit' => $option['prepopulate'],
423
- ));
424
  break;
425
  default :
426
  return '(Invalid <code>population</code> parameter)';
427
  }
428
 
429
- $option['attr']['data-options'] = json_encode( $items );
430
- $option['attr']['data-population'] = $population;
431
- $option['attr']['data-show-type'] = (int)fw_akg( 'show-type', $option, false );
432
- $option['attr']['data-source'] = json_encode( $source );
433
- $option['attr']['data-limit'] = ( intval( $option['limit'] ) > 0 ) ? $option['limit'] : 0;
 
434
  } else {
435
  return '(The <code>population</code> parameter is required)';
436
  }
342
  switch ( $option['population'] ) {
343
  case 'array' :
344
  if ( isset( $option['choices'] ) && is_array( $option['choices'] ) ) {
345
+ foreach ( $option['choices'] as $c_key => $c_val ) {
346
  $items[] = array(
347
+ 'val' => $c_key,
348
  'title' => $c_val,
349
  );
350
  }
352
  break;
353
  case 'posts' :
354
  if ( isset( $option['source'] ) ) {
 
355
 
356
+ $items = self::get_posts( (array) $data['value'] );
357
+
358
+ $query = new WP_Query( array(
359
+ 'post_type' => $option['source'],
360
+ 'post__not_in' => $data['value'],
361
+ 'posts_per_page' => $option['prepopulate'],
362
+ 'fields' => 'ids',
363
+ 'ignore_sticky_posts' => 1
364
+ ) );
365
+
366
+ $items = array_merge( $items, $query->get_posts() );
 
 
 
 
 
 
 
367
  }
368
+
369
  $items = array_map(
370
  array( $this, 'build_post' ),
371
  $items,
378
 
379
  $items = self::get_terms( $data['value'], $source );
380
 
381
+ $terms = get_terms( array(
382
+ 'taxonomy' => $option['source'],
383
+ 'hide_empty' => false,
384
+ 'exclude' => $data['value'],
385
+ 'number' => $option['prepopulate'],
386
+ 'fields' => 'ids'
387
+ ) );
388
+
389
+ if ( ! is_wp_error( $terms ) ) {
390
+ $items = array_merge( $items, (array) $terms );
 
 
 
 
 
391
  }
392
+
393
  }
394
 
395
  $items = array_map(
399
  );
400
  break;
401
  case 'users' :
402
+
403
+ $source = ! empty( $option['source'] ) ? (array) $option['source'] : array();
404
+
405
+ $items = self::query_users( array(
406
+ 'role' => array_fill_keys( $source, true ),
407
+ 'id' => $data['value'],
408
+ 'limit' => $option['prepopulate']
409
+ ) );
410
+
411
+ $users = get_users(
412
+ array(
413
+ 'role__in' => $source,
414
+ 'exclude' => (array) $data['value'],
415
+ 'number' => (int) $option['prepopulate'],
416
+ 'fields' => array( 'ID', 'display_name' )
417
+ )
418
+ );
419
+
420
+ foreach ( $users as $user ) {
421
+ $items[] = array( 'val' => $user->ID, 'title' => $user->display_name );
422
  }
423
 
 
 
 
 
 
424
  break;
425
  default :
426
  return '(Invalid <code>population</code> parameter)';
427
  }
428
 
429
+ $option['attr']['data-options'] = json_encode( $items );
430
+ $option['attr']['data-population'] = $population;
431
+ $option['attr']['data-show-type'] = (int) fw_akg( 'show-type', $option, false );
432
+ $option['attr']['data-source'] = json_encode( $source );
433
+ $option['attr']['data-limit'] = ( intval( $option['limit'] ) > 0 ) ? $option['limit'] : 0;
434
+
435
  } else {
436
  return '(The <code>population</code> parameter is required)';
437
  }
framework/includes/option-types/rgba-color-picker/class-fw-option-type-rgba-color-picker.php CHANGED
@@ -63,7 +63,7 @@ class FW_Option_Type_Rgba_Color_Picker extends FW_Option_Type {
63
 
64
  $option['attr']['data-palettes'] = json_encode( $palettes );
65
 
66
- return '<input type="text" ' . fw_attr_to_html( $option['attr'] ) . '>';
67
  }
68
 
69
  /**
63
 
64
  $option['attr']['data-palettes'] = json_encode( $palettes );
65
 
66
+ return '<input type="text" ' . fw_attr_to_html( $option['attr'] ) . ' data-alpha="true">';
67
  }
68
 
69
  /**
framework/includes/option-types/rgba-color-picker/static/css/styles.css CHANGED
@@ -1,86 +1,21 @@
1
- input.fw-option-type-rgba-color-picker.iris-error {
2
- background-color: #ffebe8;
3
- border-color: #c00;
4
- color: #000;
5
  }
6
-
7
- .fw-alpha-container .transparency {
8
- height: 24px;
9
- width: 100%;
10
- background-color: #FFF;
11
- background-image: url(../images/transparency-grid.png);
12
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.4) inset;
13
- -webkit-border-radius: 3px;
14
- -moz-border-radius: 3px;
15
- border-radius: 3px;
16
- padding: 0;
17
  }
18
-
19
- .fw-alpha-container .ui-slider-handle {
20
- color: #777;
21
- background-color: #FFF;
22
- text-shadow: 0 1px 0 #FFF;
23
- text-decoration: none;
24
- position: absolute;
25
- z-index: 2;
26
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
27
- border: 1px solid #aaa;
28
- -webkit-border-radius: 4px;
29
- -moz-border-radius: 4px;
30
- border-radius: 4px;
31
- opacity: 0.9;
32
- margin-top: -2px;
33
- height: 20px;
34
- cursor: ew-resize;
35
- font-size: 12px;
36
- padding: 3px;
37
  }
38
-
39
- .fw-alpha-container .ui-slider {
40
- position: relative;
41
- text-align: center;
42
- width: 88%;
43
  }
44
-
45
- .fw-alpha-container {
46
- box-sizing: padding-box;
47
- display: block;
48
- border-right: 1px solid #aaa;
49
- border-left: 1px solid #aaa;
50
- border-top: none;
51
- border-bottom: 1px solid #aaa;
52
- background: #FFF;
53
- padding: 0 11px 6px;
54
- position: relative;
55
- top: 176px;
56
- width: 89%;
57
- left: -1px;
58
  }
59
-
60
-
61
- .fw-option-type-rgba-color-picker-iris,
62
- .fw-option-type-rgba-color-picker-iris.iris-picker {
63
- z-index: 999;
64
- position: absolute;
65
- }
66
-
67
- .fw-option-type-rgba-color-picker-iris .fw-option-type-rgba-color-picker-reset-default {
68
- width: 139px;
69
- margin-top: 2px;
70
- cursor: pointer;
71
- }
72
-
73
- .fw-option-type-rgba-color-picker-iris .fw-option-type-rgba-color-picker-reset-default span {
74
- line-height: 16px;
75
- padding-left: 2px;
76
- float: left;
77
- }
78
-
79
-
80
- .fw-option-type-rgba-color-picker-with-reset-default > .iris-palette-container {
81
- bottom: 27px !important;
82
- }
83
-
84
- .fw-option-type-rgba-color-picker-with-reset-default .fw-alpha-container {
85
- top: 192px;
86
  }
1
+ .fw-backend-option-input-type-rgba-color-picker .iris-palette {
2
+ height: 33px !important;
3
+ width: 33px !important;
 
4
  }
5
+ .fw-backend-option-input-type-rgba-color-picker .fw-option-help-in-input {
6
+ top: 2px !important;
 
 
 
 
 
 
 
 
 
7
  }
8
+ .fw-backend-option-input-type-rgba-color-picker .wp-picker-holder {
9
+ position:absolute;
10
+ z-index:99999;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  }
12
+ .fw-backend-option-input-type-rgba-color-picker .wp-picker-clear {
13
+ height: 23px !important;
 
 
 
14
  }
15
+ .fw-backend-option-input-type-rgba-color-picker .wp-color-result {
16
+ height: auto;
17
+ box-shadow:none;
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
+ .fw-backend-option-input-type-rgba-color-picker input {
20
+ max-width: 140px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
framework/includes/option-types/rgba-color-picker/static/js/scripts.js CHANGED
@@ -1,288 +1,330 @@
1
- jQuery( function ( $ ) {
2
- Color.prototype.toStr = function ( remove_alpha ) {
3
- if ( remove_alpha == 'no-alpha' ) {
4
- return this.toRgbaCSS( 1 ).replace( /\s+/g, '' );
5
- }
6
-
7
- return this.toRgbaCSS( this._alpha ).replace( /\s+/g, '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  };
9
- Color.prototype.toRgba = function ( a ) {
10
- var rgb = this.toRgb();
11
- rgb['a'] = ( typeof a === 'undefined' ) ? 1 : a;
12
-
13
- return rgb;
14
- }
15
- Color.prototype.toRgbaCSS = function ( a ) {
16
- var rgba = this.toRgba( a );
17
-
18
- return 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')';
19
- }
20
-
21
- var helpers = {
22
- optionClass: 'fw-option-type-rgba-color-picker',
23
- eventNamespace: '.fwOptionTypeRgbaColorPicker',
24
- hexColorRegex: /^#([a-f0-9]{3}){1,2}$/i,
25
- localized: window._fw_option_type_rgba_color_picker_localized,
26
- increment: 0,
27
- isColorDark: function ( rgbaColor ) {
28
- var r, g, b, o = 1;
29
-
30
- if ( this.hexColorRegex.test( rgbaColor ) ) {
31
- var color = rgbaColor.substring( 1 ); // remove #
32
-
33
- r = parseInt( color.substr( 0, 2 ), 16 );
34
- g = parseInt( color.substr( 2, 2 ), 16 );
35
- b = parseInt( color.substr( 4, 2 ), 16 );
36
- } else {
37
- var rgba = rgbaColor
38
- .replace( /^(rgb|rgba)\(/, '' )
39
- .replace( /\)$/, '' )
40
- .replace( /\s/g, '' )
41
- .split( ',' );
42
-
43
- r = rgba[0];
44
- g = rgba[1];
45
- b = rgba[2];
46
- o = rgba[3] || 1;
47
- }
48
 
49
- var yiq = (
50
- (
51
- r * 299
52
- ) + (
53
- g * 587
54
- ) + (
55
- b * 114
56
- )
57
- ) / 1000;
58
-
59
- return yiq < 128 && o > 0.4;
60
- },
61
- isColorValid: function ( rgbaColor ) {
62
- return ! Color( rgbaColor ).error;
63
- },
64
- getInstance: function ( $iris ) {
65
- return $iris.data( 'a8cIris' );
66
- },
67
- updatePreview: function ( $input, color ) {
68
 
69
- if ( this.isColorValid( color ) ) {
70
- $input.css( {
71
- 'background-color': color,
72
- 'color': this.isColorDark( color ) ? 'rgb(255,255,255)' : 'rgb(0,0,0)'
73
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  } else {
75
- $input.css( {
76
- 'background-color': '',
77
- 'color': ''
78
- } );
79
  }
80
- }
81
- };
82
 
83
- fwEvents.on( 'fw:options:init', function ( data ) {
84
- data.$elements.find( 'input.' + helpers.optionClass + ':not(.initialized)' ).each( function () {
85
- var $input = $( this ),
86
- changeTimeoutId = 0,
87
- eventNamespace = helpers.eventNamespace + '_' + (
88
- ++ helpers.increment
89
- );
90
-
91
- /**
92
- * Improvement: Initialized picker only on first focus
93
- * Do not initialize all pickers on the page, for performance reasons, maybe none of them will be opened
94
- */
95
- $input.one( 'focus', function () {
96
-
97
- var savedVal = $input.val();
98
-
99
- if ( ! $.trim( savedVal ).length ) { // If the input value is empty, there a glitches with opacity slider
100
- $input.val( 'rgba(255,255,255,1)' );
 
 
 
 
 
 
 
 
 
 
 
101
  }
 
102
 
103
- $input.iris( {
104
- palettes: JSON.parse( $input.attr( 'data-palettes' ) ),
105
- defaultColor: false,
106
- create: function () {
107
- $( this ).val( Color( savedVal ).toStr() )
108
- },
109
- change: function ( event, ui ) {
110
- var $transparency = $input.next( '.iris-picker' ).find( '.transparency' );
111
- $transparency.css( 'backgroundColor', ui.color.toStr( 'no-alpha' ) );
112
-
113
- $alphaSlider.slider( "option", "value", ui.color._alpha * 100 );
114
-
115
- clearTimeout( changeTimeoutId );
116
- changeTimeoutId = setTimeout( function () {
117
- $input.trigger( 'fw:option-type:rgba-color-picker:change', {
118
- $element: $input,
119
- iris: $input.data( 'a8cIris' ),
120
- alphaSlider: $alphaSlider.data( 'uiSlider' )
121
- } );
122
- $input.val( ui.color.toStr() )
123
- $input.trigger( 'change' );
124
-
125
- }, 12 );
126
- }
127
- } );
128
 
129
- var $picker = helpers.getInstance( $input ).picker;
 
 
 
 
 
130
 
131
- $picker.addClass( helpers.optionClass + '-iris' );
 
 
 
132
 
133
- /**
134
- * Hide if clicked outside option
135
- */
136
- {
137
- $input.parent().attr( 'id', 'fw-rgba-color-picker-r-' + (
138
- ++ helpers.increment
139
- ) );
 
 
 
 
 
 
 
 
 
 
140
 
141
- var originalShowCallback = helpers.getInstance( $input ).show;
 
 
 
 
142
 
143
- helpers.getInstance( $input ).show = function () {
144
- $( document.body )
145
- .off( 'click' + eventNamespace )
146
- .on( 'click' + eventNamespace, function ( e ) {
147
- if ( ! $( e.target ).closest( '#' + $input.parent().attr( 'id' ) ).length ) {
148
- $( document.body ).off( 'click' + eventNamespace );
149
- $input.iris( 'hide' );
150
- }
151
- } );
 
 
 
 
 
 
 
 
152
 
153
- originalShowCallback.apply( this );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  };
 
 
 
 
 
155
  }
156
 
157
- /**
158
- * After the second hide the picker is not showing on the next focus (I don't know why)
159
- * Show it manually
160
- */
161
- $input.on( 'focus', function () {
162
- if ( ! $picker.is( ':visible' ) ) {
163
- $input.iris( 'show' );
164
- }
165
- } );
166
 
167
- $input.on( 'change keyup blur', function () {
168
- // iris::change is not triggered when the input is empty or color is wrong
 
169
 
170
- $( this ).val( Color( $( this ).val() ).toStr() )
171
- helpers.updatePreview( $input, $input.val() );
 
172
  } );
173
 
174
- $( ''
175
- + '<div class="fw-alpha-container">'
176
- + /**/'<div class="slider-alpha"></div>'
177
- + /**/'<div class="transparency"></div>'
178
- + '</div>'
179
- ).appendTo( $input.next( '.iris-picker' ) );
180
-
181
- var $alphaSlider = $input.next( '.iris-picker:first' ).find( '.slider-alpha' );
182
-
183
- $alphaSlider.slider( {
184
- value: Color( $input.val() )._alpha * 100,
185
- range: "max",
186
- step: 1,
187
- min: 0,
188
- max: 100,
189
- slide: function ( event, ui ) {
190
- $( this ).find( '.ui-slider-handle' ).text( ui.value );
191
-
192
- $input.data( 'a8cIris' )._color._alpha = parseFloat( ui.value ) / 100.0;
193
-
194
- var color = $input.iris( 'color', true )
195
- var cssColor = color.toCSS( 'rgba', ui.value / 100 )
196
-
197
- $input.val( cssColor );
198
-
199
- clearTimeout( changeTimeoutId );
200
- changeTimeoutId = setTimeout( function () {
201
- $input.trigger( 'fw:option-type:rgba-color-picker:change', {
202
- $element: $input,
203
- iris: $input.data( 'a8cIris' ),
204
- alphaSlider: $alphaSlider.data( 'uiSlider' )
205
- } );
206
- $input.trigger( 'change' );
207
- }, 12 );
208
- },
209
- create: function ( event, ui ) {
210
- var v = $( this ).slider( 'value' ),
211
- $transparency = $input.next( '.iris-picker:first' ).find( '.transparency' );
212
-
213
- $( this ).find( '.ui-slider-handle' ).text( v );
214
-
215
- $transparency.css( 'backgroundColor', Color( $input.val() ).toRgbaCSS( 1 ) );
216
- },
217
- change: function ( event, ui ) {
218
- $( this ).find( '.ui-slider-handle' ).text( ui.value );
219
-
220
- $input.data( 'a8cIris' )._color._alpha = parseFloat( ui.value ) / 100.0;
221
- $input.val( $input.iris( 'color', true ).toRgbaCSS( ui.value / 100 ) )
222
-
223
- clearTimeout( changeTimeoutId );
224
- changeTimeoutId = setTimeout( function () {
225
- $input.trigger( 'fw:option-type:rgba-color-picker:change', {
226
- $element: $input,
227
- iris: $input.data( 'a8cIris' ),
228
- alphaSlider: $alphaSlider.data( 'uiSlider' )
229
- } );
230
- $input.trigger( 'change' );
231
- }, 12 );
232
- }
233
  } );
234
 
235
- var $firstPalette = $picker.find( '.iris-palette-container > .iris-palette:first-child' );
236
-
237
- /**
238
- * "Reset" color button
239
- */
240
- $.each( [
241
- {
242
- color: $input.attr( 'data-default' ),
243
- text: helpers.localized.l10n.reset_to_default
244
- }, {
245
- color: $input.val(),
246
- text: helpers.localized.l10n.reset_to_initial
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
- ], function ( i, data ) {
249
- if ( data.color && helpers.isColorValid( data.color ) ) {
250
- $picker.find( '> .iris-picker-inner' ).append( ''
251
- + '<div class="' + helpers.optionClass + '-reset-default fw-pull-left">'
252
- + /**/'<a class="iris-palette" style="'
253
- + /**//**/'background-color:' + data.color + ';'
254
- + /**//**/'height:' + $firstPalette.css( 'height' ) + ';'
255
- + /**//**/'width:' + $firstPalette.css( 'width' ) + ';'
256
- + /**//**/'"></a>'
257
- + /**/'<span>' + data.text + '</span>'
258
- + '</div>'
259
- );
260
-
261
- $picker
262
- .on(
263
- 'click',
264
- '.' + helpers.optionClass + '-reset-default',
265
- function () {
266
- $input.iris( 'color', $( this ).find( '.iris-palette' ).css( 'background-color' ) );
267
- }
268
- )
269
- .on( 'remove', function () {
270
- $( document.body ).off( eventNamespace );
271
- } )
272
- .addClass( helpers.optionClass + '-with-reset-default' )
273
- .css( 'height', parseFloat( $picker.css( 'height' ) ) + 17 );
274
-
275
- return false;
 
 
 
 
 
 
 
 
 
276
  }
277
- } );
 
278
 
279
- $input.iris( 'show' );
280
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
- helpers.updatePreview( $input, $input.val() );
283
 
284
- $input.addClass( 'initialized' );
285
- } );
 
 
 
 
 
286
  } );
 
287
 
 
 
 
 
 
 
 
288
  } );
1
+ ( function( $ ) {
2
+ // Variable for some backgrounds ( grid )
3
+ var image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAAHnlligAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHJJREFUeNpi+P///4EDBxiAGMgCCCAGFB5AADGCRBgYDh48CCRZIJS9vT2QBAggFBkmBiSAogxFBiCAoHogAKIKAlBUYTELAiAmEtABEECk20G6BOmuIl0CIMBQ/IEMkO0myiSSraaaBhZcbkUOs0HuBwDplz5uFJ3Z4gAAAABJRU5ErkJggg==',
4
+ // html stuff for wpColorPicker copy of the original color-picker.js
5
+ _before = '<a tabindex="0" class="wp-color-result" />',
6
+ _after = '<div class="wp-picker-holder" />',
7
+ _wrap = '<div class="wp-picker-container" />',
8
+ _button = '<input type="button" class="button button-small hidden" />';
9
+
10
+ /**
11
+ * Overwrite Color
12
+ * for enable support rbga
13
+ */
14
+ Color.fn.toString = function() {
15
+ if ( this._alpha < 1 )
16
+ return this.toCSS( 'rgba', this._alpha ).replace( /\s+/g, '' );
17
+
18
+ var hex = parseInt( this._color, 10 ).toString( 16 );
19
+
20
+ if ( this.error )
21
+ return '';
22
+
23
+ if ( hex.length < 6 )
24
+ hex = ( '00000' + hex ).substr( -6 );
25
+
26
+ return '#' + hex;
27
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ /**
30
+ * Overwrite wpColorPicker
31
+ */
32
+ $.widget( 'wp.wpColorPicker', $.wp.wpColorPicker, {
33
+ _create: function() {
34
+ // bail early for unsupported Iris.
35
+ if ( ! $.support.iris )
36
+ return;
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ var self = this,
39
+ el = self.element;
40
+
41
+ $.extend( self.options, el.data() );
42
+
43
+ // keep close bound so it can be attached to a body listener
44
+ self.close = $.proxy( self.close, self );
45
+
46
+ self.initialValue = el.val();
47
+
48
+ // Set up HTML structure, hide things
49
+ el.addClass( 'wp-color-picker' ).hide().wrap( _wrap );
50
+ self.wrap = el.parent();
51
+ self.toggler = $( _before ).insertBefore( el ).css( { backgroundColor : self.initialValue } ).attr( 'title', wpColorPickerL10n.pick ).attr( 'data-current', wpColorPickerL10n.current );
52
+ self.pickerContainer = $( _after ).insertAfter( el );
53
+ self.button = $( _button );
54
+
55
+ if ( self.options.defaultColor ) {
56
+ self.button.addClass( 'wp-picker-default' ).val( wpColorPickerL10n.defaultString );
57
  } else {
58
+ self.button.addClass( 'wp-picker-clear' ).val( wpColorPickerL10n.clear );
 
 
 
59
  }
 
 
60
 
61
+ el.wrap( '<span class="wp-picker-input-wrap" />' ).after( self.button );
62
+
63
+ el.iris( {
64
+ target : self.pickerContainer,
65
+ hide : self.options.hide,
66
+ width : self.options.width,
67
+ mode : self.options.mode,
68
+ palettes : self.options.palettes,
69
+ change : function( event, ui ) {
70
+
71
+ if ( self.options.alpha ) {
72
+ self.toggler.css( { 'background-image' : 'url(' + image + ')' } ).html( '<span />' );
73
+ self.toggler.find( 'span' ).css( {
74
+ 'width' : '100%',
75
+ 'height' : '100%',
76
+ 'position' : 'absolute',
77
+ 'top' : 0,
78
+ 'left' : 0,
79
+ 'border-top-left-radius' : '3px',
80
+ 'border-bottom-left-radius' : '3px',
81
+ 'background' : ui.color.toString()
82
+ } );
83
+ } else {
84
+ self.toggler.css( { backgroundColor : ui.color.toString() } );
85
+ }
86
+
87
+ // Check for a custom cb
88
+ if ( $.isFunction( self.options.change ) )
89
+ self.options.change.call( this, event, ui );
90
  }
91
+ } );
92
 
93
+ el.val( self.initialValue );
94
+ self._addListeners();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ if ( ! self.options.hide ) {
97
+ self.toggler.click();
98
+ }
99
+ },
100
+ _addListeners: function() {
101
+ var self = this;
102
 
103
+ // prevent any clicks inside this widget from leaking to the top and closing it
104
+ self.wrap.on( 'click.wpcolorpicker', function( event ) {
105
+ event.stopPropagation();
106
+ } );
107
 
108
+ self.toggler.on( 'click', function() {
109
+ if ( self.toggler.hasClass( 'wp-picker-open' ) ) {
110
+ self.close();
111
+ } else {
112
+ self.open();
113
+ }
114
+ });
115
+
116
+ self.element.on( 'change', function( event ) {
117
+ // Empty or Error = clear
118
+ if ( $( this ).val() === '' || self.element.hasClass( 'iris-error' ) ) {
119
+ if ( self.options.alpha ) {
120
+ self.toggler.removeAttr( 'style' );
121
+ self.toggler.find( 'span' ).css( 'backgroundColor', '' );
122
+ } else {
123
+ self.toggler.css( 'backgroundColor', '' );
124
+ }
125
 
126
+ // fire clear callback if we have one
127
+ if ( $.isFunction( self.options.clear ) )
128
+ self.options.clear.call( this, event );
129
+ }
130
+ } );
131
 
132
+ // open a keyboard-focused closed picker with space or enter
133
+ self.toggler.on( 'keyup', function( event ) {
134
+ if ( event.keyCode === 13 || event.keyCode === 32 ) {
135
+ event.preventDefault();
136
+ self.toggler.trigger( 'click' ).next().focus();
137
+ }
138
+ });
139
+
140
+ self.button.on( 'click', function( event ) {
141
+ if ( $( this ).hasClass( 'wp-picker-clear' ) ) {
142
+ self.element.val( '' );
143
+ if ( self.options.alpha ) {
144
+ self.toggler.removeAttr( 'style' );
145
+ self.toggler.find( 'span' ).css( 'backgroundColor', '' );
146
+ } else {
147
+ self.toggler.css( 'backgroundColor', '' );
148
+ }
149
 
150
+ if ( $.isFunction( self.options.clear ) )
151
+ self.options.clear.call( this, event );
152
+
153
+ } else if ( $( this ).hasClass( 'wp-picker-default' ) ) {
154
+ self.element.val( self.options.defaultColor ).change();
155
+ }
156
+ });
157
+ }
158
+ });
159
+
160
+ /**
161
+ * Overwrite iris
162
+ */
163
+ $.widget( 'a8c.iris', $.a8c.iris, {
164
+ _create: function() {
165
+ this._super();
166
+
167
+ // Global option for check is mode rbga is enabled
168
+ this.options.alpha = this.element.data( 'alpha' ) || false;
169
+
170
+ // Is not input disabled
171
+ if ( ! this.element.is( ':input' ) )
172
+ this.options.alpha = false;
173
+
174
+ if ( typeof this.options.alpha !== 'undefined' && this.options.alpha ) {
175
+ var self = this,
176
+ el = self.element,
177
+ _html = '<div class="iris-strip iris-slider iris-alpha-slider"><div class="iris-slider-offset iris-slider-offset-alpha"></div></div>',
178
+ aContainer = $( _html ).appendTo( self.picker.find( '.iris-picker-inner' ) ),
179
+ aSlider = aContainer.find( '.iris-slider-offset-alpha' ),
180
+ controls = {
181
+ aContainer : aContainer,
182
+ aSlider : aSlider
183
  };
184
+
185
+ if ( typeof el.data( 'custom-width' ) !== 'undefined' ) {
186
+ self.options.customWidth = parseInt( el.data( 'custom-width' ) ) || 0;
187
+ } else {
188
+ self.options.customWidth = 100;
189
  }
190
 
191
+ // Set default width for input reset
192
+ self.options.defaultWidth = el.width();
 
 
 
 
 
 
 
193
 
194
+ // Update width for input
195
+ if ( self._color._alpha < 1 || self._color.toString().indexOf('rgb') != -1 )
196
+ el.width( parseInt( self.options.defaultWidth + self.options.customWidth ) );
197
 
198
+ // Push new controls
199
+ $.each( controls, function( k, v ) {
200
+ self.controls[k] = v;
201
  } );
202
 
203
+ // Change size strip and add margin for sliders
204
+ self.controls.square.css( { 'margin-right': '0' } );
205
+ var emptyWidth = ( self.picker.width() - self.controls.square.width() - 20 ),
206
+ stripsMargin = ( emptyWidth / 6 ),
207
+ stripsWidth = ( ( emptyWidth / 2 ) - stripsMargin );
208
+
209
+ $.each( [ 'aContainer', 'strip' ], function( k, v ) {
210
+ self.controls[v].width( stripsWidth ).css( { 'margin-left' : stripsMargin + 'px' } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  } );
212
 
213
+ // Add new slider
214
+ self._initControls();
215
+
216
+ // For updated widget
217
+ self._change();
218
+ }
219
+ },
220
+ _initControls: function() {
221
+ this._super();
222
+
223
+ if ( this.options.alpha ) {
224
+ var self = this,
225
+ controls = self.controls;
226
+
227
+ controls.aSlider.slider({
228
+ orientation : 'vertical',
229
+ min : 0,
230
+ max : 100,
231
+ step : 1,
232
+ value : parseInt( self._color._alpha * 100 ),
233
+ slide : function( event, ui ) {
234
+ // Update alpha value
235
+ self._color._alpha = parseFloat( ui.value / 100 );
236
+ self._change.apply( self, arguments );
237
  }
238
+ });
239
+ }
240
+ },
241
+ _change: function() {
242
+ this._super();
243
+
244
+ var self = this,
245
+ el = self.element;
246
+
247
+ if ( this.options.alpha ) {
248
+ var controls = self.controls,
249
+ alpha = parseInt( self._color._alpha * 100 ),
250
+ color = self._color.toRgb(),
251
+ gradient = [
252
+ 'rgb(' + color.r + ',' + color.g + ',' + color.b + ') 0%',
253
+ 'rgba(' + color.r + ',' + color.g + ',' + color.b + ', 0) 100%'
254
+ ],
255
+ defaultWidth = self.options.defaultWidth,
256
+ customWidth = self.options.customWidth,
257
+ target = self.picker.closest( '.wp-picker-container' ).find( '.wp-color-result' );
258
+
259
+ // Generate background slider alpha, only for CSS3 old browser fuck!! :)
260
+ controls.aContainer.css( { 'background' : 'linear-gradient(to bottom, ' + gradient.join( ', ' ) + '), url(' + image + ')' } );
261
+
262
+ if ( target.hasClass( 'wp-picker-open' ) ) {
263
+ // Update alpha value
264
+ controls.aSlider.slider( 'value', alpha );
265
+
266
+ /**
267
+ * Disabled change opacity in default slider Saturation ( only is alpha enabled )
268
+ * and change input width for view all value
269
+ */
270
+ if ( self._color._alpha < 1 ) {
271
+ controls.strip.attr( 'style', controls.strip.attr( 'style' ).replace( /rgba\(([0-9]+,)(\s+)?([0-9]+,)(\s+)?([0-9]+)(,(\s+)?[0-9\.]+)\)/g, 'rgb($1$3$5)' ) );
272
+ el.width( parseInt( defaultWidth + customWidth ) );
273
+ } else {
274
+ el.width( defaultWidth );
275
  }
276
+ }
277
+ }
278
 
279
+ var reset = el.data( 'reset-alpha' ) || false;
280
+
281
+ if ( reset ) {
282
+ self.picker.find( '.iris-palette-container' ).on( 'click.palette', '.iris-palette', function() {
283
+ self._color._alpha = 1;
284
+ self.active = 'external';
285
+ self._change();
286
+ } );
287
+ }
288
+ },
289
+ _addInputListeners: function( input ) {
290
+ var self = this,
291
+ debounceTimeout = 100,
292
+ callback = function( event ) {
293
+ var color = new Color( input.val() ),
294
+ val = input.val();
295
+
296
+ input.removeClass( 'iris-error' );
297
+ // we gave a bad color
298
+ if ( color.error ) {
299
+ // don't error on an empty input
300
+ if ( val !== '' )
301
+ input.addClass( 'iris-error' );
302
+ } else {
303
+ if ( color.toString() !== self._color.toString() ) {
304
+ // let's not do this on keyup for hex shortcodes
305
+ if ( ! ( event.type === 'keyup' && val.match( /^[0-9a-fA-F]{3}$/ ) ) )
306
+ self._setOption( 'color', color.toString() );
307
+ }
308
+ }
309
+ };
310
 
311
+ input.on( 'change', callback ).on( 'keyup', self._debounce( callback, debounceTimeout ) );
312
 
313
+ // If we initialized hidden, show on first focus. The rest is up to you.
314
+ if ( self.options.hide ) {
315
+ input.on( 'focus', function() {
316
+ self.show();
317
+ } );
318
+ }
319
+ }
320
  } );
321
+ }( jQuery ) );
322
 
323
+ // Auto Call plugin is class is color-picker
324
+ jQuery( document ).ready( function( $ ) {
325
+ fwEvents.on( 'fw:options:init', function ( data ) {
326
+ data.$elements.find( 'input.fw-option-type-rgba-color-picker' ).each( function () {
327
+ $( this ).wpColorPicker();
328
+ });
329
+ });
330
  } );
framework/includes/option-types/simple.php CHANGED
@@ -560,16 +560,19 @@ class FW_Option_Type_Checkboxes extends FW_Option_Type {
560
  if ( is_array( $input_value ) ) {
561
  $value = array();
562
 
563
- foreach ( $input_value as $choice => $val ) {
564
- if ( $val === '' ) {
565
- continue;
566
- }
567
-
568
- if ( ! isset( $option['choices'][ $choice ] ) ) {
569
- continue;
 
 
 
 
 
570
  }
571
-
572
- $value[ $choice ] = true;
573
  }
574
  } else {
575
  $value = $option['value'];
560
  if ( is_array( $input_value ) ) {
561
  $value = array();
562
 
563
+ foreach ($option['choices'] as $choice => $choice_value){
564
+ if (isset($input_value[$choice]) && $input_value[$choice] ) {
565
+ // Handle boolean values that got lost into the universe
566
+ // and somehow become strings by the miracle of
567
+ // PHP's $_POST parsing
568
+ $value[$choice] = is_string(
569
+ $input_value[$choice]
570
+ ) ? $input_value[$choice] === 'true' : true;
571
+ } else {
572
+ // If the value's missing from the input it is falsy,
573
+ // for sure
574
+ $value[$choice] = false;
575
  }
 
 
576
  }
577
  } else {
578
  $value = $option['value'];
framework/includes/option-types/wp-editor/class-fw-option-type-wp-editor.php CHANGED
@@ -92,39 +92,37 @@ class FW_Option_Type_Wp_Editor extends FW_Option_Type {
92
  * {@inheritdoc}
93
  */
94
  protected function _enqueue_static( $id, $option, $data ) {
95
- if (! wp_script_is('editor')) {
96
- ob_start();
97
- wp_editor('', fw_rand_md5());
98
- ob_end_clean();
99
  }
 
100
  /**
101
  * The below styles usually are included directly in html when wp_editor() is called
102
  * but since we call it (below) wrapped in ob_start()...ob_end_clean() the html is not printed.
103
  * So included the styles manually.
104
  */
105
- {
106
- wp_enqueue_style(
107
- /**
108
- * https://github.com/WordPress/WordPress/blob/4.4.2/wp-includes/script-loader.php#L731
109
- * without prefix it won't enqueue
110
- */
111
- 'fw-option-type-' . $this->get_type() .'-dashicons',
112
- includes_url('css/dashicons.min.css'),
113
- array(),
114
- fw()->manifest->get_version()
115
- );
116
 
117
- wp_enqueue_style(
118
- /**
119
- * https://github.com/WordPress/WordPress/blob/4.4.2/wp-includes/script-loader.php#L737
120
- * without prefix it won't enqueue
121
- */
122
- 'fw-option-type-' . $this->get_type() .'-editor-buttons',
123
- includes_url('/css/editor.min.css'),
124
- array('dashicons', 'fw-unycon'),
125
- fw()->manifest->get_version()
126
- );
127
- }
 
 
 
 
 
 
 
 
 
 
128
 
129
  $uri = fw_get_framework_directory_uri(
130
  '/includes/option-types/' . $this->get_type() . '/static'
@@ -133,7 +131,7 @@ class FW_Option_Type_Wp_Editor extends FW_Option_Type {
133
  wp_enqueue_script(
134
  'fw-option-type-' . $this->get_type(),
135
  $uri . '/scripts.js',
136
- array('jquery', 'fw-events', 'editor', 'fw'),
137
  fw()->manifest->get_version(),
138
  true
139
  );
@@ -141,11 +139,11 @@ class FW_Option_Type_Wp_Editor extends FW_Option_Type {
141
  wp_enqueue_style(
142
  'fw-option-type-' . $this->get_type(),
143
  $uri . '/styles.css',
144
- array('dashicons', 'editor-buttons'),
145
  fw()->manifest->get_version()
146
  );
147
 
148
- do_action('fw:option-type:wp-editor:enqueue-scripts');
149
  }
150
 
151
  /**
92
  * {@inheritdoc}
93
  */
94
  protected function _enqueue_static( $id, $option, $data ) {
95
+ if ( ! wp_script_is( 'editor' ) ) {
96
+ wp_enqueue_script( 'editor' );
 
 
97
  }
98
+
99
  /**
100
  * The below styles usually are included directly in html when wp_editor() is called
101
  * but since we call it (below) wrapped in ob_start()...ob_end_clean() the html is not printed.
102
  * So included the styles manually.
103
  */
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ wp_enqueue_style(
106
+ /**
107
+ * https://github.com/WordPress/WordPress/blob/4.4.2/wp-includes/script-loader.php#L731
108
+ * without prefix it won't enqueue
109
+ */
110
+ 'fw-option-type-' . $this->get_type() . '-dashicons',
111
+ includes_url( 'css/dashicons.min.css' ),
112
+ array(),
113
+ fw()->manifest->get_version()
114
+ );
115
+
116
+ wp_enqueue_style(
117
+ /**
118
+ * https://github.com/WordPress/WordPress/blob/4.4.2/wp-includes/script-loader.php#L737
119
+ * without prefix it won't enqueue
120
+ */
121
+ 'fw-option-type-' . $this->get_type() . '-editor-buttons',
122
+ includes_url( '/css/editor.min.css' ),
123
+ array( 'dashicons', 'fw-unycon' ),
124
+ fw()->manifest->get_version()
125
+ );
126
 
127
  $uri = fw_get_framework_directory_uri(
128
  '/includes/option-types/' . $this->get_type() . '/static'
131
  wp_enqueue_script(
132
  'fw-option-type-' . $this->get_type(),
133
  $uri . '/scripts.js',
134
+ array( 'jquery', 'fw-events', 'editor', 'fw' ),
135
  fw()->manifest->get_version(),
136
  true
137
  );
139
  wp_enqueue_style(
140
  'fw-option-type-' . $this->get_type(),
141
  $uri . '/styles.css',
142
+ array( 'dashicons', 'editor-buttons' ),
143
  fw()->manifest->get_version()
144
  );
145
 
146
+ do_action( 'fw:option-type:wp-editor:enqueue-scripts' );
147
  }
148
 
149
  /**
framework/manifest.php CHANGED
@@ -4,4 +4,4 @@ $manifest = array();
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
- $manifest['version'] = '2.7.8';
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
+ $manifest['version'] = '2.7.9';
framework/static/css/fw.css CHANGED
@@ -3235,6 +3235,35 @@ body > .fw-sole-modal .fw-sole-modal-content p {
3235
 
3236
  /* end: SoleModal */
3237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3238
 
3239
  /* qtip custom style */
3240
 
3235
 
3236
  /* end: SoleModal */
3237
 
3238
+ /* Fixed: https://github.com/ThemeFuse/Unyson/issues/2888 */
3239
+ .appearance_page_fw-settings .fw-sole-modal-content .wp-spinner{
3240
+ -webkit-animation: fw-spin 1.2s linear infinite;
3241
+ -moz-animation: fw-spin 1.2s linear infinite;
3242
+ -ms-animation: fw-spin 1.2s linear infinite;
3243
+ -o-animation: fw-spin 1.2s linear infinite;
3244
+ animation: fw-spin 1.2s linear infinite;
3245
+ }
3246
+ @-ms-keyframes fw-spin {
3247
+ from { -ms-transform: rotate(0deg); }
3248
+ to { -ms-transform: rotate(360deg); }
3249
+ }
3250
+ @-moz-keyframes fw-spin {
3251
+ from { -moz-transform: rotate(0deg); }
3252
+ to { -moz-transform: rotate(360deg); }
3253
+ }
3254
+ @-webkit-keyframes fw-spin {
3255
+ from { -webkit-transform: rotate(0deg); }
3256
+ to { -webkit-transform: rotate(360deg); }
3257
+ }
3258
+ @keyframes fw-spin {
3259
+ from {
3260
+ transform:rotate(0deg);
3261
+ }
3262
+ to {
3263
+ transform:rotate(360deg);
3264
+ }
3265
+ }
3266
+ /* End Fixed: https://github.com/ThemeFuse/Unyson/issues/2888 */
3267
 
3268
  /* qtip custom style */
3269
 
framework/views/backend-settings-form.php CHANGED
@@ -188,7 +188,7 @@
188
  fw.soleModal.show(
189
  loadingModalId,
190
  '<h2 class="fw-text-muted">'+
191
- '<img src="'+ fw.img.loadingSpinner +'" alt="Loading" class="wp-spinner" /> '+
192
  title +
193
  '</h2>'+
194
  '<p class="fw-text-muted"><em>'+ description +'</em></p>'+ loadingExtraMessage,
@@ -206,8 +206,16 @@
206
  afterSubmitDelay: function (elements) {
207
  fwEvents.trigger('fw:options:init:tabs', {$elements: elements.$form});
208
  },
209
- onErrors: function() {
210
- fw.soleModal.hide(loadingModalId);
 
 
 
 
 
 
 
 
211
  },
212
  onAjaxError: function(elements, data) {
213
  {
@@ -228,6 +236,7 @@
228
  /**
229
  * Display messages
230
  */
 
231
  do {
232
  /**
233
  * Don't display the "Settings successfully saved" message
188
  fw.soleModal.show(
189
  loadingModalId,
190
  '<h2 class="fw-text-muted">'+
191
+ '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAAAAACo4kLRAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAnRSTlMA/1uRIrUAAAACYktHRADdUu+NWwAAAAd0SU1FB+EKCgYABDchIukAAACZelRYdFJhdyBwcm9maWxlIHR5cGUgZ2lmOnhtcCBkYXRheG1wAAAImU2NMQ5CMQxD957iHyGNnaTlNkVtEQMSAwPHJx8WHMleXuxyu+/L+/E85niNzHJ85b1gRwvx6vCrWxCGBkdNF1WVaD5DODkx2H0Fgkmv5I2dxFQ9mRKiy3seMdBZCZ4lnUqjQYj/GRW3LPm95w42VvkACzYml9QYNjwAAACjSURBVBjTfZChDoNAEETfbZpgVqJPXHIO0w+hCZYPxJKUD6mpIzmBPrmmigoQcCSM2eRlszM7buWqxzbSd8Y0NgEAtwJ5WvYd39Y7TKOB7xkW0C6AQB4N6KuqB2zMIDDZ0cQmENJ2b8i/AYAl4db3pwj0fAlzmXJGsBIagl4/kitShFjCiNCUsEEI/sx8QKA9WWkLAnV3oNrVN9VtJZtyKrnUH9vgMmhlXVedAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTEwLTEwVDA2OjAwOjA0LTA3OjAw4kWx4AAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0xMC0xMFQwNjowMDowNC0wNzowMJMYCVwAAAAASUVORK5CYII=" alt="Loading" class="wp-spinner" /> '+
192
  title +
193
  '</h2>'+
194
  '<p class="fw-text-muted"><em>'+ description +'</em></p>'+ loadingExtraMessage,
206
  afterSubmitDelay: function (elements) {
207
  fwEvents.trigger('fw:options:init:tabs', {$elements: elements.$form});
208
  },
209
+ onErrors: function( elements, data ) {
210
+ var message = $.map( data.errors, function( mssg ) { return '<p class="fw-text-danger">' + mssg + '</p>' } ) + fw.soleModal.renderFlashMessages( data.flash_messages );
211
+
212
+ fw.soleModal.hide( loadingModalId );
213
+
214
+ fw.soleModal.show(
215
+ 'fw-options-ajax-save-error',
216
+ '<p class="fw-text-danger">' + message + '</p>'
217
+ );
218
+
219
  },
220
  onAjaxError: function(elements, data) {
221
  {
236
  /**
237
  * Display messages
238
  */
239
+
240
  do {
241
  /**
242
  * Don't display the "Settings successfully saved" message
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.8
6
- Stable tag: 2.7.8
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -85,6 +85,9 @@ Yes; Unyson will work with any theme.
85
 
86
  == Changelog ==
87
 
 
 
 
88
  = 2.7.8 =
89
  * Fixed [#2905](https://github.com/ThemeFuse/Unyson/issues/2905),[#2862](https://github.com/ThemeFuse/Unyson/issues/2862),[#2909](https://github.com/ThemeFuse/Unyson/issues/2909),[#2924](https://github.com/ThemeFuse/Unyson/issues/2924),[#2925](https://github.com/ThemeFuse/Unyson/issues/2925),[#2921](https://github.com/ThemeFuse/Unyson/issues/2921),[#2844](https://github.com/ThemeFuse/Unyson/issues/2844)
90
 
3
  Tags: page builder, shortcodes, backup, seo, breadcrumbs, portfolio, framework
4
  Requires at least: 4.4
5
  Tested up to: 4.8
6
+ Stable tag: 2.7.9
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
85
 
86
  == Changelog ==
87
 
88
+ = 2.7.9 =
89
+ * Fixed [#1351](https://github.com/ThemeFuse/Unyson/issues/1351),[#2716](https://github.com/ThemeFuse/Unyson/issues/2716),[#2833](https://github.com/ThemeFuse/Unyson/issues/2833),[#2736](https://github.com/ThemeFuse/Unyson/issues/2736),[#2718](https://github.com/ThemeFuse/Unyson/issues/2718),[#2953](https://github.com/ThemeFuse/Unyson/issues/2953),[#2888](https://github.com/ThemeFuse/Unyson/issues/2888),[#2855](https://github.com/ThemeFuse/Unyson/issues/2855),[#2906](https://github.com/ThemeFuse/Unyson/issues/2906)
90
+
91
  = 2.7.8 =
92
  * Fixed [#2905](https://github.com/ThemeFuse/Unyson/issues/2905),[#2862](https://github.com/ThemeFuse/Unyson/issues/2862),[#2909](https://github.com/ThemeFuse/Unyson/issues/2909),[#2924](https://github.com/ThemeFuse/Unyson/issues/2924),[#2925](https://github.com/ThemeFuse/Unyson/issues/2925),[#2921](https://github.com/ThemeFuse/Unyson/issues/2921),[#2844](https://github.com/ThemeFuse/Unyson/issues/2844)
93
 
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.7.8
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.7.9
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+