public function get_value_from_attributes($attributes)
 {
     $attributes['type'] = $this->get_type();
     $options = $this->get_shortcode_options();
     if (!empty($options)) {
         if (empty($attributes['atts'])) {
             /**
              * The options popup was never opened and there are no attributes.
              * Extract options default values.
              */
             $attributes['atts'] = fw_get_options_values_from_input($options, array());
         } else {
             /**
              * There are saved attributes.
              * But we need to execute the _get_value_from_input() method for all options,
              * because some of them may be (need to be) changed (auto-generated) https://github.com/ThemeFuse/Unyson/issues/275
              * Add the values to $option['value']
              */
             $options = fw_extract_only_options($options);
             foreach ($attributes['atts'] as $option_id => $option_value) {
                 if (isset($options[$option_id])) {
                     $options[$option_id]['value'] = $option_value;
                 }
             }
             $attributes['atts'] = fw_get_options_values_from_input($options, array());
         }
     }
     return $attributes;
 }
 /**
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     if (is_array($input_value) || empty($option['value'])) {
         $value = array();
     } else {
         $value = $option['value'];
     }
     foreach (fw_extract_only_options($option['inner-options']) as $inner_id => $inner_option) {
         $value[$inner_id] = fw()->backend->option_type($inner_option['type'])->get_value_from_input(isset($value[$inner_id]) ? array_merge($inner_option, array('value' => $value[$inner_id])) : $inner_option, isset($input_value[$inner_id]) ? $input_value[$inner_id] : null);
     }
     return $value;
 }
 /**
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     if (!is_array($input_value)) {
         return $option['value'];
     }
     $value = array();
     $box_options = fw_extract_only_options($option['box-options']);
     foreach ($input_value as &$list_item_value) {
         $current_value = array();
         foreach ($box_options as $id => $input_option) {
             $current_value[$id] = fw()->backend->option_type($input_option['type'])->get_value_from_input($input_option, isset($list_item_value[$id]) ? $list_item_value[$id] : null);
         }
         $value[] = $current_value;
     }
     return $value;
 }
 protected function get_fixed_attributes($attributes)
 {
     // do not allow sub items
     unset($attributes['_items']);
     $default_attributes = array('type' => $this->get_type(), 'shortcode' => false, 'width' => '', 'options' => array());
     // remove unknown attributes
     $attributes = array_intersect_key($attributes, $default_attributes);
     $attributes = array_merge($default_attributes, $attributes);
     $only_options = array();
     foreach (fw_extract_only_options($this->get_options()) as $option_id => $option) {
         if (array_key_exists($option_id, $attributes['options'])) {
             $option['value'] = $attributes['options'][$option_id];
         }
         $only_options[$option_id] = $option;
     }
     $attributes['options'] = fw_get_options_values_from_input($only_options, array());
     unset($only_options, $option_id, $option);
     $constraints = $attributes['options']['constraints'];
     if (!empty($constraints['constraint'])) {
         $constraint = $constraints['constraint'];
         $constraint_data = $constraints[$constraint];
         switch ($constraint) {
             case 'characters':
             case 'words':
                 if (!empty($constraint_data['min'])) {
                     $constraint_data['min'] = intval($constraint_data['min']);
                     if ($constraint_data['min'] < 0) {
                         $constraint_data['min'] = 0;
                     }
                 }
                 if (!empty($constraint_data['max'])) {
                     $constraint_data['max'] = intval($constraint_data['max']);
                     if ($constraint_data['max'] < 0 || $constraint_data['max'] < $constraint_data['min']) {
                         $constraint_data['max'] = null;
                     }
                 }
                 break;
             default:
                 trigger_error('Invalid constraint: ' . $constraint, E_USER_WARNING);
                 $attributes['options']['constraints']['constraint'] = '';
         }
         $attributes['options']['constraints'][$constraint] = $constraint_data;
     }
     return $attributes;
 }
 protected function get_fixed_attributes($attributes)
 {
     // do not allow sub items
     unset($attributes['_items']);
     $default_attributes = array('type' => $this->get_type(), 'shortcode' => 'form-header-title', 'width' => '', 'options' => array());
     // remove unknown attributes
     $attributes = array_intersect_key($attributes, $default_attributes);
     $attributes = array_merge($default_attributes, $attributes);
     $only_options = array();
     foreach (fw_extract_only_options($this->get_options()) as $option_id => $option) {
         if (array_key_exists($option_id, $attributes['options'])) {
             $option['value'] = $attributes['options'][$option_id];
         }
         $only_options[$option_id] = $option;
     }
     $attributes['options'] = fw_get_options_values_from_input($only_options, array());
     unset($only_options, $option_id, $option);
     return $attributes;
 }
 protected function check_settings()
 {
     if (!$this->get_config('display')) {
         return;
     }
     $theme_options = fw_extract_only_options($this->get_parent()->get_settings_options());
     $options = false;
     foreach ($theme_options as $option_name => $option_settings) {
         if ($option_settings['type'] !== 'style') {
             unset($theme_options[$option_name]);
             continue;
         }
         $options = $option_settings;
         break;
     }
     if (!empty($options['predefined'])) {
         $this->options = $options;
         $this->add_theme_actions();
     }
 }
 protected function check_settings()
 {
     if (!fw_get_db_ext_settings_option($this->get_parent()->get_name(), 'switch_style_panel_display')) {
         return;
     }
     $theme_options = fw_extract_only_options($this->get_parent()->get_options('appearance-settings'));
     $options = false;
     foreach ($theme_options as $option_name => $option_settings) {
         if ($option_settings['type'] !== 'style') {
             unset($theme_options[$option_name]);
             continue;
         }
         $options = $option_settings;
         break;
     }
     if (!empty($options['predefined'])) {
         $this->options = $options;
         $this->add_theme_actions();
     }
 }
 /**
  * @internal
  * {@inheritdoc}
  */
 protected function _get_value_from_input($option, $input_value)
 {
     if (is_null($input_value)) {
         $value = $option['value'];
     } elseif (is_array($input_value)) {
         $option['limit'] = intval($option['limit']);
         $value = array();
         $box_options = fw_extract_only_options($option['box-options']);
         foreach ($input_value as &$list_item_value) {
             $current_value = array();
             foreach ($box_options as $id => $input_option) {
                 $current_value[$id] = fw()->backend->option_type($input_option['type'])->get_value_from_input($input_option, isset($list_item_value[$id]) ? $list_item_value[$id] : null);
             }
             $value[] = $current_value;
             if ($option['limit'] && count($value) === $option['limit']) {
                 break;
             }
         }
     } else {
         $value = array();
     }
     return $value;
 }
 /**
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     reset($option['picker']);
     $picker_key = key($option['picker']);
     $picker_type = $option['picker'][$picker_key]['type'];
     $picker = $option['picker'][$picker_key];
     $value = array();
     if (is_null($input_value) && isset($option['value'][$picker_key])) {
         $value[$picker_key] = $option['value'][$picker_key];
     } else {
         $value[$picker_key] = fw()->backend->option_type($picker_type)->get_value_from_input($picker, isset($input_value[$picker_key]) ? $input_value[$picker_key] : null);
     }
     // choices
     switch ($picker_type) {
         case 'switch':
             $choices = array_intersect_key($option['choices'], array($picker['left-choice']['value'] => array(), $picker['right-choice']['value'] => array()));
             break;
         case 'select':
         case 'short-select':
             // we need to treat the case with optgroups
             $collected_choices = array();
             foreach ($picker['choices'] as $key => $choice_value) {
                 if (is_array($choice_value) && isset($choice_value['choices'])) {
                     // we have an optgroup
                     $collected_choices = array_merge($collected_choices, $choice_value['choices']);
                 } else {
                     $collected_choices[$key] = $choice_value;
                 }
             }
             $choices = array_intersect_key($option['choices'], $collected_choices);
             break;
         default:
             $choices = array_intersect_key($option['choices'], $picker['choices']);
     }
     foreach ($choices as $choice_id => $choice_options) {
         if (is_null($input_value) && isset($option['value'][$choice_id])) {
             $value[$choice_id] = $option['value'][$choice_id];
         } else {
             foreach (fw_extract_only_options($choice_options) as $choice_option_id => $choice_option) {
                 $value[$choice_id][$choice_option_id] = fw()->backend->option_type($choice_option['type'])->get_value_from_input($choice_option, isset($input_value[$choice_id][$choice_option_id]) ? $input_value[$choice_id][$choice_option_id] : null);
             }
         }
     }
     return $value;
 }
Example #10
0
/**
 * Set post option value in database
 *
 * @param null|int $post_id
 * @param string|null $option_id Specific option id (accepts multikey). null - all options
 * @param $value
 */
function fw_set_db_post_option($post_id = null, $option_id = null, $value)
{
    $post_id = intval($post_id);
    if (!$post_id) {
        /** @var WP_Post $post */
        global $post;
        if (!$post) {
            return;
        } else {
            $post_id = $post->ID;
        }
    }
    $options = fw_extract_only_options(fw()->theme->get_post_options(get_post_type(($post_revision_id = wp_is_post_revision($post_id)) ? $post_revision_id : $post_id)));
    $sub_keys = null;
    if ($option_id) {
        $option_id = explode('/', $option_id);
        // 'option_id/sub/keys'
        $_option_id = array_shift($option_id);
        // 'option_id'
        $sub_keys = implode('/', $option_id);
        // 'sub/keys'
        $option_id = $_option_id;
        unset($_option_id);
        $old_value = fw_get_db_post_option($post_id, $option_id);
        if ($sub_keys) {
            // update sub_key in old_value and use the entire value
            $new_value = $old_value;
            fw_aks($sub_keys, $value, $new_value);
            $value = $new_value;
            unset($new_value);
            $old_value = fw_akg($sub_keys, $old_value);
        }
        if (isset($options[$option_id])) {
            $value = fw()->backend->option_type($options[$option_id]['type'])->storage_save($option_id, $options[$option_id], $value, array('post-id' => $post_id));
        }
        FW_WP_Meta::set('post', $post_id, 'fw_options/' . $option_id, $value);
    } else {
        $old_value = fw_get_db_post_option($post_id);
        if (!is_array($value)) {
            $value = array();
        }
        foreach ($value as $_option_id => $_option_value) {
            if (isset($options[$_option_id])) {
                $value[$_option_id] = fw()->backend->option_type($options[$_option_id]['type'])->storage_save($_option_id, $options[$_option_id], $_option_value, array('post-id' => $post_id));
            }
        }
        FW_WP_Meta::set('post', $post_id, 'fw_options', $value);
    }
    /**
     * @deprecated
     */
    fw()->backend->_sync_post_separate_meta($post_id);
    /**
     * @since 2.2.8
     */
    do_action('fw_post_options_update', $post_id, $option_id, explode('/', $sub_keys), $old_value);
}
Example #11
0
 /**
  * Render options html from input json
  *
  * POST vars:
  * - options: '[{option_id: {...}}, {option_id: {...}}, ...]'                  // Required // String JSON
  * - values:  {option_id: value, option_id: {...}, ...}                        // Optional // Object
  * - data:    {id_prefix: 'fw_options-a-b-', name_prefix: 'fw_options[a][b]'}  // Optional // Object
  */
 public function _action_ajax_options_render()
 {
     if (!isset($_POST['options'])) {
         wp_send_json_error(array('message' => 'No options'));
     }
     $options = json_decode(FW_Request::POST('options'), true);
     if (!$options) {
         wp_send_json_error(array('message' => 'Wrong options'));
     }
     if (isset($_POST['values'])) {
         $values = FW_Request::POST('values');
     } else {
         $values = array();
     }
     if (isset($_POST['data'])) {
         $data = FW_Request::POST('data');
     } else {
         $data = array();
     }
     foreach (fw_extract_only_options($options) as $option_id => $option) {
         if (!isset($values[$option_id])) {
             continue;
         }
         /**
          * We detect if option is using booleans by sending it a boolean input value
          * If it returns a boolean, then it works with booleans
          */
         if (!is_bool(fw()->backend->option_type($option['type'])->get_value_from_input($option, true))) {
             continue;
         }
         if (is_bool($values[$option_id])) {
             // value is already boolean, does not need to fix
             continue;
         }
         $values[$option_id] = $values[$option_id] === 'true';
     }
     wp_send_json_success(array('html' => fw()->backend->render_options($options, $values, $data)));
 }
 /**
  * Extract correct value for $option['value'] from input array
  * If input value is empty, will be returned $option['value']
  *
  * @param array $option
  * @param array|string|null $input_value
  *
  * @return string|array|int|bool Correct value
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     if (empty($input_value)) {
         if (empty($option['popup-options'])) {
             return array();
         }
         $popup_options = array();
         foreach (fw_extract_only_options($option['popup-options']) as $popup_option_id => $popup_option) {
             if (isset($option['value'][$popup_option_id])) {
                 $popup_option['value'] = $option['value'][$popup_option_id];
             }
             $popup_options[$popup_option_id] = $popup_option;
         }
         $values = fw_get_options_values_from_input($popup_options, array());
     } else {
         if (is_array($input_value)) {
             /**
              * Don't decode if we have already an array
              */
             $values = $input_value;
         } else {
             $values = json_decode($input_value, true);
         }
     }
     return $values;
 }
 /**
  * {@inheritdoc}
  * fixes https://github.com/ThemeFuse/Unyson/issues/1440
  */
 protected function _storage_save($id, array $option, $value, array $params)
 {
     if (apply_filters('fw:option-type:multi-picker:fw-storage:process-inner-options', false)) {
         foreach ($option['choices'] as $choice_id => $choice) {
             foreach (fw_extract_only_options($choice) as $opt_id => $opt) {
                 $value[$choice_id][$opt_id] = fw()->backend->option_type($opt['type'])->storage_save($opt_id, $opt, $value[$choice_id][$opt_id], $params);
             }
         }
     }
     return fw_db_option_storage_save($id, $option, $value, $params);
 }
 /**
  * Triggers when the extension settings are saved,
  * it generates css from the styling settings and stores it
  * @internal
  */
 public function _admin_action_generate_css()
 {
     $theme_options = fw_extract_only_options($this->get_options('appearance-settings'));
     $saved_data = fw_get_db_extension_data($this->get_name(), 'options');
     $css_for_style_options = FW_Styling_Css_Generator::get_css($theme_options, $saved_data);
     fw_set_db_extension_data($this->get_name(), 'css', $css_for_style_options);
 }
 /**
  * Extract correct value for $option['value'] from input array
  * If input value is empty, will be returned $option['value']
  * @param array $option
  * @param array|string|null $input_value
  * @return string|array|int|bool Correct value
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     if (!is_array($input_value)) {
         return $option['value'];
     }
     // unset the last slide that is default for add
     array_pop($input_value);
     $value = array();
     $slides_options = fw_extract_only_options($option['slides_options']);
     foreach ($input_value as &$list_item_value) {
         $current_value = array();
         foreach ($slides_options as $id => $input_option) {
             $current_value[$id] = fw()->backend->option_type($input_option['type'])->get_value_from_input($input_option, isset($list_item_value[$id]) ? $list_item_value[$id] : null);
             $current_value['thumb'] = isset($list_item_value['thumb']) ? $list_item_value['thumb'] : null;
         }
         $value[] = $current_value;
     }
     return $value;
 }
Example #16
0
 /**
  * Render options html from input json
  *
  * POST vars:
  * - options: '[{option_id: {...}}, {option_id: {...}}, ...]'                  // Required // String JSON
  * - values:  {option_id: value, option_id: {...}, ...}                        // Optional // Object
  * - data:    {id_prefix: 'fw_options-a-b-', name_prefix: 'fw_options[a][b]'}  // Optional // Object
  */
 public function _action_ajax_options_render()
 {
     if (!isset($_POST['options'])) {
         wp_send_json_error(array('message' => 'No options'));
     }
     $options = json_decode(FW_Request::POST('options'), true);
     if (!$options) {
         wp_send_json_error(array('message' => 'Wrong options'));
     }
     if (isset($_POST['values'])) {
         $values = FW_Request::POST('values');
         if (is_string($values)) {
             $values = json_decode($values, true);
         }
     } else {
         $values = array();
     }
     if (isset($_POST['data'])) {
         $data = FW_Request::POST('data');
     } else {
         $data = array();
     }
     foreach (fw_extract_only_options($options) as $option_id => $option) {
         if (!isset($values[$option_id])) {
             continue;
         }
         /**
          * Fix booleans
          *
          * In POST, booleans are transformed to strings: 'true' and 'false'
          * Transform them back to booleans
          */
         do {
             /**
              * Detect if option is using booleans by sending it a boolean input value
              * If it returns a boolean, then it works with booleans
              */
             if (!is_bool(fw()->backend->option_type($option['type'])->get_value_from_input($option, true))) {
                 break;
             }
             if (is_bool($values[$option_id])) {
                 // value is already boolean, does not need to fix
                 break;
             }
             $values[$option_id] = $values[$option_id] === 'true';
             continue 2;
         } while (false);
         /**
          * Fix integers
          *
          * In POST, integers are transformed to strings: '0', '1', '2', ...
          * Transform them back to integers
          */
         do {
             if (!is_numeric($values[$option_id])) {
                 // do nothing if value is not a number
                 break;
             }
             /**
              * Detect if option is using integer value by checking $option['value']
              */
             if (isset($option['value']) && !is_int($option['value'])) {
                 continue;
             }
             $values[$option_id] = (double) $values[$option_id];
             continue 2;
         } while (false);
     }
     wp_send_json_success(array('html' => fw()->backend->render_options($options, $values, $data)));
 }
Example #17
0
/**
 * Get correct values from input (POST) for given options
 * This values can be saved in db then replaced with $option['value'] for each option
 * @param array $options
 * @param array $input_array
 * @return array Values
 */
function fw_get_options_values_from_input(array $options, $input_array = null)
{
    if (!is_array($input_array)) {
        $input_array = FW_Request::POST(FW_Option_Type::get_default_name_prefix());
    }
    $values = array();
    foreach (fw_extract_only_options($options) as $id => $option) {
        $values[$id] = fw()->backend->option_type($option['type'])->get_value_from_input($option, isset($input_array[$id]) ? $input_array[$id] : null);
        if (is_null($values[$id])) {
            // do not save null values
            unset($values[$id]);
        }
    }
    return $values;
}
 /**
  * Enqueue options static
  *
  * Useful when you have dynamic options html on the page (for e.g. options modal)
  * and in order to initialize that html properly, the option types scripts styles must be enqueued on the page
  *
  * @param array $options
  */
 public function enqueue_options_static($options)
 {
     /**
      * register scripts and styles
      * in case if this method is called before enqueue_scripts action
      * and option types has some of these in their dependencies
      */
     $this->register_static();
     wp_enqueue_media();
     wp_enqueue_style('fw-backend-options');
     wp_enqueue_script('fw-backend-options');
     foreach (fw_extract_only_options($options) as $option_id => $option) {
         fw()->backend->option_type($option['type'])->enqueue_static($option_id, $option);
     }
 }
 /**
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     reset($option['picker']);
     $picker_key = key($option['picker']);
     $picker_type = $option['picker'][$picker_key]['type'];
     $picker = $option['picker'][$picker_key];
     $value = array();
     if (is_null($input_value) && isset($option['value'][$picker_key])) {
         $value[$picker_key] = $option['value'][$picker_key];
     } else {
         $value[$picker_key] = fw()->backend->option_type($picker_type)->get_value_from_input($picker, isset($input_value[$picker_key]) ? $input_value[$picker_key] : null);
     }
     foreach ($this->get_picker_choices($option, $picker, $picker_type) as $choice_id => $choice_options) {
         if (is_null($input_value) && isset($option['value'][$choice_id])) {
             $value[$choice_id] = $option['value'][$choice_id];
         } else {
             foreach (fw_extract_only_options($choice_options) as $choice_option_id => $choice_option) {
                 $value[$choice_id][$choice_option_id] = fw()->backend->option_type($choice_option['type'])->get_value_from_input($choice_option, isset($input_value[$choice_id][$choice_option_id]) ? $input_value[$choice_id][$choice_option_id] : null);
             }
         }
     }
     return $value;
 }
 /**
  * Extract correct value for $option['value'] from input array
  * If input value is empty, will be returned $option['value']
  *
  * @param array $option
  * @param array|string|null $input_value
  *
  * @return string|array|int|bool Correct value
  * @internal
  */
 protected function _get_value_from_input($option, $input_value)
 {
     if (empty($input_value)) {
         if (empty($option['popup-options'])) {
             return array();
         }
         /**
          * $option['value'] has DB format (not $input_value HTML format)
          * so it can't be used as second parameter in fw_get_options_values_from_input()
          * thus we need to move each option value in option array default values
          */
         $popup_options = array();
         foreach (fw_extract_only_options($option['popup-options']) as $popup_option_id => $popup_option) {
             if (isset($option['value'][$popup_option_id])) {
                 $popup_option['value'] = $option['value'][$popup_option_id];
             }
             $popup_options[$popup_option_id] = $popup_option;
         }
         $values = fw_get_options_values_from_input($popup_options, array());
     } else {
         if (is_array($input_value)) {
             /**
              * Don't decode if we have already an array
              */
             $values = fw_get_options_values_from_input($option['popup-options'], $input_value);
         } else {
             $values = fw_get_options_values_from_input($option['popup-options'], json_decode($input_value, true));
         }
     }
     return $values;
 }
Example #21
0
/**
 * Set post option value in database
 *
 * @param null|int $post_id
 * @param string|null $option_id Specific option id (accepts multikey). null - all options
 * @param $value
 */
function fw_set_db_post_option($post_id = null, $option_id = null, $value)
{
    FW_Cache::del('fw_post_options/values');
    $meta_key = 'fw_options';
    $post_id = intval($post_id);
    if (!$post_id) {
        /** @var WP_Post $post */
        global $post;
        if (!$post) {
            return;
        } else {
            $post_id = $post->ID;
        }
    }
    $post_type = get_post_type(($post_revision_id = wp_is_post_revision($post_id)) ? $post_revision_id : $post_id);
    try {
        $options = FW_Cache::get($cache_key = 'fw_post_options/only/' . $post_type);
    } catch (FW_Cache_Not_Found_Exception $e) {
        FW_Cache::set($cache_key, $options = array());
        // prevent recursion
        if (apply_filters('fw_get_db_post_option:fw-storage-enabled', $post_type !== 'fw-slider', $post_type)) {
            FW_Cache::set($cache_key, $options = fw_extract_only_options(fw()->theme->get_post_options($post_type)));
        }
    }
    $sub_keys = null;
    if ($option_id) {
        $option_id = explode('/', $option_id);
        // 'option_id/sub/keys'
        $_option_id = array_shift($option_id);
        // 'option_id'
        $sub_keys = empty($option_id) ? null : implode('/', $option_id);
        // 'sub/keys'
        $option_id = $_option_id;
        unset($_option_id);
        $old_value = fw_get_db_post_option($post_id, $option_id);
        if ($sub_keys) {
            // update sub_key in old_value and use the entire value
            $new_value = $old_value;
            fw_aks($sub_keys, $value, $new_value);
            $value = $new_value;
            unset($new_value);
            $old_value = fw_akg($sub_keys, $old_value);
        }
        if (isset($options[$option_id])) {
            $value = fw()->backend->option_type($options[$option_id]['type'])->storage_save($option_id, $options[$option_id], $value, array('post-id' => $post_id));
        }
        FW_WP_Meta::set('post', $post_id, $meta_key . '/' . $option_id, $value);
    } else {
        $old_value = fw_get_db_post_option($post_id);
        if (!is_array($value)) {
            $value = array();
        }
        foreach ($value as $_option_id => $_option_value) {
            if (isset($options[$_option_id])) {
                $value[$_option_id] = fw()->backend->option_type($options[$_option_id]['type'])->storage_save($_option_id, $options[$_option_id], $_option_value, array('post-id' => $post_id));
            }
        }
        FW_WP_Meta::set('post', $post_id, $meta_key, $value);
    }
    FW_Cache::del('fw_post_options/values');
    // fixes https://github.com/ThemeFuse/Unyson/issues/1538
    /**
     * @deprecated
     */
    fw()->backend->_sync_post_separate_meta($post_id);
    /**
     * @since 2.2.8
     */
    do_action('fw_post_options_update', $post_id, $option_id, explode('/', $sub_keys), $old_value);
}
Example #22
0
 /**
  * Render options html from input json
  *
  * POST vars:
  * - options: '[{option_id: {...}}, {option_id: {...}}, ...]'                  // Required // String JSON
  * - values:  {option_id: value, option_id: {...}, ...}                        // Optional // Object
  * - data:    {id_prefix: 'fw_options-a-b-', name_prefix: 'fw_options[a][b]'}  // Optional // Object
  */
 public function _action_ajax_options_render()
 {
     if (!isset($_POST['options'])) {
         wp_send_json_error(array('message' => 'No options'));
     }
     $options = json_decode(FW_Request::POST('options'), true);
     if (!$options) {
         wp_send_json_error(array('message' => 'Wrong options'));
     }
     if (isset($_POST['values'])) {
         $values = FW_Request::POST('values');
         if (is_string($values)) {
             $values = json_decode($values, true);
         }
     } else {
         $values = array();
     }
     $values = array_intersect_key($values, fw_extract_only_options($options));
     if (isset($_POST['data'])) {
         $data = FW_Request::POST('data');
     } else {
         $data = array();
     }
     wp_send_json_success(array('html' => fw()->backend->render_options($options, $values, $data)));
 }
 public final function set($item_id = null, $option_id = null, $value, array $extra_data = array())
 {
     FW_Cache::del($cache_key_values = $this->get_cache_key('values', $item_id, $extra_data));
     FW_Cache::del($cache_key_values_processed = $this->get_cache_key('values:processed', $item_id, $extra_data));
     try {
         $options = FW_Cache::get($cache_key = $this->get_cache_key('options', $item_id, $extra_data));
     } catch (FW_Cache_Not_Found_Exception $e) {
         FW_Cache::set($cache_key, array());
         // prevent recursion
         FW_Cache::set($cache_key, $options = fw_extract_only_options($this->get_options($item_id, $extra_data)));
     }
     $sub_keys = null;
     if ($option_id) {
         $option_id = explode('/', $option_id);
         // 'option_id/sub/keys'
         $_option_id = array_shift($option_id);
         // 'option_id'
         $sub_keys = empty($option_id) ? null : implode('/', $option_id);
         // 'sub/keys'
         $option_id = $_option_id;
         unset($_option_id);
         $old_values = is_array($old_values = $this->get_values($item_id, $extra_data)) ? $old_values : array();
         $old_value = isset($old_values[$option_id]) ? $old_values[$option_id] : null;
         if ($sub_keys) {
             // update sub_key in old_value and use the entire value
             $new_value = $old_value;
             fw_aks($sub_keys, $value, $new_value);
             $value = $new_value;
             unset($new_value);
             $old_value = fw_akg($sub_keys, $old_value);
         }
         if (isset($options[$option_id])) {
             $value = fw()->backend->option_type($options[$option_id]['type'])->storage_save($option_id, $options[$option_id], $value, $this->get_fw_storage_params($item_id, $extra_data));
         }
         $old_values[$option_id] = $value;
         $this->set_values($item_id, $old_values, $extra_data);
         unset($old_values);
     } else {
         $old_value = is_array($old_values = $this->get_values($item_id, $extra_data)) ? $old_values : array();
         if (!is_array($value)) {
             $value = array();
         }
         foreach ($value as $_option_id => $_option_value) {
             if (isset($options[$_option_id])) {
                 $value[$_option_id] = fw()->backend->option_type($options[$_option_id]['type'])->storage_save($_option_id, $options[$_option_id], $_option_value, $this->get_fw_storage_params($item_id, $extra_data));
             }
         }
         $this->set_values($item_id, $value, $extra_data);
     }
     FW_Cache::del($cache_key_values);
     // fixes https://github.com/ThemeFuse/Unyson/issues/1538
     FW_Cache::del($cache_key_values_processed);
     $this->_after_set($item_id, $option_id, $sub_keys, $old_value, $extra_data);
 }