PluginProbe
MarqueeAll – Elementor Marquee for Image, Text, Post Grid, Testimonial, Cryptocurrency, Faqs & News Ticker 🌀 / 1.2.2
MarqueeAll – Elementor Marquee for Image, Text, Post Grid, Testimonial, Cryptocurrency, Faqs & News Ticker 🌀 v1.2.2
1.2.3 1.2.2 trunk 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1
marqueeall / includes / class-widget-manager.php

class-widget-manager.php in MarqueeAll – Elementor Marquee for Image, Text, Post Grid, Testimonial, Cryptocurrency, Faqs & News Ticker 🌀 1.2.2, at includes/class-widget-manager.php

237 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Widget Manager
4 *
5 * Manages enable/disable state for every MarqueeAll widget and drives
6 * the dynamic Elementor registration loop.
7 *
8 * @package MarqueeAll
9 * @since 1.3.0
10 */
11
12 namespace MASSCIE;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 /**
19 * Class Widget_Manager
20 */
21 final class Widget_Manager {
22
23 /**
24 * WordPress option key for widget statuses.
25 *
26 * @var string
27 */
28 const OPTION_KEY = 'marqueeall_widget_status';
29
30 /**
31 * Singleton instance.
32 *
33 * @var Widget_Manager|null
34 */
35 private static $instance = null;
36
37 /**
38 * In-memory status cache (loaded once per request).
39 *
40 * @var array|null
41 */
42 private $status_cache = null;
43
44 /**
45 * Get singleton instance.
46 *
47 * @return Widget_Manager
48 */
49 public static function instance() {
50 if ( null === self::$instance ) {
51 self::$instance = new self();
52 }
53 return self::$instance;
54 }
55
56 /**
57 * Private constructor.
58 */
59 private function __construct() {}
60
61 /**
62 * Build default statuses: free widgets = 1, pro widgets = 0.
63 *
64 * @return array [ 'slug' => 1|0 ]
65 */
66 public function get_default_status() {
67 $defaults = [];
68 foreach ( Widget_Registry::instance()->get_all() as $widget ) {
69 $defaults[ $widget['slug'] ] = empty( $widget['pro'] ) ? 1 : 0;
70 }
71 return $defaults;
72 }
73
74 /**
75 * Get the stored (or default) status map.
76 *
77 * Uses an in-memory cache so the DB is only hit once per request.
78 *
79 * @return array [ 'slug' => 1|0 ]
80 */
81 public function get_status() {
82 if ( null !== $this->status_cache ) {
83 return $this->status_cache;
84 }
85
86 $saved = get_option( self::OPTION_KEY, false );
87
88 // No option yet (fresh install or upgrade without activation): use defaults.
89 if ( false === $saved || ! is_array( $saved ) ) {
90 $saved = $this->get_default_status();
91 }
92
93 $this->status_cache = $saved;
94 return $this->status_cache;
95 }
96
97 /**
98 * Check whether a widget is currently enabled.
99 *
100 * Widgets that exist in the registry but not yet in the saved option
101 * (added in a later version) default to enabled.
102 *
103 * @param string $slug Widget slug.
104 * @return bool
105 */
106 public function is_enabled( $slug ) {
107 $status = $this->get_status();
108
109 // Slug not present → newly added widget → treat as enabled.
110 if ( ! array_key_exists( $slug, $status ) ) {
111 return true;
112 }
113
114 return 1 === (int) $status[ $slug ];
115 }
116
117 /**
118 * Count currently enabled widgets.
119 *
120 * @return int
121 */
122 public function get_enabled_count() {
123 return count( array_filter( $this->get_status() ) );
124 }
125
126 /**
127 * Persist a new status map.
128 *
129 * Builds a canonical full map from the registry so every slug is
130 * always present in the option, whether the JS sent it or not.
131 *
132 * Returns:
133 * 'saved' — value changed and was written to DB
134 * 'unchanged' — value identical to what was already stored
135 * 'error' — update_option failed
136 *
137 * @param array $incoming [ slug => 1|0 ] from the AJAX call.
138 * @return string 'saved'|'unchanged'|'error'
139 */
140 public function save_status( array $incoming ) {
141 $canonical = [];
142
143 foreach ( Widget_Registry::instance()->get_slugs() as $slug ) {
144 // Anything in $incoming is a real value (JS sends explicit 0 or 1).
145 // Anything missing defaults to 0 (safety fallback only).
146 $canonical[ $slug ] = isset( $incoming[ $slug ] ) ? (int) (bool) $incoming[ $slug ] : 0;
147 }
148
149 // Compare to current stored value before writing.
150 $existing = get_option( self::OPTION_KEY, false );
151
152 // Update the in-memory cache immediately so subsequent calls in this
153 // request (e.g. get_enabled_count()) reflect the new values.
154 $this->status_cache = $canonical;
155
156 if ( is_array( $existing ) && $existing === $canonical ) {
157 // Force a write anyway so autoload flag is consistent.
158 update_option( self::OPTION_KEY, $canonical, false );
159 return 'unchanged';
160 }
161
162 $ok = update_option( self::OPTION_KEY, $canonical, false );
163 return $ok ? 'saved' : 'error';
164 }
165
166 /**
167 * Register all enabled widgets with Elementor.
168 *
169 * Called from Plugin on the elementor/widgets/register action.
170 * Disabled widgets never have their files loaded.
171 *
172 * @param \Elementor\Widgets_Manager $widgets_manager Elementor widget manager instance.
173 * @return void
174 */
175 public function register_with_elementor( $widgets_manager ) {
176 foreach ( Widget_Registry::instance()->get_all() as $widget ) {
177 if ( ! $this->is_enabled( $widget['slug'] ) ) {
178 continue;
179 }
180
181 $file = MASSCIE_PATH . $widget['file'];
182
183 if ( ! file_exists( $file ) ) {
184 continue;
185 }
186
187 require_once $file;
188
189 $class = $widget['class'];
190
191 if ( ! class_exists( $class ) ) {
192 continue;
193 }
194
195 $widgets_manager->register( new $class() );
196 }
197 }
198
199 /**
200 * Seed default statuses on plugin activation (runs once).
201 *
202 * Uses add_option so it is a no-op on subsequent activations,
203 * preserving any settings the user has already saved.
204 *
205 * @return void
206 */
207 public static function on_activation() {
208 if ( false === get_option( self::OPTION_KEY ) ) {
209 add_option(
210 self::OPTION_KEY,
211 self::instance()->get_default_status(),
212 '',
213 false // Do not autoload.
214 );
215 }
216 }
217
218 /**
219 * Upgrade routine — ensure the option exists for users upgrading
220 * from versions before 1.3.0 that never triggered the activation hook.
221 *
222 * Hooked on init (low priority) from Plugin::init_hooks().
223 *
224 * @return void
225 */
226 public static function maybe_seed_option() {
227 if ( false === get_option( self::OPTION_KEY ) ) {
228 add_option(
229 self::OPTION_KEY,
230 self::instance()->get_default_status(),
231 '',
232 false
233 );
234 }
235 }
236 }
237