648 lines
20 KiB
PHP
648 lines
20 KiB
PHP
<?php
|
||
/**
|
||
* OptiCore Admin Framework
|
||
*
|
||
* Professional admin panel framework inspired by CodeStar Framework
|
||
*
|
||
* @package OptiCore
|
||
* @version 1.0.0
|
||
*/
|
||
|
||
if (!defined('ABSPATH')) {
|
||
exit;
|
||
}
|
||
|
||
class OptiCore_Framework {
|
||
|
||
/**
|
||
* Instance
|
||
*/
|
||
private static $instance = null;
|
||
|
||
/**
|
||
* Settings
|
||
*/
|
||
private $settings = array();
|
||
|
||
/**
|
||
* Sections
|
||
*/
|
||
private $sections = array();
|
||
|
||
/**
|
||
* Options
|
||
*/
|
||
private $options = array();
|
||
|
||
/**
|
||
* Errors
|
||
*/
|
||
private $errors = array();
|
||
|
||
/**
|
||
* Get instance
|
||
*/
|
||
public static function get_instance() {
|
||
if (null === self::$instance) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* Constructor
|
||
*/
|
||
private function __construct() {
|
||
// Set settings without translations first
|
||
$this->settings = array(
|
||
'menu_slug' => 'opticore-settings',
|
||
'menu_type' => 'submenu',
|
||
'menu_parent' => 'opticore',
|
||
'menu_icon' => 'dashicons-admin-settings',
|
||
'menu_position' => null,
|
||
'ajax_save' => true,
|
||
'show_reset' => true,
|
||
'show_export' => true,
|
||
'show_import' => true,
|
||
'theme' => 'modern',
|
||
'database' => 'opticore_settings',
|
||
);
|
||
|
||
$this->init_hooks();
|
||
}
|
||
|
||
/**
|
||
* Get translatable settings
|
||
*/
|
||
private function get_translatable_settings() {
|
||
return array(
|
||
'framework_title' => __('OptiCore Settings', 'opticore'),
|
||
'menu_title' => __('Settings', 'opticore'),
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Initialize hooks
|
||
*/
|
||
private function init_hooks() {
|
||
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||
add_action('admin_init', array($this, 'load_config'));
|
||
add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
|
||
add_action('wp_ajax_opticore_save_settings', array($this, 'ajax_save_settings'));
|
||
add_action('wp_ajax_opticore_reset_section', array($this, 'ajax_reset_section'));
|
||
add_action('wp_ajax_opticore_reset_all', array($this, 'ajax_reset_all'));
|
||
}
|
||
|
||
/**
|
||
* Load configuration
|
||
*/
|
||
public function load_config() {
|
||
if (file_exists(OPTICORE_PLUGIN_DIR . 'includes/framework-config.php')) {
|
||
require_once OPTICORE_PLUGIN_DIR . 'includes/framework-config.php';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Add admin menu
|
||
*/
|
||
public function add_admin_menu() {
|
||
// Get translatable strings
|
||
$translatable = $this->get_translatable_settings();
|
||
|
||
if ($this->settings['menu_type'] === 'submenu' && !empty($this->settings['menu_parent'])) {
|
||
add_submenu_page(
|
||
$this->settings['menu_parent'],
|
||
$translatable['framework_title'],
|
||
$translatable['menu_title'],
|
||
'manage_options',
|
||
$this->settings['menu_slug'],
|
||
array($this, 'render_framework')
|
||
);
|
||
} else {
|
||
add_menu_page(
|
||
$translatable['framework_title'],
|
||
$translatable['menu_title'],
|
||
'manage_options',
|
||
$this->settings['menu_slug'],
|
||
array($this, 'render_framework'),
|
||
$this->settings['menu_icon'],
|
||
$this->settings['menu_position']
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Create section
|
||
*/
|
||
public function create_section($section) {
|
||
$defaults = array(
|
||
'id' => '',
|
||
'title' => '',
|
||
'icon' => 'dashicons-admin-generic',
|
||
'fields' => array(),
|
||
);
|
||
|
||
$section = wp_parse_args($section, $defaults);
|
||
|
||
if (empty($section['id'])) {
|
||
return false;
|
||
}
|
||
|
||
$this->sections[$section['id']] = $section;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Create field
|
||
*/
|
||
public function create_field($section_id, $field) {
|
||
$defaults = array(
|
||
'id' => '',
|
||
'type' => 'text',
|
||
'title' => '',
|
||
'subtitle' => '',
|
||
'desc' => '',
|
||
'default' => '',
|
||
'attributes' => array(),
|
||
'dependency' => array(),
|
||
'sanitize' => '',
|
||
);
|
||
|
||
$field = wp_parse_args($field, $defaults);
|
||
|
||
if (empty($field['id'])) {
|
||
return false;
|
||
}
|
||
|
||
if (isset($this->sections[$section_id])) {
|
||
$this->sections[$section_id]['fields'][] = $field;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Get option value
|
||
*/
|
||
public function get_option($key = '', $default = '') {
|
||
$options = get_option($this->settings['database'], array());
|
||
|
||
if (empty($key)) {
|
||
return $options;
|
||
}
|
||
|
||
return isset($options[$key]) ? $options[$key] : $default;
|
||
}
|
||
|
||
/**
|
||
* Set option value
|
||
*/
|
||
public function set_option($key, $value) {
|
||
$options = get_option($this->settings['database'], array());
|
||
$options[$key] = $value;
|
||
update_option($this->settings['database'], $options);
|
||
}
|
||
|
||
/**
|
||
* Enqueue scripts and styles
|
||
*/
|
||
public function enqueue_scripts($hook) {
|
||
// Check if we're on the framework page
|
||
$valid_hooks = array(
|
||
'toplevel_page_' . $this->settings['menu_slug'],
|
||
'opticore_page_' . $this->settings['menu_slug'],
|
||
);
|
||
|
||
if (!in_array($hook, $valid_hooks)) {
|
||
return;
|
||
}
|
||
|
||
// Enqueue WordPress color picker
|
||
wp_enqueue_style('wp-color-picker');
|
||
wp_enqueue_script('wp-color-picker');
|
||
|
||
// Enqueue media uploader
|
||
wp_enqueue_media();
|
||
|
||
// Framework styles
|
||
wp_enqueue_style(
|
||
'opticore-framework',
|
||
OPTICORE_PLUGIN_URL . 'assets/css/framework.css',
|
||
array('wp-color-picker'),
|
||
OPTICORE_VERSION
|
||
);
|
||
|
||
// Framework scripts
|
||
wp_enqueue_script(
|
||
'opticore-framework',
|
||
OPTICORE_PLUGIN_URL . 'assets/js/framework.js',
|
||
array('jquery', 'wp-color-picker'),
|
||
OPTICORE_VERSION,
|
||
true
|
||
);
|
||
|
||
// Localize script
|
||
wp_localize_script('opticore-framework', 'opticoreFramework', array(
|
||
'ajaxurl' => admin_url('admin-ajax.php'),
|
||
'nonce' => wp_create_nonce('opticore_framework_nonce'),
|
||
'i18n' => array(
|
||
'saving' => __('Saving...', 'opticore'),
|
||
'saved' => __('Settings Saved!', 'opticore'),
|
||
'error' => __('Error saving settings', 'opticore'),
|
||
'resetting' => __('Resetting...', 'opticore'),
|
||
'reset' => __('Reset successful!', 'opticore'),
|
||
'confirmReset' => __('Are you sure you want to reset this section?', 'opticore'),
|
||
'confirmResetAll' => __('Are you sure you want to reset ALL settings? This cannot be undone!', 'opticore'),
|
||
),
|
||
));
|
||
}
|
||
|
||
/**
|
||
* Render framework
|
||
*/
|
||
public function render_framework() {
|
||
$options = $this->get_option();
|
||
|
||
// Merge translatable settings
|
||
$this->settings = array_merge($this->settings, $this->get_translatable_settings());
|
||
|
||
include OPTICORE_PLUGIN_DIR . 'admin/framework-template.php';
|
||
}
|
||
|
||
/**
|
||
* AJAX: Save settings
|
||
*/
|
||
public function ajax_save_settings() {
|
||
check_ajax_referer('opticore_framework_nonce', 'nonce');
|
||
|
||
if (!current_user_can('manage_options')) {
|
||
wp_send_json_error(array('message' => __('Permission denied.', 'opticore')));
|
||
}
|
||
|
||
$data = isset($_POST['data']) ? $_POST['data'] : array();
|
||
$sanitized = array();
|
||
|
||
// Sanitize each field
|
||
foreach ($data as $key => $value) {
|
||
$sanitized[$key] = $this->sanitize_field($key, $value);
|
||
}
|
||
|
||
// Update options
|
||
update_option($this->settings['database'], $sanitized);
|
||
|
||
wp_send_json_success(array(
|
||
'message' => __('Settings saved successfully!', 'opticore'),
|
||
));
|
||
}
|
||
|
||
/**
|
||
* AJAX: Reset section
|
||
*/
|
||
public function ajax_reset_section() {
|
||
check_ajax_referer('opticore_framework_nonce', 'nonce');
|
||
|
||
if (!current_user_can('manage_options')) {
|
||
wp_send_json_error(array('message' => __('Permission denied.', 'opticore')));
|
||
}
|
||
|
||
$section_id = isset($_POST['section']) ? sanitize_text_field($_POST['section']) : '';
|
||
|
||
if (empty($section_id) || !isset($this->sections[$section_id])) {
|
||
wp_send_json_error(array('message' => __('Invalid section.', 'opticore')));
|
||
}
|
||
|
||
$options = $this->get_option();
|
||
$section = $this->sections[$section_id];
|
||
|
||
// Reset fields in this section
|
||
foreach ($section['fields'] as $field) {
|
||
if (isset($options[$field['id']])) {
|
||
unset($options[$field['id']]);
|
||
}
|
||
}
|
||
|
||
update_option($this->settings['database'], $options);
|
||
|
||
wp_send_json_success(array(
|
||
'message' => __('Section reset successfully!', 'opticore'),
|
||
));
|
||
}
|
||
|
||
/**
|
||
* AJAX: Reset all settings
|
||
*/
|
||
public function ajax_reset_all() {
|
||
check_ajax_referer('opticore_framework_nonce', 'nonce');
|
||
|
||
if (!current_user_can('manage_options')) {
|
||
wp_send_json_error(array('message' => __('Permission denied.', 'opticore')));
|
||
}
|
||
|
||
delete_option($this->settings['database']);
|
||
|
||
wp_send_json_success(array(
|
||
'message' => __('All settings reset successfully!', 'opticore'),
|
||
));
|
||
}
|
||
|
||
/**
|
||
* Sanitize field value
|
||
*/
|
||
private function sanitize_field($key, $value) {
|
||
// Find field type
|
||
$field_type = 'text';
|
||
|
||
foreach ($this->sections as $section) {
|
||
foreach ($section['fields'] as $field) {
|
||
if ($field['id'] === $key) {
|
||
$field_type = $field['type'];
|
||
break 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Sanitize based on type
|
||
switch ($field_type) {
|
||
case 'text':
|
||
case 'select':
|
||
case 'radio':
|
||
return sanitize_text_field($value);
|
||
|
||
case 'textarea':
|
||
return sanitize_textarea_field($value);
|
||
|
||
case 'email':
|
||
return sanitize_email($value);
|
||
|
||
case 'url':
|
||
return esc_url_raw($value);
|
||
|
||
case 'number':
|
||
return absint($value);
|
||
|
||
case 'checkbox':
|
||
case 'switcher':
|
||
return (bool) $value;
|
||
|
||
case 'color':
|
||
return sanitize_hex_color($value);
|
||
|
||
case 'editor':
|
||
return wp_kses_post($value);
|
||
|
||
default:
|
||
return sanitize_text_field($value);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Render field
|
||
*/
|
||
public function render_field($field, $value = '') {
|
||
// Fields like heading, notice, content don't have ID
|
||
$field_id = isset($field['id']) ? $field['id'] : '';
|
||
$field_name = $field_id;
|
||
|
||
// Get value (only for fields with ID)
|
||
if ($field_id && $value === '') {
|
||
$value = $this->get_option($field_id, isset($field['default']) ? $field['default'] : '');
|
||
}
|
||
|
||
// Check dependency
|
||
$dependency_class = '';
|
||
$dependency_data = '';
|
||
|
||
if (!empty($field['dependency'])) {
|
||
$dependency_class = 'opticore-dependency-field';
|
||
$dependency_data = ' data-dependency="' . esc_attr(json_encode($field['dependency'])) . '"';
|
||
}
|
||
|
||
echo '<div class="opticore-field opticore-field-' . esc_attr($field['type']) . ' ' . esc_attr($dependency_class) . '"' . $dependency_data . '>';
|
||
|
||
// Field wrapper
|
||
echo '<div class="opticore-field-content">';
|
||
|
||
// Render field based on type
|
||
$method = 'render_field_' . $field['type'];
|
||
|
||
if (method_exists($this, $method)) {
|
||
$this->$method($field, $value);
|
||
} else {
|
||
$this->render_field_text($field, $value);
|
||
}
|
||
|
||
// Field description
|
||
if (!empty($field['desc'])) {
|
||
echo '<p class="opticore-field-desc">' . wp_kses_post($field['desc']) . '</p>';
|
||
}
|
||
|
||
echo '</div>'; // .opticore-field-content
|
||
echo '</div>'; // .opticore-field
|
||
}
|
||
|
||
/**
|
||
* Render text field
|
||
*/
|
||
private function render_field_text($field, $value) {
|
||
$attributes = $this->get_field_attributes($field);
|
||
|
||
echo '<input type="text" ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo 'value="' . esc_attr($value) . '" ';
|
||
echo $attributes;
|
||
echo 'class="opticore-input-text" />';
|
||
}
|
||
|
||
/**
|
||
* Render textarea field
|
||
*/
|
||
private function render_field_textarea($field, $value) {
|
||
$attributes = $this->get_field_attributes($field);
|
||
|
||
echo '<textarea ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo $attributes;
|
||
echo 'class="opticore-input-textarea" rows="5">';
|
||
echo esc_textarea($value);
|
||
echo '</textarea>';
|
||
}
|
||
|
||
/**
|
||
* Render number field
|
||
*/
|
||
private function render_field_number($field, $value) {
|
||
$attributes = $this->get_field_attributes($field);
|
||
|
||
echo '<input type="number" ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo 'value="' . esc_attr($value) . '" ';
|
||
echo $attributes;
|
||
echo 'class="opticore-input-number" />';
|
||
}
|
||
|
||
/**
|
||
* Render switcher field
|
||
*/
|
||
private function render_field_switcher($field, $value) {
|
||
$checked = !empty($value) ? 'checked' : '';
|
||
|
||
echo '<label class="opticore-switcher">';
|
||
echo '<input type="checkbox" ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo 'value="1" ';
|
||
echo $checked;
|
||
echo ' class="opticore-input-switcher" />';
|
||
echo '<span class="opticore-switcher-slider"></span>';
|
||
echo '<span class="opticore-switcher-on">' . __('ON', 'opticore') . '</span>';
|
||
echo '<span class="opticore-switcher-off">' . __('OFF', 'opticore') . '</span>';
|
||
echo '</label>';
|
||
}
|
||
|
||
/**
|
||
* Render select field
|
||
*/
|
||
private function render_field_select($field, $value) {
|
||
$attributes = $this->get_field_attributes($field);
|
||
$options = isset($field['options']) ? $field['options'] : array();
|
||
|
||
echo '<select ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo $attributes;
|
||
echo 'class="opticore-input-select">';
|
||
|
||
foreach ($options as $option_value => $option_label) {
|
||
echo '<option value="' . esc_attr($option_value) . '" ';
|
||
selected($value, $option_value);
|
||
echo '>' . esc_html($option_label) . '</option>';
|
||
}
|
||
|
||
echo '</select>';
|
||
}
|
||
|
||
/**
|
||
* Render radio field
|
||
*/
|
||
private function render_field_radio($field, $value) {
|
||
$options = isset($field['options']) ? $field['options'] : array();
|
||
|
||
echo '<div class="opticore-radio-group">';
|
||
|
||
foreach ($options as $option_value => $option_label) {
|
||
$checked = checked($value, $option_value, false);
|
||
$radio_id = $field['id'] . '_' . $option_value;
|
||
|
||
echo '<label class="opticore-radio-label">';
|
||
echo '<input type="radio" ';
|
||
echo 'id="' . esc_attr($radio_id) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo 'value="' . esc_attr($option_value) . '" ';
|
||
echo $checked;
|
||
echo ' class="opticore-input-radio" />';
|
||
echo '<span class="opticore-radio-text">' . esc_html($option_label) . '</span>';
|
||
echo '</label>';
|
||
}
|
||
|
||
echo '</div>';
|
||
}
|
||
|
||
/**
|
||
* Render color field
|
||
*/
|
||
private function render_field_color($field, $value) {
|
||
$default = isset($field['default']) ? $field['default'] : '#667eea';
|
||
|
||
echo '<input type="text" ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo 'value="' . esc_attr($value) . '" ';
|
||
echo 'data-default-color="' . esc_attr($default) . '" ';
|
||
echo 'class="opticore-color-picker" />';
|
||
}
|
||
|
||
/**
|
||
* Render media field
|
||
*/
|
||
private function render_field_media($field, $value) {
|
||
$preview = '';
|
||
|
||
if (!empty($value)) {
|
||
$preview = '<div class="opticore-media-preview">';
|
||
$preview .= '<img src="' . esc_url($value) . '" alt="" />';
|
||
$preview .= '<a href="#" class="opticore-media-remove">×</a>';
|
||
$preview .= '</div>';
|
||
}
|
||
|
||
echo '<div class="opticore-media-wrapper">';
|
||
echo $preview;
|
||
echo '<input type="hidden" ';
|
||
echo 'id="' . esc_attr($field['id']) . '" ';
|
||
echo 'name="' . esc_attr($field['id']) . '" ';
|
||
echo 'value="' . esc_attr($value) . '" ';
|
||
echo 'class="opticore-input-media" />';
|
||
echo '<button type="button" class="button opticore-media-upload">';
|
||
echo '<span class="dashicons dashicons-upload"></span> ';
|
||
echo __('Upload', 'opticore');
|
||
echo '</button>';
|
||
echo '</div>';
|
||
}
|
||
|
||
/**
|
||
* Render heading field
|
||
*/
|
||
private function render_field_heading($field, $value) {
|
||
$content = isset($field['content']) ? $field['content'] : '';
|
||
|
||
echo '<div class="opticore-heading">';
|
||
echo wp_kses_post($content);
|
||
echo '</div>';
|
||
}
|
||
|
||
/**
|
||
* Render content field
|
||
*/
|
||
private function render_field_content($field, $value) {
|
||
$content = isset($field['content']) ? $field['content'] : '';
|
||
|
||
echo '<div class="opticore-content">';
|
||
echo wp_kses_post($content);
|
||
echo '</div>';
|
||
}
|
||
|
||
/**
|
||
* Render notice field
|
||
*/
|
||
private function render_field_notice($field, $value) {
|
||
$content = isset($field['content']) ? $field['content'] : '';
|
||
$notice_type = isset($field['notice_type']) ? $field['notice_type'] : 'info';
|
||
|
||
echo '<div class="opticore-notice opticore-notice-' . esc_attr($notice_type) . '">';
|
||
echo wp_kses_post($content);
|
||
echo '</div>';
|
||
}
|
||
|
||
/**
|
||
* Get field attributes
|
||
*/
|
||
private function get_field_attributes($field) {
|
||
$attributes = '';
|
||
|
||
if (!empty($field['attributes']) && is_array($field['attributes'])) {
|
||
foreach ($field['attributes'] as $attr_key => $attr_value) {
|
||
$attributes .= esc_attr($attr_key) . '="' . esc_attr($attr_value) . '" ';
|
||
}
|
||
}
|
||
|
||
// Add placeholder if exists
|
||
if (!empty($field['placeholder'])) {
|
||
$attributes .= 'placeholder="' . esc_attr($field['placeholder']) . '" ';
|
||
}
|
||
|
||
return $attributes;
|
||
}
|
||
}
|
||
|