Location: PHPKode > scripts > TablePress > tablepress/models/model-options.php
<?php
/**
 * Options Model
 *
 * @package TablePress
 * @subpackage Models
 * @author Tobias Bäthge
 * @since 1.0.0
 */

// Prohibit direct script loading
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );

/**
 * Options Model class
 * @package TablePress
 * @subpackage Models
 * @author Tobias Bäthge
 * @since 1.0.0
 */
class TablePress_Options_Model extends TablePress_Model {

	/**
	 * Default Plugin Options
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	protected $default_plugin_options = array(
		'plugin_options_db_version' => 0,
		'table_scheme_db_version' => 0,
		'prev_tablepress_version' => '0',
		'tablepress_version' => TablePress::version,
		'first_activation' => 0,
		'message_plugin_update' => false,
		'message_plugin_update_content' => '',
		'message_donation_nag' => true,
		'use_custom_css' => true,
		'use_custom_css_file' => true,
		'custom_css' => '',
		'custom_css_minified' => '',
		'custom_css_version' => 0
	);

	/**
	 * Default User Options
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	protected $default_user_options = array(
		'user_options_db_version' => TablePress::db_version, // to prevent saving on first load
		'admin_menu_parent_page' => 'middle',
		'plugin_language' => 'auto',
		'message_first_visit' => true
	);

	/**
	 * Instance of WP_Option class for Plugin Options
	 *
	 * @since 1.0.0
	 *
	 * @var object
	 */
	protected $plugin_options;

	/**
	 * Instance of WP_User_Option class for User Options
	 *
	 * @since 1.0.0
	 *
	 * @var object
	 */
	protected $user_options;

	/**
	 * Init Options Model by creating the object instances for the Plugin and User Options
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		parent::__construct();

		$params = array(
			'option_name' => 'tablepress_plugin_options',
			'default_value' => $this->default_plugin_options
		);
		$this->plugin_options = TablePress::load_class( 'TablePress_WP_Option', 'class-wp_option.php', 'classes', $params );

		$params = array(
			'option_name' => 'tablepress_user_options',
			'default_value' => $this->default_user_options
		);
		$this->user_options = TablePress::load_class( 'TablePress_WP_User_Option', 'class-wp_user_option.php', 'classes', $params );

		// Filter to map Meta capabilities to Primitive Capabilities
		add_filter( 'map_meta_cap', array( $this, 'map_tablepress_meta_caps' ), 10, 4 );
	}

	/**
	 * Update a single option or an array of options with new values
	 *
	 * @since 1.0.0
	 *
	 * @param array|string $new_options Array of new options ( name => value ) or name of a single option
	 * @param mixed $single_value (optional) New value for a single option (only if $new_options is not an array)
	 */
	public function update( $new_options, $single_value = null ) {
		// allow saving of single options that are not in an array
		if ( ! is_array( $new_options ) )
			$new_options = array( $new_options => $single_value );

		$plugin_options = $this->plugin_options->get();
		$user_options = $this->user_options->get();
		foreach ( $new_options as $name => $value ) {
			if ( isset( $this->default_plugin_options[ $name ] ) ) {
				$plugin_options[ $name ] = $value;
			} elseif ( isset( $this->default_user_options[ $name ] ) ) {
				$user_options[ $name ] = $value;
			} else {
				// no valid Plugin or User Option -> discard the name/value pair
			}
		}

		$this->plugin_options->update( $plugin_options );
		$this->user_options->update( $user_options );
	}

	/**
	 * Get the value of a single option, or an array with all options
	 *
	 * @since 1.0.0
	 *
	 * @param string $name (optional) Name of a single option to get, or false for all options
	 * @param mixed $default_value (optional) Default value, if the option $name does not exist
	 * @return mixed Value of the retrieved option $name, or $default_value if it does not exist, or array of all options
	 */
	public function get( $name = false, $default_value = null ) {
		if ( false === $name )
			return array_merge( $this->plugin_options->get(), $this->user_options->get() );

		// Single Option wanted
		if ( $this->plugin_options->is_set( $name ) ) {
			return $this->plugin_options->get( $name );
		} elseif ( $this->user_options->is_set( $name ) ) {
			return $this->user_options->get( $name );
		} else {
			// no valid Plugin or User Option
			return $default_value;
		}
	}

	/**
	 * Get all Plugin Options (only used in Debug)
	 *
	 * @since 1.0.0
	 *
	 * @return array Array of all Plugin Options
	 */
	public function _debug_get_plugin_options() {
		return $this->plugin_options->get();
	}

	/**
	 * Get all User Options (only used in Debug)
	 *
	 * @since 1.0.0
	 *
	 * @return array Array of all User Options
	 */
	public function _debug_get_user_options() {
		return $this->user_options->get();
	}

	/**
	 * Merge existing Plugin Options with default Plugin Options,
	 * remove (no longer) existing options, e.g. after a plugin update
	 *
	 * @since 1.0.0
	 */
	public function merge_plugin_options_defaults() {
		$plugin_options = $this->plugin_options->get();
		// remove old (i.e. no longer existing) Plugin Options:
		$plugin_options = array_intersect_key( $plugin_options, $this->default_plugin_options );
		// merge into new Plugin Options:
		$plugin_options = array_merge( $this->default_plugin_options, $plugin_options );

		$this->plugin_options->update( $plugin_options );
	}

	/**
	 * Merge existing User Options with default User Options,
	 * remove (no longer) existing options, e.g. after a plugin update
	 *
	 * @since 1.0.0
	 */
	public function merge_user_options_defaults() {
		$user_options = $this->user_options->get();
		// remove old (i.e. no longer existing) User Options:
		$user_options = array_intersect_key( $user_options, $this->default_user_options );
		// merge into new User Options:
		$user_options = array_merge( $this->default_user_options, $user_options );

		$this->user_options->update( $user_options );
	}

	/**
	 * Add default capabilities to "Administrator", "Editor", and "Author" roles
	 *
	 * @since 1.0.0
	 */
	public function add_access_capabilities() {
		// Capabilities for all roles
		$roles = array( 'administrator', 'editor', 'author' );
		foreach ( $roles as $role ) {
			$role =& get_role( $role );
			if ( empty( $role ) )
				continue;

			// from get_post_type_capabilities()
			$role->add_cap( 'tablepress_edit_tables' );
			// $role->add_cap( 'tablepress_edit_others_tables' );
			$role->add_cap( 'tablepress_delete_tables' );
			// $role->add_cap( 'tablepress_delete_others_tables' );

			// custom capabilities()
			$role->add_cap( 'tablepress_list_tables' );
			$role->add_cap( 'tablepress_add_tables' );
			$role->add_cap( 'tablepress_copy_tables' );
			$role->add_cap( 'tablepress_import_tables' );
			$role->add_cap( 'tablepress_export_tables' );
			$role->add_cap( 'tablepress_access_options_screen' );
			$role->add_cap( 'tablepress_access_about_screen' );
		}

		// Capabilities for single roles
		$role =& get_role( 'administrator' );
		if ( ! empty( $role ) ) {
			$role->add_cap( 'tablepress_import_tables_wptr' );
			$role->add_cap( 'tablepress_edit_options' );
		}

		// Refresh current set of capabilities of the user, to be able to directly use the new caps
		$user = wp_get_current_user();
		$user->get_role_caps();
	}

	/**
	 * Map TablePress meta capabilities to primitive capabilities
	 *
	 * @since 1.0.0
	 *
	 * @param array $caps Current set of primitive caps
	 * @param string $cap Meta cap that is to be checked/mapped
	 * @param int $user_id User ID for which meta cap is to be checked
	 * @param array $args Arguments for the check, here e.g. the table ID
	 * @return bool
	 */
	public function map_tablepress_meta_caps( $caps, $cap, $user_id, $args ) {
		if ( ! in_array( $cap, array( 'tablepress_edit_table', 'tablepress_edit_table_id', 'tablepress_copy_table', 'tablepress_delete_table', 'tablepress_export_table', 'tablepress_preview_table' ), true ) )
			return $caps;

		// $user = get_userdata( $user_id );
		// $username = $user->user_login);
		// $table_id = ( ! empty( $args ) ) ? $args[0] : false;

		// reset current set of primitive caps
		$caps = array();

		switch ( $cap ) {
			case 'tablepress_edit_table':
				$caps[] = 'tablepress_edit_tables';
				break;
			case 'tablepress_edit_table_id':
				$caps[] = 'tablepress_edit_tables';
				break;
			case 'tablepress_copy_table':
				$caps[] = 'tablepress_copy_tables';
				break;
			case 'tablepress_delete_table':
				$caps[] = 'tablepress_delete_tables';
				break;
			case 'tablepress_export_table':
				$caps[] = 'tablepress_export_tables';
				break;
			case 'tablepress_preview_table':
				$caps[] = 'tablepress_edit_tables';
				break;
			default:
				// something went wrong; deny access to be on the safe side
				$caps[] = 'do_not_allow';
				break;
		}

		$caps = apply_filters( 'tablepress_map_meta_caps', $caps, $cap, $user_id, $args );

		return $caps;
	}

	/**
	 * Retrieve the update message from the development server to notify users of the included changes in this update, in his language
	 *
	 * @since 1.0.0
	 *
	 * @param string $current_version Version before the update
	 * @param string $new_version Version after the update
	 * @param string $locale Desired locale of the message
	 * @return string Plugin update message
	 */
	public function plugin_update_message( $current_version, $new_version, $locale ) {
		$update_message = wp_remote_fopen( "http://dev.tablepress.org/plugin/update/{$current_version}/{$new_version}/{$locale}/" );
		if ( empty( $update_message ) )
			$update_message = '';
		return $update_message;
	}

	/**
	 * Get the location (file path or URL) of the "Custom CSS" file, depending on whether it's a Multisite or not
	 *
	 * @since 1.0.0
	 *
 	 * @param string $type "normal" version or "minified" version
	 * @param string $location "path" or "url", for file path or URL
	 * @return string Full file path or full URL for the "Custom CSS" file
	*/
	public function get_custom_css_location( $type, $location ) {
		$suffix = ( 'minified' == $type ) ? '.min' : '';
		$file = "tablepress-custom{$suffix}.css";

		if ( is_multisite() ) {
			// Multisite installation: /wp-content/uploads/sites/<ID>/
			$upload_location = wp_upload_dir();
		} else {
			// Singlesite installation: /wp-content/
			$upload_location = array(
				'basedir' => WP_CONTENT_DIR,
				'baseurl' => content_url()
			);
		}

		switch ( $location ) {
			case 'url':
				$url = $upload_location['baseurl'] . '/' . $file;
				$url = apply_filters( 'tablepress_custom_css_url', $url, $file, $type );
				return $url;
				break;
			case 'path':
				$path = $upload_location['basedir'] . '/' . $file;
				$path = apply_filters( 'tablepress_custom_css_file_name', $path, $file, $type );
				return $path;
				break;
		}
	}

	/**
	 * Load the contents of the file with the "Custom CSS"
	 *
	 * @since 1.0.0
	 *
	 * @param string $type "normal" version or "minified" version
	 * @return string|bool Custom CSS on success, false on error
	 */
	public function load_custom_css_from_file( $type = 'normal' ) {
		$filename = $this->get_custom_css_location( $type, 'path' );
		// Check if file name is valid (0 means yes)
		if ( 0 !== validate_file( $filename ) )
			return false;
		if ( ! @is_file( $filename ) )
			return false;
		if ( ! @is_readable( $filename ) )
			return false;
		return file_get_contents( $filename );
	}

	/**
	 * Save "Custom CSS" to a file, or return HTML for the credentials form
	 *
	 * @since 1.0.0
	 *
	 * @return string (if necessary) HTML for the credentials form for the WP_Filesystem API
	 */
	public function save_custom_css_to_file() {
		// Set current screen to get Screen Icon to have a custom HTML ID, so that we can hide it with CSS
		set_current_screen( 'tablepress_options_invisible' );

		// Start capturing the output, to get HTML of the credentials form (if needed)
		ob_start();

		$url = ''; // same page
		$credentials = request_filesystem_credentials( $url, '', false, false, null );
		// do we have credentials already? (Otherwise the form will have been rendered already.)
		if ( false === $credentials ) {
			$form_data = ob_get_contents();
			ob_end_clean();
			$form_data = str_replace( 'name="upgrade" id="upgrade" class="button"', 'name="upgrade" id="upgrade" class="button button-primary button-large"', $form_data );
			return $form_data;
		}

		// we have received credentials, but don't know if they are valid yet
		if ( ! WP_Filesystem( $credentials ) ) {
			// credentials failed, so ask again (with $error flag true)
			request_filesystem_credentials( $url, '', true, false, null );
			$form_data = ob_get_contents();
			ob_end_clean();
			$form_data = str_replace( 'name="upgrade" id="upgrade" class="button"', 'name="upgrade" id="upgrade" class="button button-primary button-large"', $form_data );
			return $form_data;
		}

		// we have valid access to the filesystem now -> try to save the file
		$filename = $this->get_custom_css_location( 'normal', 'path' );
		$filename_min = $this->get_custom_css_location( 'minified', 'path' );
		// Check if file name is valid (0 means yes)
		if ( 0 !== validate_file( $filename ) || 0 !== validate_file( $filename_min ) )
			TablePress::redirect( array( 'action' => 'options', 'message' => 'success_save_error_custom_css' ) );
		global $wp_filesystem;

		// WP_CONTENT_DIR and (FTP-)Content-Dir can be different (e.g. if FTP working dir is /)
		// We need to account for that by replacing the path difference in the filename
		$path_difference = str_replace( $wp_filesystem->wp_content_dir(), '', trailingslashit( WP_CONTENT_DIR ) );
		if ( '' != $path_difference ) {
			$filename = str_replace( $path_difference, '', $filename );
			$filename_min = str_replace( $path_difference, '', $filename_min );
		}

		$custom_css = $this->get( 'custom_css' );
		$custom_css_minified = $this->get( 'custom_css_minified' );
		$result = $wp_filesystem->put_contents( $filename, $custom_css, FS_CHMOD_FILE );
		$result_min = $wp_filesystem->put_contents( $filename_min, $custom_css_minified, FS_CHMOD_FILE );
		if ( ! $result || ! $result_min )
			TablePress::redirect( array( 'action' => 'options', 'message' => 'success_save_error_custom_css' ) );

		// at this point, saving was successful, so enable the checkbox again
		// (if it was not enabled before, we would never have tried to save)
		// and also increase the "Custom CSS" version number (for cache busting)
		$this->update( array(
			'use_custom_css_file' => true,
			'custom_css_version' => $this->get( 'custom_css_version' ) + 1
		) );
		TablePress::redirect( array( 'action' => 'options', 'message' => 'success_save' ) );
	}


	/**
	 * Delete the "Custom CSS" files, of possible
	 *
	 * @since 1.0.0
	 *
	 * @return bool True on success, false on failure
	 */
	public function delete_custom_css_files() {
		// Start capturing the output, to later prevent it
		ob_start();

		$url = ''; // same page
		$credentials = request_filesystem_credentials( $url, '', false, false, null );
		// do we have credentials already? (Otherwise the form will have been rendered, which is not supported here.)
		// or, if we have credentials, are they valid?
		if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
			ob_end_clean();
			return false;
		}

		// we have valid access to the filesystem now -> try to save the file
		$filename = $this->get_custom_css_location( 'normal', 'path' );
		$filename_min = $this->get_custom_css_location( 'minified', 'path' );
		// Check if file name is valid (0 means yes)
		if ( 0 !== validate_file( $filename ) || 0 !== validate_file( $filename_min ) )
			return false;

		global $wp_filesystem;

		// WP_CONTENT_DIR and (FTP-)Content-Dir can be different (e.g. if FTP working dir is /)
		// We need to account for that by replacing the path difference in the filename
		$path_difference = str_replace( $wp_filesystem->wp_content_dir(), '', trailingslashit( WP_CONTENT_DIR ) );
		if ( '' != $path_difference ) {
			$filename = str_replace( $path_difference, '', $filename );
			$filename_min = str_replace( $path_difference, '', $filename_min );
		}

		$result = $result_min = true;
		if ( $wp_filesystem->exists( $filename ) )
			$result = $wp_filesystem->delete( $filename );
		if ( $wp_filesystem->exists( $filename_min ) )
			$result_min = $wp_filesystem->delete( $filename_min );

		if ( ! $result || ! $result_min )
			return false;

		return true;
	}

	/**
	 * Delete the WP_Option and the user option of the model
	 *
	 * @since 1.0.0
	 */
	public function destroy() {
		$this->plugin_options->delete();
		$this->user_options->delete_for_all_users();
	}

} // class TablePress_Options_Model
Return current item: TablePress