Unyson - Version 2.3.2

Version Description

  • Added option-type unique to make possible this
  • Added the fw_get_google_fonts_v2() function
  • Fixed: The newline character in textarea option in Customizer was replaced with 'rn' string
  • Fixed: Post options (meta) lost after Quick Edit
  • Option-type multi-picker: Allow support for any option type in picker (docs)
Download this release

Release Info

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

Code changes from version 2.3.1 to 2.3.2

framework/core/components/backend.php CHANGED
@@ -635,7 +635,13 @@ final class _FW_Component_Backend {
635
  * @param bool $update
636
  */
637
  public function _action_save_post( $post_id, $post, $update ) {
638
- if (intval(FW_Request::POST('post_ID')) == $post_id) {
 
 
 
 
 
 
639
  /**
640
  * This happens on regular post form submit
641
  * All data from $_POST belongs this $post
@@ -714,29 +720,37 @@ final class _FW_Component_Backend {
714
  **/
715
  public function _action_update_autosave_options( $post_id, $post ) {
716
  remove_action( 'save_post', array( $this, '_action_update_autosave_options' ) );
 
717
  remove_action( 'save_post', array( $this, '_action_save_post' ), 7 );
718
 
719
- $parent = get_post($post->post_parent);
 
720
 
721
- if ( ! $parent instanceof WP_Post ) {
722
- return;
723
- }
724
 
725
- $current_values = fw_get_options_values_from_input(
726
- fw()->theme->get_post_options($parent->post_type)
727
- );
 
728
 
729
- fw_set_db_post_option(
730
- $post->ID,
731
- null,
732
- array_diff_key( // remove handled values
733
- $current_values,
734
- $this->process_options_handlers(
735
- fw()->theme->get_post_options($parent->post_type),
736
- $current_values
 
 
 
 
 
737
  )
738
- )
739
- );
740
 
741
  add_action( 'save_post', array( $this, '_action_save_post' ), 7, 3 );
742
  }
@@ -1714,32 +1728,32 @@ final class _FW_Component_Backend {
1714
  }
1715
  }
1716
 
 
 
 
 
1717
  if (!class_exists('_FW_Customizer_Control_Option_Wrapper')) {
1718
  require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-control-option-wrapper.php');
1719
  }
1720
 
1721
  $wp_customize->add_setting(
1722
- $setting_id,
1723
- array(
1724
- 'default' => fw()->backend->option_type($opt['option']['type'])->get_value_from_input($opt['option'], null),
1725
-
1726
- // added later because we can't create control first without an existing setting
1727
- //'sanitize_callback' => array($control, 'setting_sanitize_callback'),
 
1728
  )
1729
  );
1730
 
1731
- $control = new _FW_Customizer_Control_Option_Wrapper(
1732
- $wp_customize,
1733
- $opt['id'],
1734
- $args,
1735
- array(
1736
- 'fw_option' => $opt['option']
1737
  )
1738
  );
1739
-
1740
- add_filter( "customize_sanitize_{$setting_id}", array($control, 'setting_sanitize_callback'), 10, 2 );
1741
-
1742
- $wp_customize->add_control($control);
1743
  break;
1744
  default:
1745
  trigger_error('Not supported option in customizer, type: '. $opt['type'], E_USER_WARNING); // todo: uncomment
635
  * @param bool $update
636
  */
637
  public function _action_save_post( $post_id, $post, $update ) {
638
+ if (
639
+ isset($_POST['post_ID'])
640
+ &&
641
+ intval($_POST['post_ID']) === intval($post_id)
642
+ &&
643
+ !empty($_POST[ FW_Option_Type::get_default_name_prefix() ]) // this happens on Quick Edit
644
+ ) {
645
  /**
646
  * This happens on regular post form submit
647
  * All data from $_POST belongs this $post
720
  **/
721
  public function _action_update_autosave_options( $post_id, $post ) {
722
  remove_action( 'save_post', array( $this, '_action_update_autosave_options' ) );
723
+
724
  remove_action( 'save_post', array( $this, '_action_save_post' ), 7 );
725
 
726
+ do {
727
+ $parent = get_post($post->post_parent);
728
 
729
+ if ( ! $parent instanceof WP_Post ) {
730
+ break;
731
+ }
732
 
733
+ if (empty($_POST[ FW_Option_Type::get_default_name_prefix() ])) {
734
+ // this happens on Quick Edit
735
+ break;
736
+ }
737
 
738
+ $current_values = fw_get_options_values_from_input(
739
+ fw()->theme->get_post_options($parent->post_type)
740
+ );
741
+
742
+ fw_set_db_post_option(
743
+ $post->ID,
744
+ null,
745
+ array_diff_key( // remove handled values
746
+ $current_values,
747
+ $this->process_options_handlers(
748
+ fw()->theme->get_post_options($parent->post_type),
749
+ $current_values
750
+ )
751
  )
752
+ );
753
+ } while(false);
754
 
755
  add_action( 'save_post', array( $this, '_action_save_post' ), 7, 3 );
756
  }
1728
  }
1729
  }
1730
 
1731
+ if (!class_exists('_FW_Customizer_Setting_Option')) {
1732
+ require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-setting-option.php');
1733
+ }
1734
+
1735
  if (!class_exists('_FW_Customizer_Control_Option_Wrapper')) {
1736
  require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-control-option-wrapper.php');
1737
  }
1738
 
1739
  $wp_customize->add_setting(
1740
+ new _FW_Customizer_Setting_Option(
1741
+ $wp_customize,
1742
+ $setting_id,
1743
+ array(
1744
+ 'default' => fw()->backend->option_type($opt['option']['type'])->get_value_from_input($opt['option'], null),
1745
+ 'fw_option' => $opt['option'],
1746
+ )
1747
  )
1748
  );
1749
 
1750
+ $wp_customize->add_control(
1751
+ new _FW_Customizer_Control_Option_Wrapper(
1752
+ $wp_customize,
1753
+ $opt['id'],
1754
+ $args
 
1755
  )
1756
  );
 
 
 
 
1757
  break;
1758
  default:
1759
  trigger_error('Not supported option in customizer, type: '. $opt['type'], E_USER_WARNING); // todo: uncomment
framework/helpers/general.php CHANGED
@@ -570,12 +570,16 @@ function fw_get_variables_from_file($file_path, array $_variables) {
570
  /**
571
  * Use this function to not include files directly and to not give access to current context variables (like $this)
572
  * @param string $file_path
 
573
  * @return bool If was included or not
574
  */
575
- function fw_include_file_isolated($file_path) {
576
  if (file_exists($file_path)) {
577
- include $file_path;
578
-
 
 
 
579
  return true;
580
  } else {
581
  return false;
@@ -830,6 +834,42 @@ function fw_get_google_fonts() {
830
  }
831
  }
832
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
833
  /**
834
  * @return string Current url
835
  */
570
  /**
571
  * Use this function to not include files directly and to not give access to current context variables (like $this)
572
  * @param string $file_path
573
+ * @param bool $once
574
  * @return bool If was included or not
575
  */
576
+ function fw_include_file_isolated($file_path, $once = false) {
577
  if (file_exists($file_path)) {
578
+ if ( (bool) $once ) {
579
+ include_once $file_path;
580
+ } else {
581
+ include $file_path;
582
+ }
583
  return true;
584
  } else {
585
  return false;
834
  }
835
  }
836
 
837
+ /**
838
+ * @return Array with Google fonts
839
+ */
840
+ function fw_get_google_fonts_v2() {
841
+ $saved_data = get_option( 'fw_google_fonts', false );
842
+
843
+ if (
844
+ false === $saved_data
845
+ ||
846
+ ( time() - $saved_data['last_update'] > ( time() - 7 * DAY_IN_SECONDS ) )
847
+ ) {
848
+ $response = wp_remote_get( apply_filters( 'fw_googleapis_webfonts_url', 'http://google-webfonts-cache.unyson.io/v1/webfonts' ) );
849
+ $body = wp_remote_retrieve_body( $response );
850
+
851
+ if (
852
+ 200 === wp_remote_retrieve_response_code( $response )
853
+ &&
854
+ ! is_wp_error( $body ) && ! empty( $body )
855
+ ) {
856
+ update_option( 'fw_google_fonts', array(
857
+ 'last_update' => time(),
858
+ 'fonts' => $body
859
+ ) );
860
+
861
+ return $body;
862
+ } else {
863
+ return ( ! empty( $saved_data['fonts'] ) )
864
+ ? $saved_data['fonts']
865
+ : json_encode( array( 'items' => array() )
866
+ );
867
+ }
868
+ }
869
+
870
+ return $saved_data['fonts'];
871
+ }
872
+
873
  /**
874
  * @return string Current url
875
  */
framework/includes/customizer/class--fw-customizer-control-option-wrapper.php CHANGED
@@ -1,14 +1,6 @@
1
  <?php if (!defined('FW')) die('Forbidden');
2
 
3
  class _FW_Customizer_Control_Option_Wrapper extends WP_Customize_Control {
4
- private $fw_option = array();
5
-
6
- public function __construct( $manager, $id, array $args, array $data ) {
7
- parent::__construct( $manager, $id, $args);
8
-
9
- $this->fw_option = $data['fw_option'];
10
- }
11
-
12
  public function render_content() {
13
  fw()->backend->_set_default_render_design('customizer');
14
  ?>
@@ -17,7 +9,7 @@ class _FW_Customizer_Control_Option_Wrapper extends WP_Customize_Control {
17
  <div class="fw-backend-customizer-option-inner fw-force-xs">
18
  <?php
19
  echo fw()->backend->render_options(
20
- array($this->id => $this->fw_option),
21
  array(
22
  $this->id => $this->value()
23
  ),
@@ -30,30 +22,4 @@ class _FW_Customizer_Control_Option_Wrapper extends WP_Customize_Control {
30
  <?php
31
  fw()->backend->_set_default_render_design();
32
  }
33
-
34
- public function setting_sanitize_callback($input) {
35
- $input = json_decode($input, true);
36
-
37
- if (is_null($input)) {
38
- return null;
39
- }
40
-
41
- $POST = array();
42
- foreach ($input as $var) {
43
- fw_aks(
44
- fw_html_attr_name_to_array_multi_key($var['name']),
45
- $var['value'],
46
- $POST
47
- );
48
- }
49
-
50
- $value = fw_get_options_values_from_input(
51
- array($this->id => $this->fw_option),
52
- fw_akg(FW_Option_Type::get_default_name_prefix(), $POST)
53
- );
54
-
55
- $value = array_pop($value);
56
-
57
- return $value;
58
- }
59
  }
1
  <?php if (!defined('FW')) die('Forbidden');
2
 
3
  class _FW_Customizer_Control_Option_Wrapper extends WP_Customize_Control {
 
 
 
 
 
 
 
 
4
  public function render_content() {
5
  fw()->backend->_set_default_render_design('customizer');
6
  ?>
9
  <div class="fw-backend-customizer-option-inner fw-force-xs">
10
  <?php
11
  echo fw()->backend->render_options(
12
+ array($this->id => $this->setting->get_fw_option()),
13
  array(
14
  $this->id => $this->value()
15
  ),
22
  <?php
23
  fw()->backend->_set_default_render_design();
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
framework/includes/customizer/class--fw-customizer-setting-option.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if (!defined('FW')) die('Forbidden');
2
+
3
+ class _FW_Customizer_Setting_Option extends WP_Customize_Setting {
4
+ /**
5
+ * @var array
6
+ * This is sent in args and set in parent construct
7
+ */
8
+ protected $fw_option = array();
9
+
10
+ public function get_fw_option() {
11
+ return $this->fw_option;
12
+ }
13
+
14
+ public function sanitize($value) {
15
+ $value = json_decode($value, true);
16
+
17
+ if (is_null($value) || !is_array($value)) {
18
+ return null;
19
+ }
20
+
21
+ $POST = array();
22
+
23
+ foreach ($value as $var) {
24
+ fw_aks(
25
+ fw_html_attr_name_to_array_multi_key($var['name']),
26
+ $var['value'],
27
+ $POST
28
+ );
29
+ }
30
+
31
+ $value = fw()->backend->option_type($this->fw_option['type'])->get_value_from_input(
32
+ $this->fw_option,
33
+ fw_akg(fw_html_attr_name_to_array_multi_key($this->id), $POST)
34
+ );
35
+
36
+ return $value;
37
+ }
38
+ }
framework/includes/option-types/icon/static/js/backend.js CHANGED
@@ -11,10 +11,10 @@ jQuery(function ($) {
11
 
12
  if ($this.hasClass('active')) {
13
  $this.removeClass('active');
14
- $this.closest(optionTypeClass).find('input').val('');
15
  } else {
16
  $this.addClass('active').siblings().removeClass('active');
17
- $this.closest(optionTypeClass).find('input').val($this.data('value'));
18
  }
19
  });
20
 
11
 
12
  if ($this.hasClass('active')) {
13
  $this.removeClass('active');
14
+ $this.closest(optionTypeClass).find('input').val('').trigger('change');
15
  } else {
16
  $this.addClass('active').siblings().removeClass('active');
17
+ $this.closest(optionTypeClass).find('input').val($this.data('value')).trigger('change');
18
  }
19
  });
20
 
framework/includes/option-types/map/class-fw-option-type-map.php CHANGED
@@ -80,9 +80,9 @@ class FW_Option_Type_Map extends FW_Option_Type {
80
  'country' => ( isset ( $input_value['country'] ) ) ? $input_value['country'] : '',
81
  'zip' => ( isset ( $input_value['zip'] ) ) ? $input_value['zip'] : '',
82
  'coordinates' => ( isset ( $input_value['coordinates'] ) )
83
- ? ( is_array($input_value['coordinates']) )
84
  ? $input_value['coordinates']
85
- : json_decode($input_value['coordinates'])
86
  : ''
87
  );
88
 
80
  'country' => ( isset ( $input_value['country'] ) ) ? $input_value['country'] : '',
81
  'zip' => ( isset ( $input_value['zip'] ) ) ? $input_value['zip'] : '',
82
  'coordinates' => ( isset ( $input_value['coordinates'] ) )
83
+ ? ( is_array($input_value['coordinates']) || is_object($input_value['coordinates']) )
84
  ? $input_value['coordinates']
85
+ : json_decode($input_value['coordinates'], true)
86
  : ''
87
  );
88
 
framework/includes/option-types/multi-picker/class-fw-option-type-multi-picker.php CHANGED
@@ -41,13 +41,13 @@ class FW_Option_Type_Multi_Picker extends FW_Option_Type
41
  $uri = fw_get_framework_directory_uri('/includes/option-types/' . $this->get_type());
42
 
43
  wp_enqueue_style(
44
- 'fw-option-type' . $this->get_type(),
45
  $uri . '/static/css/multi-picker.css',
46
  array(),
47
  fw()->manifest->get_version()
48
  );
49
  wp_enqueue_script(
50
- 'fw-option-type' . $this->get_type(),
51
  $uri . '/static/js/multi-picker.js',
52
  array('jquery', 'fw-events'),
53
  fw()->manifest->get_version(),
@@ -126,36 +126,13 @@ class FW_Option_Type_Multi_Picker extends FW_Option_Type
126
  return 'multi-picker';
127
  }
128
 
129
- private function prepare_option($id, $option)
130
- {
131
- if (empty($option['picker'])) {
132
- trigger_error(
133
- sprintf(__('No \'picker\' key set for multi-picker option: %s', 'fw'), $id),
134
- E_USER_ERROR
135
- );
136
- }
137
-
138
- {
139
- reset($option['picker']);
140
- $picker_key = key($option['picker']);
141
- $picker = $option['picker'][$picker_key];
142
- $picker_type = $picker['type'];
143
- }
144
-
145
- $supported_picker_types = array('select', 'short-select', 'radio', 'image-picker', 'switch',
146
- 'color-palette' // fixme: this is a temporary hardcode for a ThemeFuse theme option-type, think a way to allow other option-types here
147
- );
148
- if (!in_array($picker_type, $supported_picker_types)) {
149
- trigger_error(
150
- sprintf(
151
- __('Invalid picker type for multi-picker option %s, only pickers of types %s are supported', 'fw'),
152
- $id,
153
- implode(', ', $supported_picker_types)
154
- ),
155
- E_USER_ERROR
156
- );
157
- }
158
-
159
  switch($picker_type) {
160
  case 'switch':
161
  $picker_choices = array_intersect_key($option['choices'], array(
@@ -177,10 +154,46 @@ class FW_Option_Type_Multi_Picker extends FW_Option_Type
177
  }
178
  $picker_choices = array_intersect_key($option['choices'], $collected_choices);
179
  break;
180
- default:
 
181
  $picker_choices = array_intersect_key($option['choices'], $picker['choices']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
183
 
 
 
 
 
 
 
184
  $hide_picker = '';
185
  $show_borders = '';
186
 
@@ -207,7 +220,10 @@ class FW_Option_Type_Multi_Picker extends FW_Option_Type
207
  if (!empty($set)) {
208
  $choices_groups[$id . '-' . $key] = array(
209
  'type' => 'group',
210
- 'attr' => array('class' => 'choice-group choice-' . $key),
 
 
 
211
  'options' => array(
212
  $key => array(
213
  'type' => 'multi',
@@ -257,33 +273,14 @@ class FW_Option_Type_Multi_Picker extends FW_Option_Type
257
  );
258
  }
259
 
260
- // choices
261
- switch($picker_type) {
262
- case 'switch':
263
- $choices = array_intersect_key($option['choices'], array(
264
- $picker['left-choice']['value'] => array(),
265
- $picker['right-choice']['value'] => array()
266
- ));
267
- break;
268
- case 'select':
269
- case 'short-select':
270
- // we need to treat the case with optgroups
271
- $collected_choices = array();
272
- foreach ($picker['choices'] as $key => $choice_value) {
273
- if (is_array($choice_value) && isset($choice_value['choices'])) {
274
- // we have an optgroup
275
- $collected_choices = array_merge($collected_choices, $choice_value['choices']);
276
- } else {
277
- $collected_choices[$key] = $choice_value;
278
- }
279
- }
280
- $choices = array_intersect_key($option['choices'], $collected_choices);
281
- break;
282
- default:
283
- $choices = array_intersect_key($option['choices'], $picker['choices']);
284
- }
285
-
286
- foreach ($choices as $choice_id => $choice_options) {
287
  if (is_null($input_value) && isset($option['value'][$choice_id])) {
288
  $value[$choice_id] = $option['value'][$choice_id];
289
  } else {
41
  $uri = fw_get_framework_directory_uri('/includes/option-types/' . $this->get_type());
42
 
43
  wp_enqueue_style(
44
+ 'fw-option-type-' . $this->get_type(),
45
  $uri . '/static/css/multi-picker.css',
46
  array(),
47
  fw()->manifest->get_version()
48
  );
49
  wp_enqueue_script(
50
+ 'fw-option-type-' . $this->get_type(),
51
  $uri . '/static/js/multi-picker.js',
52
  array('jquery', 'fw-events'),
53
  fw()->manifest->get_version(),
126
  return 'multi-picker';
127
  }
128
 
129
+ /**
130
+ * @param array $option
131
+ * @param array $picker
132
+ * @param string $picker_type
133
+ * @return array( 'choice_id' => array( Choice Options ) )
134
+ */
135
+ private function get_picker_choices($option, $picker, $picker_type) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  switch($picker_type) {
137
  case 'switch':
138
  $picker_choices = array_intersect_key($option['choices'], array(
154
  }
155
  $picker_choices = array_intersect_key($option['choices'], $collected_choices);
156
  break;
157
+ case 'radio':
158
+ case 'image-picker':
159
  $picker_choices = array_intersect_key($option['choices'], $picker['choices']);
160
+ break;
161
+ default:
162
+ $picker_choices = apply_filters(
163
+ 'fw_option_type_multi_picker_choices:'. $picker_type,
164
+ $option['choices'],
165
+ array(
166
+ 'picker' => $picker,
167
+ 'option' => $option,
168
+ )
169
+ );
170
+ }
171
+
172
+ return $picker_choices;
173
+ }
174
+
175
+ private function prepare_option($id, $option)
176
+ {
177
+ if (empty($option['picker'])) {
178
+ trigger_error(
179
+ sprintf(__('No \'picker\' key set for multi-picker option: %s', 'fw'), $id),
180
+ E_USER_ERROR
181
+ );
182
+ }
183
+
184
+ {
185
+ reset($option['picker']);
186
+ $picker_key = key($option['picker']);
187
+ $picker = $option['picker'][$picker_key];
188
+ $picker_type = $picker['type'];
189
  }
190
 
191
+ $picker_choices = $this->get_picker_choices(
192
+ $option,
193
+ $picker,
194
+ $picker_type
195
+ );
196
+
197
  $hide_picker = '';
198
  $show_borders = '';
199
 
220
  if (!empty($set)) {
221
  $choices_groups[$id . '-' . $key] = array(
222
  'type' => 'group',
223
+ 'attr' => array(
224
+ 'class' => 'choice-group',
225
+ 'data-choice-key' => $key,
226
+ ),
227
  'options' => array(
228
  $key => array(
229
  'type' => 'multi',
273
  );
274
  }
275
 
276
+ foreach (
277
+ $this->get_picker_choices(
278
+ $option,
279
+ $picker,
280
+ $picker_type
281
+ )
282
+ as $choice_id => $choice_options
283
+ ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  if (is_null($input_value) && isset($option['value'][$choice_id])) {
285
  $value[$choice_id] = $option['value'][$choice_id];
286
  } else {
framework/includes/option-types/multi-picker/static/js/multi-picker.js CHANGED
@@ -6,7 +6,7 @@
6
  $choicesGroups: $this.find('> .choice-group')
7
  },
8
  chooseGroup = function(groupId) {
9
- var $choicesToReveal = elements.$choicesGroups.filter('.choice-' + groupId);
10
 
11
  /**
12
  * The group options html was rendered in an attribute to make page load faster.
@@ -40,8 +40,8 @@
40
  var $this = $(this),
41
  checked = $(this).is(':checked'),
42
  value = checked
43
- ? $this.attr('data-switch-right-value')
44
- : $this.attr('data-switch-left-value');
45
 
46
  chooseGroup(value);
47
  }).trigger('change');
@@ -66,10 +66,23 @@
66
  }
67
  };
68
 
69
- if (!pickerType || !flows[pickerType]) {
70
  console.error('unknown multi-picker type:', pickerType);
71
  } else {
72
- flows[pickerType]();
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
  };
75
 
6
  $choicesGroups: $this.find('> .choice-group')
7
  },
8
  chooseGroup = function(groupId) {
9
+ var $choicesToReveal = elements.$choicesGroups.filter('.choice-group[data-choice-key="'+ groupId +'"]');
10
 
11
  /**
12
  * The group options html was rendered in an attribute to make page load faster.
40
  var $this = $(this),
41
  checked = $(this).is(':checked'),
42
  value = checked
43
+ ? $this.attr('data-switch-right-value')
44
+ : $this.attr('data-switch-left-value');
45
 
46
  chooseGroup(value);
47
  }).trigger('change');
66
  }
67
  };
68
 
69
+ if (!pickerType) {
70
  console.error('unknown multi-picker type:', pickerType);
71
  } else {
72
+ if (flows[pickerType]) {
73
+ flows[pickerType]();
74
+ } else {
75
+ var eventName = 'fw:option-type:multi-picker:init:'+ pickerType;
76
+
77
+ if (fwe.hasListeners(eventName)) {
78
+ fwe.trigger(eventName, {
79
+ '$pickerGroup': elements.$pickerGroup,
80
+ 'chooseGroup': chooseGroup
81
+ });
82
+ } else {
83
+ console.error('uninitialized multi-picker type:', pickerType);
84
+ }
85
+ }
86
  }
87
  };
88
 
framework/includes/option-types/simple.php CHANGED
@@ -960,3 +960,95 @@ class FW_Option_Type_Select_Multiple extends FW_Option_Type_Select {
960
  }
961
 
962
  FW_Option_Type::register( 'FW_Option_Type_Select_Multiple' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
  }
961
 
962
  FW_Option_Type::register( 'FW_Option_Type_Select_Multiple' );
963
+
964
+
965
+ class FW_Option_Type_Unique extends FW_Option_Type
966
+ {
967
+ private static $ids = array();
968
+
969
+ public function get_type()
970
+ {
971
+ return 'unique';
972
+ }
973
+
974
+ protected function _get_defaults()
975
+ {
976
+ return array(
977
+ 'value' => ''
978
+ );
979
+ }
980
+
981
+ protected function _render($id, $option, $data) {
982
+ return fw_html_tag('input', array(
983
+ 'type' => 'hidden',
984
+ 'name' => $option['attr']['name'],
985
+ 'id' => $option['attr']['id'],
986
+ 'value' => $data['value'],
987
+ ));
988
+ }
989
+
990
+ protected function _init() {
991
+ add_action('save_post', array($this, '_action_reset_post_ids'), 8);
992
+ }
993
+
994
+ /**
995
+ * After the post has been saved
996
+ * other scripts may call wp_update_post() and the save will start again
997
+ * If the unique ids array will not be reset, on the next save
998
+ * the previously processed ids will all be detected as duplicate and will be regenerated
999
+ *
1000
+ * @param $post_id
1001
+ * @internal
1002
+ */
1003
+ public function _action_reset_post_ids($post_id)
1004
+ {
1005
+ if ( wp_is_post_autosave( $post_id ) ) {
1006
+ $original_id = wp_is_post_autosave( $post_id );
1007
+ } else if ( wp_is_post_revision( $post_id ) ) {
1008
+ $original_id = wp_is_post_revision( $post_id );
1009
+ } else {
1010
+ $original_id = $post_id;
1011
+ }
1012
+
1013
+ self::$ids[$post_id] = array();
1014
+ self::$ids[$original_id] = array();
1015
+ }
1016
+
1017
+ protected function _get_value_from_input($option, $input_value) {
1018
+ if (is_null($input_value)) {
1019
+ $id = empty($option['value']) ? fw_rand_md5() : $option['value'];
1020
+ } else {
1021
+ $id = $input_value;
1022
+ }
1023
+
1024
+ if (empty($id) || !is_string($id)) {
1025
+ $id = fw_rand_md5();
1026
+ }
1027
+
1028
+ /**
1029
+ * Regenerate if found the same id again
1030
+ */
1031
+ {
1032
+ global $post;
1033
+
1034
+ if ($post) {
1035
+ $post_id = $post->ID;
1036
+ } else {
1037
+ $post_id = '~';
1038
+ }
1039
+
1040
+ if (!isset(self::$ids[$post_id])) {
1041
+ self::$ids[$post_id] = array();
1042
+ }
1043
+
1044
+ while (isset(self::$ids[$post_id][$id])) {
1045
+ $id = fw_rand_md5();
1046
+ }
1047
+
1048
+ self::$ids[$post_id][$id] = true;
1049
+ }
1050
+
1051
+ return $id;
1052
+ }
1053
+ }
1054
+ FW_Option_Type::register('FW_Option_Type_Unique');
framework/manifest.php CHANGED
@@ -4,4 +4,4 @@ $manifest = array();
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
- $manifest['version'] = '2.3.1';
4
 
5
  $manifest['name'] = __('Unyson', 'fw');
6
 
7
+ $manifest['version'] = '2.3.2';
framework/static/css/fw.css CHANGED
@@ -3005,6 +3005,7 @@ body.rtl .fw-options-modal .media-frame-content > form .fw-options-tabs-contents
3005
 
3006
  .fw-sole-modal .fw-sole-modal-content h2 {
3007
  font-size: 22px;
 
3008
  }
3009
 
3010
  .fw-sole-modal .fw-sole-modal-content p {
3005
 
3006
  .fw-sole-modal .fw-sole-modal-content h2 {
3007
  font-size: 22px;
3008
+ line-height: initial;
3009
  }
3010
 
3011
  .fw-sole-modal .fw-sole-modal-content p {
framework/static/css/option-types.css CHANGED
@@ -54,7 +54,8 @@ textarea.fw-option-type-textarea {
54
  }
55
 
56
 
57
- .fw-backend-option-type-hidden {
 
58
  display: none;
59
  }
60
 
54
  }
55
 
56
 
57
+ .fw-backend-option-type-hidden,
58
+ .fw-backend-option-type-unique {
59
  display: none;
60
  }
61
 
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: unyson, themefusecom
3
  Tags: page builder, cms, grid, layout, responsive, back up, backup, db backup, dump, migrate, schedule, search engine optimization, seo, media, slideshow, shortcode, slide, slideshare, slideshow, google sitemaps, sitemaps, analytics, google analytics, calendar, event, events, google maps, learning, lessons, sidebars, breadcrumbs, review, portfolio, framework
4
  Requires at least: 4.0.0
5
  Tested up to: 4.2
6
- Stable tag: 2.3.1
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -86,6 +86,13 @@ Yes; Unyson will work with any theme.
86
 
87
  == Changelog ==
88
 
 
 
 
 
 
 
 
89
  = 2.3.1 =
90
  * Fixed [#566](https://github.com/ThemeFuse/Unyson/issues/566), [#550](https://github.com/ThemeFuse/Unyson/issues/550)
91
  * Fixed: Options default values not working in Customizer [#410](https://github.com/ThemeFuse/Unyson/issues/410#issuecomment-103343955)
3
  Tags: page builder, cms, grid, layout, responsive, back up, backup, db backup, dump, migrate, schedule, search engine optimization, seo, media, slideshow, shortcode, slide, slideshare, slideshow, google sitemaps, sitemaps, analytics, google analytics, calendar, event, events, google maps, learning, lessons, sidebars, breadcrumbs, review, portfolio, framework
4
  Requires at least: 4.0.0
5
  Tested up to: 4.2
6
+ Stable tag: 2.3.2
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
86
 
87
  == Changelog ==
88
 
89
+ = 2.3.2 =
90
+ * Added option-type `unique` to make possible [this](http://manual.unyson.io/en/latest/extension/shortcodes/index.html#enqueue-shortcode-dynamic-css-in-page-head)
91
+ * Added the `fw_get_google_fonts_v2()` function
92
+ * Fixed: The newline character in textarea option in Customizer was replaced with `'rn'` string
93
+ * Fixed: Post options (meta) lost after Quick Edit
94
+ * Option-type `multi-picker`: Allow support for any option type in picker ([docs](http://manual.unyson.io/en/latest/options/built-in-option-types.html#multi-picker-add-support-for-new-option-type-in-picker))
95
+
96
  = 2.3.1 =
97
  * Fixed [#566](https://github.com/ThemeFuse/Unyson/issues/566), [#550](https://github.com/ThemeFuse/Unyson/issues/550)
98
  * Fixed: Options default values not working in Customizer [#410](https://github.com/ThemeFuse/Unyson/issues/410#issuecomment-103343955)
unyson.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Unyson
4
  * Plugin URI: http://unyson.themefuse.com/
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.3.1
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+
3
  * Plugin Name: Unyson
4
  * Plugin URI: http://unyson.themefuse.com/
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.3.2
7
  * Author: ThemeFuse
8
  * Author URI: http://themefuse.com
9
  * License: GPL2+