HEX
Server: Apache
System: Linux 244.240.109.208.host.secureserver.net 5.14.0-611.11.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Dec 3 09:47:37 EST 2025 x86_64
User: icsla (1002)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/icsla/public_html/wp-content/plugins/megamenu/classes/style-manager.class.php
<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit; // disable direct access
}

if ( ! class_exists( 'Mega_Menu_Style_Manager' ) ) :

	/**
	 * Manages the plugin's CSS generation, caching, and enqueueing, including
	 * SCSS compilation and filesystem storage. WPML is handled in {@see Mega_Menu_Integration_Wpml}. Polylang is handled in {@see Mega_Menu_Integration_Polylang}.
	 *
	 * @since   1.0
	 * @package MegaMenu
	 */
	final class Mega_Menu_Style_Manager {

		/**
		 * Saved plugin settings from the database.
		 *
		 * @var array
		 */
		public $settings = [];


		/**
		 * When true, {@see delete_cache_after_nav_menu_locations_save} will run on shutdown.
		 *
		 * @var bool
		 */
		private $pending_delete_cache_after_nav_menu_locations = false;


		/**
		 * Constructor. Loads settings from the database.
		 *
		 * @since 1.0
		 */
		public function __construct() {
			$settings       = get_option( 'megamenu_settings', [] );
			$this->settings = is_array( $settings ) ? $settings : [];
		}


		/**
		 * Register all WordPress actions and filters used by the style manager.
		 *
		 * @since 1.0
		 * @return void
		 */
		public function setup_actions() {
			add_action( 'megamenu_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
			add_action( 'megamenu_enqueue_styles', [ $this, 'enqueue_styles' ] );

			add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ] );
			add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 999 );
			add_action( 'wp_head', [ $this, 'head_css' ], 9999 );
			add_action( 'megamenu_delete_cache', [ $this, 'delete_cache' ] );
			add_action( 'after_switch_theme', [ $this, 'delete_cache' ] );

			add_filter( 'pre_set_theme_mod_nav_menu_locations', [ $this, 'schedule_delete_cache_on_nav_menu_locations_change' ], 10, 2 );

			add_action( 'megamenu_head_css', [ $this, 'head_css' ], 999 );

			add_filter( 'megamenu_scripts_in_footer', [ $this, 'scripts_in_footer' ] );
			add_filter( "filesystem_method", [ $this, "use_direct_filesystem_method" ], 10, 4 );

		}


		/**
		 * Always use the 'direct' filesystem method when writing the style.css file.
		 *
		 * @since 3.0.1
		 * @param string $method                    Filesystem access method.
		 * @param mixed  $args                      Optional arguments passed to the filesystem method.
		 * @param string $context                   Full path to the directory that is tested for being writable.
		 * @param bool   $allow_relaxed_file_ownership Whether to allow group file ownership.
		 * @return string 'direct' within the plugin's upload directory, otherwise unchanged method.
		 */
		public function use_direct_filesystem_method( $method, $args, $context, $allow_relaxed_file_ownership ) {
			if ( $method != 'direct' && str_contains( $context, "/maxmegamenu" ) ) {
				return 'direct';
			}

		    return $method;
		}


		/**
		 * Determines whether to load JavaScript in the footer based on the configured option.
		 *
		 * @since 2.9
		 * @return bool True to load scripts in the footer, false to load in the head.
		 */
		public function scripts_in_footer() {
			return ( $this->settings['js'] ?? '' ) !== 'head';
		}


		/**
		 * When menu-location assignments change, invalidate CSS if Max Mega Menu is enabled for an affected location.
		 * Defer to `shutdown` so `nav_menu_locations` is saved before regeneration (see `set_theme_mod`).
		 *
		 * @param array|false $new_value New location => menu ID map.
		 * @param array|false $old_value Previous map or false.
		 * @return array Map passed through unchanged.
		 */
		public function schedule_delete_cache_on_nav_menu_locations_change( $new_value, $old_value ) {
			if ( ! is_array( $new_value ) ) {
				$new_value = [];
			}
			if ( ! is_array( $old_value ) ) {
				$old_value = [];
			}

			$keys = array_unique( array_merge( array_keys( $old_value ), array_keys( $new_value ) ) );

			foreach ( $keys as $location ) {
				$old_id = isset( $old_value[ $location ] ) ? (int) $old_value[ $location ] : 0;
				$new_id = isset( $new_value[ $location ] ) ? (int) $new_value[ $location ] : 0;

				if ( $old_id === $new_id ) {
					continue;
				}

				$mega = Mega_Menu_Location::find( $location );

				if ( $mega && $mega->is_enabled() ) {
					if ( ! $this->pending_delete_cache_after_nav_menu_locations ) {
						$this->pending_delete_cache_after_nav_menu_locations = true;
						add_action( 'shutdown', [ $this, 'delete_cache_after_nav_menu_locations_save' ], 999 );
					}
					break;
				}
			}

			return $new_value;
		}


		/**
		 * Run after theme mods are persisted so `get_nav_menu_locations()` matches the new assignments.
		 *
		 * @return void
		 */
		public function delete_cache_after_nav_menu_locations_save() {
			if ( ! $this->pending_delete_cache_after_nav_menu_locations ) {
				return;
			}

			$this->pending_delete_cache_after_nav_menu_locations = false;

			do_action( 'megamenu_delete_cache' );
		}


		/**
		 * Clear external plugin caches when CSS is updated or menu settings are changed.
		 *
		 * @since 2.0
		 * @return void
		 */
		public function clear_external_caches() {
			// Breeze: https://wordpress.org/plugins/breeze/
			do_action( 'breeze_clear_all_cache' );
		}


		/**
		 * Return the version of MMM that was used to generate the current CSS file.
		 *
		 * @since 1.0
		 * @return string|false Plugin version string, or false if not set.
		 */
		public static function get_css_version() {
			if ( $version = get_option('megamenu_css_version') ) {
				return $version;
			}

			return get_transient('megamenu_css_version');
		}


		/**
		 * Return the timestamp when the menu CSS was last generated.
		 *
		 * @since 1.0
		 * @return int|false Unix timestamp, or false if not set.
		 */
		public static function get_css_last_updated() {
			if ( $date = get_option('megamenu_css_last_updated') ) {
				return $date;
			}

			return get_transient('megamenu_css_last_updated');
		}


		/**
		 * Return the default menu theme settings array.
		 * Delegates to Mega_Menu_Theme::get_default() for backward compatibility.
		 *
		 * @since 1.0
		 * @return array Default theme settings.
		 */
		public function get_default_theme() {
			return Mega_Menu_Theme::get_default()->settings;
		}


		/**
		 * Return a filtered list of all themes (default + saved custom), fully merged.
		 * Delegates to Mega_Menu_Theme::get_all() for backward compatibility.
		 *
		 * @since 1.0
		 * @return array Map of theme ID to settings array.
		 */
		public function get_themes() {
			$theme_objects = Mega_Menu_Theme::get_all();
			$themes        = [];

			foreach ( $theme_objects as $id => $theme ) {
				$themes[ $id ] = $theme->settings;
			}

			return $themes;
		}


		/**
		 * Whether the plugin is running in debug mode.
		 *
		 * @since 1.3.1
		 * @return bool True if MEGAMENU_DEBUG is defined and true.
		 */
		private function is_debug_mode() {
			return ( defined( 'MEGAMENU_DEBUG' ) && MEGAMENU_DEBUG === true );
		}


		/**
		 * Return the menu CSS. Uses the transient cache when available.
		 *
		 * @since 1.3.1
		 * @return string Compiled CSS string.
		 */
		public function get_css() {

			if ( ( $css = $this->get_cached_css() ) && ! $this->is_debug_mode() ) {
				return $css;
			} else {
				return $this->generate_css();
			}
		}


		/**
		 * Compile CSS for all active menu locations (same rules as {@see generate_css()}).
		 *
		 * @since 3.9.3
		 * @param bool $fail_on_location_error When true, return a {@see WP_Error} if any location fails to compile.
		 *                                     When false, skip failed locations (legacy {@see generate_css()} behaviour).
		 * @return string|WP_Error Full CSS string including filters and `.wp-block {}` hack, or {@see WP_Error}.
		 *
		 * The {@see 'megamenu_include_location_in_compiled_css'} filter controls whether each location is compiled (default true).
		 */
		private function compile_active_menu_css( $fail_on_location_error = false ) {

			if ( function_exists( 'wp_raise_memory_limit' ) ) {
				wp_raise_memory_limit();
			}

			$settings       = get_option( 'megamenu_settings', [] );
			$this->settings = is_array( $settings ) ? $settings : [];

			if ( empty( $this->settings ) ) {
				if ( $fail_on_location_error ) {
					return new WP_Error(
						'megamenu_no_settings',
						__( 'CSS generation failed: no menu settings found.', 'megamenu' )
					);
				}

				return '/** CSS Generation Failed. No menu settings found **/';
			}

			$date = date( 'l jS F Y H:i:s e' );
			$time = time();

			$css = '@charset "UTF-8";' . "\n\n";
			$css .= "/** THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MAKE MANUAL EDITS! **/\n";
			$css .= "/** Custom CSS should be added to Mega Menu > Menu Themes > Custom Styling **/\n\n";
			$css .= ".mega-menu-last-modified-{$time} { content: '{$date}'; }\n\n";

			foreach ( Mega_Menu_Location::get_all() as $location ) {
				if ( $location->is_active() && apply_filters( 'megamenu_include_location_in_compiled_css', true, $location->id ) ) {
					$compiled_css = $location->generate_css();

					if ( is_wp_error( $compiled_css ) ) {
						if ( $fail_on_location_error ) {
							return $compiled_css;
						}
						continue;
					}

					$css .= $compiled_css;
				}
			}

			if ( strlen( $css ) ) {
				$css = apply_filters( 'megamenu_compiled_css', $css );

				$css .= ".wp-block {}"; // hack required for loading CSS in site editor https://github.com/WordPress/gutenberg/issues/40603#issuecomment-1112807162
			}

			return $css;
		}


		/**
		 * Generate and cache the CSS for all active menus.
		 * CSS is compiled by scssphp using the file located in /css/megamenu.scss.
		 *
		 * @since 1.0
		 * @return string Compiled CSS string, or an error comment if generation failed.
		 */
		public function generate_css() {

			$css = $this->compile_active_menu_css( false );

			if ( is_wp_error( $css ) ) {
				return '';
			}

			if ( $this->settings && strlen( $css ) ) {
				$this->set_cached_css( $css );
				$this->save_to_filesystem( $css );
			}

			return $css;
		}

		/**
		 * Saves the generated CSS to the uploads folder.
		 *
		 * @since 1.6.1
		 * @param string $css The compiled CSS to write.
		 * @return void
		 */
		private function save_to_filesystem( $css ) {
			global $wp_filesystem;

			if ( ! $wp_filesystem ) {
				require_once( ABSPATH . 'wp-admin/includes/file.php' );
			}

			$upload_dir = wp_upload_dir();
			$filename   = $this->get_css_filename();

			$dir = trailingslashit( $upload_dir['basedir'] ) . 'maxmegamenu/';

			delete_option( 'megamenu_failed_to_write_css_to_filesystem' );

			WP_Filesystem(false, $dir);

			if ( ! $wp_filesystem->is_dir( $dir ) ) {
				$wp_filesystem->mkdir( $dir );
			}

			if ( ! $wp_filesystem->put_contents( $dir . $filename, $css ) ) {
				// File write failed.
				// Update CSS output option to 'head' to stop us from attempting to regenerate the CSS on every request.

				$method = $this->get_css_output_method();

				if ( in_array( $method, [ 'disabled' ] ) ) {
					return;
				}

				$this->settings['css'] = 'head';
				update_option( 'megamenu_settings', $this->settings );

				update_option( 'megamenu_failed_to_write_css_to_filesystem', 'true' );
			}

		}


		/**
		 * Before a theme is saved, attempt to compile it to verify it produces valid CSS.
		 * Delegates to Mega_Menu_Theme for backward compatibility.
		 *
		 * @since 1.3
		 * @param array $theme Theme settings array to test.
		 * @return string|WP_Error Compiled CSS on success, or WP_Error on failure.
		 */
		public function test_theme_compilation( $theme ) {
			return Mega_Menu_Theme::from_settings( $theme )->test_compilation();
		}


		/**
		 * Compiles raw SCSS into CSS for a particular menu location.
		 * Delegates to Mega_Menu_Location::generate_css() for backward compatibility.
		 *
		 * @since 1.3
		 * @param string $location The menu location slug.
		 * @param array  $theme    Theme settings array.
		 * @param int    $menu_id  Menu term ID.
		 * @return string|WP_Error Compiled CSS on success, or WP_Error on failure.
		 */
		public function generate_css_for_location( $location, $theme, $menu_id ) {
			$location_obj = Mega_Menu_Location::find( $location );

			if ( ! $location_obj ) {
				$location_obj = new Mega_Menu_Location( $location, $location );
			}

			return $location_obj->generate_css( Mega_Menu_Theme::from_settings( $theme ) );
		}

		/**
		 * Enqueue public CSS files required by Mega Menu.
		 *
		 * @since 1.0
		 * @return void
		 */
		public function enqueue_styles() {
			if ( ! ( defined( 'MEGAMENU_PREVIEW' ) && MEGAMENU_PREVIEW ) && 'fs' === $this->get_css_output_method() ) {
				$this->enqueue_fs_style();
			}

			do_action( 'megamenu_enqueue_public_scripts' );

		}

		/**
		 * Enqueue public JavaScript files required by Mega Menu.
		 *
		 * @since 1.0
		 * @return void
		 */
		public function enqueue_scripts() {
			$js_path = MEGAMENU_BASE_URL . 'js/maxmegamenu.js';

			$dependencies = apply_filters( 'megamenu_javascript_dependencies', [ 'jquery', 'hoverIntent' ] );

			$scripts_in_footer = apply_filters( 'megamenu_scripts_in_footer', true );

			if ( defined( 'MEGAMENU_SCRIPTS_IN_FOOTER' ) ) {
				$scripts_in_footer = MEGAMENU_SCRIPTS_IN_FOOTER;
			}

			$handle = apply_filters( 'megamenu_javascript_handle', 'megamenu' );

			wp_enqueue_script( $handle, $js_path, $dependencies, MEGAMENU_VERSION, $scripts_in_footer );

			$params = apply_filters( 'megamenu_javascript_localisation', [] );

			if ( count( $params) ) {
				wp_localize_script( $handle, 'megamenu', $params );
			}
		}



		/**
		 * Enqueue the stylesheet saved to the uploads filesystem.
		 *
		 * @since 1.6.1
		 * @return void
		 */
		public function enqueue_fs_style() {

			$upload_dir = wp_upload_dir();

			$filename = $this->get_css_filename();

			$filepath = trailingslashit( $upload_dir['basedir'] ) . 'maxmegamenu/' . $filename;

			if ( ! is_file( $filepath ) || $this->is_debug_mode() ) {
				// regenerate the CSS and save to filesystem.
				$this->generate_css();
			}

			// file should now exist.
			if ( is_file( $filepath ) ) {

				$css_url = trailingslashit( $upload_dir['baseurl'] ) . 'maxmegamenu/' . $filename;

				$protocol = is_ssl() ? 'https://' : 'http://';

				// ensure we're using the correct protocol.
				$css_url = str_replace( [ 'http://', 'https://' ], $protocol, $css_url );

				wp_enqueue_style( 'megamenu', $css_url, false, substr( md5( (string) filemtime( $filepath ) ), 0, 6 ) );

			}

		}


		/**
		 * Store compiled CSS in the transient cache and update version/timestamp options.
		 *
		 * @since 1.6.1
		 * @param string $css The compiled CSS to cache.
		 * @return void
		 */
		private function set_cached_css( $css ) {
			// set a far expiration date to prevent transient from being autoloaded.
			$hundred_years_in_seconds = 3153600000;

			set_transient( $this->get_transient_key(), $css, $hundred_years_in_seconds );
			update_option( 'megamenu_css_version', MEGAMENU_VERSION );
			update_option( 'megamenu_css_last_updated', time() );
		}


		/**
		 * Return the cached CSS if it exists.
		 *
		 * @since 1.9
		 * @return string|false Cached CSS string, or false if the transient has expired.
		 */
		private function get_cached_css() {
			return get_transient( $this->get_transient_key() );
		}


		/**
		 * Regenerate CSS, flush the filesystem directory, and repopulate the cache.
		 *
		 * @return bool True on success, false if CSS compilation produced a WP_Error.
		 */
		public function delete_cache() {
			global $wp_filesystem;

			$css = $this->compile_active_menu_css( true );

			if ( is_wp_error( $css ) ) {
				do_action( 'megamenu_delete_cache_failed', $css );
				return false;
			}

			if ( ! $wp_filesystem ) {
				require_once( ABSPATH . 'wp-admin/includes/file.php' );
			}

			$upload_dir = wp_upload_dir();
			$dir        = trailingslashit( $upload_dir['basedir'] ) . 'maxmegamenu/';

			WP_Filesystem(false, $dir);
			$wp_filesystem->rmdir( $dir, true );

			delete_transient( $this->get_transient_key() );

			if ( strlen( $css ) ) {
				$this->set_cached_css( $css );
				$this->save_to_filesystem( $css );
			}

			$this->clear_external_caches();

			do_action( 'megamenu_after_delete_cache' );

			return true;

		}


		/**
		 * Return the key to use for the CSS transient
		 *
		 * @since 1.9
		 * @return string
		 */
		private function get_transient_key() {
			return apply_filters( 'megamenu_css_transient_key', 'megamenu_css' );
		}


		/**
		 * Return the filename to use for the stylesheet.
		 * The filename is filtered to be unique on multisite setups.
		 *
		 * @since 1.6.1
		 * @return string CSS filename with .css extension.
		 */
		public function get_css_filename() {
			return apply_filters( 'megamenu_css_filename', 'style' ) . '.css';
		}


		/**
		 * Return the configured CSS output method.
		 *
		 * @since 1.0
		 * @return string 'fs' (filesystem), 'head', or 'disabled'.
		 */
		private function get_css_output_method() {
			return $this->settings['css'] ?? 'fs';
		}


		/**
		 * Output the Mega Menu CSS inline in the <head>.
		 *
		 * @since 1.3.1
		 * @return void
		 */
		public function head_css() {

			if ( defined( 'MEGAMENU_PREVIEW' ) && MEGAMENU_PREVIEW ) {
				$css = $this->get_css();
				echo '<style class="megamenu-css">' . str_replace( [ '  ', "\n" ], '', $css ) . "</style>\n";
				return;
			}

			$method = $this->get_css_output_method();

			// Filesystem and disabled modes use linked styles or no CSS — do not emit a
			// placeholder <style class="megamenu-css"> (breaks block editor iframe rules in WP 6.9+).
			if ( in_array( $method, [ 'disabled', 'fs' ], true ) ) {
				return;
			}

			$css = $this->get_css();

			echo '<style class="megamenu-css">' . str_replace( [ '  ', "\n" ], '', $css ) . "</style>\n";

		}


	}

endif;