This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ if ( ! defined( 'ABSPATH' ) ) { exit; } define( 'JAMIFY_HFS_PLUGIN_DIR', str_replace( '\\', '/', dirname( __FILE__ ) ) ); if ( !class_exists( 'HeaderAndFooterScripts' ) ) { class HeaderAndFooterScripts { /** * Constructor. */ public function __construct() { add_action( 'init', array( $this, 'init' ) ); add_action( 'admin_init', array( $this, 'admin_init' ) ); add_action( 'admin_menu', array( $this, 'admin_menu' ) ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'update_option_jamify_hfs_allow_author', array( $this, 'update_role_author' ), 10, 2 ); add_action( 'update_option_jamify_hfs_allow_contributor', array( $this, 'update_role_contributor' ), 10, 2 ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_action( 'wp_head', array( $this, 'wp_head' ), \get_option( 'jamify_hfs_insert_header_priority', 10 ) ); add_action( 'wp_body_open', array( $this, 'wp_body_open' ), \get_option( 'jamify_hfs_insert_body_priority', 10 ) ); add_action( 'wp_footer', array( $this, 'wp_footer' ), \get_option( 'jamify_hfs_insert_footer_priority', 10 ) ); } /** * Load textdomain for internationalization. */ public function init() { load_plugin_textdomain( 'header-and-footer-scripts', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' ); } /** * Initialize admin settings, migration logic, and meta boxes. */ public function admin_init() { // Migration Logic $options_to_migrate = array( 'shfs_insert_header' => 'jamify_hfs_insert_header', 'shfs_insert_body' => 'jamify_hfs_insert_body', 'shfs_insert_footer' => 'jamify_hfs_insert_footer', 'shfs_insert_header_priority' => 'jamify_hfs_insert_header_priority', 'shfs_insert_body_priority' => 'jamify_hfs_insert_body_priority', 'shfs_insert_footer_priority' => 'jamify_hfs_insert_footer_priority', 'shfs_allow_author' => 'jamify_hfs_allow_author', 'shfs_allow_contributor' => 'jamify_hfs_allow_contributor', ); foreach ( $options_to_migrate as $old => $new ) { if ( false === get_option( $new ) && false !== get_option( $old ) ) { update_option( $new, get_option( $old ) ); } } // register settings for sitewide script register_setting( 'header-and-footer-scripts', 'jamify_hfs_insert_header', 'trim' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_insert_body', 'trim' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_insert_footer', 'trim' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_insert_header_priority', 'intval' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_insert_body_priority', 'intval' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_insert_footer_priority', 'intval' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_allow_author', 'sanitize_text_field' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_allow_contributor', 'sanitize_text_field' ); register_setting( 'header-and-footer-scripts', 'jamify_hfs_clean_on_uninstall', 'sanitize_text_field' ); // add meta box to all post types foreach ( get_post_types( '', 'names' ) as $type ) { if ( current_user_can( 'unfiltered_html' ) ) { add_meta_box( 'jamify_hfs_all_post_meta', esc_html__( 'Insert Script to <head>', 'header-and-footer-scripts' ), array( $this, 'meta_setup' ), $type, 'normal', 'high' ); } } add_action( 'save_post', array( $this, 'post_meta_save' ) ); } /** * Register the options page. */ public function admin_menu() { $page = add_submenu_page( 'options-general.php', esc_html__( 'Header and Footer Scripts', 'header-and-footer-scripts' ), esc_html__( 'Header and Footer Scripts', 'header-and-footer-scripts' ), 'manage_options', 'header-and-footer-scripts', array( $this, 'jamify_hfs_options_panel' ) ); } /** * Enqueue admin scripts (CodeMirror). * * @param string $hook Current admin page hook. */ public function admin_enqueue_scripts( $hook ) { if ( 'settings_page_header-and-footer-scripts' !== $hook && 'post.php' !== $hook && 'post-new.php' !== $hook ) { return; } // Enqueue code editor for syntax highlighting $settings = wp_enqueue_code_editor( array( 'type' => 'text/html' ) ); // Enqueue admin styles wp_enqueue_style( 'jamify-hfs-admin-css', plugins_url( 'css/jamify-hfs-admin.css', __FILE__ ), array(), '2.4.2' ); // If the code editor is enabled, we need to initialize it. if ( false !== $settings ) { wp_add_inline_script( 'code-editor', sprintf( 'jQuery( function() { if ( jQuery( "#jamify_hfs_insert_header" ).length ) { wp.codeEditor.initialize( "jamify_hfs_insert_header", %1$s ); } if ( jQuery( "#jamify_hfs_insert_body" ).length ) { wp.codeEditor.initialize( "jamify_hfs_insert_body", %1$s ); } if ( jQuery( "#jamify_hfs_insert_footer" ).length ) { wp.codeEditor.initialize( "jamify_hfs_insert_footer", %1$s ); } if ( jQuery( "#jamify_hfs_inpost_head_script" ).length ) { var inpostSettings = wp.codeEditor.initialize( "jamify_hfs_inpost_head_script", %1$s ); var cm = inpostSettings.codemirror; // Fallback for some WP versions where initialize returns jQuery object or structure differs. if ( ! cm && jQuery( "#jamify_hfs_inpost_head_script" ).next( ".CodeMirror" ).length ) { cm = jQuery( "#jamify_hfs_inpost_head_script" ).next( ".CodeMirror" ).get( 0 ).CodeMirror; } if ( cm ) { cm.on( "change", function() { cm.save(); jQuery( "#jamify_hfs_inpost_head_script" ).trigger( "change" ); } ); // Refresh to ensure gutter renders correctly setTimeout( function() { cm.refresh(); }, 1 ); } } } );', wp_json_encode( $settings ) ) ); } } /** * Output scripts in wp_head. */ public function wp_head() { $meta = get_option( 'jamify_hfs_insert_header', '' ); if ( '' !== $meta ) { echo $meta, "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } $shfs_post_meta = get_post_meta( get_the_ID(), '_inpost_head_script', true ); if ( is_singular() && '' !== $shfs_post_meta ) { echo $shfs_post_meta['synth_header_script'], "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } /** * Output scripts in wp_body_open. */ public function wp_body_open() { if ( ! is_admin() && ! is_feed() && ! is_robots() && ! is_trackback() ) { $meta = get_option( 'jamify_hfs_insert_body', '' ); if ( '' !== $meta ) { echo $meta, "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } } /** * Output scripts in wp_footer. */ public function wp_footer() { if ( ! is_admin() && ! is_feed() && ! is_robots() && ! is_trackback() ) { $text = get_option( 'jamify_hfs_insert_footer', '' ); $text = convert_smilies( $text ); $text = do_shortcode( $text ); if ( '' !== $text ) { echo $text, "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } } /** * Render the plugin options page. */ public function jamify_hfs_options_panel() { // Load options page require_once JAMIFY_HFS_PLUGIN_DIR . '/inc/options.php'; } /** * Display admin notices (e.g. migration warning). */ public function admin_notices() { // Check for previous version option to show notice if ( get_option( 'shfs_script_access_level' ) ) { $dismiss_link = add_query_arg( 'shfs_dismiss_notice', 'true' ); if ( isset( $_GET['shfs_dismiss_notice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended delete_option( 'shfs_script_access_level' ); } else { ?>
add_cap( 'unfiltered_html' ); } else { $role->remove_cap( 'unfiltered_html' ); } } /** * Update 'contributor' role capabilities when option changes. * * @param mixed $old_value Old option value. * @param mixed $new_value New option value. */ public function update_role_contributor( $old_value, $new_value ) { $role = get_role( 'contributor' ); if ( 'yes' === $new_value ) { $role->add_cap( 'unfiltered_html' ); } else { $role->remove_cap( 'unfiltered_html' ); } } /** * Render the post meta box. */ public function meta_setup() { global $post; // using an underscore, prevents the meta variable // from showing up in the custom fields section $meta = get_post_meta( $post->ID, '_inpost_head_script', true ); // instead of writing HTML here, lets do an include include_once JAMIFY_HFS_PLUGIN_DIR . '/inc/meta.php'; // create a custom nonce for submit verification later echo ''; } /** * Save post meta data. * * @param int $post_id The ID of the post being saved. * @return int Post ID. */ public function post_meta_save( $post_id ) { // authentication checks // make sure data came from our meta box if ( ! isset( $_POST['jamify_hfs_post_meta_noncename'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['jamify_hfs_post_meta_noncename'] ) ), 'jamify_hfs_post_meta_save' ) ) { return $post_id; } // check user permissions if ( isset( $_POST['post_type'] ) && 'page' === $_POST['post_type'] ) { if ( ! current_user_can( 'edit_page', $post_id ) ) { return $post_id; } } else { if ( ! current_user_can( 'edit_post', $post_id ) ) { return $post_id; } } // check configured access level if ( ! current_user_can( 'unfiltered_html' ) ) { return $post_id; } $current_data = get_post_meta( $post_id, '_inpost_head_script', true ); $new_data = isset( $_POST['_inpost_head_script'] ) ? wp_unslash( $_POST['_inpost_head_script'] ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Intentional raw input for script insertion. $this->post_meta_clean( $new_data ); if ( $current_data ) { if ( is_null( $new_data ) ) { delete_post_meta( $post_id, '_inpost_head_script' ); } else { update_post_meta( $post_id, '_inpost_head_script', $new_data ); } } elseif ( ! is_null( $new_data ) ) { add_post_meta( $post_id, '_inpost_head_script', $new_data, true ); } return $post_id; } /** * Clean empty values from the meta array. * * @param array $arr The array to clean. */ public function post_meta_clean( &$arr ) { if ( is_array( $arr ) ) { foreach ( $arr as $i => $v ) { if ( is_array( $arr[ $i ] ) ) { $this->post_meta_clean( $arr[ $i ] ); if ( ! count( $arr[ $i ] ) ) { unset( $arr[ $i ] ); } } else { if ( trim( $arr[ $i ] ) == '' ) { unset( $arr[ $i ] ); } } } if ( ! count( $arr ) ) { $arr = null; } } } } $jamify_hfs = new HeaderAndFooterScripts(); // Legacy global for backward compatibility $GLOBALS['shfs_header_and_footer_scripts'] = $jamify_hfs; } /** * Backward Compatibility Shims * These functions ensure that external code calling the old global functions * continues to work by delegating to the new class instance. */ if ( ! function_exists( 'shfs_meta_setup' ) ) { function shfs_meta_setup() { global $jamify_hfs; if ( isset( $jamify_hfs ) ) { $jamify_hfs->meta_setup(); } } } if ( ! function_exists( 'shfs_post_meta_save' ) ) { function shfs_post_meta_save( $post_id ) { global $jamify_hfs; if ( isset( $jamify_hfs ) ) { return $jamify_hfs->post_meta_save( $post_id ); } return $post_id; } } if ( ! function_exists( 'shfs_post_meta_clean' ) ) { function shfs_post_meta_clean( &$arr ) { global $jamify_hfs; if ( isset( $jamify_hfs ) ) { $jamify_hfs->post_meta_clean( $arr ); } } }