Files
2026-04-28 15:13:50 +02:00

937 lines
34 KiB
PHP

<?php
/**
* Admin settings handler.
*
* Manages rendering of the options page, sanitization of inputs,
* and saving general plugin configurations.
*
* @package Head_Footer_Code
* @since 1.0.0
*/
namespace Techwebux\Hfc;
// If this file is called directly, abort.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Settings {
/** @var array Settings retrieved from the main controller. */
private $settings;
/** @var Plugin_Info Plugin metadata object. */
protected $plugin;
/** @var array Allowed HTML tags for sanitization. */
public $allowed_html;
public $form_allowed_html;
public $security_risk_notice;
/**
* Initializes the class and registers admin hooks.
*
* @param Plugin_Info $plugin Instance of the plugin info object.
* @param array $settings Plugin settings array.
*/
public function __construct( Plugin_Info $plugin, $settings ) {
$this->plugin = $plugin;
$this->settings = $settings;
// Add Settings page link to plugin actions cell.
add_filter( 'plugin_action_links_' . $this->plugin->basename, array( $this, 'plugin_settings_link' ) );
// Update links in plugin row on Plugins page.
add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 2 );
// Create menu item for settings page.
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
// add_action( 'admin_init', array( $this, 'settings_init' ) );
add_action( 'admin_init', array( $this, 'settings_register' ) );
// Plugins settings page only hooks.
$current_page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( ! empty( $current_page ) && $current_page === $this->plugin->slug ) {
$this->allowed_html = Common::allowed_html();
$this->form_allowed_html = Common::form_allowed_html();
// Initiate settings section and fields.
add_action( 'admin_init', array( $this, 'settings_prepare' ) );
// Add Review CTA to the footer thankyou
add_filter( 'admin_footer_text', array( $this, 'custom_footer_thankyou' ) );
}
}
/**
* Add submenu for Head & Footer code to Tools.
*/
public function add_admin_menu() {
add_submenu_page(
'tools.php', // Parent Slug.
$this->plugin->name, // Page Title.
$this->plugin->name, // Menu Title.
'manage_options', // Capability.
$this->plugin->slug, // Menu Slug.
array( $this, 'options_page' ) // Callback.
// Position.
);
}
/**
* Register a setting and its sanitization callback.
*
* This is part of the Settings API, which lets you automatically generate
* wp-admin settings pages by registering your settings and using a few
* callbacks to control the output.
*
* @return void
*/
public function settings_register() {
$homepage_blog_posts = 'posts' === get_option( 'show_on_front', false ) ? true : false;
// Site-wide.
register_setting(
'head_footer_code_settings', // Option group.
'auhfc_settings_sitewide', // Option name.
array(
'sanitize_callback' => array( $this, 'sanitize_sitewide' ),
)
);
// Blog gomepage.
if ( $homepage_blog_posts ) {
register_setting(
'head_footer_code_settings', // Option group.
'auhfc_settings_homepage', // Option name.
array(
'sanitize_callback' => array( $this, 'sanitize_homepage' ),
)
);
}
// Articles
register_setting(
'head_footer_code_settings', // Option group.
'auhfc_settings_article', // Option name.
array(
'sanitize_callback' => array( $this, 'sanitize_article' ),
)
);
}
/**
* Register a setting and its sanitization callback
* define section and settings fields
*/
public function settings_prepare() {
/**
* Get settings from options table
*/
$homepage_blog_posts = 'posts' === get_option( 'show_on_front', false ) ? true : false;
$head_note = $this->head_note();
$this->security_risk_notice = Common::get_security_risk_notice();
/**
* Settings Sections are the groups of settings you see on WordPress settings pages
* with a shared heading. In your plugin you can add new sections to existing
* settings pages rather than creating a whole new page. This makes your plugin
* simpler to maintain and creates less new pages for users to learn.
* You just tell them to change your setting on the relevant existing page.
*/
add_settings_section(
'head_footer_code_settings_sitewide', // Id.
__( 'Site-wide head, body and footer code', 'head-footer-code' ), // Title.
array( $this, 'sitewide_settings_section_description' ), // Callback.
$this->plugin->slug // Page.
);
/**
* Register a settings field to a settings page and section.
* This is part of the Settings API, which lets you automatically generate
* wp-admin settings pages by registering your settings and using a few
* callbacks to control the output.
*/
$this->add_field(
'head', // Id.
esc_html__( 'HEAD Code', 'head-footer-code' ), // Title.
'textarea_field_render', // Callback method name.
'sitewide', // Section.
array( // Arguments.
'description' => $head_note . '<p>' . sprintf(
/* translators: %s will be replaced with preformatted HTML tag </head> */
esc_html__( 'Code to enqueue in HEAD section (before the %s).', 'head-footer-code' ),
Common::format_as_code( '</head>' )
) . '</p>',
'field_class' => 'widefat code codeEditor',
'rows' => 7,
)
);
$this->add_field(
'priority_h',
esc_html__( 'HEAD Priority', 'head-footer-code' ),
'number_field_render',
'sitewide',
array(
'description' => sprintf(
/* translators: 1: default HEAD priority, 2: preformatted HTML tag </head> */
esc_html__( 'Priority for enqueued HEAD code. Default is %1$d. Larger number inject code closer to %2$s.', 'head-footer-code' ),
10,
Common::format_as_code( '</head>' )
),
'class' => 'num',
'min' => 1,
'max' => 1000,
'step' => 1,
)
);
$this->add_field(
'do_shortcode_h',
esc_html__( 'Process HEAD Shortcodes', 'head-footer-code' ),
'select_field_render',
'sitewide',
array(
'items' => array(
'y' => __( 'Enable', 'head-footer-code' ),
'n' => __( 'Disable', 'head-footer-code' ),
),
'description' => esc_html__( 'If you wish to process shortcodes in the HEAD section, enable this option. Please note, shortcodes with malformed output in the HEAD section can break the rendering of your website!', 'head-footer-code' ),
'class' => 'regular-text',
)
);
$this->add_field(
'body',
esc_html__( 'BODY Code', 'head-footer-code' ),
'textarea_field_render',
'sitewide',
array(
'description' => '<p>' . sprintf(
/* translators: %s will be replaced with preformatted HTML tag <body> */
esc_html__( 'Code to enqueue in BODY section (after the %s).', 'head-footer-code' ),
Common::format_as_code( '<body>' )
) . '</p>',
'field_class' => 'widefat code codeEditor',
'rows' => 7,
)
);
$this->add_field(
'priority_b',
esc_html__( 'BODY Priority', 'head-footer-code' ),
'number_field_render',
'sitewide',
array(
'description' => sprintf(
/* translators: 1: default BODY priority, 2: preformatted HTML tag <body> */
esc_html__(
'Priority for enqueued BODY code. Default is %1$d. Smaller number inject code closer to %2$s.',
'head-footer-code'
),
10,
Common::format_as_code( '<body>' )
),
'class' => 'num',
'min' => 1,
'max' => 1000,
'step' => 1,
)
);
$this->add_field(
'do_shortcode_b',
esc_html__( 'Process BODY Shortcodes', 'head-footer-code' ),
'select_field_render',
'sitewide',
array(
'items' => array(
'y' => __( 'Enable', 'head-footer-code' ),
'n' => __( 'Disable', 'head-footer-code' ),
),
'description' => esc_html__( 'If you wish to process shortcodes in the BODY section, enable this option.', 'head-footer-code' ),
'class' => 'regular-text',
)
);
$this->add_field(
'footer',
esc_html__( 'FOOTER Code', 'head-footer-code' ),
'textarea_field_render',
'sitewide',
array(
'description' => '<p>' . sprintf(
/* translators: %s will be replaced with preformatted HTML tag </body> */
esc_html__( 'Code to enqueue in footer section (before the %s).', 'head-footer-code' ),
Common::format_as_code( '</body>' )
) . '</p>',
'field_class' => 'widefat code codeEditor',
'rows' => 7,
)
);
$this->add_field(
'priority_f',
esc_html__( 'FOOTER Priority', 'head-footer-code' ),
'number_field_render',
'sitewide',
array(
'description' => sprintf(
/* translators: 1: default FOOTER priority, 2: preformatted HTML tag </body> */
esc_html__( 'Priority for enqueued FOOTER code. Default is %1$d. Larger number inject code closer to %2$s.', 'head-footer-code' ),
10,
Common::format_as_code( '</body>' )
),
'class' => 'num',
'min' => 1,
'max' => 1000,
'step' => 1,
)
);
$this->add_field(
'do_shortcode_f',
esc_html__( 'Process FOOTER Shortcodes', 'head-footer-code' ),
'select_field_render',
'sitewide',
array(
'items' => array(
'y' => __( 'Enable', 'head-footer-code' ),
'n' => __( 'Disable', 'head-footer-code' ),
),
'description' => esc_html__( 'If you wish to process shortcodes in the FOOTER section, enable this option.', 'head-footer-code' ),
'class' => 'regular-text',
)
);
/**
* Add section for Homepage if show_on_front is set to Blog Posts
*/
if ( $homepage_blog_posts ) {
/**
* Settings Sections are the groups of settings you see on WordPress settings pages
* with a shared heading. In your plugin you can add new sections to existing
* settings pages rather than creating a whole new page. This makes your plugin
* simpler to maintain and creates less new pages for users to learn.
* You just tell them to change your setting on the relevant existing page.
*/
add_settings_section(
'head_footer_code_settings_homepage', // Id.
esc_html__( 'Head, body and footer code on Homepage in Blog Posts mode', 'head-footer-code' ), // Title.
array( $this, 'homepage_settings_section_description' ), // Callback.
$this->plugin->slug // Page.
);
/**
* Register a settings field to a settings page and section.
* This is part of the Settings API, which lets you automatically generate
* wp-admin settings pages by registering your settings and using a few
* callbacks to control the output.
*/
$this->add_field(
'head', // Id.
esc_html__( 'Homepage HEAD Code', 'head-footer-code' ), // Title.
'textarea_field_render', // Callback name.
'homepage', // Section.
array( // Arguments.
'label' => __( 'Homepage HEAD Code', 'head-footer-code' ),
'description' => $head_note . '<p>' . sprintf(
/* translators: %s will be replaced with preformatted HTML tag </head> */
esc_html__( 'Code to enqueue in HEAD section (before the %s) on Homepage.', 'head-footer-code' ),
Common::format_as_code( '</head>' )
) . '</p>',
'field_class' => 'widefat code codeEditor',
'rows' => 5,
)
);
$this->add_field(
'body',
esc_html__( 'Homepage BODY Code', 'head-footer-code' ),
'textarea_field_render',
'homepage',
array(
'label' => __( 'Homepage BODY Code', 'head-footer-code' ),
'description' => '<p>' . sprintf(
/* translators: %s: preformatted HTML tag <body> */
esc_html__( 'Code to enqueue in BODY section (after the %s) on Homepage.', 'head-footer-code' ),
Common::format_as_code( '<body>' )
) . '</p>',
'field_class' => 'widefat code codeEditor',
'rows' => 5,
)
);
$this->add_field(
'footer',
esc_html__( 'Homepage FOOTER Code', 'head-footer-code' ),
'textarea_field_render',
'homepage',
array(
'label' => __( 'Homepage FOOTER Code', 'head-footer-code' ),
'description' => '<p>' . sprintf(
/* translators: %s will be replaced with preformatted HTML tag </body> */
esc_html__( 'Code to enqueue in footer section (before the %s) on Homepage.', 'head-footer-code' ),
Common::format_as_code( '</body>' )
) . '</p>',
'field_class' => 'widefat code codeEditor',
'rows' => 5,
)
);
$this->add_field(
'behavior',
esc_html__( 'Behavior', 'head-footer-code' ),
'select_field_render',
'homepage',
array(
'items' => array(
'append' => __( 'Append to the site-wide code', 'head-footer-code' ),
'replace' => __( 'Replace the site-wide code', 'head-footer-code' ),
),
'description' => esc_html__( 'Chose how the Homepage specific code will be enqueued in relation to site-wide code.', 'head-footer-code' ),
'class' => 'regular-text',
)
);
$this->add_field(
'paged',
esc_html__( 'On paged homepage', 'head-footer-code' ),
'select_field_render',
'homepage',
array(
'items' => array(
'yes' => __( 'Add on paged homepage', 'head-footer-code' ),
'no' => __( 'Do not add on paged homepage', 'head-footer-code' ),
),
'description' => esc_html__( 'Chose if the Homepage specific code will be enqueued on paged pages 2, 3, and so on.', 'head-footer-code' ),
'class' => 'regular-text',
)
);
} // END condition: $homepage_blog_posts
/**
* Settings Sections are the groups of settings you see on WordPress settings pages
* with a shared heading. In your plugin you can add new sections to existing
* settings pages rather than creating a whole new page. This makes your plugin
* simpler to maintain and creates less new pages for users to learn.
* You just tell them to change your setting on the relevant existing page.
*/
add_settings_section(
'head_footer_code_settings_article', // Id.
esc_html__( 'Article specific settings', 'head-footer-code' ), // Title.
array( $this, 'article_settings_section_description' ), // Callback.
$this->plugin->slug // Page.
);
// Prepare clean list of post types w/o attachment.
$public_post_types = get_post_types( array( 'public' => true ), 'objects' );
$clean_post_types = array();
foreach ( $public_post_types as $public_post_type => $public_post_object ) {
if ( 'attachment' === $public_post_type ) {
continue;
}
$clean_post_types[ $public_post_type ] = esc_html( $public_post_object->label ) . ' (' . esc_attr( $public_post_type ) . ')';
}
$this->add_field(
'post_types', // Field key.
esc_html__( 'Post Types', 'head-footer-code' ), // Title.
'checkbox_group_field_render', // Callback method name.
'article', // Section.
array( // Arguments.
'label_for' => false,
'items' => $clean_post_types,
'description' => esc_html__( 'Choose the post types that will have an article specific section.', 'head-footer-code' )
. '<br>'
. esc_html__( 'Please note, if you add head, body, and footer code for individual articles and then disable that post type, the article-specific code will no longer be output and only the site-wide code will be used.', 'head-footer-code' ),
'class' => 'checkbox',
)
);
// Prepare list of public taxonomies, including built-in ones
$public_taxonomies = get_taxonomies( array( 'public' => true ), 'objects' );
$clean_taxonomies = array();
foreach ( $public_taxonomies as $tax_slug => $tax_object ) {
// Skip specific eg. nav_menu, post_format
if ( in_array( $tax_slug, array( 'nav_menu', 'post_format' ), true ) ) {
continue;
}
$clean_taxonomies[ $tax_slug ] = esc_html( $tax_object->label ) . ' (' . esc_attr( $tax_slug ) . ')';
}
$this->add_field(
'taxonomies', // Field key.
esc_html__( 'Taxonomies', 'head-footer-code' ), // Title.
'checkbox_group_field_render', // Callback method name.
'article', // Section.
array( // Arguments.
'label_for' => false,
'items' => $clean_taxonomies,
'description' => esc_html__( 'Choose the taxonomies that will have a taxonomy specific section.', 'head-footer-code' )
. '<br>'
. esc_html__( 'Please note, if you add head, body, and footer code for individual taxonomy and then disable that taxonomy, the txonomy-specific code will no longer be output and only the site-wide code will be used.', 'head-footer-code' ),
'class' => 'checkbox',
)
);
$this->add_field(
'allowed_roles', // Field key.
esc_html__( 'Allow for User Roles', 'head-footer-code' ), // Title.
'checkbox_group_field_render', // Callback method name.
'article', // Section.
array( // Arguments.
'label_for' => false,
'items' => array(
'editor' => __( 'Editor', 'head-footer-code' ),
'author' => __( 'Author', 'head-footer-code' ),
),
'description' => esc_html__( 'Choose which unprivileged user roles can manage article-specific and taxonomy-specific code.', 'head-footer-code' )
. '<br>'
. '<span class="warn"><strong>'
. esc_html__( 'Security Notice', 'head-footer-code' )
. '</strong><br>'
. '<i></i>' . esc_html__( 'Granting access to non-administrator roles (e.g., Editors) allows users to inject raw HTML, CSS, and JavaScript into individual posts and pages, and taxonomies!', 'head-footer-code' )
. '<br><i></i>' . esc_html__( 'This may pose a security risk if those users are not fully trusted!', 'head-footer-code' )
. '<br><i></i>' . esc_html__( 'Only allow for roles you trust to handle code responsibly!', 'head-footer-code' )
. '</span>',
'class' => 'checkbox',
)
);
}
/**
* Wrapper for add_settings_field
*
* @param string $key Field key to identify the field.
* @param string $title Formatted title of the field. Shown as the label for the field during output.
* @param string $callback_name Name of the function that fills the field with the desired form inputs.
* @param string $section Key of the section of the settings page in which to show the box.
* @param array $args {
* Optional. Extra arguments that get passed to add_settingd_field $args
*
* @type bool $label_for If `false` the setting title will not be wrapped in a `<label>` element
* @type string $class CSS Class to be added to the `<tr>` element when the field is output.
* }
*/
private function add_field(
$key,
$title,
$callback_name,
$section,
$args = array()
) {
// Compose field name from args
$full_field_name = "auhfc_settings_{$section}[{$key}]";
$args['field'] = $full_field_name;
// Generate clean_id
$clean_id = $this->generate_clean_id( $full_field_name );
// Append label_for to args if missing
if ( ! isset( $args['label_for'] ) ) {
$args['label_for'] = $clean_id;
}
// Extract value from the settings array
$args['value'] = isset( $this->settings[ $section ][ $key ] ) ? $this->settings[ $section ][ $key ] : '';
// Call WordPress Core metod
add_settings_field(
'auhfc_' . $key,
$title,
array( $this, $callback_name ),
$this->plugin->slug,
'head_footer_code_settings_' . $section,
$args
);
}
/**
* Generate valid HTML ID from the field name.
*
* @param string $field Field name (eg. 'prefix[settings][key]').
* @param string $suffix Optional suffix (eg. 'editor' or 'post').
*
* @return string Clean ID ready for HTML.
*/
public function generate_clean_id( $field, $suffix = '' ) {
$id = $field . ( $suffix ? '_' . $suffix : '' );
$id = rtrim( str_replace( array( '[', ']', '__' ), '_', $id ), '_' );
return sanitize_key( $id );
}
/**
* This function provides textarea for settings fields
*
* @param array $args Array of arguments (field, rows, field_class, value, description).
*/
public function textarea_field_render( $args ) {
if ( empty( $args ) ) {
return;
}
// Set defaults and sanitize values.
$field = isset( $args['field'] ) ? $args['field'] : '';
$field_id = isset( $args['label_for'] ) ? $args['label_for'] : $field;
$field_class = isset( $args['field_class'] ) ? $args['field_class'] : '';
$label = isset( $args['label'] ) ? $args['label'] : '';
$value = isset( $args['value'] ) ? $args['value'] : '';
$rows = isset( $args['rows'] ) ? $args['rows'] : 7;
// Compose input HTML.
$html = '<div class="description">';
$html .= '<p class="notice notice-warning">';
$html .= '<strong>' . esc_html( $this->security_risk_notice['title'] ) . '</strong> ';
$html .= esc_html( $this->security_risk_notice['message'] );
$html .= '</p></div>';
$html .= sprintf(
'<textarea name="%1$s" id="%2$s" rows="%3$s" class="%4$s" title="%5$s">%6$s</textarea>',
esc_attr( $field ), // 1
esc_attr( $field_id ), // 2
intval( $rows ), // 3
esc_attr( $field_class ), // 4
esc_html( $label ), // 5
esc_textarea( $value ) // 6
);
// Append description if exists.
if ( ! empty( $args['description'] ) ) {
$html .= sprintf( '<div class="description">%s</div>', $args['description'] );
}
// Remove Jetpack filter that may interfere with wp_kses.
remove_filter( 'pre_kses', array( 'Filter_Embedded_HTML_Objects', 'maybe_create_links' ), 100 );
// Filter allowed HTML tags and attributes.
echo wp_kses( $html, $this->allowed_html );
// Re-add the Jetpack filter if it exists.
// This is to ensure compatibility with Jetpack and other plugins that may use this filter.
if ( is_callable( array( 'Filter_Embedded_HTML_Objects', 'maybe_create_links' ) ) ) {
add_filter( 'pre_kses', array( 'Filter_Embedded_HTML_Objects', 'maybe_create_links' ), 100 );
}
}
/**
* This function provides number input for settings fields
*
* @param array $args Array of arguments (field, value, min, max, step, rows, class, description).
*/
public function number_field_render( $args ) {
if ( empty( $args ) ) {
return;
}
// Set defaults and sanitize values.
$field = isset( $args['field'] ) ? $args['field'] : '';
$field_id = isset( $args['label_for'] ) ? $args['label_for'] : $field;
$field_class = isset( $args['class'] ) ? $args['class'] : '';
$value = isset( $args['value'] ) ? $args['value'] : 0;
$min = isset( $args['min'] ) ? $args['min'] : 0;
$max = isset( $args['max'] ) ? $args['max'] : 100;
$step = isset( $args['step'] ) ? $args['step'] : 1;
// Compose input HTML.
$html = sprintf(
'<input type="number" name="%1$s" id="%2$s" value="%3$s" class="%4$s" min="%5$s" max="%6$s" step="%7$s" />',
esc_attr( $field ), // 1
esc_attr( $field_id ), // 2
intval( $value ), // 3
esc_attr( $field_class ), // 4
intval( $min ), // 5
intval( $max ), // 6
intval( $step ) // 7
);
// Append description if exists.
if ( ! empty( $args['description'] ) ) {
$html .= sprintf( '<p class="description">%s</p>', $args['description'] );
}
// Filter allowed HTML tags and attributes.
echo wp_kses( $html, $this->form_allowed_html );
}
/**
* This function provides checkbox group for settings fields
*
* @param array $args Array of arguments (items, value, field, class, description).
*/
public function checkbox_group_field_render( $args ) {
if ( empty( $args ) || ! isset( $args['items'] ) ) {
return;
}
// Set defaults and sanitize values.
$field = isset( $args['field'] ) ? $args['field'] : '';
$field_class = isset( $args['class'] ) ? $args['class'] : '';
$values = isset( $args['value'] ) ? (array) $args['value'] : array();
// Checkbox items.
$html = '<fieldset>';
foreach ( $args['items'] as $key => $label ) {
$item_id = $this->generate_clean_id( $field, $key );
$checked = checked( in_array( $key, $values, true ), true, false );
// Output filtered checkbox field.
$html .= sprintf(
'<label for="%1$s"><input type="checkbox" name="%2$s[]" id="%1$s" value="%3$s" class="%4$s" %5$s />%6$s</label><br>',
esc_attr( $item_id ), // 1
esc_attr( $field ), // 2
esc_attr( $key ), // 3
esc_attr( $field_class ), // 4
$checked, // 5
esc_html( $label ) // 6
);
}
// Append description if exists.
if ( ! empty( $args['description'] ) ) {
$html .= sprintf( '<p class="description">%s</p>', $args['description'] );
}
$html .= '</fieldset>';
// Filter allowed HTML tags and attributes.
echo wp_kses( $html, $this->form_allowed_html );
}
/**
* This function provides select for settings fields
*
* @param array $args Array of field arguments (class, field, items, value, description).
*/
public function select_field_render( $args ) {
if ( empty( $args ) ) {
return;
}
// Set defaults and sanitize values.
$field = isset( $args['field'] ) ? esc_attr( $args['field'] ) : '';
$field_id = isset( $args['label_for'] ) ? esc_attr( $args['label_for'] ) : $field;
$field_class = isset( $args['class'] ) ? esc_attr( $args['class'] ) : 'regular-text';
$current_val = isset( $args['value'] ) ? $args['value'] : '';
// Open SELECT tag.
$html = sprintf(
'<select id="%1$s" name="%2$s" class="%3$s">',
$field_id, // 1
$field, // 2
$field_class // 3
);
// Append OPTIONS.
foreach ( $args['items'] as $key => $val ) {
// Determine if this dropdown option is selected or not.
$selected = selected( $current_val, $key, false );
$html .= sprintf(
'<option %1$s value="%2$s">%3$s</option>',
$selected, // 1
esc_attr( $key ), // 2
esc_html( $val ) // 3
);
}
// Close SELECT tag.
$html .= '</select>';
// Append description if exists.
if ( ! empty( $args['description'] ) ) {
$html .= sprintf( '<p class="description">%s</p>', $args['description'] );
}
// Filter allowed HTML tags and attributes.
echo wp_kses( $html, $this->form_allowed_html );
}
/**
* Print description for site-wide section
*/
public function sitewide_settings_section_description() {
echo '<p>' . esc_html__( 'Define site-wide code and behavior. You can Add custom content like JavaScript, CSS, HTML meta and link tags, Google Analytics, site verification, etc.', 'head-footer-code' ) . '</p>';
}
/**
* Print description for homepage section
*/
public function homepage_settings_section_description() {
echo '<p>' . esc_html__( 'Define code and behavior for the Homepage in Blog Posts mode.', 'head-footer-code' ) . '</p>';
}
/**
* Print description for article section
*/
public function article_settings_section_description() {
echo '<p>' . esc_html__( 'Define what post types will support article specific features, and which non-priviledged user roles will have access to it.', 'head-footer-code' ) . '</p>';
}
/**
* Print settings page from template
*/
public function options_page() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'head-footer-code' ) );
}
// Render the settings template.
include $this->plugin->dir . '/templates/settings.php';
}
/**
* Append Settings link for Plugins page
*
* @param array $links Array of default plugin links
*
* @return array Array of plugin links with appended link for Settings page
*/
public function plugin_settings_link( $links ) {
$settings_link = '<a href="' . esc_url( admin_url( 'tools.php?page=' . $this->plugin->slug ) ) . '">' . esc_html__( 'Settings', 'head-footer-code' ) . '</a>';
array_unshift( $links, $settings_link );
return $links; // Return updated array of links
}
/**
* Add link to plugin community support
*
* @param array $links Array of default plugin meta links
* @param string $file Current hook file path
*
* @return array Array of default plugin meta links with appended link for Support community forum
*/
public function add_plugin_meta_links( $links, $file ) {
if ( $this->plugin->basename === $file ) {
$links[] = '<a href="https://wordpress.org/support/plugin/head-footer-code/" target="_blank">' . esc_html__( 'Support', 'head-footer-code' ) . '</a>';
}
// Return updated array of links
return $links;
}
/**
* Set custom footer thankyou text on plugin settings page
*
* @param string $text Default WordPress admin footer thankyou text
*
* @return string Custom Head & Footer Code review CTA text
*/
public function custom_footer_thankyou( $text ) {
return '<span id="footer-thankyou">If you ♥ <strong>Head & Footer Code</strong> please leave us a <a target="_blank" rel="nofollow" href="https://wordpress.org/support/plugin/head-footer-code/reviews/#new-post">★★★★★</a> rating. A huge thanks in advance!</span>';
}
/**
* Function to print note for head section
*/
public function head_note() {
return '<p class="notice">' . sprintf(
/* translators: 1: italicized 'unseen elements', 2: <script>, 3: <style>, 4: italicized sentence 'could break layouts or lead to unexpected situations' */
esc_html__( 'Usage of this field should be reserved for output of %1$s like %2$s and %3$s tags or additional metadata. It should not be used to add arbitrary HTML content to a page that %4$s.', 'head-footer-code' ),
'<em>' . esc_html__( 'unseen elements', 'head-footer-code' ) . '</em>',
Common::format_as_code( '<script>' ),
Common::format_as_code( '<style>' ),
'<em>' . esc_html__( 'could break layouts or lead to unexpected situations', 'head-footer-code' ) . '</em>'
) . '</p>';
}
/**
* Sanitize SiteWide settings
*
* @param array $options
* @return array $sanitized
*/
public function sanitize_sitewide( $options ) {
// Sanitize SiteWide HEAD/BODY/FOOTER code and behavior
$sanitized = Common::sanitize_hfc_data( $options );
// Sanitize SiteWide Priority
$sanitized['priority_h'] = isset( $options['priority_h'] ) ? absint( $options['priority_h'] ) : 10;
$sanitized['priority_b'] = isset( $options['priority_b'] ) ? absint( $options['priority_b'] ) : 10;
$sanitized['priority_f'] = isset( $options['priority_f'] ) ? absint( $options['priority_f'] ) : 10;
// Sanitize SiteWide shortcodes
$allowed_do_shortcode = array( 'y', 'n' );
$sanitized['do_shortcode_h'] = ( isset( $options['do_shortcode_h'] ) && in_array( $options['do_shortcode_h'], $allowed_do_shortcode, true ) ) ? $options['do_shortcode_h'] : 'n';
$sanitized['do_shortcode_b'] = ( isset( $options['do_shortcode_b'] ) && in_array( $options['do_shortcode_b'], $allowed_do_shortcode, true ) ) ? $options['do_shortcode_b'] : 'n';
$sanitized['do_shortcode_f'] = ( isset( $options['do_shortcode_f'] ) && in_array( $options['do_shortcode_f'], $allowed_do_shortcode, true ) ) ? $options['do_shortcode_f'] : 'n';
return $sanitized;
}
/**
* Sanitize Homepage settings
*
* @param array $options
* @return array $sanitized
*/
public function sanitize_homepage( $options ) {
// Sanitize SiteWide HFC code
$sanitized = Common::sanitize_hfc_data( $options );
// Sanitize Homepage Paged
$allowed_paged = array( 'yes', 'no' );
$sanitized['paged'] = ( isset( $options['paged'] ) && in_array( $options['paged'], $allowed_paged, true ) ) ? $options['paged'] : 'yes';
return $sanitized;
}
/**
* Sanitize Article settings
*
* @param array $options
* @return array $sanitized
*/
public function sanitize_article( $options ) {
$sanitized = array(
'post_types' => array(),
'taxonomies' => array(),
'allowed_roles' => array(),
);
// Sanitize Article Post Types (allow only registered public post types)
if ( ! empty( $options['post_types'] ) && is_array( $options['post_types'] ) ) {
$registered_post_types = get_post_types( array( 'public' => true ) );
foreach ( $options['post_types'] as $post_type ) {
$post_type = sanitize_key( $post_type );
if ( isset( $registered_post_types[ $post_type ] ) ) {
$sanitized['post_types'][] = $post_type;
}
}
}
// Sanitize Article Taxonomies (allow only registered public taxonomies)
if ( ! empty( $options['taxonomies'] ) && is_array( $options['taxonomies'] ) ) {
$registered_taxonmies = get_taxonomies( array( 'public' => true ) );
foreach ( $options['taxonomies'] as $taxonomy ) {
$taxonomy = sanitize_key( $taxonomy );
if ( isset( $registered_taxonmies[ $taxonomy ] ) ) {
$sanitized['taxonomies'][] = $taxonomy;
}
}
}
// Sanitize Article Allowed Roles (allow only existing WP roles on this site)
if ( ! empty( $options['allowed_roles'] ) && is_array( $options['allowed_roles'] ) ) {
$wp_roles = wp_roles()->get_names();
foreach ( $options['allowed_roles'] as $role ) {
$role = sanitize_key( $role );
if ( isset( $wp_roles[ $role ] ) ) {
$sanitized['allowed_roles'][] = $role;
}
}
}
return $sanitized;
}
}