PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.4.2
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.4.2
0.5.6 0.5.5 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.9 0.4.8 0.4.7 0.4.6 trunk 0.0.1 0.0.2 0.2.8 0.2.9 0.3.0 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 0.3.8 All 32 releases
← All changes | common/admin.php +216 -87 0.3.20.4.2 View file →
@@ -1,13 +1,29 @@
1 1 <?php
2 2
3 -if ( !class_exists( 'MeowCommon_Admin' ) ) {
3 +if ( !class_exists( 'MeowKit_MWCODE_Admin' ) ) {
4 4
5 - class MeowCommon_Admin {
5 + class MeowKit_MWCODE_Admin {
6 + public static $loaded = false;
7 + public static $version = '4.0';
8 + public static $admin_version = '4.0';
6 9
7 - public static $loaded = false;
8 - public static $version = "4.0";
9 - public static $admin_version = "4.0";
10 + /**
11 + * Storage for instances that need deferred initialization.
12 + *
13 + * WordPress Loading Sequence Problem:
14 + * 1. Load all plugin files
15 + * 2. Fire 'plugins_loaded' hook ← Most plugins instantiate Admin here
16 + * 3. Load wp-includes/pluggable.php ← current_user_can() defined here
17 + * 4. Fire 'init' hook ← Safe to use pluggable functions
18 + *
19 + * When plugins instantiate during 'plugins_loaded', the pluggable functions
20 + * (current_user_can, wp_get_current_user) don't exist yet. This array stores
21 + * instances until 'init' when we can safely call those functions.
22 + *
23 + * @var array
24 + */
25 + private static $deferred_instances = array();
10 26
11 27 public $prefix; // prefix used for actions, filters (mfrh)
12 28 public $mainfile; // plugin main file (media-file-renamer.php)
13 29 public $domain; // domain used for translation (media-file-renamer)
@@ -12,65 +28,166 @@
12 28 public $mainfile; // plugin main file (media-file-renamer.php)
13 29 public $domain; // domain used for translation (media-file-renamer)
14 30 public $isPro = false;
15 31
16 - public static $logo = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAxNDM0IDk0NyI+CiAgPHBhdGggZmlsbD0iIzAwMCIgZD0iTTgwNSA3NzdhNzkyIDc5MiAwIDAgMS0yNjItNDMgODExIDgxMSAwIDAgMS0yODYtMTY0QTk1OSA5NTkgMCAwIDEgNiAyMDAgMTU4IDE1OCAwIDAgMSAzMDQgOTdjNDEgOTYgOTQgMTc1IDE1OSAyMzNhNDk3IDQ5NyAwIDAgMCAzNzYgMTI5IDYwIDYwIDAgMCAxIDY3IDYwbDI3IDE4NmM0IDMzLTE4IDYzLTUxIDY4LTYgMC0zNCA0LTc3IDRaTTEyMiAxNjhsMiA1YTg0MSA4NDEgMCAwIDAgMjEyIDMwNyA2OTIgNjkyIDAgMCAwIDQ2OSAxNzdsLTExLTc2YTYxNiA2MTYgMCAwIDEtNDEyLTE2MiA3NjkgNzY5IDAgMCAxLTE4OC0yNzYgMzggMzggMCAwIDAtNTAtMjBjLTE4IDctMjcgMjctMjIgNDVaIi8+CiAgPHBhdGggZmlsbD0iI0ZEQTk2MCIgZD0ibTY0IDE4NCA0IDEyYTkwMCA5MDAgMCAwIDAgMjI4IDMyOSA3NTIgNzUyIDAgMCAwIDU3NyAxODhsLTI3LTE5NGE1NjMgNTYzIDAgMCAxLTQyMy0xNDQgNzA5IDcwOSAwIDAgMS0xNzQtMjU1IDk4IDk4IDAgMCAwLTE4NSA2NFoiLz4KICA8bWFzayBpZD0iYSIgd2lkdGg9IjgxNCIgaGVpZ2h0PSI2NTciIHg9IjYwIiB5PSI2MCIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiPgogICAgPHBhdGggZmlsbD0iI2ZmZiIgZD0ibTY0IDE4NCA0IDEyYTkwMCA5MDAgMCAwIDAgMjI4IDMyOSA3NTIgNzUyIDAgMCAwIDU3NyAxODhsLTI3LTE5NGE1NjMgNTYzIDAgMCAxLTQyMy0xNDQgNzA5IDcwOSAwIDAgMS0xNzQtMjU1IDk4IDk4IDAgMCAwLTE4NSA2NFoiLz4KICA8L21hc2s+CiAgPGcgbWFzaz0idXJsKCNhKSI+CiAgICA8cGF0aCBmaWxsPSIjODA0NjI1IiBkPSJNMTIwIDUzMmMtNDEgMC04NC01LTEzMC0xNWwzMS0xNDVjMTAxIDIxIDE4MCAxMiAyMzMtMjcgNzAtNTEgODAtMTQxIDgwLTE0MmwxNDkgMTNhMzYzIDM2MyAwIDAgMS0xMzkgMjQ4IDM1MSAzNTEgMCAwIDEtMjI0IDY4Wm0zNjkgMTc1YzQ3LTMxIDg0LTcxIDExMC0xMTYgMzItNTYgNDYtMTIzIDQyLTE5Mi0zLTUxLTE1LTg3LTE2LTkxbC0xNDEgNDhhMjI1IDIyNSAwIDAgMS0xNSAxNjFjLTMzIDU4LTEwMSA5OS0yMDMgMTIwbDMwIDE0NmM3Ni0xNiAxNDEtNDEgMTkzLTc2Wk02MiAyNjljNjQtNCAxMjItMjIgMTc0LTUzQTQxMyA0MTMgMCAwIDAgNDIxLTQ3TDE4NC05MnYtMXMtMTYgNzEtNzMgMTAzQzkyIDIxIDcwIDI3IDQ0IDI5IDcgMzEtMzcgMjQtODYgOGwtNzQgMjI5YTYyMyA2MjMgMCAwIDAgMjIyIDMyWiIvPgogIDwvZz4KICA8cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTM3MyA5NDdoLTExMGMtMzMgMC02MC0yNy02MC02MHYtOTdsLTM2IDg3YTYyIDYyIDAgMCAxLTU2IDM3aC03OWMtMjUgMC00Ni0xNC01Ni0zN2wtMzYtODd2OTdjMCAzMy0yNyA2MC02MCA2MEg3NjljLTMzIDAtNjAtMjctNjAtNjBWMzE2YzAtMzMgMjctNjAgNjAtNjBoMTQxYzI0IDAgNDYgMTUgNTUgMzdsMTA2IDI1OCAxMDctMjU4YzktMjIgMzEtMzcgNTUtMzdoMTQwYzM0IDAgNjAgMjcgNjAgNjB2NTcxYzAgMzMtMjYgNjAtNjAgNjBabS0zMTYtMTg4IDE0IDM0IDE1LTM0LTExIDFoLTdsLTExLTFabTE5OS0zMTRoN2MyMSAwIDQwIDExIDUwIDI4di05N2gtNDBsLTI5IDcwIDEyLTFabS00MjctNjl2OTdjMTEtMTcgMjktMjggNTEtMjhoNmwxMyAxLTI5LTcwaC00MVoiLz4KICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNNzY5IDg4N1YzMTZoMTQxbDE1OCAzODRoN2wxNTgtMzg0aDE0MHY1NzFoLTExMFY1MDVoLTdsLTE0NSAzNDloLTc5TDg4NiA1MDVoLTZ2MzgySDc2OVoiLz4KPC9zdmc+Cg==';
32 + // Store constructor params that affect per-instance setup
33 + private $disableReview = false;
17 34
35 + public static $logo = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNDYiIHZpZXdCb3g9IjAgMCA2NCA0NiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZyBjbGlwUGF0aD0idXJsKCNjbGlwMF8zMTBfMjI5KSI+CiAgICA8cGF0aCBkPSJNNjQgMzAuNjQwOEM2NCAyNy43OTg1IDYwLjA4MTYgMjUuODMwMyA1NS44Mjk4IDI1LjgzMDNDNTQuODU5MyAyNS44MzAzIDUzLjkzMTEgMjUuOTMzIDUzLjA3NiAyNi4xMjUzQzQ5Ljg4NjUgMTkuMDc5IDQxLjY1MzkgMTMuMDg1MyAzMi4wMDAyIDEzLjA4NTNDMzAuODMzNyAxMy4wODUzIDI5LjY4ODEgMTMuMTcyNyAyOC41Njk4IDEzLjMzOTJDMjcuMjA2OSAxMC4zMDc2IDIyLjY3NjIgMi40MzQyNiAxMS41OTU0IDAuMDgzMDA2NEMxMS4wNDkxIC0wLjAzMjc0NTYgMTAuNDk0NiAwLjI0MDU3OCAxMC4yNTkgMC43NDY5QzguODU5MTMgMy43NTYwOCA0Ljc0MjQ3IDE0LjQxMTYgMTAuMjQwMyAyNS45OTMxQzkuNTgxNjUgMjUuODg2NCA4Ljg4NzUxIDI1LjgzMDMgOC4xNzAyMiAyNS44MzAzQzMuOTE4MzkgMjUuODMwMyAwIDI3Ljc5ODUgMCAzMC42NDA4QzAgMzMuNDgzIDMuOTE4MzkgMzUuMjI3MiA4LjE3MDIyIDM1LjIyNzJDOC43MTEyNyAzNS4yMjcyIDkuMjM5MjUgMzUuMTk4OCA5Ljc0ODkzIDM1LjE0MzVDOS40MzYwMiAzNS4yNjY0IDkuMTIyNzUgMzUuNDA3NSA4LjgxMTcxIDM1LjU2NzdDNS42OTM4OCAzNy4xNzA3IDMuOTY4OCA0MC4wMzEyIDQuOTU5MDQgNDEuOTU2OEM1Ljk0ODkgNDMuODgyNCA5LjI3OTIgNDQuMTQ0MiAxMi4zOTcgNDIuNTQxMkMxMy4wNDY0IDQyLjIwNzQgMTMuNjM0OCA0MS44MTkgMTQuMTUxNiA0MS4zOTZDMTguMjYyNyA0NC40OTY3IDI0LjcyODMgNDUuOTgwOSAzMS45OTk4IDQ1Ljk4MDlDMzkuMjcxMyA0NS45ODA5IDQ1LjczNyA0NC40OTY3IDQ5Ljg0OCA0MS4zOTZDNTAuMzY0NCA0MS44MTkgNTAuOTUzMyA0Mi4yMDc0IDUxLjYwMjYgNDIuNTQxMkM1NC43MjA0IDQ0LjE0NDIgNTguMDUwMyA0My44ODI0IDU5LjA0MDYgNDEuOTU2OEM2MC4wMzA1IDQwLjAzMTIgNTguMzA1NyAzNy4xNzA3IDU1LjE4NzkgMzUuNTY3N0M1NC44NzYxIDM1LjQwNzUgNTQuNTYyMSAzNS4yNjY3IDU0LjI0ODUgMzUuMTQzNUM1NC43NTg5IDM1LjE5ODggNTUuMjg3NiAzNS4yMjc1IDU1LjgyOTQgMzUuMjI3NUM2MC4wODEyIDM1LjIyNzUgNjMuOTk5NiAzMy40ODM0IDYzLjk5OTYgMzAuNjQxMUw2NCAzMC42NDA4WiIgZmlsbD0id2hpdGUiLz4KICAgIDxwYXRoIGQ9Ik0yMi4yMjkzIDM2Ljc0NDNDMjYuNTkzNSAzNi43NDQzIDMwLjEzMTQgMzMuMjA2NCAzMC4xMzE0IDI4Ljg0MjJDMzAuMTMxNCAyNC40NzggMjYuNTkzNSAyMC45NDAxIDIyLjIyOTMgMjAuOTQwMUMxNy44NjUxIDIwLjk0MDEgMTQuMzI3MSAyNC40NzggMTQuMzI3MSAyOC44NDIyQzE0LjMyNzEgMzMuMjA2NCAxNy44NjUxIDM2Ljc0NDMgMjIuMjI5MyAzNi43NDQzWiIgZmlsbD0iIzAwRTI4RSIvPgogICAgPHBhdGggZD0iTTIyLjI2NTUgMzMuMTM2MUMyMy41MDIyIDMzLjEzNjEgMjQuNTA0NyAzMS4yODA1IDI0LjUwNDcgMjguOTkxNUMyNC41MDQ3IDI2LjcwMjQgMjMuNTAyMiAyNC44NDY4IDIyLjI2NTUgMjQuODQ2OEMyMS4wMjg4IDI0Ljg0NjggMjAuMDI2MiAyNi43MDI0IDIwLjAyNjIgMjguOTkxNUMyMC4wMjYyIDMxLjI4MDUgMjEuMDI4OCAzMy4xMzYxIDIyLjI2NTUgMzMuMTM2MVoiIGZpbGw9IiMzQzZFOEIiLz4KICAgIDxwYXRoIGQ9Ik0zMS45OTk4IDM3LjkxNTZDMzMuNDIzNyAzNy45MTU2IDM0LjU3ODEgMzcuMzQwOSAzNC41NzgxIDM2LjYzMTlDMzQuNTc4MSAzNS45MjI5IDMzLjQyMzcgMzUuMzQ4MSAzMS45OTk4IDM1LjM0ODFDMzAuNTc1OCAzNS4zNDgxIDI5LjQyMTUgMzUuOTIyOSAyOS40MjE1IDM2LjYzMTlDMjkuNDIxNSAzNy4zNDA5IDMwLjU3NTggMzcuOTE1NiAzMS45OTk4IDM3LjkxNTZaIiBmaWxsPSIjRkY5NDkzIi8+CiAgICA8cGF0aCBkPSJNNTQuMjUwMyAzNS4xMDU4QzU0Ljc2IDM1LjE2MTEgNTUuMjg3OSAzNS4xODk0IDU1LjgyOSAzNS4xODk0QzYwLjA4MDggMzUuMTg5NCA2My45OTkyIDMzLjQ0NTMgNjMuOTk5MiAzMC42MDNDNjMuOTk5MiAyNy43NjA4IDYwLjA4MDggMjUuNzkyNiA1NS44MjkgMjUuNzkyNkM1NS4xMTE3IDI1Ljc5MjYgNTQuNDE3NiAyNS44NDkgNTMuNzU4NSAyNS45NTU4QzU5LjI1NjcgMTQuMzc0MiA1NS4xMzk3IDMuNzE4NzIgNTMuNzQwMiAwLjcwOTU0NkM1My41MDQ2IDAuMjAzMjI1IDUyLjk1MDEgLTAuMDcwMDk5MSA1Mi40MDM4IDAuMDQ1NjUyOUM0MS4zMjMgMi4zOTY5MSAzNi43OTIzIDEwLjI3MDcgMzUuNDI5OCAxMy4zMDE1QzM0LjQ1NDEgMTMuMTU2NiAzMy40NTc5IDEzLjA3MTEgMzIuNDQ1MiAxMy4wNTE3QzMxLjI3NDMgMjAuMDMzIDI4Ljk2NTYgNDMuOTM2NSA1NC4zNDM2IDM1LjE0MzlDNTQuMzEyMyAzNS4xMzEyIDU0LjI4MTMgMzUuMTE4MSA1NC4yNDk5IDM1LjEwNThINTQuMjUwM1oiIGZpbGw9IiMyQjlERkYiLz4KICAgIDxwYXRoIGQ9Ik00MS43MzQyIDMzLjEzNjFDNDIuOTcwOSAzMy4xMzYxIDQzLjk3MzUgMzEuMjgwNSA0My45NzM1IDI4Ljk5MTVDNDMuOTczNSAyNi43MDI0IDQyLjk3MDkgMjQuODQ2OCA0MS43MzQyIDI0Ljg0NjhDNDAuNDk3NSAyNC44NDY4IDM5LjQ5NSAyNi43MDI0IDM5LjQ5NSAyOC45OTE1QzM5LjQ5NSAzMS4yODA1IDQwLjQ5NzUgMzMuMTM2MSA0MS43MzQyIDMzLjEzNjFaIiBmaWxsPSIjM0M2RThCIi8+CiAgPC9nPgogIDxkZWZzPgogICAgPGNsaXBQYXRoIGlkPSJjbGlwMF8zMTBfMjI5Ij4KICAgICAgPHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjQ1Ljk2MTciIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDAuMDE5MTY1KSIvPgogICAgPC9jbGlwUGF0aD4KICA8L2RlZnM+Cjwvc3ZnPgo=';
36 +
18 37 public function __construct( $prefix, $mainfile, $domain, $isPro = false, $disableReview = false, $freeOnly = false ) {
19 38
20 - if ( !MeowCommon_Admin::$loaded ) {
21 - if ( is_admin() ) {
39 + // ALWAYS set instance properties first - these are needed regardless of when setup runs
40 + $this->prefix = $prefix;
41 + $this->mainfile = $mainfile;
42 + $this->domain = $domain;
43 + $this->isPro = $isPro;
44 + $this->disableReview = $disableReview;
22 45
23 - if ( MeowCommon_Helpers::is_asynchronous_request() ) {
24 - return;
25 - }
46 + if ( is_admin() ) {
26 47
27 - // Check potential issues with this WordPress install, other plugins, etc.
28 - new MeowCommon_Issues( $prefix, $mainfile, $domain );
48 + // Skip AJAX and REST requests to avoid unnecessary processing
49 + if ( MeowKit_MWCODE_Helpers::is_asynchronous_request() ) {
50 + return;
51 + }
29 52
30 - // Create the Meow Apps Menu (priority 5 to ensure it's created early)
31 - add_action( 'admin_menu', array( $this, 'admin_menu_start' ), 5 );
32 - $page = isset( $_GET["page"] ) ? sanitize_text_field( $_GET["page"] ) : null;
33 - if ( $page === 'meowapps-main-menu' ) {
34 - add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 100000, 1 );
35 - }
53 + // Check if WordPress pluggable functions are available yet.
54 + // These are defined in wp-includes/pluggable.php, which WordPress loads
55 + // AFTER the 'plugins_loaded' hook but BEFORE the 'init' hook.
56 + if ( !function_exists( 'current_user_can' ) || !function_exists( 'wp_get_current_user' ) ) {
57 + // Functions don't exist yet - defer admin setup until 'init' hook
58 + // This is NORMAL behavior when plugins instantiate on 'plugins_loaded'
59 + $this->defer_admin_setup();
60 + // Continue to rest of constructor (filters, license checks, etc.)
61 + } else {
62 + // Functions already exist - safe to run admin setup immediately
63 + // This happens when plugins instantiate on 'init' or later
64 + $this->run_admin_setup();
36 65 }
37 - MeowCommon_Admin::$loaded = true;
66 +
67 + // License-related admin notices (doesn't require pluggable functions)
68 + $license = get_option( $this->prefix . '_license', '' );
69 + if ( !empty( $license ) && !$this->isPro ) {
70 + add_action( 'admin_notices', [ $this, 'admin_notices_licensed_free' ] );
71 + }
38 72 }
39 73
40 - // Variables for this plugin
41 - $this->prefix = $prefix;
42 - $this->mainfile = $mainfile;
43 - $this->domain = $domain;
44 - $this->isPro = $isPro;
74 + // ALWAYS register these filters (they work at any time)
75 + add_filter( 'plugin_row_meta', [ $this, 'custom_plugin_row_meta' ], 10, 2 );
76 + add_filter( 'edd_sl_api_request_verify_ssl', [ $this, 'request_verify_ssl' ], 10, 0 );
77 + }
45 78
46 - // If there is no mainfile, it's either a Pro only Plugin (with no Free version available) or a Theme.
79 + /**
80 + * Defer admin setup until WordPress 'init' hook.
81 + *
82 + * This method stores the current instance and registers a one-time
83 + * 'init' hook callback that will process all deferred instances.
84 + *
85 + * Why defer? Because we need current_user_can() to check permissions,
86 + * and that function doesn't exist until after 'plugins_loaded'.
87 + */
88 + private function defer_admin_setup() {
89 + // Add this instance to the queue for processing on 'init'
90 + self::$deferred_instances[] = $this;
47 91
48 - if ( is_admin() ) {
49 - $license = get_option( $this->prefix . '_license', "" );
50 - if ( !empty( $license ) && !$this->isPro ) {
51 - add_action( 'admin_notices', array( $this, 'admin_notices_licensed_free' ) );
92 + // Register the 'init' hook only once (for the first deferred instance)
93 + if ( count( self::$deferred_instances ) === 1 ) {
94 + add_action( 'init', array( __CLASS__, 'process_deferred_instances' ) );
95 + }
96 + }
97 +
98 + /**
99 + * Static callback for 'init' hook - processes all deferred instances.
100 + *
101 + * By the time 'init' fires, WordPress has loaded pluggable.php and
102 + * current_user_can() is guaranteed to exist. We process all instances
103 + * that were created during 'plugins_loaded' or earlier.
104 + *
105 + * This is called as a static method because it processes multiple instances.
106 + */
107 + public static function process_deferred_instances() {
108 + // Belt-and-suspenders check: pluggable functions should ALWAYS exist by 'init'
109 + // If they somehow don't, log a warning and bail (this should never happen)
110 + if ( !function_exists( 'current_user_can' ) || !function_exists( 'wp_get_current_user' ) ) {
111 + trigger_error(
112 + 'MeowKit_MWCODE_Admin: Pluggable functions still unavailable on init hook. ' .
113 + 'This should never happen and indicates a serious WordPress core issue.',
114 + E_USER_WARNING
115 + );
116 + return;
117 + }
118 +
119 + // Process each deferred instance's admin setup
120 + foreach ( self::$deferred_instances as $instance ) {
121 + $instance->run_admin_setup();
122 + }
123 +
124 + // Clear the array to free memory (we won't need these references anymore)
125 + self::$deferred_instances = array();
126 + }
127 +
128 + /**
129 + * Run admin setup - both shared (once) and per-instance (each plugin).
130 + *
131 + * SHARED SETUP (once for all plugins):
132 + * - Issues detection
133 + * - Meow Apps menu creation
134 + * - Admin footer customization
135 + *
136 + * PER-INSTANCE SETUP (once per plugin):
137 + * - Ratings system
138 + * - News system
139 + *
140 + * This method is called either immediately (if pluggable functions exist)
141 + * or deferred until 'init' (if they don't). Either way, it's safe to call
142 + * current_user_can() here.
143 + */
144 + private function run_admin_setup() {
145 + // SHARED SETUP: Only run once for all Meow Apps plugins
146 + if ( !MeowKit_MWCODE_Admin::$loaded ) {
147 + // Check for potential issues with WordPress install, other plugins, etc.
148 + new MeowKit_MWCODE_Issues( $this->prefix, $this->mainfile, $this->domain );
149 +
150 + // Create the unified Meow Apps menu (priority 5 to ensure early creation)
151 + add_action( 'admin_menu', [ $this, 'admin_menu_start' ], 5 );
152 +
153 + // Customize admin footer on Meow Apps pages
154 + $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : null;
155 + if ( $page === 'meowapps-main-menu' ) {
156 + add_filter( 'admin_footer_text', [ $this, 'admin_footer_text' ], 100000, 1 );
52 157 }
53 - if ( $this->is_user_admin() ) {
54 - if ( !$disableReview ) {
55 - new MeowCommon_Ratings( $prefix, $mainfile, $domain );
56 - }
57 - new MeowCommon_News( $domain );
158 +
159 + MeowKit_MWCODE_Admin::$loaded = true;
160 + }
161 +
162 + // PER-INSTANCE SETUP: Run for each plugin that uses this library
163 + // Only admins get ratings prompts and news
164 + if ( $this->is_user_admin() ) {
165 + if ( !$this->disableReview ) {
166 + new MeowKit_MWCODE_Ratings( $this->prefix, $this->mainfile, $this->domain );
58 167 }
168 + new MeowKit_MWCODE_News( $this->domain );
59 169 }
60 - add_filter( 'plugin_row_meta', array( $this, 'custom_plugin_row_meta' ), 10, 2 );
61 - add_filter( 'edd_sl_api_request_verify_ssl', array( $this, 'request_verify_ssl' ), 10, 0 );
62 170 }
63 171
64 - function is_user_admin() {
172 + /**
173 + * Check if current user is a site administrator.
174 + *
175 + * This method is only called from run_admin_setup(), which guarantees
176 + * that pluggable functions exist. No error logging needed - if the
177 + * functions don't exist, we simply return false as a defensive fallback.
178 + *
179 + * @return bool True if user can manage options, false otherwise
180 + */
181 + public function is_user_admin() {
182 + // Defensive check (should never fail if called from run_admin_setup)
65 183 if ( !function_exists( 'current_user_can' ) || !function_exists( 'wp_get_current_user' ) ) {
66 - error_log( 'MeowCommon_Admin is called too early. Please make sure it is called after the plugins_loaded filter.' );
67 184 return false;
68 185 }
69 186 return current_user_can( 'manage_options' );
70 187 }
71 188
72 - function custom_plugin_row_meta( $links, $file ) {
189 + public function custom_plugin_row_meta( $links, $file ) {
73 190 $path = pathinfo( $file );
74 191 $pathName = basename( $path['dirname'] );
75 192 $thisPath = pathinfo( $this->mainfile );
76 193 $thisPathName = basename( $thisPath['dirname'] );
@@ -79,30 +196,30 @@
79 196 return $links;
80 197 }
81 198 $isIssue = $this->isPro && !$this->is_registered();
82 199 if ( strpos( $pathName, $thisPathName ) !== false ) {
83 - $new_links = array(
84 - 'settings' =>
85 - sprintf( __( '<a href="admin.php?page=%s_settings">Settings</a>', $this->domain ), $this->prefix ),
86 - 'license' =>
87 - $this->is_registered() ?
88 - ('<span style="color: #a75bd6;">' . __( 'Pro Version', $this->domain ) . '</span>') :
89 - ( $isIssue ? (sprintf( '<span style="color: #ff3434;">' . __( 'License Issue', $this->domain ), $this->prefix ) . '</span>') : (sprintf( '<span>' . __( '<a target="_blank" href="https://meowapps.com">Get the <u>Pro Version</u></a>', $this->domain ), $this->prefix ) . '</span>') ),
90 - );
200 + $new_links = [
201 + 'settings' =>
202 + sprintf( __( '<a href="admin.php?page=%s_settings">Settings</a>', $this->domain ), $this->prefix ),
203 + 'license' =>
204 + $this->is_registered() ?
205 + ( '<span style="color: #a75bd6;">' . __( 'Pro Version', $this->domain ) . '</span>' ) :
206 + ( $isIssue ? ( sprintf( '<span style="color: #ff3434;">' . __( 'License Issue', $this->domain ), $this->prefix ) . '</span>' ) : ( sprintf( '<span>' . __( '<a target="_blank" href="https://meowapps.com">Get the <u>Pro Version</u></a>', $this->domain ), $this->prefix ) . '</span>' ) ),
207 + ];
91 208 $links = array_merge( $new_links, $links );
92 209 }
93 210 return $links;
94 211 }
95 212
96 - function request_verify_ssl() {
213 + public function request_verify_ssl() {
97 214 return get_option( 'force_sslverify', false );
98 215 }
99 216
100 - function nice_name_from_file( $file ) {
217 + public function nice_name_from_file( $file ) {
101 218 $info = pathinfo( $file );
102 219 if ( !empty( $info ) ) {
103 220 if ( $info['filename'] == 'wplr-sync' ) {
104 - return "WP/LR Sync";
221 + return 'WP/LR Sync';
105 222 }
106 223 $info['filename'] = str_replace( '-', ' ', $info['filename'] );
107 224 $file = ucwords( $info['filename'] );
108 225 }
@@ -108,9 +225,9 @@
108 225 }
109 226 return $file;
110 227 }
111 228
112 - function admin_notices_licensed_free() {
229 + public function admin_notices_licensed_free() {
113 230 if ( isset( $_POST[$this->prefix . '_reset_sub'] ) ) {
114 231 delete_option( $this->prefix . '_pro_serial' );
115 232 delete_option( $this->prefix . '_license' );
116 233 return;
@@ -117,25 +234,26 @@
117 234 }
118 235 $html = '<div class="notice notice-error">';
119 236 $html .= sprintf(
120 237 __( '<p>It looks like you are using the free version of the plugin (<b>%s</b>) but a license for the Pro version was also found. The Pro version might have been replaced by the Free version during an update (might be caused by a temporarily issue). If it is the case, <b>please download it again</b> from the <a target="_blank" href="https://meowapps.com">Meow Store</a>. If you wish to continue using the free version and clear this message, click on this button.', $this->domain ),
121 - $this->nice_name_from_file( $this->mainfile ) );
122 - $html .= '<p>
123 - <form method="post" action="">
124 - <input type="hidden" name="' . $this->prefix . '_reset_sub" value="true">
125 - <input type="submit" name="submit" id="submit" class="button" value="'
126 - . __( 'Remove the license', $this->domain ) . '">
127 - </form>
128 - </p>';
238 + $this->nice_name_from_file( $this->mainfile )
239 + );
240 + $html .= '<p>
241 + <form method="post" action="">
242 + <input type="hidden" name="' . $this->prefix . '_reset_sub" value="true">
243 + <input type="submit" name="submit" id="submit" class="button" value="'
244 + . __( 'Remove the license', $this->domain ) . '">
245 + </form>
246 + </p>';
129 247 $html .= '</div>';
130 248 wp_kses_post( $html );
131 249 }
132 250
133 - function admin_menu_start() {
251 + public function admin_menu_start() {
134 252 // Hide the admin if user doesn't like Meow much
135 253 if ( get_option( 'meowapps_hide_meowapps', false ) ) {
136 254 register_setting( 'general', 'meowapps_hide_meowapps' );
137 - add_settings_field( 'meowapps_hide_ads', 'Meow Apps Menu', array( $this, 'meowapps_hide_dashboard_callback' ), 'general' );
255 + add_settings_field( 'meowapps_hide_ads', 'Meow Apps Menu', [ $this, 'meowapps_hide_dashboard_callback' ], 'general' );
138 256 return;
139 257 }
140 258
141 259 // Create standard menu if it does not already exist
@@ -140,38 +258,50 @@
140 258
141 259 // Create standard menu if it does not already exist
142 260 global $submenu;
143 261 if ( !isset( $submenu[ 'meowapps-main-menu' ] ) ) {
144 - add_menu_page( 'Meow Apps', '<img alt="Meow Apps" style="width: 22px; margin-left: -30px; position: absolute; margin-top: -0px;" src="' . MeowCommon_Admin::$logo . '" />Meow Apps', 'manage_options', 'meowapps-main-menu',
145 - array( $this, 'admin_meow_apps' ), '', 82 );
146 - add_submenu_page( 'meowapps-main-menu', __( 'Dashboard', $this->domain ),
147 - __( 'Dashboard', $this->domain ), 'manage_options',
148 - 'meowapps-main-menu', array( $this, 'admin_meow_apps' ) );
262 + add_menu_page(
263 + 'Meow Apps',
264 + '<img alt="Meow Apps" style="width: 21px; margin-left: -28px; position: absolute; margin-top: 2px;" src="' . MeowKit_MWCODE_Admin::$logo . '" />Meow Apps',
265 + 'manage_options',
266 + 'meowapps-main-menu',
267 + [ $this, 'admin_meow_apps' ],
268 + '',
269 + 82
270 + );
271 + add_submenu_page(
272 + 'meowapps-main-menu',
273 + __( 'Dashboard', $this->domain ),
274 + __( 'Dashboard', $this->domain ),
275 + 'manage_options',
276 + 'meowapps-main-menu',
277 + [ $this, 'admin_meow_apps' ]
278 + );
149 279 }
150 280
151 281 // Add CSS to hide the default icon
152 - add_action( 'admin_head', function() {
282 + add_action( 'admin_head', function () {
153 283 echo '<style>
154 - #toplevel_page_meowapps-main-menu .wp-menu-image {
155 - display: none;
156 - }
157 - </style>';
158 - });
284 + #toplevel_page_meowapps-main-menu .wp-menu-image {
285 + display: none;
286 + }
287 + </style>';
288 + } );
159 289 }
160 290
161 - function meowapps_hide_dashboard_callback() {
291 + public function meowapps_hide_dashboard_callback() {
162 292 $html = '<input type="checkbox" id="meowapps_hide_meowapps" name="meowapps_hide_meowapps" value="1" ' .
163 - checked( 1, get_option( 'meowapps_hide_meowapps' ), false ) . '/>';
293 + checked( 1, get_option( 'meowapps_hide_meowapps' ), false ) . '/>';
164 294 $html .= __( '<label>Hide <b>Meow Apps</b> Menu</label><br /><small>Hide Meow Apps menu and all its components, for a cleaner admin. This option will be reset if a new Meow Apps plugin is installed.<br /><b>Once activated, an option will be added in your General settings to display it again.</b></small>', $this->domain );
165 - echo MeowCommon_Helpers::wp_kses( $html );
295 + echo MeowKit_MWCODE_Helpers::wp_kses( $html );
166 296 }
167 297
168 - function is_registered() {
298 + public function is_registered() {
169 299 $is_registered = apply_filters( $this->prefix . '_meowapps_is_registered', false, $this->prefix );
170 300 return $is_registered;
171 301 }
172 302
173 - function get_phpinfo() {
303 + public function get_phpinfo() {
174 304 if ( !$this->is_user_admin() || !function_exists( 'phpinfo' ) ) {
175 305 return;
176 306 }
177 307 ob_start();
@@ -179,28 +309,27 @@
179 309 phpinfo( INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES );
180 310 // phpcs:enable
181 311 $html = ob_get_contents();
182 312 ob_end_clean();
183 - $html = preg_replace( '%^.*<body>(.*)</body>.*$%ms','$1', $html );
313 + $html = preg_replace( '%^.*<body>(.*)</body>.*$%ms', '$1', $html );
184 314 return $html;
185 315 }
186 -
187 - function admin_meow_apps() {
316 +
317 + public function admin_meow_apps() {
188 318 $html = "<div id='meow-common-dashboard'></div>";
189 319 $html .= "<div style='height: 0; width: 0; overflow: hidden;' id='meow-common-phpinfo'>";
190 - $html .= $this->get_phpinfo();
191 - $html .= "</div>";
192 - $html = preg_replace("/<img[^>]+\>/i", "", $html);
320 + $html .= $this->get_phpinfo();
321 + $html .= '</div>';
322 + $html = preg_replace( "/<img[^>]+\>/i", '', $html );
193 323 echo wp_kses_post( $html );
194 324 }
195 325
196 - function admin_footer_text( $current ) {
326 + public function admin_footer_text( $current ) {
197 327 return sprintf(
198 328 // translators: %1$s is the version of the interface; %2$s is a file path.
199 329 __( 'Thanks for using <a href="https://meowapps.com">Meow Apps</a>! This is the Meow Admin %1$s <br /><i>Loaded from %2$s </i>', $this->domain ),
200 - MeowCommon_Admin::$version,
330 + MeowKit_MWCODE_Admin::$version,
201 331 __FILE__
202 332 );
203 333 }
204 334 }
205 335 }
206 -?>