PluginProbe
Post Grid Gutenberg Blocks – PostX / 5.1.1
Post Grid Gutenberg Blocks – PostX v5.1.1
5.1.1 5.1.0 5.0.41 5.0.39 5.0.38 5.0.37 5.0.36 5.0.35 5.0.34 5.0.33 5.0.32 5.0.31 5.0.30 5.0.29 5.0.28 5.0.27 5.0.26 5.0.25 5.0.23 5.0.24 5.0.22 5.0.21 5.0.20 5.0.19 5.0.18 All 240 releases
ultimate-post / includes / notice / class-notice.php

class-notice.php in Post Grid Gutenberg Blocks – PostX 5.1.1, at includes/notice/class-notice.php

428 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php //phpcs:ignore
2 namespace ULTP\Includes\Notice;
3
4 defined( 'ABSPATH' ) || exit;
5
6 use ULTP\Includes\Durbin\Xpo;
7
8 require_once __DIR__ . '/interface-notice-type.php';
9 require_once __DIR__ . '/class-notice-assets.php';
10 require_once __DIR__ . '/class-abstract-campaign-notice.php';
11 require_once __DIR__ . '/types/class-banner-notice.php';
12 require_once __DIR__ . '/types/class-content-notice.php';
13 require_once __DIR__ . '/types/class-image-notice.php';
14 require_once __DIR__ . '/types/class-consent-notice.php';
15 require_once __DIR__ . '/class-promo-links.php';
16
17 /**
18 * Plugin Notice
19 */
20 class Notice {
21
22
23 /**
24 * Campaigns showing on this request. Null until first worked out.
25 *
26 * @var Abstract_Campaign_Notice[]|null
27 */
28 private $active_campaigns = null;
29
30 /**
31 * Data-collection consent notice. Null until first needed.
32 *
33 * @var Consent_Notice|null
34 */
35 private $consent_notice = null;
36
37 /**
38 * Notice Priority
39 *
40 * @var string $plugin_notice_priority
41 */
42 private $plugin_notice_priority = 1;
43
44 /**
45 * Notice Priority
46 *
47 * @var string $plugin_notice_priority
48 */
49 private $plugin_notice_priority_key = 'postx';
50
51 /**
52 * Notice Constructor
53 */
54 public function __construct() {
55 add_action( 'admin_notices', array( $this, 'admin_notices_callback' ) );
56 add_action( 'admin_init', array( $this, 'set_dismiss_notice_callback' ) );
57 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
58
59 // REST API routes.
60 add_action( 'rest_api_init', array( $this, 'register_rest_route' ) );
61
62 add_filter( 'xpo_active_notice_lists', array( $this, 'handle_xpo_active_notice_lists' ), 99, 1 );
63
64 // Upgrade link on the Plugins page and upgrade item in the PostX menu.
65 new Promo_Links();
66 }
67
68 /**
69 * Registers REST API endpoints.
70 *
71 * @return void
72 */
73 public function register_rest_route() {
74 $routes = array(
75 // Hello Bar.
76 array(
77 'endpoint' => 'hello_bar',
78 'methods' => 'POST',
79 'callback' => array( $this, 'hello_bar_callback' ),
80 'permission_callback' => function () {
81 return current_user_can( 'manage_options' );
82 },
83 'args' => array(
84 'type' => array(
85 'type' => 'string',
86 'required' => true,
87 'enum' => array( 'hello_bar' ),
88 ),
89 'id' => array(
90 'type' => 'string',
91 'required' => true,
92 'enum' => self::get_hellobar_keys(),
93 ),
94 ),
95 ),
96 );
97
98 foreach ( $routes as $route ) {
99 register_rest_route(
100 'ultp',
101 $route['endpoint'],
102 array(
103 array(
104 'methods' => $route['methods'],
105 'callback' => $route['callback'],
106 'permission_callback' => $route['permission_callback'],
107 'args' => isset( $route['args'] ) ? $route['args'] : array(),
108 ),
109 )
110 );
111 }
112 }
113
114 /**
115 * Hellobar config
116 *
117 * @return array
118 */
119 public static function get_hellobar_config() {
120 $config = array();
121 foreach ( self::get_hellobar_keys() as $key ) {
122 $config[ $key ] = Xpo::get_transient_without_cache( $key );
123 }
124 return $config;
125 }
126
127 /**
128 * Hello bar ids the dashboard may dismiss, from Campaigns::hellobar_keys().
129 *
130 * class-campaigns.php is edited for every sale, so it is loaded inside a try: a mistake in it
131 * empties this list (hello bars can't be closed until it's fixed) instead of breaking wp-admin.
132 *
133 * @return string[]
134 */
135 private static function get_hellobar_keys() {
136 try {
137 require_once __DIR__ . '/class-campaigns.php';
138 $keys = Campaigns::hellobar_keys();
139 } catch ( \Throwable $e ) {
140 return array();
141 }
142 return is_array( $keys ) ? array_values( array_filter( $keys, 'is_string' ) ) : array();
143 }
144
145 /**
146 * Handles Hello Bar dismissal action via REST API .
147 *
148 * @param \WP_REST_Request $request REST request object .
149 * @return \WP_REST_Response
150 */
151 public function hello_bar_callback( \WP_REST_Request $request ) {
152 $type = $request->get_param( 'type' );
153 $id = $request->get_param( 'id' );
154
155 // The route's args already reject unknown ids; check again in case this is called directly.
156 if ( 'hello_bar' === $type && is_string( $id ) && in_array( $id, self::get_hellobar_keys(), true ) ) {
157 Xpo::set_transient_without_cache( $id, 'hide', 1296000 );
158 }
159
160 return new \WP_REST_Response(
161 array(
162 'success' => true,
163 'message' => __( 'Hello Bar Action performed', 'ultimate-post' ),
164 ),
165 200
166 );
167 }
168
169 /**
170 * Set Notice Dismiss Callback
171 *
172 * @return void
173 */
174 public function set_dismiss_notice_callback() {
175
176 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['wpnonce'] ?? '' ) ), 'ultp-nonce' ) ) {
177 return;
178 }
179
180 // Durbin notice dismiss.
181 Consent_Notice::handle_dismiss_request();
182
183 // Install notice dismiss.
184 $install_key = sanitize_text_field( wp_unslash( $_GET['ultp_install_key'] ?? '' ) );
185 if ( ! empty( $install_key ) ) {
186 Xpo::set_transient_without_cache( 'ultp_install_notice_' . $install_key, 'off' );
187 }
188 $notice_key = sanitize_text_field( wp_unslash( $_GET['ultp_notice'] ?? '' ) );
189 if ( ! empty( $notice_key ) ) {
190 $interval = (int) sanitize_text_field( wp_unslash( $_GET['ultp_interval'] ?? '' ) );
191 if ( ! empty( $interval ) ) {
192 Xpo::set_transient_without_cache( 'ultp_get_pro_notice_' . $notice_key, 'off', $interval );
193 } else {
194 Xpo::set_transient_without_cache( 'ultp_get_pro_notice_' . $notice_key, 'off' );
195 }
196 }
197 }
198
199 /**
200 * Admin Notices Callback
201 *
202 * @return void
203 */
204 public function admin_notices_callback() {
205 $this->ultp_dashboard_notice_callback();
206 $this->ultp_dashboard_durbin_notice_callback();
207 }
208
209 /**
210 * Admin Dashboard Notice Callback
211 *
212 * @return void
213 */
214 public function ultp_dashboard_notice_callback() {
215 if ( $this->is_available_for_notice() ) {
216 $this->ultp_dashboard_banner_notice();
217 $this->ultp_dashboard_content_notice();
218 $this->ultp_dashboard_image_banner_notice();
219
220 }
221 }
222
223 /**
224 * Dashboard Banner Notice
225 *
226 * @param bool $return_bool True to only report whether a banner would show.
227 * @return bool|void
228 */
229 public function ultp_dashboard_banner_notice( $return_bool = false ) {
230 return $this->render_campaigns( Banner_Notice::class, $return_bool );
231 }
232
233 /**
234 * Queue the stylesheets and scripts for the notices that will show on this page.
235 *
236 * Runs on admin_enqueue_scripts, before admin_notices, so the styles load in <head>.
237 *
238 * @return void
239 */
240 public function enqueue_assets() {
241 if ( $this->is_available_for_notice() ) {
242 foreach ( $this->get_active_campaigns() as $campaign ) {
243 $campaign->enqueue_assets();
244 }
245 }
246 $consent = $this->get_consent_notice();
247 if ( $consent->should_display() ) {
248 $consent->enqueue_assets();
249 }
250 }
251
252 /**
253 * Banner JS
254 *
255 * @deprecated The countdown script is now the file assets/js/ultp-notice-countdown.js. This queues it.
256 *
257 * @return void
258 */
259 public function ultp_banner_notice_js() {
260 Notice_Assets::enqueue_countdown_script();
261 }
262
263 /**
264 * The Durbin Html
265 *
266 * @return void
267 */
268 public function ultp_dashboard_durbin_notice_callback() {
269 $consent = $this->get_consent_notice();
270 if ( $consent->should_display() ) {
271 $this->render_safely( $consent );
272 }
273 }
274
275 /**
276 * The data-collection consent notice, created once per request.
277 *
278 * @return Consent_Notice
279 */
280 private function get_consent_notice() {
281 if ( null === $this->consent_notice ) {
282 $this->consent_notice = new Consent_Notice();
283 }
284 return $this->consent_notice;
285 }
286 /**
287 * Dashboard Content Notice
288 *
289 * @param bool $return_bool True to only report whether a content notice would show.
290 * @return bool|void
291 */
292 public function ultp_dashboard_content_notice( $return_bool = false ) {
293 return $this->render_campaigns( Content_Notice::class, $return_bool );
294 }
295
296 /**
297 * Handle Plugin Notice for all plugins
298 *
299 * @param array $active_lists Lists of all active plugin notice.
300 * @return array
301 */
302 public function handle_xpo_active_notice_lists( $active_lists ) {
303 if ( ! empty( $this->get_active_campaigns() ) ) {
304 $active_lists[ $this->plugin_notice_priority_key ] = $this->plugin_notice_priority;
305 }
306 return $active_lists;
307 }
308
309 /**
310 * Handle Plugin Notice for all plugins
311 *
312 * @return bool
313 */
314 public function is_available_for_notice() {
315 $active_notices = apply_filters( 'xpo_active_notice_lists', array() );
316 if ( empty( $active_notices ) ) {
317 return true;
318 }
319 asort( $active_notices );
320 return array_key_first( $active_notices ) === $this->plugin_notice_priority_key;
321 }
322
323 /**
324 * Dashboard Image Banner Notice
325 *
326 * @param bool $return_bool True to only report whether an image banner would show.
327 * @return bool|void
328 */
329 public function ultp_dashboard_image_banner_notice( $return_bool = false ) {
330 return $this->render_campaigns( Image_Notice::class, $return_bool );
331 }
332
333 /**
334 * Campaigns that should show on this request, in display order.
335 *
336 * Worked out once per request: the xpo_active_notice_lists filter and the render pass both need it,
337 * and each campaign's dismissal check is an uncached database read.
338 *
339 * @param string $type Optional class name to keep only one kind of campaign.
340 * @return Abstract_Campaign_Notice[]
341 */
342 private function get_active_campaigns( $type = '' ) {
343 if ( null === $this->active_campaigns ) {
344 $this->active_campaigns = array();
345
346 // The registry is the file edited for every sale. Load it here, not at the top, so a
347 // mistake in it (even a syntax error) hides the campaigns instead of breaking wp-admin.
348 try {
349 require_once __DIR__ . '/class-campaigns.php';
350 $campaigns = Campaigns::all();
351 } catch ( \Throwable $e ) {
352 self::log_error( $e );
353 $campaigns = array();
354 }
355
356 foreach ( $campaigns as $campaign ) {
357 try {
358 if ( $campaign instanceof Abstract_Campaign_Notice && $campaign->should_display() ) {
359 $this->active_campaigns[] = $campaign;
360 }
361 } catch ( \Throwable $e ) {
362 self::log_error( $e );
363 }
364 }
365 }
366
367 if ( '' === $type ) {
368 return $this->active_campaigns;
369 }
370 return array_values(
371 array_filter(
372 $this->active_campaigns,
373 function ( $campaign ) use ( $type ) {
374 return $campaign instanceof $type;
375 }
376 )
377 );
378 }
379
380 /**
381 * Print every live campaign of one kind, or report whether there is one.
382 *
383 * @param string $type Campaign class name.
384 * @param bool $return_bool True to only report whether one would show.
385 * @return bool|void
386 */
387 private function render_campaigns( $type, $return_bool ) {
388 $campaigns = $this->get_active_campaigns( $type );
389
390 if ( $return_bool ) { // Early return for Other plugin notice.
391 return ! empty( $campaigns );
392 }
393
394 foreach ( $campaigns as $campaign ) {
395 $this->render_safely( $campaign );
396 }
397 }
398
399 /**
400 * Print one notice. It's buffered, so a notice that fails halfway leaves no broken markup behind.
401 *
402 * @param Notice_Type $notice Notice to print.
403 * @return void
404 */
405 private function render_safely( Notice_Type $notice ) {
406 ob_start();
407 try {
408 $notice->render();
409 ob_end_flush();
410 } catch ( \Throwable $e ) {
411 ob_end_clean();
412 self::log_error( $e );
413 }
414 }
415
416 /**
417 * Record a notice failure when debugging. Notices are never worth breaking wp-admin for.
418 *
419 * @param \Throwable $e What went wrong.
420 * @return void
421 */
422 private static function log_error( \Throwable $e ) {
423 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
424 error_log( 'PostX notice: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug-only.
425 }
426 }
427 }
428