Generic::get_instance()->get_options(), 'Flickr.php' => Flickr::get_instance()->get_options(), // 'Google.php' => Google::get_instance()->get_options(), 'SmugMug.php' => SmugMug::get_instance()->get_options(), 'Zenfolio.php' => Zenfolio::get_instance()->get_options(), // 'Instagram.php' => Instagram::get_instance()->get_options(), 'DeviantArt.php' => DeviantArt::get_instance()->get_options(), 'Lightbox.php' => Lightbox::get_instance()->get_options(), ]; $tab_name_array = [ 'Generic.php' => 'Generic Options', 'Flickr.php' => 'Flickr Options', // 'Google.php' => 'Google Photos Options', 'SmugMug.php' => 'SmugMug Options', 'Zenfolio.php' => 'Zenfolio Options', // 'Instagram.php' => 'Instagram Options', 'DeviantArt.php' => 'DeviantArt Options', 'Lightbox.php' => 'Lightbox Options', ]; $this->core = $core; $this->file = $file; $this->tab = 'Generic.php'; if (isset($_REQUEST['tab']) && array_key_exists($_REQUEST['tab'], $tab_name_array)) { // phpcs:ignore WordPress.Security.NonceVerification $this->tab = sanitize_text_field($_REQUEST['tab']); // phpcs:ignore WordPress.Security.NonceVerification } $this->tab_options = $options_page_array[$this->tab]; $this->tab_name = $tab_name_array[$this->tab]; $this->reverse_options = []; $this->nested_options = []; $this->displayed_sections = 0; $this->option_structure = $this->get_option_structure(); $this->option_defaults = []; $this->allowed_values = []; $all_options = get_option('photonic_options'); if (!isset($all_options)) { $this->hidden_options = []; } else { $this->hidden_options = $all_options; } foreach ($this->tab_options as $option) { if (isset($option['id'])) { if (isset($this->hidden_options[$option['id']])) { unset($this->hidden_options[$option['id']]); } } } $defaults = Defaults::get_options(); foreach ($photonic_setup_options as $option) { if (isset($option['category']) && !isset($this->nested_options[$option['category']])) { $this->nested_options[$option['category']] = []; } if (isset($option['id'])) { $this->reverse_options[$option['id']] = $option['type']; $this->option_defaults[$option['id']] = $defaults[$option['id']]; $option['std'] = $defaults[$option['id']]; if (isset($option['options'])) { if ('radio-group' !== $option['type']) { $this->allowed_values[$option['id']] = $option['options']; } else { $this->allowed_values[$option['id']] = []; foreach ($option['options'] as $radio_group) { $this->allowed_values[$option['id']] = array_merge($this->allowed_values[$option['id']], $radio_group['options']); } } } if (isset($option['grouping'])) { if (!isset($this->nested_options[$option['grouping']])) { $this->nested_options[$option['grouping']] = []; } $this->nested_options[$option['grouping']][] = $option['id']; } } } } public function render_content() { $saved_options = get_option('photonic_options'); if (!empty($saved_options) && current_user_can('edit_theme_options')) { $generated_css = $this->core->generate_css(false); update_option('photonic_css', $generated_css); if (!empty($saved_options['css_in_file'])) { $this->save_css_to_file($generated_css); } } ?>
get_option_structure(); $group = substr($this->tab, 0, stripos($this->tab, '.')); echo "
"; echo ""; do_settings_sections($this->file); $last_option = end($option_structure); $last_slug = key($option_structure); $this->show_buttons($last_slug, $last_option); echo "\n"; echo "
\n"; echo "
\n"; ?> \n"; echo "\n"; echo "\n"; echo "\n"; echo "\n"; } } public function init() { foreach ($this->option_structure as $slug => $option) { if (!in_array($slug, Defaults::get_options_pages(), true)) { wp_die(esc_html("Invalid option section: $slug")); } // Since we are not including this file on all admin pages due to the size and associated load, register_setting cannot be done here. // It is instead done via Admin_Menu, which is loaded for all admin pages. // register_setting('photonic_options-'.$slug, 'photonic_options', [&$this, 'validate_options']); add_settings_section($slug, "", [&$this, "create_settings_section"], $this->file); $this->add_settings_fields($this->file); } } public function validate_options($options): array { foreach ($options as $option => $option_value) { if (isset($this->reverse_options[$option])) { // Sanitize options switch ($this->reverse_options[$option]) { // For all text type of options make sure that the eventual text is properly escaped. case "text": case "background": case "border": $options[$option] = sanitize_text_field($option_value); break; case "textarea": $options[$option] = sanitize_textarea_field($option_value); break; case "select": case "radio": case "radio-group": if (isset($this->allowed_values[$option])) { if (!array_key_exists($option_value, $this->allowed_values[$option])) { $options[$option] = $this->option_defaults[$option]; } } break; case "multi-select": $selections = explode(',', $option_value); $final_selections = []; foreach ($selections as $selection) { if (array_key_exists($selection, $this->allowed_values[$option])) { $final_selections[] = $selection; } } $options[$option] = implode(',', $final_selections); break; case "checkbox": if (!in_array($option_value, ['on', 'off', 'true', 'false'], true) && isset($this->option_defaults[$option])) { $options[$option] = $this->option_defaults[$option]; } break; } } } /* * The Settings API does an update_option($option, $value), overwriting the $photonic_options array with the values on THIS page * This is problematic because all options are stored in a single array, but are displayed on different options pages. * Hence the overwrite kills the options from the other pages. * So this is a workaround to include the options from other pages as hidden fields on this page, so that the array gets properly updated. * The alternative would be to separate options for each page, but that would cause a migration headache for current users. */ $current_options = array_keys($options); if (isset($this->hidden_options) && is_array($this->hidden_options)) { foreach ($this->hidden_options as $hidden_option => $hidden_value) { if (strlen($hidden_option) >= 7 && ('submit-' === substr($hidden_option, 0, 7) || 'reset-' === substr($hidden_option, 0, 6)) || in_array($hidden_option, $current_options, true)) { continue; } $options[$hidden_option] = esc_attr($hidden_value); } } foreach ($this->nested_options as $section => $children) { if (isset($options['submit-' . $section])) { $options['last-set-section'] = $section; if ('Save page' === substr($options['submit-' . $section], 0, 9) || 'Reset page' === substr($options['submit-' . $section], 0, 10)) { global $photonic_options; foreach ($this->nested_options as $inner_section => $inner_children) { if ($section !== $inner_section) { foreach ($inner_children as $inner_child) { if (isset($photonic_options[$inner_child]) && !in_array($inner_child, $current_options, true)) { $options[$inner_child] = $photonic_options[$inner_child]; } } } } if ('Reset page' === substr($options['submit-' . $section], 0, 10)) { unset($options['submit-' . $section]); // This is a reset for an individual section. So we will unset the child fields. foreach ($children as $child) { unset($options[$child]); } } unset($options['submit-' . $section]); } elseif ('Delete' === substr($options['submit-' . $section], 0, 6)) { return []; } break; } } return $options; } public function get_option_structure() { if (isset($this->option_structure)) { return $this->option_structure; } $options = $this->tab_options; $option_structure = []; foreach ($options as $value) { switch ($value['type']) { case "title": case "section": $option_structure[$value['category']] = []; $option_structure[$value['category']]['slug'] = $value['category']; $option_structure[$value['category']]['name'] = $value['name']; $option_structure[$value['category']]['children'] = []; if (isset($value['help'])) { $option_structure[$value['category']]['help'] = $value['help']; } if (isset($value['buttons'])) { $option_structure[$value['category']]['buttons'] = $value['buttons']; } if (isset($value['preface'])) { $option_structure[$value['category']]['preface'] = $value['preface']; } break; default: if (isset($value['id'])) { $option_structure[$value['grouping']]['children'][$value['id']] = $value['name']; } } } return $option_structure; } private function add_settings_fields($page) { $ctr = 0; foreach ($this->tab_options as $value) { $ctr++; switch ($value['type']) { case "blurb": add_settings_field($value['grouping'] . '-' . $ctr, $value['name'], [&$this, "create_section_for_blurb"], $page, $value['grouping'], $value); break; case "text": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_text"], $page, $value['grouping'], $value); break; case "textarea": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_textarea"], $page, $value['grouping'], $value); break; case "select": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_select"], $page, $value['grouping'], $value); break; case "multi-select": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_multi_select"], $page, $value['grouping'], $value); break; case "radio": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_radio"], $page, $value['grouping'], $value); break; case "radio-group": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_radio_group"], $page, $value['grouping'], $value); break; case "checkbox": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_checkbox"], $page, $value['grouping'], $value); break; case "background": add_settings_field($value['id'], $value['name'], [&$this, "create_section_for_background"], $page, $value['grouping'], $value); break; } } } public function create_section_for_radio($value) { $defaults = Defaults::get_options(); $this->create_opening_tag($value); foreach ($value['options'] as $option_value => $option_text) { $this->create_individual_radio_item($value, $option_value, $option_text, $defaults); } $this->create_closing_tag(); } public function create_section_for_radio_group($value) { $defaults = Defaults::get_options(); $this->create_opening_tag($value); foreach ($value['options'] as $radio_group) { if (!empty($radio_group['header'])) { echo "

" . wp_kses_post($radio_group['header']) . "

\n"; } if (!empty($radio_group['description'])) { echo wp_kses_post($radio_group['description']) . "\n"; } foreach ($radio_group['options'] as $option_value => $option_text) { $this->create_individual_radio_item($value, $option_value, $option_text, $defaults); } } $this->create_closing_tag(); } private function create_individual_radio_item($value, $option_value, $option_text, $defaults) { global $photonic_options; $replacements = Defaults::get_migrated_options(); if (isset($photonic_options[$value['id']])) { $check_value = (isset($replacements[$value['id']]) && isset($replacements[$value['id']][$photonic_options[$value['id']]])) ? $replacements[$value['id']][$photonic_options[$value['id']]] : $photonic_options[$value['id']]; $checked = checked($check_value, $option_value, false); } else { $checked = checked($defaults[$value['id']], $option_value, false); } echo '
\n"; } public function create_section_for_text($value) { global $photonic_options; $defaults = Defaults::get_options(); $this->create_opening_tag($value); if (!isset($photonic_options[$value['id']])) { $text = $defaults[$value['id']]; } else { $text = $photonic_options[$value['id']]; $text = stripslashes($text); } echo '' . "\n"; if (isset($value['hint'])) { echo " « " . wp_kses_post($value['hint']) . "
\n"; } $this->create_closing_tag(); } public function create_section_for_textarea($value) { global $photonic_options; $defaults = Defaults::get_options(); $this->create_opening_tag($value); echo ''; if (isset($value['hint'])) { echo " « " . wp_kses_post($value['hint']) . "
\n"; } $this->create_closing_tag(); } public function create_section_for_select($value) { global $photonic_options; $defaults = Defaults::get_options(); $replacements = Defaults::get_migrated_options(); $this->create_opening_tag($value); echo '\n"; $this->create_closing_tag(); } public function create_section_for_multi_select($value) { global $photonic_options; $defaults = Defaults::get_options(); $this->create_opening_tag($value); echo '
' . "\n"; echo '\n"; if (isset($photonic_options[$value['id']])) { $set_value = $photonic_options[$value['id']]; } elseif (isset($defaults[$value['id']])) { $set_value = $defaults[$value['id']]; } else { $set_value = ""; } echo '' . "\n"; echo "
\n"; $this->create_closing_tag(); } public function create_settings_section($section) { $option_structure = $this->option_structure; if (0 !== $this->displayed_sections) { $this->show_buttons($this->previous_displayed_section, $option_structure[$this->previous_displayed_section]); echo "\n"; echo "\n"; } echo "
\n"; echo "
\n"; if (!empty($option_structure[$section['id']]['preface'])) { echo wp_kses_post($option_structure[$section['id']]['preface']); } echo '

' . wp_kses_post($option_structure[$section['id']]['name']) . "

\n"; /* * We store all options in one array, but display them across multiple pages. Hence we need the following hack. * We are registering the same setting across multiple pages, hence we need to pass the "page" parameter to options.php. * Otherwise options.php returns an error saying "Options page not found" */ echo "\n"; // phpcs:ignore WordPress.Security.NonceVerification if (!isset($_REQUEST['tab'])) { // phpcs:ignore WordPress.Security.NonceVerification $tab = 'Generic.php'; } else { $tab = sanitize_text_field($_REQUEST['tab']); // phpcs:ignore WordPress.Security.NonceVerification } echo "\n"; settings_fields("photonic_options-{$section['id']}"); $this->displayed_sections++; $this->previous_displayed_section = $section['id']; } public function create_section_for_blurb($value) { $this->create_opening_tag($value); $this->create_closing_tag(); } /** * Renders an option whose type is "checkbox". Invoked by add_settings_field. * * @param $value * @return void */ public function create_section_for_checkbox($value) { global $photonic_options; $checked = ''; if (isset($photonic_options[$value['id']])) { $checked = checked(esc_attr($photonic_options[$value['id']]), 'on', false); } $this->create_opening_tag($value); echo '\n"; $this->create_closing_tag(); } /** * Renders an option whose type is "background". Invoked by add_settings_field. * * @param $value * @return void */ public function create_section_for_background($value) { global $photonic_options; $defaults = Defaults::get_options(); $this->create_opening_tag($value); $original = $defaults[$value['id']]; if (!isset($photonic_options[$value['id']])) { $default = $defaults[$value['id']]; $default_txt = ""; foreach ($defaults[$value['id']] as $opt => $opt_val) { $default_txt .= $opt . "=" . $opt_val . ";"; } } else { $default_txt = $photonic_options[$value['id']]; $default = $default_txt; $vals = explode(";", $default); $default = []; foreach ($vals as $val) { $pair = explode("=", $val); if (isset($pair[0]) && isset($pair[1])) { $default[$pair[0]] = $pair[1]; } elseif (isset($pair[0]) && !isset($pair[1])) { $default[$pair[0]] = ""; } } } $repeats = [ "repeat" => "Repeat horizontally and vertically", "repeat-x" => "Repeat horizontally only", "repeat-y" => "Repeat vertically only", "no-repeat" => "Do not repeat" ]; $positions = [ "top left" => "Top left", "top center" => "Top center", "top right" => "Top right", "center left" => "Center left", "center center" => "Middle of the page", "center right" => "Center right", "bottom left" => "Bottom left", "bottom center" => "Bottom center", "bottom right" => "Bottom right" ]; foreach ($value['options'] as $option_value => $option_text) { if (isset($photonic_options[$value['id']])) { $checked = checked($photonic_options[$value['id']], $option_value, false); } else { $checked = checked($defaults[$value['id']], $option_value, false); } echo '
" . wp_kses_post($option_text) . "
\n"; } ?>
Background Color:


Default:
Image URL:
display_upload_field($default['image'], $value['id'] . "-bgimg", $value['id'] . "-bgimg"); ?>

create_closing_tag(); } /** * Creates the opening markup for each option. * * @param $value * @return void */ public function create_opening_tag($value) { echo "
\n"; if (isset($value['desc']) && 'checkbox' !== $value['type']) { echo wp_kses_post($value['desc']) . "
"; } if (isset($value['note'])) { echo "" . wp_kses_post($value['note']) . "
"; } } /** * Creates the closing markup for each option. * * @return void */ public function create_closing_tag() { echo "
\n"; } /** * This method displays an upload field and button. This has been separated from the create_section_for_upload method, * because this is used by the create_section_for_background as well. * * @param $upload * @param $id * @param $name * @param null $hint * @return void */ private function display_upload_field($upload, $id, $name, $hint = null) { echo '' . "\n"; if (null !== $hint) { echo " « " . wp_kses_post($hint) . "
\n"; } } /** * Save generated options to a file. This uses the WP_Filesystem to validate the credentials of the user attempting to save options. * * @param $custom_css * @return bool */ public function save_css_to_file($custom_css): bool { if (!isset($_GET['settings-updated'])) { // phpcs:ignore WordPress.Security.NonceVerification return false; } $url = wp_nonce_url('admin.php?page=photonic-options-manager'); $creds = request_filesystem_credentials($url, '', false, false); if (false === $creds) { return true; } if (!WP_Filesystem($creds)) { request_filesystem_credentials($url, '', true, false); return true; } /** @var $wp_filesystem \WP_Filesystem_Base */ global $wp_filesystem; if (!is_dir(PHOTONIC_UPLOAD_DIR)) { if (!$wp_filesystem->mkdir(PHOTONIC_UPLOAD_DIR)) { echo "

Failed to create directory " . esc_attr(PHOTONIC_UPLOAD_DIR) . ". Please check your folder permissions.

"; return false; } } $filename = trailingslashit(PHOTONIC_UPLOAD_DIR) . 'custom-styles.css'; if (empty($custom_css)) { return false; } if (!$wp_filesystem->put_contents($filename, $custom_css, FS_CHMOD_FILE)) { echo wp_kses_post("

Failed to save file $filename. Please check your folder permissions.

"); return false; } return true; } }