File "options-class.php"

Full path: /home/webide/saman-clinic.ir/wp-content/plugins/wp-shamsi/lib/Options/options-class.php
File size: 64.41 KB
MIME-type: text/x-php
Charset: utf-8

<?php if (! defined('ABSPATH')) {
    die;
}
/**
 * Copyright https://joe.szalai.org released under GPL V3
 * License URI: http://www.gnu.org/licenses/gpl-3.0.html
 */
if (! class_exists('Exopite_Simple_Options_Framework')) :

    class Exopite_Simple_Options_Framework
    {

        /**
         *
         * dirname
         * @access public
         * @var string
         *
         */
        public $dirname = '';

        /**
         *
         * unique
         * @access public
         * @var string
         *
         */
        public $unique = '';

        /**
         *
         * notice
         * @access public
         * @var boolean
         *
         */
        public $notice = false;

        /**
         *
         * settings
         * @access public
         * @var array
         *
         */
        public $config = array();

        /**
         *
         * options
         * @access public
         * @var array
         *
         */
        public $fields = array();
        public $elements = array();

        public $is_multilang = null;

        public $multilang = false;
        public $lang_default;
        public $lang_current;

        public $languages = array();

        public $version;

        // public $debug = true;
        public $debug = false;

        /**
         *
         * options store
         * @access public
         * @var array
         *
         */
        public $db_options = array();

        /**
         * Sets the type to  metabox|menu
         * @var string
         */
        private $type;

        /**
         * @var object WP_Error
         */
        protected $errors;

        /**
         * @var array required fields for $type = menu
         */
        protected $required_keys_all_types = array();

        /**
         * @var array required fields for $type = menu
         */
        protected $required_keys_menu = array( 'id', 'menu_title', 'plugin_basename' );

        /**
         * @var array required fields for $type = metabox
         */
        protected $required_keys_metabox = array( 'id', 'post_types', 'title', 'capability' );

        public function __construct($config, $elements)
        {

            // If we are not in admin area exit.
            if (! is_admin()) {
                return;
            }

            $this->version = WPSH_VERSION;

            // TODO: Do sanitize $config['id']
            $this->unique = $config['id'];

            // Filter for override every exopite $config and $fields
            // $this->config = apply_filters( 'exopite_sof_config', $config );
            // $this->elements = apply_filters( 'exopite_sof_options', $fields );

            // Filter for override $config and $fields with respect to $config and $fields
            $this->config = apply_filters('exopite_sof_config_' . $this->unique, $config);
            $this->elements = apply_filters('exopite_sof_options_' . $this->unique, $elements);

            // now is_menu() and is_metabox() available

            $this->load_textdomain();

            $this->get_fields();

            $this->check_required_configuration_keys();

            $this->load_classes();

            $this->set_properties();

            $this->setup_multilang();

            $this->include_field_classes();

            $this->define_shared_hooks();

            $this->define_hooks();
        }

        public function get_mo_file()
        {
            $path = wp_normalize_path(dirname(__FILE__)) . '/lang';
            $domain = 'exopite-sof';
            if (function_exists('determine_locale')) {
                $locale = determine_locale();
            } else {
                $locale = get_locale();
            }

            return $path . '/' . $domain . '-' . $locale . '.mo';
        }

        public function load_textdomain()
        {
            $mofile = $this->get_mo_file();
            load_textdomain('exopite-sof', $mofile);
        }

        protected function setup_multilang()
        {

            // Srt Defaults for all cases
            $multilang_defaults = Exopite_Simple_Options_Framework_Helper::get_language_defaults();

            if (is_array($multilang_defaults)) {
                $this->config['multilang'] = $this->multilang = $multilang_defaults;

                $this->lang_current = $this->multilang['current'];
                $this->lang_default = $this->multilang['default'];
                $this->languages    = $this->multilang['languages'];
            }
        }

        /**
         * Return the array of languages except current language
         *
         * @return array $languages_except_current
         *
         */
        public function languages_except_current_language()
        {
            $all_languages = $this->languages;

            if (empty($all_languages)) {
                return $all_languages;
            }

            $languages_except_current = array_diff($all_languages, array( $this->lang_current ));

            unset($all_languages);

            return $languages_except_current;
        }

        /**
         * Checks for required keys in configuration array
         * and throw admin error if a required key is missing
         */
        protected function check_required_configuration_keys()
        {
            // instantiate the Wp_Error for $this->errors
            $this->errors = new WP_Error();

            $required_key_array = $this->required_keys_all_types;

            if ($this->is_menu()) {
                $required_key_array = $this->required_keys_menu;
            }

            if ($this->is_metabox()) {
                $required_key_array = $this->required_keys_metabox;
            }

            // Loop through all required keys array to check if every required key is set.
            if (! empty($required_key_array) && ! empty($this->config)) {
                foreach ($required_key_array as $key) :

                    if (! array_key_exists($key, $this->config)) {
                        // Add error message to the WP_Error object
                        $this->errors->add("missing_config_key_{$key}", sprintf(esc_attr__("%s is missing in the configuration array", 'exopite-sof'), $key));
                    }

                endforeach;

                $errors_array = $this->errors->get_error_messages();

                // if the errors are logged, add the admin display hook
                if (! empty($errors_array)) {
                    add_action('admin_notices', array( $this, 'display_admin_error' ));
                }
            } // ! empty( $required_key_array )
        } //check_required_keys()

        /**
         * Set Properties of the class
         */
        protected function set_properties()
        {
            if (isset($this->config['type'])) {
                $this->set_type($this->config['type']);
            }

            // Parse the configuration against default values for Menu
            if ($this->is_menu()) {
                $default_menu_config = $this->get_config_default_menu();
                $this->config        = wp_parse_args($this->config, $default_menu_config);

                // override option type to nullify 'simple' even if added
                $this->config['options'] = ''; // so, even if options is 'simple', we make it non-simple
            }

            // Parse the configuration against default values for Metabox
            if ($this->is_metabox()) {
                $default_metabox_config = $this->get_config_default_metabox();
                $this->config           = wp_parse_args($this->config, $default_metabox_config);

                // This override s done for testing active plugin for qTranX and Wp-Multilang
                if ($this->is_special_multilang_active()) {
                    $this->config['multilang'] = true; // so, even if multilingual was TRUE, we make it FALSE
                    $this->config['options']   = false; // We can only save data for special languages in non-simple options
                } else {
                    // override multilang true so that we dont save meta for language
                    $this->config['multilang'] = false; // so, even if multilingual was TRUE, we make it FALSE
                }
            }

            $this->set_is_multilang_from_config();

            $this->config['is_options_simple'] = ($this->is_options_simple()) ? true : false;

            $this->dirname = wp_normalize_path(dirname(__FILE__));
        }

        public function is_special_multilang_active()
        {
            return Exopite_Simple_Options_Framework_Helper::is_special_multilang_plugin_active();
        }

        public function display_error()
        {
            add_action('admin_notices', array( $this, 'display_admin_error' ));
        }

        public function display_admin_error()
        {
            $class        = 'notice notice-error';
            $message      = '';
            $errors_array = $this->errors->get_error_messages();


            if (! empty($errors_array)) {
                // Get the error messages from the array
                $message .= esc_html(implode(', ', $errors_array));
            } else {
                // if no message is set, throw generic error message
                $message .= esc_attr__('Irks! An un-known error has occurred.', 'exopite-sof');
            }

            printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), esc_html($message));
        }

        /**
         * Register all of the hooks shared by all $type  metabox | menu
         */
        protected function define_shared_hooks()
        {

            // Upload hooks are only required for both,
            Exopite_Simple_Options_Framework_Upload::add_hooks();

            //scripts and styles
            add_action('admin_enqueue_scripts', array( $this, 'load_scripts_styles' ));

            /**
             * Add "code" plugin for TinyMCE
             * @link https://www.tinymce.com/docs/plugins/code/
             */
            add_filter('mce_external_plugins', array( $this, 'mce_external_plugins' ));
        }//define_shared_hooks()

        protected function define_hooks()
        {
            if ($this->is_menu()) {
                $this->define_menu_hooks();
            } elseif ($this->is_metabox()) {
                $this->define_metabox_hooks();
            }
        }

        /**
         * Register all of the hooks related to 'menu' functionality
         *
         * @access   protected
         */
        protected function define_menu_hooks()
        {

            /**
             * Load options only if menu
             * on metabox, page id is not yet available
             */
            $this->db_options = apply_filters('exopite_sof_menu_get_options', get_option($this->unique), $this->unique);


            add_action('admin_init', array( $this, 'register_setting' ));
            add_action('admin_menu', array( $this, 'add_admin_menu' ));
            add_action('wp_ajax_exopite-sof-export-options', array( $this, 'export_options' ));
            add_action('wp_ajax_exopite-sof-import-options', array( $this, 'import_options' ));
            add_action('wp_ajax_exopite-sof-reset-options', array( $this, 'reset_options' ));

            if (isset($this->config['plugin_basename']) && ! empty($this->config['plugin_basename'])) {
                add_filter('plugin_action_links_' . $this->config['plugin_basename'], array(
                    $this,
                    'plugin_action_links'
                ));
            }
        }

        /**
         * Register all of the hooks related to 'metabox' functionality
         *
         * @access   protected
         */
        protected function define_metabox_hooks()
        {

            /**
             * Add metabox and register custom fields
             *
             * @link https://code.tutsplus.com/articles/rock-solid-wordpress-30-themes-using-custom-post-types--net-12093
             */
            add_action('admin_init', array( $this, 'add_meta_box' ));
            add_action('save_post', array( $this, 'save' ));
        }

        /**
         * Sets the $type property
         *
         * @param string $config_type
         */
        protected function set_type($config_type)
        {
            $config_type = sanitize_key($config_type);

            switch ($config_type) {
                case ('menu'):
                    $this->type = 'menu';
                    break;

                case ('metabox'):
                    $this->type = 'metabox';
                    break;

                default:
                    $this->type = '';
            }
        }

        /**
         * @return bool true if its a metabox type
         */
        protected function is_metabox()
        {
            return ($this->type === 'metabox') ? true : false;
        }

        /**
         * @return bool true if its a metabox type
         */
        protected function is_menu()
        {
            return ($this->type === 'menu') ? true : false;
        }

        /**
         * @return bool true if its a 'options'   => 'simple' in $config
         */
        protected function is_options_simple()
        {

            // 'options'    => 'simple' in $config only required for metabox type
            return (! $this->is_menu() && isset($this->config['options']) && $this->config['options'] === 'simple') ? true : false;
        }

        protected function set_is_multilang_from_config()
        {

            /**
             * We need to store the value from $config to a property, so that we can keep track of the actual input
             */

            /**
             * Make sure if metabox and simple options are activated,
             * then multilang is disabled.
             * Enabled multilang is only required for qTranslate-X.
             */
            if ($this->is_metabox() && $this->is_options_simple()) {
                $this->is_multilang = $this->config['is_multilang'] = false;
            }

            $this->is_multilang = $this->config['is_multilang'] = isset($this->config['multilang']) && ($this->config['multilang'] === true) ? true : false;
        }

        /**
         * @return bool true if multilang is set to true
         */
        protected function is_multilang()
        {
            return $this->is_multilang;
        }

        /**
         * @return bool true if its menu options
         */
        protected function is_menu_page_loaded()
        {
            $current_screen = get_current_screen();

            return substr($current_screen->id, - strlen($this->unique)) === $this->unique;
        }

        /**
         * check if the admin screen is of the post_type defined in config
         * @return bool true if its menu options
         */
        protected function is_metabox_enabled_post_type()
        {
            if (! isset($this->config['post_types'])) {
                return false;
            }

            $current_screen = get_current_screen();

            $post_type_loaded = $current_screen->id;

            return (in_array($post_type_loaded, $this->config['post_types'])) ? true : false;
        }


        // for TinyMCE Code Plugin
        public function mce_external_plugins($plugins)
        {
            $url             = $this->get_url($this->dirname);
            $base            = trailingslashit(join('/', array( $url, 'assets' )));
            $plugins['code'] = $base . 'plugin.code.min.js';

            return $plugins;
        }

        public function import_options()
        {
            $retval = 'error';

            $nonce_v = wp_verify_nonce(sanitize_key($_POST['wpnonce']), 'exopite_sof_backup');
            $nonce = sanitize_key($_POST['wpnonce']);

            if (isset($_POST['unique']) && ! empty($_POST['value']) && isset($_POST['wpnonce']) && wp_verify_nonce($_POST['wpnonce'], 'exopite_sof_backup')) {
                $option_key = sanitize_key($_POST['unique']);

                //Using json_decode
                $value = json_decode(stripslashes(wp_kses_post($_POST['value'])), true);

                if (is_array($value)) {
                    update_option($option_key, $value);
                    $retval = 'success';
                }
            }

            die($retval);
        }

        public function export_options()
        {
            if (isset($_GET['export']) && isset($_GET['wpnonce']) && wp_verify_nonce($_GET['wpnonce'], 'exopite_sof_backup')) {
                $option_key = sanitize_key($_GET['export']);

                header('Content-Type: plain/text');
                header('Content-disposition: attachment; filename=exopite-sof-options-' . gmdate('d-m-Y') . '.txt');
                header('Content-Transfer-Encoding: binary');
                header('Pragma: no-cache');
                header('Expires: 0');

                // Using json_encode()
                echo esc_html(json_encode(get_option($option_key)));
            }

            die();
        }

        public function reset_options()
        {
            $retval = 'error';

            if (isset($_POST['unique']) && isset($_POST['wpnonce']) && wp_verify_nonce($_POST['wpnonce'], 'exopite_sof_backup')) {
                delete_option(sanitize_key($_POST['unique']));

                $retval = 'success';
            }

            die($retval);
        }

        /**
         * Load classes
         */
        public function load_classes()
        {
            require_once 'multilang-class.php';
            require_once 'fields-class.php';
            require_once 'upload-class.php';
            require_once 'sanitize-class.php';
        }

        /**
         * Get url from path
         * works only for local urls
         *
         * @param  string $path the path
         *
         * @return string   the generated url
         */
        public function get_url($path = '')
        {
            $url = str_replace(
                wp_normalize_path(untrailingslashit(ABSPATH)),
                site_url(),
                $path
            );

            return $url;
        }

        public function locate_template($type)
        {

            /**
             * Ideas:
             * - May extend this with override.
             */
            // This should be the name of directory in theme/ child theme
            $override_dir_name = 'exopite';
            $fields_dir_name   = 'fields';

            $template = join(DIRECTORY_SEPARATOR, array( $this->dirname, $fields_dir_name, $type . '.php' ));

            return $template;
        }

        /**
         * Register "settings" for plugin option page in plugins list
         *
         * @param array $links plugin links
         *
         * @return array possibly modified $links
         */
        public function plugin_action_links($links)
        {
            /**
             *  Documentation : https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
             */

            // BOOL of settings is given true | false
            if (is_bool($this->config['settings_link'])) {

                // FALSE: If it is false, no need to go further
                if (! $this->config['settings_link']) {
                    return $links;
                }

                // TRUE: if Settings link is not defined, lets create one
                if ($this->config['settings_link']) {
                    $options_base_file_name = sanitize_file_name($this->config['parent']);

                    $options_page_id = $this->unique;

                    $settings_link = "{$options_base_file_name}?page={$options_page_id}";

                    $settings_link_array = array(
                        '<a href="' . admin_url($settings_link) . '">' . __('Settings', '') . '</a>',
                    );

                    return array_merge($settings_link_array, $links);
                }
            } // if ( is_bool( $this->config['settings_link'] ) )

            // URL of settings is given
            if (! is_bool($this->config['settings_link']) && ! is_array($this->config['settings_link'])) {
                $settings_link = esc_url($this->config['settings_link']);

                return array_merge($settings_link, $links);
            }

            // Array of settings_link is given
            if (is_array($this->config['settings_link'])) {
                $settings_links_config_array = $this->config['settings_link'];
                $settings_link_array         = array();

                foreach ($settings_links_config_array as $link) {
                    $link_text         = isset($link['text']) ? sanitize_text_field($link['text']) : __('Settings', '');
                    $link_url_un_clean = isset($link['url']) ? $link['url'] : '#';

                    $link_type = isset($link['type']) ? sanitize_key($link['type']) : 'default';

                    switch ($link_type) {
                        case ('external'):
                            $link_url = esc_url_raw($link_url_un_clean);
                            break;

                        case ('file'):
                            $link_url = admin_url(sanitize_file_name($link_url_un_clean));
                            break;

                        default:

                            if ($this->config['submenu']) {
                                $options_base_file_name = sanitize_file_name($this->config['parent']);

                                $options_base_file_name_extension = pathinfo(parse_url($options_base_file_name)['path'], PATHINFO_EXTENSION);

                                if ($options_base_file_name_extension === 'php') {
                                    $options_base = $options_base_file_name;
                                } else {
                                    $options_base = 'admin.php';
                                }
                                $options_page_id = $this->unique;

                                $settings_link = "{$options_base}?page={$options_page_id}";

                                $link_url = admin_url($settings_link);
                            } else {
                                $settings_link = "?page={$this->unique}";
                                $link_url      = admin_url($settings_link);
                            }

                    }

                    $settings_link_array[] = '<a href="' . $link_url . '">' . $link_text . '</a>';
                }

                return array_merge($settings_link_array, $links);
            } // if ( is_array( $this->config['settings_link'] ) )

            // if nothing is returned so far, return original $links
            return $links;
        }

        /**
         * Get default config for menu
         * @return array $default
         */
        public function get_config_default_menu()
        {
            $default = array(
                //
                'parent'        => 'options-general.php',
                'menu'          => 'plugins.php', // For backward compatibility
                'menu_title'    => __('Plugin Options', 'exopite-options-framework'),
                // Required for submenu
                'submenu'       => false,
                //The name of this page
                'title'         => __('Plugin Options', 'exopite-options-framework'),
                'option_title'  => '',
                // The capability needed to view the page
                'capability'    => 'manage_options',
                'settings_link' => true,
                'tabbed'        => true,
                'position'      => 100,
                'icon'          => '',
                'multilang'     => true,
                'options'       => false
            );

            return apply_filters('exopite_sof_filter_config_default_menu_array', $default);
        }

        /**
         * Get default config for metabox
         * @return array $default
         */
        public function get_config_default_metabox()
        {
            $default = array(

                'title'      => '',
                'post_types' => array( 'post' ),
                'context'    => 'advanced',
                'priority'   => 'default',
                'capability' => 'edit_posts',
                'tabbed'     => true,
                'options'    => false,
                'multilang'  => false

            );

            return apply_filters('exopite_sof_filter_config_default_metabox_array', $default);
        }

        /* Create a meta box for our custom fields */
        public function add_meta_box()
        {
            add_meta_box(
                $this->unique,
                $this->config['title'],
                array( $this, 'display_page' ),
                $this->config['post_types'],
                $this->config['context'],
                $this->config['priority']
            );
        }

        /**
         * Register settings for plugin option page with a callback to save
         */
        public function register_setting()
        {
            register_setting($this->unique, $this->unique, array( $this, 'save' ));
        }

        /**
         * Register plugin option page
         */
        public function add_admin_menu()
        {

            // Is it a main menu or sub_menu
            if (! (bool) $this->config['submenu']) {

//				$default['icon']     = '';
                //				$default['position'] = 100;
                //				$default['menu']     = 'Plugin menu';

                //				$this->config = wp_parse_args( $this->config, $default );

                $menu = add_menu_page(
                    $this->config['title'],
                    $this->config['menu_title'],
                    $this->config['capability'],
                    $this->unique, //slug
                    array( $this, 'display_page' ),
                    $this->config['icon'],
                    $this->config['position']
                );
            } else {

//				$this->config = wp_parse_args( $this->config, $default );

                $submenu = add_submenu_page(
                    $this->config['parent'],
                    $this->config['title'],
                    $this->config['title'],
                    $this->config['capability'],
                    $this->unique, // slug
                    array( $this, 'display_page' ),
                    $this->config['position']
                );
            }
        }

        /**
         * Load scripts and styles
         *
         * @hooked  admin_enqueue_scripts
         *
         * @param string hook name
         */
        public function load_scripts_styles($hook)
        {

            // return if not admin
            if (! is_admin()) {
                return;
            }

            /**
             * Load Scripts for only Menu page
             */
            if ($this->is_menu_page_loaded()):
                // TODO: Shift Scripts from all $type to this section

            endif; //$this->is_menu_page_loaded()


            /**
             * Load Scripts for metabox that have enabled metabox using Exopite framework
             */
            if ($this->is_metabox_enabled_post_type()):
                // TODO: Shift Scripts from all $type to this section

            endif; // $this->is_metabox_enabled_post_type()


            /**
             * Load Scripts shared by all $type
             */
            if ($this->is_menu_page_loaded() || $this->is_metabox_enabled_post_type()) :

                $url  = $this->get_url($this->dirname);
            $base = trailingslashit(join('/', array( $url, 'assets' )));

            if (! wp_style_is('font-awesome') || ! wp_style_is('font-awesome-470') || ! wp_style_is('FontAwesome')) {
                wp_enqueue_style('font-awesome-470', $base . 'font-awesome-4.7.0/font-awesome.min.css', array(), $this->version, 'all');
            }

            /**
             * jquery-ui-core is built into WordPress
             *
             * @link https://developer.wordpress.org/reference/functions/wp_enqueue_script/
             */
            wp_enqueue_style('jquery-ui-core');
            // wp_enqueue_style( 'jquery-ui', $base . 'jquery-ui.css', array(), '1.8.24', 'all' );

            if (is_rtl()) {
                wp_enqueue_style('exopite-simple-options-framework', $base . 'styles-rtl.css', array(), $this->version, 'all');
            } else {
                wp_enqueue_style('exopite-simple-options-framework', $base . 'styles.css', array(), $this->version, 'all');
            }

            // Add jQuery form scripts for menu options AJAX save
            wp_enqueue_script('jquery-form');

            // Add the date picker script
            //wp_enqueue_script( 'jquery-ui-datepicker' );

            // wp_enqueue_script( 'jquery-ui-sortable' );

            $scripts_styles = array(
                    array(
                        'name' => 'jquery-interdependencies',
                        'fn'   => 'jquery.interdependencies.min.js',
                        'dep'  => array(
                            'jquery',
                            //'jquery-ui-datepicker',
                            'wp-color-picker'
                        ),
                    ),
                    array(
                        'name' => 'jquery.sortable',
                        'fn'   => 'html5sortable.min.js',
                        'dep'  => array(
                            'jquery',
                        ),
                    ),
                    array(
                        'name' => 'exopite-simple-options-framework-js',
                        'fn'   => 'scripts.min.js',
                        'dep'  => array(
                            'jquery',
                            //'jquery-ui-datepicker',
                            'wp-color-picker',
                            'jquery-interdependencies'
                        ),
                    ),
                );

            foreach ($scripts_styles as $item) {
                wp_enqueue_script($item['name'], $base . $item['fn'], $item['dep'], $this->version, true);
            }

            /**
             * Enqueue class scripts
             * with this, only enqueue scripts if class/field is used
             */
            $this->enqueue_field_classes();

            endif; //$this->is_menu_page_loaded() || $this->is_metabox_enabled_post_type()
        }

        /**
         * Save options or metabox to meta
         *
         * @return mixed
         *
         * @hooked  'save_post' for metabox
         * @hooked  register_setting() for menu
         *
         */
        public function save($posted_data)
        {

            // Is user has ability to save?
            if (! current_user_can($this->config['capability'])) {
                return null;
            }

            //TODO Verify nonce

            $valid   = array();
            $post_id = null;

            /**
             * @var $section_fields_with_values it will hold the sanitized fields => value
             */
            $section_fields_with_values = array();

            // Specific to Menu Options
            if ($this->is_menu()) {

                /**
                 * Import options should not be checked.
                 */
                if (isset($_POST['action']) && sanitize_key($_POST['action']) === 'exopite-sof-import-options') {
                    return apply_filters('exopite_sof_import_options', $posted_data, $this->unique);
                }

                // Preserve values start with "_".
                $options = get_option($this->unique);
                if (is_array($options)) {
                    foreach ($options as $key => $value) {
                        if (substr($key, 0, 1) === '_') {
                            $valid[ $key ] = $value;
                        }
                    }
                }
            }

            // Specific to Metabox
            if ($this->is_metabox()) {

                // if this is metabox, $posted_data is post_id we are saving
                $post_id = $posted_data;
                if ($this->is_options_simple()) {
                    $posted_data = wp_kses_post($_POST);
                } else {
                    if (isset($_POST[ $this->unique ])) {
                        $posted_data = wp_kses_post($_POST[ $this->unique ]);
                    } else {
                        return false;
                    }
                }

                if ($posted_data === null) {
                    return;
                }

                // Stop WP from clearing custom fields on autosave
                if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
                    return null;
                }

                // Prevent quick edit from clearing custom fields
                if (defined('DOING_AJAX') && DOING_AJAX) {
                    return null;
                }

                // If the post type is not in our post_type array, do nothing
                if (! in_array(get_post_type($post_id), $this->config['post_types'])) {
                    return null;
                }
            }

            /**
             * This must be running on post meta too because of the qTranslate-X.
             * Maybe check first if qTranslate-X is installed and active?
             */
            // Preserve other language setting
            // Get current field value, make sure we are not override other language values

            if ($this->is_multilang()) {
                $other_languages = $this->languages_except_current_language();

                foreach ($other_languages as $language) {
                    if ($this->is_metabox()) {
                        // required for qTranslate-X and WP Multilang
                        $post_meta = get_post_meta($post_id);

                        if (! empty($post_meta)) {
                            $options_array_from_post_meta = maybe_unserialize($post_meta[ $this->unique ][0]);
                            if (is_array($options_array_from_post_meta)) {
                                $section_fields_with_values[ $language ] = $options_array_from_post_meta[ $language ];
                            }
                        }
                    }

                    if ($this->is_menu()) {
                        $section_fields_with_values[ $language ] = $this->db_options[ $language ];
                    }
                }
            }

            $sanitizer = new Exopite_Simple_Options_Framework_Sanitize($this->is_multilang(), $this->lang_current, $this->config, $this->fields);
            if ($this->is_multilang()) {
                $section_fields_with_values[ $this->lang_current ] = $sanitizer->get_sanitized_values($this->fields, $posted_data[$this->lang_current]);
            } else {
                $section_fields_with_values = $sanitizer->get_sanitized_values($this->fields, $posted_data);
            }

            // Add values here, which start with '_', to skip sanitization.
            // This values does not come from the framework.
            foreach ($valid as $key => $value) {
                if (! isset($section_fields_with_values[ $key ])) {
                    $section_fields_with_values[ $key ] = $value;
                }
            }

            /**
             * The idea here is that, this hook run on both.
             */
            do_action('exopite_sof_do_save_options', $section_fields_with_values, $this->unique, $post_id);
            $valid = apply_filters('exopite_sof_save_options', $section_fields_with_values, $this->unique, $post_id);

            if ($this->is_menu()) {

                // These actions and filters only for options menu
                $valid = apply_filters('exopite_sof_save_menu_options', $section_fields_with_values, $this->unique);
                do_action('exopite_sof_do_save_menu_options', $section_fields_with_values, $this->unique);

                return $valid;
            }

            if ($this->is_metabox()) {

                // When we click on "New Post" (CPT), then $post is not available, so we need to check if it is set
                /**
                 * NEED TESTING!
                 * I'm not sure about this, need check. I think post ID IS available because saving custom field
                 * should be after post is inserted.
                 */
                if (isset($post_id)) {
                    $valid = apply_filters('exopite_sof_save_meta_options', $section_fields_with_values, $this->unique, $post_id);
                    do_action('exopite_sof_do_save_meta_options', $section_fields_with_values, $this->unique, $post_id);

                    if ($this->is_options_simple()) {
                        foreach ($valid as $key => $value) {
                            update_post_meta($post_id, $key, $value);
                        }
                    } else {
                        update_post_meta($post_id, $this->unique, $valid);
                    }
                }
            }

            unset($post_id, $valid, $posted_data, $section_fields_with_values, $section_fields_current_lang);
        }

        //DEGUB
        public function write_log($type, $log_line)
        {
            $hash        = '';
            $fn          = plugin_dir_path(__FILE__) . '/' . $type . $hash . '.log';
            $log_in_file = file_put_contents($fn, date('Y-m-d H:i:s') . ' - ' . $log_line . PHP_EOL, FILE_APPEND);
        }

        /**
         * Loop fileds based on field from user
         *
         * @param $callbacks
         */
        public function loop_fields($callbacks)
        {
            if (! is_array($this->fields)) {
                return;
            }

            foreach ($this->fields as $section) {

                // before
                if ($callbacks['before']) {
                    call_user_func(array( $this, $callbacks['before'] ), $section);
                }

                if (! isset($section['fields']) || ! is_array($section['fields'])) {
                    continue;
                }

                foreach ($section['fields'] as $field) {

                    // If has subfields
                    if (($callbacks['main'] == 'include_field_class' || $callbacks['main'] == 'enqueue_field_class') && isset($field['fields'])) {
                        foreach ($field['fields'] as $subfield) {
                            if ($callbacks['main']) {
                                call_user_func(array( $this, $callbacks['main'] ), $subfield);
                            }
                        }
                    }

                    if ($callbacks['main']) {
                        call_user_func(array( $this, $callbacks['main'] ), $field);
                    }

                    // main
                }

                // after
                if ($callbacks['after']) {
                    call_user_func(array( $this, $callbacks['after'] ));
                }
            }
        }

        /**
         * @link https://thisinterestsme.com/php-using-recursion-print-values-multidimensional-array/
         */
        public function recursive_walk($array, &$fields)
        {
            foreach ($array as $key => $value) {
                if (is_array($value)) {
                    if (isset($value['type'])) {
                        if (! in_array($value['type'], $fields) && ! empty($value['type'])) {
                            $temp_array = array(
                                'type' 	=> $value['type'],
                            );

                            if (isset($value['id'])) {
                                $temp_array['id'] = $value['id'];
                            }

                            $fields[ $value['type'] ] = $temp_array;
                        }

                        if ($value['type'] == 'editor' && isset($value['editor'])) {
                            if (! isset($fields[ $value['type'] ]['editor']) || ! is_array($fields[ $value['type'] ]['editor'])) {
                                $fields[ $value['type'] ]['editor'] = array();
                            }

                            if (! in_array($value['editor'], $fields[ $value['type'] ]['editor'])) {
                                $fields[ $value['type'] ]['editor'][] = $value['editor'];
                            }
                        }
                    }

                    $this->recursive_walk($value, $fields);
                }
            }

            return $fields;
        }

        /**
         * Loop and add callback to include and enqueue
         */
        public function include_field_classes()
        {
            if (empty($this->fields)) {
                return;
            }

            $fields = array();
            $fields = $this->recursive_walk($this->fields, $fields);

            foreach ($fields as $field => $args) {
                $this->include_field_class($field);
            }
        }

        /**
         * Loop and add callback to include and enqueue
         */
        public function enqueue_field_classes()
        {
            if (empty($this->fields)) {
                return;
            }

            $fields = array();
            $fields = $this->recursive_walk($this->fields, $fields);

            foreach ($fields as $field => $args) {
                $this->enqueue_field_class($args);
            }
        }

        /**
         * Include field classes
         * and enqueue they scripts
         */
        public function include_field_class($field)
        {
            if (is_array($field) && isset($field['type'])) {
                $field = $field['type'];
            }

            $class = 'Exopite_Simple_Options_Framework_Field_' . $field;

            if (! class_exists($class)) {
                $field_filename = $this->locate_template($field);

                if (file_exists($field_filename)) {
                    require_once join(DIRECTORY_SEPARATOR, array(
                        $this->dirname,
                        'fields',
                        $field . '.php'
                    ));
                }
            }
        }

        /**
         * Include field classes
         * and enqueue they scripts
         */
        public function enqueue_field_class($field)
        {
            $class = 'Exopite_Simple_Options_Framework_Field_' . $field['type'];

            if (class_exists($class) && method_exists($class, 'enqueue')) {
                $args = array(
                    'plugin_sof_url'  => plugin_dir_url(__FILE__),
                    'plugin_sof_path' => plugin_dir_path(__FILE__),
                    'field'           => $field,
                );

                $class::enqueue($args);
            }
        }

        /**
         * Generate files
         *
         * @param  array $field field args
         *
         * @echo string   generated HTML for the field
         */
        public function add_field($field, $value = null)
        {
            do_action('exopite_sof_before_generate_field', $field, $this->config);
            do_action('exopite_sof_before_add_field', $field, $this->config);

            $output     = '';
            $class      = 'Exopite_Simple_Options_Framework_Field_' . $field['type'];
            $depend     = '';
            $wrap_class = (! empty($field['wrap_class'])) ? ' ' . $field['wrap_class'] : '';
            $hidden     = ($field['type'] == 'hidden') ? ' hidden' : '';
            $sub        = (! empty($field['sub'])) ? 'sub-' : '';

            /**
             * Add editor name to classes for styling purposes.
             */
            if ($field['type'] == 'editor') {
                if (! isset($field['editor']) || $field['editor'] == 'tinymce') {
                    $wrap_class .= ' exopite-sof-tinymce-editor';
                } elseif (isset($field['editor']) && $field['editor'] == 'trumbowyg') {
                    $wrap_class .= ' exopite-sof-trumbowyg-editor';
                }
            }

            if (! empty($field['dependency'])) {
                $hidden = ' hidden';
                $depend .= ' data-' . $sub . 'controller="' . $field['dependency'][0] . '"';
                $depend .= ' data-' . $sub . 'condition="' . $field['dependency'][1] . '"';
                $depend .= ' data-' . $sub . 'value="' . $field['dependency'][2] . '"';
            }

            if (! isset($field['pseudo']) || ! $field['pseudo']) {
                $output .= '<div class="exopite-sof-field exopite-sof-field-' . $field['type'] . $wrap_class . $hidden . '"' . $depend . '>';
            }

            if (isset($field['title'])) {
                $output .= '<h4 class="exopite-sof-title">';

                $output .= $field['title'];

                if (! empty($field['description'])) {
                    $output .= '<p class="exopite-sof-description">' . $field['description'] . '</p>';
                }

                $output .= '</h4>'; // exopite-sof-title
                $output .= '<div class="exopite-sof-fieldset">';
            }

            if (class_exists($class)) {
                if (empty($value) && $value !== 0 && $value !== '0') {

                    // NEW

                    if ((isset($field['sub']) && ! empty($field['sub'])) || $this->is_menu()) {
                        $value = $this->get_value($this->db_options, $field);
                    }

                    if ($this->is_metabox()) {
                        if ($this->is_options_simple()) {
                            $meta = get_post_meta(get_the_ID());

                            /**
                             * get_post_meta return empty on non existing meta
                             * we need to check if meta key is exist to return null,
                             * because default value can only display if value is null
                             * on empty vlaue - may saved by the user - should display empty.
                             */
                            if (isset($field['id']) && isset($meta[ $field['id'] ])) {
                                $value = array_shift($meta[ $field['id'] ]);
                            } else {
                                $value = null;
                            }
                        } else {
                            $meta  = get_post_meta(get_the_ID(), $this->unique, true);
                            $value = $this->get_value($meta, $field);
                        }
                    }
                }

                ob_start();
                $element = new $class($field, $value, $this->unique, $this->config, $this->multilang);
                $element->output();
                $output .= ob_get_clean();
            } else {
                $output .= '<div class="danger unknown">';
                $output .= esc_attr__('ERROR:', 'exopite-simple-options') . ' ';
                $output .= esc_attr__('This field class is not available!', 'exopite-simple-options');
                $output .= ' <i>(' . $field['type'] . ')</i>';
                $output .= '</div>';
            }

            if (isset($field['title'])) {
                $output .= '</div>';
            } // exopite-sof-fieldset

            if (! isset($field['pseudo']) || ! $field['pseudo']) {
                $output .= '<div class="clearfix"></div>';
                $output .= '</div>'; // exopite-sof-field
            }

            do_action('exopite_sof_after_generate_field', $field, $this->config);

            echo apply_filters('exopite_sof_add_field', $output, $field, $this->config);

            do_action('exopite_sof_after_add_field', $field, $this->config);
        }

        public function get_value($options, $field)
        {

            /**
             * IF MULTILANG
             * - get option[current-lang][id]
             * - but not the get option[default-lang][id] <- because if no options in current lang, should display default
             * NOT MULTILANG OR MULTILANG NOT EXIST THEN TRY
             * - get option[id]
             */

            $value = null;

            if (! isset($field['id'])) {
                return $value;
            }

            if (isset($options[ $this->lang_current ][ $field['id'] ])) {
                $value = $options[ $this->lang_current ][ $field['id'] ];
            } elseif ($value === null && isset($options[ $field['id'] ])) {
                $value = $options[ $field['id'] ];
            }

            /**
             * Use this filter, like:
             *
             * add_filter( 'exopite_sof_field_value', 'prefix_exopite_sof_field_value', 10, 5 );
             * public function prefix_exopite_sof_field_value( $value, $unique, $options, $field ) {
             *
             *	   if ( $unique == $this->plugin_name && $field['id'] == 'your-field-id' ) {
             *		   // do the magic ;)
             *	   }
             *
             *	   return $value;
             * }
             */
            return apply_filters('exopite_sof_field_value', $value, $this->unique, $options, $field);
        }

        /**
         * Display form and header for options page
         * for metabox no need to do this.
         */
        public function display_options_page_header()
        {
            echo '<form method="post" action="options.php" enctype="multipart/form-data" name="' . esc_attr($this->unique) . '" class="exopite-sof-form-js ' . esc_attr($this->unique) . '-form" data-save="' . esc_attr__('Saving...', 'exopite-sof') . '" data-saved="' . esc_attr__('Saved Successfully.', 'exopite-sof') . '">';

            settings_fields($this->unique);
            do_settings_sections($this->unique);
            $current_language_title = '';
            if ($this->is_multilang()) {
                $current_language_title = apply_filters('exopite_sof_title_language_notice', $this->lang_current);
                $current_language_title = ' [' . $current_language_title . ']';
            }

            $option_title = (! empty($this->config['option_title'])) ? $this->config['option_title'] : $this->config['title'];

            echo '<header class="exopite-sof-header exopite-sof-header-js">';
            echo '<h1>' . esc_html($option_title) . esc_html($current_language_title) . '</h1>';

            echo '<span class="exopite-sof-search-wrapper"><input type="text" class="exopite-sof-search"></span>';

            echo '<fieldset><span class="exopite-sof-ajax-message"></span>';
            submit_button(esc_attr__('Save Settings', 'exopite-sof'), 'primary ' . 'exopite-sof-submit-button-js', esc_attr($this->unique) . '-save', false, array());
            echo '</fieldset>';
            echo '</header>';
        }

        /**
         * Display form and footer for options page
         * for metabox no need to do this.
         */
        public function display_options_page_footer()
        {
            echo '</form>';
        }

        /**
         * Display section header, only first is visible on start
         */
        public function display_options_section_header($section)
        {
            $visibility = ' hide';
            if ($section === reset($this->fields)) {
                $visibility = '';
            }

            $section_name = (isset($section['name'])) ? $section['name'] : '';
            $section_icon = (isset($section['icon'])) ? $section['icon'] : '';

            echo '<div class="exopite-sof-section exopite-sof-section-' . esc_attr($section_name) . esc_attr($visibility) . '">';

            if (isset($section['title']) && ! empty($section['title'])) {
                $icon_before = '';
                if (strpos($section_icon, 'dashicon') !== false) {
                    $icon_before = 'dashicons-before ';
                } elseif (strpos($section_icon, 'fa') !== false) {
                    $icon_before = 'fa-before ';
                }

                echo '<h2 class="exopite-sof-section-header" data-section="' . esc_attr($section_name) . '"><span class="' . esc_attr($icon_before) . esc_attr($section_icon) . '"></span>' . esc_html($section['title']) . '</h2>';
            }
        }

        /**
         * Display section footer
         */
        public function display_options_section_footer()
        {
            echo '</div>'; // exopite-sof-section
        }

        public function get_fields()
        {
            if (! $this->elements) {
                return;
            }

            $fields = array();

            foreach ($this->elements as $key => $value) {
                if (isset($value['sections'])) {
                    foreach ($value['sections'] as $section) {
                        if (isset($section['fields'])) {
                            $fields[] = $section;
                        }
                    }
                } else {
                    if (isset($value['fields'])) {
                        $fields[] = $value;
                    }
                }
            }

            $this->fields = $fields;
        }

        public function get_menu_item_icons($section)
        {
            if (strpos($section['icon'], 'dashicon') !== false) {
                echo '<span class="exopite-sof-nav-icon dashicons-before ' . esc_attr($section['icon']) . '"></span>';
            } elseif (strpos($section['icon'], 'fa') !== false) {
                echo '<span class="exopite-sof-nav-icon fa-before ' . esc_attr($section['icon']) . '" aria-hidden="true"></span>';
            }
        }

        public function get_menu_item($section, $active = '', $force_hidden = null)
        {

            // $active = '';
            // if ( $section === reset( $this->fields ) ) {
            // 	$active = ' active';
            // }

            $depend = '';
            $hidden = ($force_hidden) ? ' hidden' : '';

            // Dependency for tabs too
            if (! empty($section['dependency'])) {
                $hidden = ' hidden';
                $depend = ' data-controller="' . $section['dependency'][0] . '"';
                $depend .= ' data-condition="' . $section['dependency'][1] . '"';
                $depend .= ' data-value="' . $section['dependency'][2] . '"';
            }

            $section_name = (isset($section['name'])) ? $section['name'] : '';

            echo '<li  class="exopite-sof-nav-list-item' . esc_attr($active) . esc_attr($hidden) . '"' . esc_attr($depend) . ' data-section="' . esc_attr($section_name) . '">';
            echo '<span class="exopite-sof-nav-list-item-title">';
            $this->get_menu_item_icons($section);
            echo esc_html($section['title']);
            echo '</span>';
            echo '</li>';
        }

        public function get_menu()
        {

            // $fields = array();

            echo '<div class="exopite-sof-nav"><ul class="exopite-sof-nav-list">';

            foreach ($this->elements as $key => $value) {
                $active = '';
                reset($this->elements);
                if ($key === key($this->elements)) {
                    $active = ' active';
                }


                if (isset($value['sections'])) {
                    echo '<li  class="exopite-sof-nav-list-parent-item' . esc_attr($active) . '">';
                    echo '<span class="exopite-sof-nav-list-item-title">';
                    $this->get_menu_item_icons($value);
                    echo esc_html($value['title']);
                    echo '</span>';
                    echo '<ul style="display:none;">';

                    foreach ($value['sections'] as $section) {
                        if (isset($section['fields'])) {
                            $this->get_menu_item($section, $active, false);
                        }
                    }

                    echo '</ul>';
                    echo '</li>';
                } else {
                    if (isset($value['fields'])) {
                        $this->get_menu_item($value, $active, false);
                    }
                }
            }

            echo '</ul></div>';
        }

        public function display_debug_infos()
        {
            return '';
        }

        /**
         * Display form form either options page or metabox
         */
        public function display_page()
        {
            do_action('exopite_simple_options_framework_form_' . $this->config['type'] . '_before');

            settings_errors();

            echo '<div class="exopite-sof-wrapper exopite-sof-wrapper-' . esc_attr($this->config['type']) . ' ' . esc_attr($this->unique) . '-options">';

            switch ($this->config['type']) {
                case 'menu':
                    add_action('exopite_sof_display_page_header', array(
                        $this,
                        'display_options_page_header'
                    ), 10, 1);
                    do_action('exopite_sof_display_page_header', $this->config);
                    break;

                case 'metabox':
                    /**
                     * Get options
                     * Can not get options in __consturct, because there, the_ID is not yet available.
                     *
                     * Only if options is not simple, if yes, then value determinated when field are displayed.
                     * Simple options is stored az induvidual meta key, value pair, otherwise it is stored in an array.
                     *
                     * I implemented this option because it is possible to search in serialized (array) post meta:
                     * @link https://wordpress.stackexchange.com/questions/16709/meta-query-with-meta-values-as-serialize-arrays
                     * @link https://stackoverflow.com/questions/15056407/wordpress-search-serialized-meta-data-with-custom-query
                     * @link https://www.simonbattersby.com/blog/2013/03/querying-wordpress-serialized-custom-post-data/
                     *
                     * but there is no way to sort them with wp_query or SQL.
                     * @link https://wordpress.stackexchange.com/questions/87265/order-by-meta-value-serialized-array/87268#87268
                     * "Not in any reliable way. You can certainly ORDER BY that value but the sorting will use the whole serialized string,
                     * which will give * you technically accurate results but not the results you want. You can't extract part of the string
                     * for sorting within the query itself. Even if you wrote raw SQL, which would give you access to database functions like
                     * SUBSTRING, I can't think of a dependable way to do it. You'd need a MySQL function that would unserialize the value--
                     * you'd have to write it yourself.
                     * Basically, if you need to sort on a meta_value you can't store it serialized. Sorry."
                     *
                     * It is possible to get all required posts and store them in an array and then sort them as an array,
                     * but what if you want multiple keys/value pair to be sorted?
                     *
                     * UPDATE
                     * it is maybe possible:
                     * @link http://www.russellengland.com/2012/07/how-to-unserialize-data-using-mysql.html
                     * but it is waaay more complicated and less documented as meta query sort and search.
                     * It should be not an excuse to use it, but it is not as reliable as it should be.
                     *
                     * @link https://wpquestions.com/Order_by_meta_key_where_value_is_serialized/7908
                     * "...meta info serialized is not a good idea. But you really are going to lose the ability to query your
                     * data in any efficient manner when serializing entries into the WP database.
                     *
                     * The overall performance saving and gain you think you are achieving by serialization is not going to be noticeable to
                     * any major extent. You might obtain a slightly smaller database size but the cost of SQL transactions is going to be
                     * heavy if you ever query those fields and try to compare them in any useful, meaningful manner.
                     *
                     * Instead, save serialization for data that you do not intend to query in that nature, but instead would only access in
                     * a passive fashion by the direct WP API call get_post_meta() - from that function you can unpack a serialized entry
                     * to access its array properties too."
                     */
                    if ($this->config['type'] == 'metabox' && (! isset($this->config['options']) || $this->config['options'] != 'simple')) {
                        $meta_options     = get_post_meta(get_the_ID(), $this->unique, true);
                        $this->db_options = apply_filters('exopite-simple-options-framework-meta-get-options', $meta_options, $this->unique, get_the_ID());
                    }

                    if ($this->debug) {
                        $meta_options = get_post_meta(get_the_ID());
                        echo '<pre>POST_META<br>';
                        echo '</pre>';
                    }

                    break;
            }

            if (isset($this->config['multilang']) && is_array($this->config['multilang'])) {
                switch ($this->config['type']) {
                    case 'menu':
                        $current_language = $this->config['multilang']['current'];
                        break;

                    case 'metabox':
                        // Pages/Posts can not have "All languages" displayed, then default will be displayed
                        $current_language = ($this->config['multilang']['current'] == 'all') ? $this->config['multilang']['default'] : $this->config['multilang']['current'];
                        break;
                }

                /**
                 * ToDos:
                 * - check this value, shouldn't be hard coded.
                 */
                $current_language = 'en';
                /**
                 * Current language need to pass to save function, if "All languages" seleted, WPML report default
                 * on save hook.
                 */
                echo '<input type="hidden" name="_language" value="' . esc_attr($current_language) . '">';
            }

            $sections = count($this->fields);

            $tabbed = ($sections > 1 && $this->config['tabbed']) ? ' exopite-sof-content-nav exopite-sof-content-js' : '';

            if ($this->debug) {
                $this->display_debug_infos();
            }

            /**
             * Generate fields
             */
            // Generate tab navigation
            echo '<div class="exopite-sof-content' . esc_attr($tabbed) . '">';

            if (! empty($tabbed)) {
                $this->get_menu();
            }

            echo '<div class="exopite-sof-sections">';

            // Generate fields
            $callbacks = array(
                'before' => 'display_options_section_header',
                'main'   => 'add_field',
                'after'  => 'display_options_section_footer'
            );

            $this->loop_fields($callbacks);

            echo '</div>'; // sections
            echo '<footer class="exopite-sof-footer-js exopite-sof-footer">';

            echo '<fieldset><span class="exopite-sof-ajax-message"></span>';
            submit_button(esc_attr__('Save Settings', 'exopite-sof'), 'primary ' . 'exopite-sof-submit-button-js wpsh-form-footer', '', false, array());
            echo '</fieldset>';

            echo '</footer>';
            
            echo '</div>'; // content

            if ($this->config['type'] == 'menu') {
                add_action('exopite_sof_display_page_footer', array(
                    $this,
                    'display_options_page_footer'
                ), 10, 1);
                do_action('exopite_sof_display_page_footer', $this->config);
            }

            echo '</div>';

            do_action('exopite_sof_form_' . $this->config['type'] . '_after');
        } // display_page()
    } //class

endif;

/**
 * Use:
 *  $options = ( function_exists( 'get_exopite_sof_option' ) ) ? get_exopite_sof_option( $this->plugin_name ) : get_option( $this->plugin_name );

 *  // OR
 *
 *  if ( function_exists( 'get_exopite_sof_option' ) ) {
 *      $options = get_exopite_sof_option( $this->plugin_name );
 *  } else {
 *      $options = get_option( $this->plugin_name );
 *  }
 */
if (! function_exists('get_exopite_sof_option')) {
    function get_exopite_sof_option($option_slug, $default = false)
    {

        /**
         * save in cache?
         * @link https://codex.wordpress.org/Transients_API
         */

        if (! class_exists('Exopite_Simple_Options_Framework_Helper')) {
            require_once 'multilang-class.php';
        }

        $language_defaults = Exopite_Simple_Options_Framework_Helper::get_language_defaults();

        $options = apply_filters('get_exopite_sof_option', get_option($option_slug, $default), $option_slug, $default);

        if (isset($options[$language_defaults['current']])) {
            return $options[$language_defaults['current']];
        } elseif (isset($options[$language_defaults['default']])) {
            return $options[$language_defaults['default']];
        } else {
            return $options;
        }
    }
}