PluginProbe
MarqueeAll – Elementor Marquee for Image, Text, Post Grid, Testimonial, Cryptocurrency, Faqs & News Ticker 🌀 / trunk
MarqueeAll – Elementor Marquee for Image, Text, Post Grid, Testimonial, Cryptocurrency, Faqs & News Ticker 🌀 vtrunk
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 / admin / class-admin.php

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

380 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin Class
4 *
5 * Registers the "MarqueeAll" top-level admin menu page (Widget Manager),
6 * enqueues admin assets, and handles AJAX save requests.
7 *
8 * @package MarqueeAll
9 * @since 1.3.0
10 */
11
12 namespace MASSCIE\Admin;
13
14 use MASSCIE\Widget_Manager;
15 use MASSCIE\Widget_Registry;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Class Admin
23 */
24 final class Admin {
25
26 /**
27 * Admin page hook suffix returned by add_menu_page().
28 *
29 * @var string
30 */
31 private $page_hook = '';
32
33 /**
34 * Singleton instance.
35 *
36 * @var Admin|null
37 */
38 private static $instance = null;
39
40 /**
41 * Get singleton instance.
42 *
43 * @return Admin
44 */
45 public static function instance() {
46 if ( null === self::$instance ) {
47 self::$instance = new self();
48 }
49 return self::$instance;
50 }
51
52 /**
53 * Constructor — hooks into WordPress.
54 */
55 private function __construct() {
56 add_action( 'admin_menu', [ $this, 'register_menu' ] );
57 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
58 add_action( 'admin_head', [ $this, 'print_menu_icon_style' ] );
59 add_action( 'wp_ajax_marqueeall_save_widget_status', [ $this, 'ajax_save_widget_status' ] );
60 }
61 /**
62 * Print inline CSS to size the GIF menu icon on every admin page.
63 *
64 * wp-admin forces width:auto and padding overrides on img menu icons
65 * so !important is required on each property.
66 *
67 * @return void
68 */
69 public function print_menu_icon_style() {
70 ?>
71 <style>
72 #adminmenu .toplevel_page_marqueeall img {
73 width: 20px !important;
74 height: 20px !important;
75 padding: 7px !important;
76 opacity: .8 !important;
77 border-radius: 0 !important;
78 }
79
80 #adminmenu .toplevel_page_marqueeall:hover img,
81 #adminmenu .toplevel_page_marqueeall.current img,
82 #adminmenu .toplevel_page_marqueeall.wp-has-current-submenu img {
83 opacity: 1 !important;
84 }
85 </style>
86 <?php
87 }
88
89 /**
90 * Register the top-level admin menu.
91 *
92 * @return void
93 */
94 public function register_menu() {
95 $this->page_hook = add_menu_page(
96 __( 'MarqueeAll — Widget Manager', 'marqueeall' ),
97 __( 'MarqueeAll', 'marqueeall' ),
98 'manage_options',
99 'marqueeall',
100 [ $this, 'render_page' ],
101 'https://ps.w.org/marqueeall/assets/icon-128x128.gif',
102 58
103 );
104 }
105
106 /**
107 * Enqueue admin-page assets — only on our own page.
108 *
109 * @param string $hook_suffix Current admin page hook.
110 * @return void
111 */
112 public function enqueue_assets( $hook_suffix ) {
113 if ( $hook_suffix !== $this->page_hook ) {
114 return;
115 }
116
117 wp_enqueue_style(
118 'marqueeall-admin',
119 MASSCIE_URL . 'assets/css/marqueeall-admin.css',
120 [],
121 MASSCIE_VERSION
122 );
123
124 wp_enqueue_script(
125 'marqueeall-admin',
126 MASSCIE_URL . 'assets/js/marqueeall-admin.js',
127 [ 'jquery' ],
128 MASSCIE_VERSION,
129 true
130 );
131
132 wp_localize_script(
133 'marqueeall-admin',
134 'MARQUEEALL_ADMIN',
135 [
136 'ajax_url' => admin_url( 'admin-ajax.php' ),
137 'nonce' => wp_create_nonce( 'marqueeall_save_widget_status' ),
138 'i18n' => [
139 'saving' => __( 'Saving…', 'marqueeall' ),
140 'saved' => __( 'Settings saved!', 'marqueeall' ),
141 'save_error' => __( 'Could not save settings. Please try again.', 'marqueeall' ),
142 /* translators: 1: Number of enabled widgets, 2: Total available widgets. */
143 'enabled_count' => __( '%1$d of %2$d available widgets enabled', 'marqueeall' ),
144 ],
145 ]
146 );
147 }
148
149 /**
150 * Render the Widget Manager admin page.
151 *
152 * @return void
153 */
154 public function render_page() {
155 if ( ! current_user_can( 'manage_options' ) ) {
156 wp_die( esc_html__( 'You do not have permission to access this page.', 'marqueeall' ) );
157 }
158
159 $registry = Widget_Registry::instance()->get_all();
160 $total = Widget_Registry::instance()->count();
161 $widget_manager = Widget_Manager::instance();
162 $status = $widget_manager->get_status();
163 $enabled_count = $widget_manager->get_enabled_count();
164 ?>
165 <div class="wrap marqueeall-wrap">
166
167 <!-- Header -->
168 <div class="marqueeall-header">
169 <div class="marqueeall-header__brand">
170 <span class="marqueeall-header__logo">
171 <img
172 src="https://ps.w.org/marqueeall/assets/icon-128x128.gif"
173 alt="<?php esc_attr_e( 'MarqueeAll', 'marqueeall' ); ?>"
174 width="36"
175 height="36"
176 />
177 </span>
178 <h1 class="marqueeall-header__title"><?php esc_html_e( 'Widgets Manager', 'marqueeall' ); ?></h1>
179 </div>
180 <div class="marqueeall-header__actions">
181 <button id="marqueeall-save" class="button marqueeall-btn marqueeall-btn--primary">
182 <?php esc_html_e( 'Save Settings', 'marqueeall' ); ?>
183 </button>
184 </div>
185 </div>
186
187 <!-- Tab bar -->
188 <div class="marqueeall-tabs">
189 <button class="marqueeall-tab marqueeall-tab--active" data-tab="widgets">
190 <span class="dashicons dashicons-screenoptions"></span>
191 <?php esc_html_e( 'Widgets', 'marqueeall' ); ?>
192 </button>
193 </div>
194
195 <!-- Panel -->
196 <div class="marqueeall-panel" id="marqueeall-tab-widgets">
197
198 <!-- Toolbar -->
199 <div class="marqueeall-toolbar">
200 <div class="marqueeall-toolbar__left">
201 <h2 class="marqueeall-section-heading"><?php esc_html_e( 'Manage Widgets', 'marqueeall' ); ?></h2>
202 <p class="marqueeall-stats" id="marqueeall-stats">
203 <?php
204 printf(
205 /* translators: 1: enabled count, 2: total count */
206 esc_html__( '%1$d of %2$d available widgets enabled', 'marqueeall' ),
207 (int) $enabled_count,
208 (int) $total
209 );
210 ?>
211 </p>
212 </div>
213 <div class="marqueeall-toolbar__right">
214 <div class="marqueeall-search-wrap">
215 <span class="dashicons dashicons-search marqueeall-search-icon"></span>
216 <input
217 type="search"
218 id="marqueeall-search"
219 class="marqueeall-search"
220 placeholder="<?php esc_attr_e( 'Search widgets…', 'marqueeall' ); ?>"
221 />
222 </div>
223 <button id="marqueeall-enable-all" class="button marqueeall-btn marqueeall-btn--outline">
224 <?php esc_html_e( 'Enable All', 'marqueeall' ); ?>
225 </button>
226 <button id="marqueeall-disable-all" class="button marqueeall-btn marqueeall-btn--outline marqueeall-btn--danger">
227 <?php esc_html_e( 'Disable All', 'marqueeall' ); ?>
228 </button>
229 </div>
230 </div>
231
232 <!-- General Widgets -->
233 <div class="marqueeall-section">
234 <h3 class="marqueeall-section__title"><?php esc_html_e( 'General Widgets', 'marqueeall' ); ?></h3>
235 <div class="marqueeall-grid" id="marqueeall-grid">
236 <?php foreach ( $registry as $widget ) : ?>
237 <?php
238 $slug = $widget['slug'];
239 $enabled = isset( $status[ $slug ] ) ? (bool) $status[ $slug ] : true;
240 $pro = ! empty( $widget['pro'] );
241 ?>
242 <div
243 class="marqueeall-card<?php echo $pro ? ' marqueeall-card--pro' : ''; ?><?php echo $enabled ? ' marqueeall-card--active' : ''; ?>"
244 data-slug="<?php echo esc_attr( $slug ); ?>"
245 data-title="<?php echo esc_attr( strtolower( $widget['title'] ) ); ?>"
246 >
247 <?php if ( $pro ) : ?>
248 <span class="marqueeall-card__badge"><?php esc_html_e( 'Pro', 'marqueeall' ); ?></span>
249 <?php endif; ?>
250
251 <div class="marqueeall-card__body">
252 <span class="marqueeall-card__icon eicon <?php echo esc_attr( $widget['icon'] ); ?>"></span>
253 <span class="marqueeall-card__name"><?php echo esc_html( $widget['title'] ); ?></span>
254 </div>
255
256 <div class="marqueeall-card__footer">
257 <label class="marqueeall-toggle">
258 <input
259 type="checkbox"
260 class="marqueeall-toggle__input"
261 name="widget_status[<?php echo esc_attr( $slug ); ?>]"
262 value="1"
263 <?php checked( $enabled ); ?>
264 <?php disabled( $pro ); ?>
265 />
266 <span class="marqueeall-toggle__slider"></span>
267 </label>
268
269 <div class="marqueeall-card__links" style="display:none;">
270 <?php if ( ! empty( $widget['docs'] ) ) : ?>
271 <a href="<?php echo esc_url( $widget['docs'] ); ?>" target="_blank" rel="noopener noreferrer" class="marqueeall-card__link">
272 <?php esc_html_e( 'Doc', 'marqueeall' ); ?>
273 </a>
274 <?php endif; ?>
275 <?php if ( ! empty( $widget['demo'] ) ) : ?>
276 <a href="<?php echo esc_url( $widget['demo'] ); ?>" target="_blank" rel="noopener noreferrer" class="marqueeall-card__link">
277 <?php esc_html_e( 'Demo', 'marqueeall' ); ?>
278 </a>
279 <?php endif; ?>
280 </div>
281 </div>
282 </div>
283 <?php endforeach; ?>
284 </div>
285 <!-- Empty search state -->
286 <p class="marqueeall-no-results" id="marqueeall-no-results" style="display:none;">
287 <?php esc_html_e( 'No widgets match your search.', 'marqueeall' ); ?>
288 </p>
289 </div>
290
291 <!-- Leave a Review -->
292 <div class="marqueeall-review-box">
293 <div class="marqueeall-review-box__icon"></div>
294 <div class="marqueeall-review-box__content">
295 <strong><?php esc_html_e( 'Enjoying MarqueeAll?', 'marqueeall' ); ?></strong>
296 <p>
297 <?php
298 printf(
299 /* translators: %s opening/closing anchor tags */
300 esc_html__( 'Please %1$sleave us a 5-star review%2$s — it really helps!', 'marqueeall' ),
301 '<a href="https://wordpress.org/support/plugin/marqueeall/reviews/#new-post" target="_blank" rel="noopener noreferrer">',
302 '</a>'
303 );
304 ?>
305 </p>
306 </div>
307 </div>
308
309 </div><!-- /.marqueeall-panel -->
310
311 <!-- Toast notification -->
312 <div id="marqueeall-toast" class="marqueeall-toast" role="status" aria-live="polite"></div>
313
314 </div><!-- /.wrap.marqueeall-wrap -->
315 <?php
316 }
317
318 /**
319 * AJAX handler — save widget statuses.
320 *
321 * Expects POST fields:
322 * nonce string WordPress nonce
323 * widget_status array Complete map of slug => 1|0 for every widget
324 *
325 * @return void Sends JSON response and exits.
326 */
327 public function ajax_save_widget_status() {
328 // Nonce verification.
329 check_ajax_referer( 'marqueeall_save_widget_status', 'nonce' );
330
331 // Capability check.
332 if ( ! current_user_can( 'manage_options' ) ) {
333 wp_send_json_error(
334 [ 'message' => __( 'You do not have permission to perform this action.', 'marqueeall' ) ],
335 403
336 );
337 return;
338 }
339
340 // Read and sanitize the incoming status map.
341 // The JS sends a complete object with 1 or 0 for every slug,
342 // so unchecked (disabled) widgets are explicitly included as 0.
343 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- already verified above.
344 $raw_status = isset( $_POST['widget_status'] ) && is_array( $_POST['widget_status'] )
345 ? wp_unslash( $_POST['widget_status'] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
346 : [];
347
348 $incoming = [];
349 foreach ( $raw_status as $slug => $value ) {
350 $incoming[ sanitize_key( $slug ) ] = absint( $value );
351 }
352
353 // Pass to the manager, which builds the full canonical map.
354 $result = Widget_Manager::instance()->save_status( $incoming );
355
356 $enabled_count = Widget_Manager::instance()->get_enabled_count();
357 $total = Widget_Registry::instance()->count();
358
359 if ( 'error' === $result ) {
360 wp_send_json_error(
361 [ 'message' => __( 'Could not save settings. Please try again.', 'marqueeall' ) ]
362 );
363 return;
364 }
365
366 // Both 'saved' and 'unchanged' are reported as success to the user.
367 $message = 'saved' === $result
368 ? __( 'Settings saved!', 'marqueeall' )
369 : __( 'Settings saved!', 'marqueeall' );
370
371 wp_send_json_success(
372 [
373 'message' => $message,
374 'enabled_count' => $enabled_count,
375 'total' => $total,
376 ]
377 );
378 }
379 }
380