Страница настроек темы от kama

/**
 * Страница настроек темы
 * Автор: Kama
 * v 0.1
 */

Themeopt::init();

class Themeopt{
	const OPT_NAME = 'theme_options';
	const PAGE_ID  = 'theme_options_page';

	static $i;

	public $opt;

	static function init(){
		if( ! is_null( self::$i ) ) return self::$i;        
		return self::$i = new self;
	}

	function __construct(){
		$this->opt = (object) get_option( self::OPT_NAME );

		add_action('admin_init', array( &$this, 'register_settings') );

		// добавим меню
		add_action('admin_menu', array( &$this, 'add_menu_page') );     
	}

	## Возвращает опцию, когда запрашивается несуществующее свойство
	function __get( $name ){
		return @ self::$i->opt->$name ?: false;
	}

	## Регистрируем настройки
	function register_settings(){
		register_setting( self::PAGE_ID, self::OPT_NAME, array( &$this, 'validate_settings') );
	}

	## Добавляем страницу в меню
	function add_menu_page(){
//      $hook_name = add_menu_page( 'Настройки темы', 'Настройки темы', 'manage_options', self::PAGE_ID, array( &$this, 'page_out'), 'dashicons-admin-generic', 100);
		$hook_name = add_submenu_page('themes.php', 'Настройки темы', 'Настройки темы', 'manage_options', self::PAGE_ID, array( &$this, 'page_out') );
		add_action("load-$hook_name", array( & $this, 'page_load') );
	}

	## Загрузка страницы
	function page_load(){
		$this->register_settings_sections();
	}

	## HTML вывод страницы
	function page_out(){
		do_action( 'before_theme_options' );
		?>
		<div class="wrap">
			<h2>Настройки темы</h2>
			<form method="POST" enctype="multipart/form-data" action="<?php echo admin_url('options.php') ?>">
				<?php 
				settings_fields( self::PAGE_ID ); // меняем под себя только здесь (название настроек)
				do_settings_sections( self::PAGE_ID );
				?>
				<p class="submit">  
					<input type="submit" class="button-primary" value="<?php _e('Сохранить') ?>" />  
				</p>
			</form>
		</div>
		<?php
		do_action( 'after_theme_options' );
	}

	## регистрирует поля настроек
	function register_settings_sections(){
		do_action( 'theme_register_settings_sections', $this  );

		// ПЕРВАЯ секция
		$section = 'section_1';
		add_settings_section( $section, 'Секция 1', '', self::PAGE_ID );        

		// текстовое поле в первой секции
		$id = 'my_text_field';
		add_settings_field( $id, 'Текстовое поле', array( __CLASS__, 'display_field'), self::PAGE_ID, $section, array(
			'id'        => $id,
			'label_for' => $id,
			'desc'      => 'Пример обычного текстового поля.', // описание
		) );

		// textarea в первой секции
		$id = 'my_textarea_field';
		add_settings_field( $id, 'Большое текстовое поле', array( __CLASS__, 'display_field'), self::PAGE_ID, $section, array(
			'id'   => $id,
			'type' => 'textarea',
			'attr' => 'style="width:100%;"',
			'desc' => 'Пример большого текстового поля.'
		) );

		// ВТОРАЯ секция
		$section = 'section_2';
		add_settings_section( $section, 'Секция 2', '', self::PAGE_ID );

		// чекбокс
		$id = 'my_checkbox_field';
		add_settings_field( $id, 'Чекбокс', array( __CLASS__, 'display_field'), self::PAGE_ID, $section, array(
			'id'   => $id,
			'type' => 'checkbox',
			'desc' => 'Пример чекбокса.'
		) );

		// выпадающий список
		$id = 'my_select_field';
		add_settings_field( $id, 'Выпадающий список', array( __CLASS__, 'display_field'), self::PAGE_ID, $section, array(
			'id'      => $id,
			'type'    => 'select',
			'desc'    => 'Пример выпадающего списка.',
			'options' => array( 'val1' => 'Значение 1', 'val2' => 'Значение 2', 'val3' => 'Значение 3')
		) );

		// радио-кнопку
		$id = 'my_radio';
		add_settings_field( $id, 'Радио кнопки', array( __CLASS__, 'display_field'), self::PAGE_ID, $section, array(
			'id'      => $id,
			'type'    => 'radio',
			'options' => array( 'val1' => 'Значение 1', 'val2' => 'Значение 2', 'val3' => 'Значение 3')
		) );

	}

	/**
	 * Выводит поля форм
	 * @param array $args Параметры поля
	 */
	static function display_field( $args ){
		$def = array(
			'type'        => '', // по умолчанию text
			'desc'        => '', // описание
			'val'         => '', // значение по умолчанию, если нет сохраненного
			'options'     => '', // варианты для select и radio
			'id'          => '', // id атрибут
			'array_key'   => '', // ключ массива, когда/чтобы настройка сохранялась во вложенный массив
			'attr'        => '', // любая строка, будет расположена внутри тега. Для создания атрибутов. Пр: style="width:100%;"
			'opt_name'    => '', // название опций, чтобы метод можно было вызывать вне этого класса. По умолчанию: self::OPT_NAME
			'placeholder' => '', // placeholder
			// 'class'=>'', 'label_for'=>'' заняты
		);

		extract( array_merge( $def, $args ) );

		if( ! $type )
			$type = 'text';

		if( ! $opt_name ) $opt_name = self::OPT_NAME; // название опций по умолчанию, чтобы метод можно было вызывать вне этого класса

		$opt   = get_option( $opt_name );

		$name  = $opt_name . ($array_key?"[$array_key]":'') ."[$id]";
		$val  = ($array_key ? @ $opt[ $array_key ][ $id ] : @ $opt[ $id ]) ?: $val;
		$placeholder = $placeholder ? ' placeholder="'. $placeholder .'"' : '';

		// textarea
		if( $type == 'textarea' ){
			$val = @ esc_attr( stripslashes($val) );
			echo '<textarea '. $attr . $placeholder .' id="'. $id .'" name="'. $name .'">'. $val .'</textarea>'.
				($desc ? '<br><span class="description">'. $desc .'</span>' : '');
		}
		// select
		elseif( $type == 'select' ){
			echo '<select '. $attr .' id="'. $id .'" name="'. $name .'">';
			foreach( (array) $options as $v => $l )
				echo '<option value="'. $v .'" '. selected(@ $val, $v, 0) .'>'. $l .'</option>';
			echo ($desc ?: '').
				'</select>';  
		}
		// checkbox
		elseif( $type == 'checkbox' ){
			echo '
			<label>
				<input type="checkbox" id="'. $id .'" name="'. $name .'" value="1" '. checked( @ $val, 1, 0) .'>  
				'.($desc ?: '').'
			</label>';  
		}
		// radio
		elseif( $type == 'radio' ){
			echo '<fieldset>';
			foreach( (array) $options as $v => $l )
				echo '<label><input type="radio" name="'. $name .'" value="'. $v .'" '. checked(@ $val, $v, 0) .'>'. $l .'</label><br>';
			echo '</fieldset>';  
		}
		// text
		else{
			$class = ($type=='text') ? ' class="regular-text"' : '';

			$val = @ esc_attr( stripslashes($val) );
			echo '<input '. $attr . $class  . $placeholder .' type="'. $type .'" id="'. $id .'" name="'. $name .'" value="'. $val .'">'.
				($desc ? '<br><span class="description">'. $desc .'</span>' : '');
		}
	}

	## Функция проверки/очистки сохраняемых данных
	function validate_settings( $input ){

		// обработка передаваемых данных
		foreach( $input as $k => & $val ){          
			if( is_string( $val ) ) $val = trim( $val );            

			// foo
			if( $k == 'foo' ){
				$val = esc_html( $val );
			}

		}

		return $input;
	}

	## Подключает скрипты опций
	function __page_css_js(){
		static $one; if( $one ) return $one = 1;
		$tpl_url = get_template_directory_uri();
		wp_enqueue_script( $this::OPT_NAME, $tpl_url .'/js/opt-page.js', array('jquery'), null, true );
		wp_enqueue_script( $this::OPT_NAME, $tpl_url .'/js/example.css', array(), null );
	}

}