notifications.php (9627B)
1 <?php 2 3 /** 4 * Notifications. 5 * 6 * @since 7.10.5 7 */ 8 if ( ! class_exists( 'SeedProd_Notifications' ) ) { 9 class SeedProd_Notifications { 10 11 protected static $instance = null; 12 /** 13 * Source of notifications content. 14 * 15 * @since {VERSION} 16 * 17 * @var string 18 */ 19 const SOURCE_URL = 'https://seedprod-notifications.s3.amazonaws.com/sp-notifications.json'; 20 21 /** 22 * Option value. 23 * 24 * @since {VERSION} 25 * 26 * @var bool|array 27 */ 28 public $option = false; 29 30 /** 31 * The name of the option used to store the data. 32 * 33 * @var string 34 */ 35 public $option_name = 'seedprod_notifications'; 36 37 /** 38 * Return an instance of this class. 39 */ 40 public static function get_instance() { 41 42 // If the single instance hasn't been set, set it now. 43 if ( null == self::$instance ) { 44 self::$instance = new self(); 45 } 46 47 return self::$instance; 48 } 49 50 /** 51 * SeedProd_Notifications constructor. 52 */ 53 public function __construct() { 54 $this->init(); 55 } 56 57 /** 58 * Initialize class. 59 * 60 * @since {VERSION} 61 */ 62 public function init() { 63 $this->hooks(); 64 } 65 66 /** 67 * Register hooks. 68 * 69 * @since {VERSION} 70 */ 71 public function hooks() { 72 add_action( 'wp_ajax_seedprod_lite_notification_dismiss', array( $this, 'dismiss' ) ); 73 } 74 75 /** 76 * Check if user has access and is enabled. 77 * 78 * @return bool 79 * @since {VERSION} 80 * 81 */ 82 public function has_access() { 83 $access = true; 84 85 if ( current_user_can( 'install_plugins' ) ) { 86 $access = true; 87 } 88 89 return apply_filters( 'seedprod_admin_notifications_has_access', $access ); 90 } 91 92 /** 93 * Get option value. 94 * 95 * @param bool $cache Reference property cache if available. 96 * 97 * @return array 98 * @since {VERSION} 99 * 100 */ 101 public function get_option( $cache = true ) { 102 if ( $this->option && $cache ) { 103 return $this->option; 104 } 105 106 $option = get_option( $this->option_name, array() ); 107 108 $this->option = array( 109 'update' => ! empty( $option['update'] ) ? $option['update'] : 0, 110 'events' => ! empty( $option['events'] ) ? $option['events'] : array(), 111 'feed' => ! empty( $option['feed'] ) ? $option['feed'] : array(), 112 'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(), 113 ); 114 115 return $this->option; 116 } 117 118 /** 119 * Fetch notifications from feed. 120 * 121 * @return array 122 * @since {VERSION} 123 * 124 */ 125 public function fetch_feed() { 126 $res = wp_remote_get( self::SOURCE_URL ); 127 128 if ( is_wp_error( $res ) ) { 129 return array(); 130 } 131 132 $body = wp_remote_retrieve_body( $res ); 133 134 if ( empty( $body ) ) { 135 return array(); 136 } 137 138 return $this->verify( json_decode( $body, true ) ); 139 } 140 141 /** 142 * Verify notification data before it is saved. 143 * 144 * @param array $notifications Array of notifications items to verify. 145 * 146 * @return array 147 * @since {VERSION} 148 * 149 */ 150 public function verify( $notifications ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh 151 152 $data = array(); 153 154 if ( ! is_array( $notifications ) || empty( $notifications ) ) { 155 return $data; 156 } 157 158 $option = $this->get_option(); 159 160 foreach ( $notifications as $notification ) { 161 162 // The message and license should never be empty, if they are, ignore. 163 if ( empty( $notification['content'] ) || empty( $notification['type'] ) ) { 164 continue; 165 } 166 167 // Ignore if license type does not match. 168 $license_name = get_option( 'seedprod_license_name' ); 169 if ( empty( $license_name ) ) { 170 $license_name = 'unlicensed'; 171 } 172 if ( ! in_array( 'any', $notification['type'] ) ) { 173 if ( ! in_array( $license_name, $notification['type'] ) ) { 174 continue; 175 } 176 } 177 178 // Ignore if expired. 179 if ( ! empty( $notification['end'] ) && time() > strtotime( $notification['end'] ) ) { 180 continue; 181 } 182 183 // Ignore if notification has already been dismissed. 184 if ( ! empty( $option['dismissed'] ) && in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict 185 continue; 186 } 187 188 // Ignore if notification existed before installing SeedProd. 189 // Prevents bombarding the user with notifications after activation. 190 $over_time = get_option( 'seedprod_over_time', array() ); 191 192 if ( 193 ! empty( $over_time['installed_date'] ) && 194 ! empty( $notification['start'] ) && 195 $over_time['installed_date'] > strtotime( $notification['start'] ) 196 ) { 197 //continue; 198 } 199 200 $data[] = $notification; 201 } 202 203 return $data; 204 } 205 206 /** 207 * Verify saved notification data for active notifications. 208 * 209 * @param array $notifications Array of notifications items to verify. 210 * 211 * @return array 212 * @since {VERSION} 213 * 214 */ 215 public function verify_active( $notifications ) { 216 if ( ! is_array( $notifications ) || empty( $notifications ) ) { 217 return array(); 218 } 219 220 // Remove notifications that are not active. 221 foreach ( $notifications as $key => $notification ) { 222 if ( 223 ( ! empty( $notification['start'] ) && time() < strtotime( $notification['start'] ) ) || 224 ( ! empty( $notification['end'] ) && time() > strtotime( $notification['end'] ) ) 225 ) { 226 unset( $notifications[ $key ] ); 227 } 228 } 229 230 return $notifications; 231 } 232 233 /** 234 * Get notification data. 235 * 236 * @return array 237 * @since {VERSION} 238 * 239 */ 240 public function get() { 241 if ( ! $this->has_access() ) { 242 return array(); 243 } 244 245 $option = $this->get_option(); 246 247 $events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array(); 248 $feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array(); 249 250 return array_merge( $events, $feed ); 251 } 252 253 /** 254 * Get notification count. 255 * 256 * @return int 257 * @since {VERSION} 258 * 259 */ 260 public function get_count() { 261 $count = count( $this->get() ); 262 263 return $count; 264 } 265 266 /** 267 * Add a manual notification event. 268 * 269 * @param array $notification Notification data. 270 * 271 * @since {VERSION} 272 * 273 */ 274 public function add( $notification ) { 275 if ( empty( $notification['id'] ) ) { 276 return; 277 } 278 279 $option = $this->get_option(); 280 281 if ( in_array( $notification['id'], $option['dismissed'] ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict 282 return; 283 } 284 285 foreach ( $option['events'] as $item ) { 286 if ( $item['id'] === $notification['id'] ) { 287 return; 288 } 289 } 290 291 $notification = $this->verify( array( $notification ) ); 292 293 update_option( 294 $this->option_name, 295 array( 296 'update' => $option['update'], 297 'feed' => $option['feed'], 298 'events' => array_merge( $notification, $option['events'] ), 299 'dismissed' => $option['dismissed'], 300 ) 301 ); 302 } 303 304 /** 305 * Update notification data from feed. 306 * 307 * @since {VERSION} 308 */ 309 public function update() { 310 $feed = $this->fetch_feed(); 311 $option = $this->get_option(); 312 313 update_option( 314 $this->option_name, 315 array( 316 'update' => time(), 317 'feed' => $feed, 318 'events' => $option['events'], 319 'dismissed' => $option['dismissed'], 320 ) 321 ); 322 } 323 324 /** 325 * Dismiss notification via AJAX. 326 * 327 * @since {VERSION} 328 */ 329 public function dismiss() { 330 331 // Run a security check. 332 check_ajax_referer( 'seedprod_lite_notification_dismiss', '_wpnonce' ); 333 334 // Check for access and required param. 335 if ( ! $this->has_access() || empty( $_POST['id'] ) ) { 336 wp_send_json_error(); 337 } 338 339 $id = sanitize_text_field( wp_unslash( $_POST['id'] ) ); 340 $option = $this->get_option(); 341 $type = is_numeric( $id ) ? 'feed' : 'events'; 342 343 $option['dismissed'][] = $id; 344 $option['dismissed'] = array_unique( $option['dismissed'] ); 345 346 // Remove notification. 347 if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) { 348 foreach ( $option[ $type ] as $key => $notification ) { 349 if ( $notification['id'] == $id ) { // phpcs:ignore WordPress.PHP.StrictComparisons 350 unset( $option[ $type ][ $key ] ); 351 break; 352 } 353 } 354 } 355 356 update_option( $this->option_name, $option ); 357 358 wp_send_json_success(); 359 } 360 361 /** 362 * This generates the markup for the notifications indicator if needed. 363 * 364 * @return string 365 */ 366 public function get_menu_count() { 367 if ( $this->get_count() > 0 ) { 368 return '<span class="seedprod-menu-notification-indicator"></span>'; 369 } 370 371 return ''; 372 } 373 374 /** 375 * Retrieve the notifications via an ajax call. 376 */ 377 public function ajax_get_notifications() { 378 379 // Run a security check. 380 check_ajax_referer( 'sp-admin-nonce', 'nonce' ); 381 382 $notifications_data = array( 383 'notifications' => $this->get(), 384 'view_url' => $this->get_view_url(), 385 ); 386 387 wp_send_json_success( $notifications_data ); 388 } 389 390 /** 391 * Get the URL for the page where users can see/read notifications. 392 * 393 * @return string 394 */ 395 public function get_view_url() { 396 $disabled = get_option( 'dashboards_disabled', false ); 397 398 $url = add_query_arg( 'page', 'seedprod_reports', admin_url( 'admin.php' ) ); 399 400 if ( false !== $disabled ) { 401 $url = is_multisite() ? network_admin_url( 'admin.php?page=seedprod_network' ) : admin_url( 'admin.php?page=seedprod_settings' ); 402 } 403 404 return $url; 405 } 406 } 407 } 408 409 //add_action( 'seedprod_notification', array( 'SeedProd_Notifications', 'update' ) ); 410 if ( ! function_exists( 'seedprod_lite_do_notifications' ) ) { 411 412 add_action( 'seedprod_notifications', 'seedprod_lite_do_notifications' ); 413 function seedprod_lite_do_notifications() { 414 $notifications = new SeedProd_Notifications(); 415 $notifications->update(); 416 417 } 418 }