You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

369 lines
12 KiB

9 months ago
<?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
* @since 2.0.0 Added the "modules" option.
* @var array<string, mixed>
*/
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_donation_nag' => true,
'use_custom_css' => true,
'use_custom_css_file' => true,
'custom_css' => '',
'custom_css_minified' => '',
'custom_css_version' => 0,
'modules' => false,
);
/**
* Default User Options.
*
* @since 1.0.0
* @var array<string, mixed>>
*/
protected $default_user_options = array(
'user_options_db_version' => TablePress::db_version, // To prevent saving on first load.
'admin_menu_parent_page' => 'middle',
'message_first_visit' => true,
'table_editor_column_width' => '170', // Default column width of 170px of the table editor on the "Edit" screen.
'table_editor_line_clamp' => '5', // Maximum number of lines per cell in the table editor on the "Edit" screen.
);
/**
* Instance of WP_Option class for Plugin Options.
*
* @since 1.0.0
* @var TablePress_WP_Option
*/
protected $plugin_options;
/**
* Instance of WP_User_Option class for User Options.
*
* @since 1.0.0
* @var TablePress_WP_User_Option
*/
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, mixed>|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( /* array|string */ $new_options, /* string|bool|int|float|null */ $single_value = null ): void {
// 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 { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedElse
// 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|false $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( /* string|false */ $name = false, /* string|bool|int|float|null */ $default_value = null ) /* : string|bool|int|float|array|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<string, mixed> Array of all Plugin Options.
*/
public function _debug_get_plugin_options(): array {
return $this->plugin_options->get();
}
/**
* Get all User Options (only used in Debug).
*
* @since 1.0.0
*
* @return array<string, mixed> Array of all User Options.
*/
public function _debug_get_user_options(): array {
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(): void {
$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 current 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(): void {
$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 current 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" user roles.
*
* @since 1.0.0
*/
public function add_access_capabilities(): void {
// 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_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();
}
/**
* Remove all TablePress capabilities from all roles.
*
* @since 1.1.0
*
* @global WP_Roles $wp_roles WordPress User Roles abstraction object.
* @see add_access_capabilities()
*/
public function remove_access_capabilities(): void {
global $wp_roles;
if ( ! isset( $wp_roles ) ) {
$wp_roles = new WP_Roles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
foreach ( $wp_roles->roles as $role => $details ) {
$role = $wp_roles->get_role( $role );
if ( empty( $role ) ) {
continue;
}
$role->remove_cap( 'tablepress_edit_tables' );
$role->remove_cap( 'tablepress_delete_tables' );
$role->remove_cap( 'tablepress_list_tables' );
$role->remove_cap( 'tablepress_add_tables' );
$role->remove_cap( 'tablepress_copy_tables' );
$role->remove_cap( 'tablepress_import_tables' );
$role->remove_cap( 'tablepress_export_tables' );
$role->remove_cap( 'tablepress_access_options_screen' );
$role->remove_cap( 'tablepress_access_about_screen' );
$role->remove_cap( 'tablepress_import_tables_wptr' ); // No longer used since 1.10, but might still be in the database.
$role->remove_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 string[] $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 mixed[] $args Arguments for the check, here e.g. the table ID.
* @return string[] Modified set of primitive caps.
*/
public function map_tablepress_meta_caps( array $caps, /* string */ $cap, int $user_id, array $args ): array {
// Don't use a type hint for the `$cap` argument as many WordPress plugins seem to be passing `null` to capability check or admin menu functions.
// Protect against other plugins not supplying a string for the `$cap` argument.
if ( ! is_string( $cap ) ) {
return $caps;
}
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;
}
// $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;
}
/**
* Filters a user's TablePress capabilities.
*
* @since 1.0.0
*
* @see map_meta_cap()
*
* @param string[] $caps The user's current TablePress capabilities.
* @param string $cap Capability name.
* @param int $user_id The user ID.
* @param mixed[] $args Adds the context to the cap, typically the table ID.
*/
return apply_filters( 'tablepress_map_meta_caps', $caps, $cap, $user_id, $args );
}
/**
* Delete the WP_Option and the user option of the model.
*
* @since 1.0.0
*/
public function destroy(): void {
$this->plugin_options->delete();
$this->user_options->delete_for_all_users();
}
} // class TablePress_Options_Model