| 1 |
<?php |
| 2 |
|
| 3 |
namespace wpforo\classes; |
| 4 |
|
| 5 |
// Exit if accessed directly |
| 6 |
if( ! defined( 'ABSPATH' ) ) exit; |
| 7 |
|
| 8 |
|
| 9 |
class Notices { |
| 10 |
private $types = []; |
| 11 |
private $notices = []; |
| 12 |
private $timeouts = []; |
| 13 |
|
| 14 |
private function init_types() { |
| 15 |
$this->types = array_merge( |
| 16 |
[ 'neutral', 'error', 'success' ], |
| 17 |
apply_filters( 'wpforo_notice_types', [] ) |
| 18 |
); |
| 19 |
$this->types = array_map( 'strtolower', array_unique( $this->types ) ); |
| 20 |
} |
| 21 |
|
| 22 |
private function init_timeouts() { |
| 23 |
foreach( $this->types as $type ) $this->timeouts[ $type ] = $this->get_timeout( $type ); |
| 24 |
} |
| 25 |
|
| 26 |
private function get_timeout( $type ) { |
| 27 |
switch( $type ) { |
| 28 |
case "success": |
| 29 |
$durr = 4000; |
| 30 |
break; |
| 31 |
case "neutral": |
| 32 |
$durr = 0; |
| 33 |
break; |
| 34 |
default: |
| 35 |
$durr = 8000; |
| 36 |
} |
| 37 |
|
| 38 |
return apply_filters( "wpforo_notice_timeout_{$type}", $durr ); |
| 39 |
} |
| 40 |
|
| 41 |
public function get_timeouts() { |
| 42 |
return $this->timeouts; |
| 43 |
} |
| 44 |
|
| 45 |
private function reset() { |
| 46 |
foreach( $this->types as $type ) $this->notices[ $type ] = []; |
| 47 |
} |
| 48 |
|
| 49 |
public function is_empty() { |
| 50 |
foreach( $this->notices as $notice ) if( ! empty( $notice ) ) return false; |
| 51 |
|
| 52 |
return true; |
| 53 |
} |
| 54 |
|
| 55 |
public function __construct() { |
| 56 |
$this->init_types(); |
| 57 |
$this->reset(); |
| 58 |
add_action( 'wpforo_before_init', [ $this, 'init' ] ); |
| 59 |
} |
| 60 |
|
| 61 |
public function init() { |
| 62 |
$this->init_timeouts(); |
| 63 |
if( WPF()->session_token ) { |
| 64 |
$sql = "SELECT DISTINCT `key`, `value` FROM `" . WPF()->tables->logs . "` WHERE `sessionid` = %s AND `key` IN('" . implode( "','", $this->types ) . "')"; |
| 65 |
if( $notices = (array) WPF()->db->get_results( WPF()->db->prepare( $sql, WPF()->session_token ), ARRAY_A ) ) { |
| 66 |
foreach( $notices as $notice ) { |
| 67 |
if( trim( (string) $notice['value'] ) ) { |
| 68 |
$this->notices[ $notice['key'] ] = array_merge( |
| 69 |
$this->notices[ $notice['key'] ], |
| 70 |
wpforo_is_json( $notice['value'] ) ? json_decode( $notice['value'], true ) : (array) $notice['value'] |
| 71 |
); |
| 72 |
} |
| 73 |
} |
| 74 |
} |
| 75 |
} |
| 76 |
} |
| 77 |
|
| 78 |
/** |
| 79 |
* |
| 80 |
* @param string|array $args |
| 81 |
* @param string $type (e.g. success|error) |
| 82 |
* @param string|array $s |
| 83 |
* |
| 84 |
* @return bool |
| 85 |
*/ |
| 86 |
public function add( $args, $type = 'neutral', $s = [] ) { |
| 87 |
if( ! $args ) return false; |
| 88 |
$args = (array) $args; |
| 89 |
if( $s && count( $args ) === 1 && is_array( $s ) && isset( $s[0] ) && ! is_array( $s[0] ) ) { |
| 90 |
$s = [ $s ]; |
| 91 |
} else { |
| 92 |
$s = (array) $s; |
| 93 |
} |
| 94 |
|
| 95 |
if( WPF()->session_token ) { |
| 96 |
$type = strtolower( (string) $type ); |
| 97 |
foreach( $args as $key => $arg ) { |
| 98 |
if( $s && isset( $s[ $key ] ) ) { |
| 99 |
$args[ $key ] = wpforo_sprintf_array( wpforo_phrase( $arg, false ), $s[ $key ] ); |
| 100 |
} else { |
| 101 |
$args[ $key ] = wpforo_phrase( $arg, false ); |
| 102 |
} |
| 103 |
} |
| 104 |
|
| 105 |
$this->notices[ $type ] = array_merge( (array) $this->notices[ $type ], (array) $args ); |
| 106 |
$this->notices[ $type ] = array_unique( $this->notices[ $type ] ); |
| 107 |
|
| 108 |
if( ! wpforo_is_ajax() ) { |
| 109 |
$insert_id_backup = WPF()->db->insert_id; |
| 110 |
WPF()->db->insert( WPF()->tables->logs, [ |
| 111 |
'sessionid' => WPF()->session_token, |
| 112 |
'key' => $type, |
| 113 |
'value' => wp_json_encode( (array) $args ), |
| 114 |
], [ '%s', '%s', '%s' ] ); |
| 115 |
WPF()->db->insert_id = $insert_id_backup; |
| 116 |
} |
| 117 |
|
| 118 |
return true; |
| 119 |
} |
| 120 |
|
| 121 |
return false; |
| 122 |
} |
| 123 |
|
| 124 |
/** |
| 125 |
* |
| 126 |
* @return bool |
| 127 |
* |
| 128 |
*/ |
| 129 |
public function clear() { |
| 130 |
if( WPF()->session_token ) { |
| 131 |
$this->reset(); |
| 132 |
|
| 133 |
WPF()->db->delete( WPF()->tables->logs, [ 'sessionid' => WPF()->session_token ], [ '%s' ] ); |
| 134 |
|
| 135 |
return true; |
| 136 |
} |
| 137 |
|
| 138 |
return false; |
| 139 |
} |
| 140 |
|
| 141 |
/** |
| 142 |
* <p class="success">success msg text</p><p class="error">error msg text</p> |
| 143 |
* |
| 144 |
* @return string |
| 145 |
*/ |
| 146 |
public function get_notices(): string { |
| 147 |
$inner = ''; |
| 148 |
if( ! $this->is_empty() ) { |
| 149 |
foreach( $this->notices as $type => $notice ) { |
| 150 |
$notice = (array) $notice; |
| 151 |
foreach( $notice as $msg ) { |
| 152 |
if( ! is_array( $msg ) ) { |
| 153 |
if( $msg = trim( (string) $msg ) ) $inner .= sprintf( '<p class="%s">%s</p>', sanitize_html_class( $type ), $msg ); |
| 154 |
} |
| 155 |
} |
| 156 |
} |
| 157 |
|
| 158 |
$this->clear(); |
| 159 |
} |
| 160 |
|
| 161 |
return $inner; |
| 162 |
} |
| 163 |
|
| 164 |
/** |
| 165 |
* |
| 166 |
* show collected wpforo notices |
| 167 |
* |
| 168 |
* @return void |
| 169 |
*/ |
| 170 |
public function show() { |
| 171 |
if( $this->is_empty() ) return; |
| 172 |
if( wpforo_is_admin() ) { |
| 173 |
$this->backend( $this->notices ); |
| 174 |
} else { |
| 175 |
$this->frontend( $this->notices ); |
| 176 |
} |
| 177 |
$this->clear(); |
| 178 |
} |
| 179 |
|
| 180 |
private function backend( $notices ) { |
| 181 |
$inner = ''; |
| 182 |
foreach( $notices as $type => $notice ) { |
| 183 |
$notice = (array) $notice; |
| 184 |
foreach( $notice as $msg ) { |
| 185 |
if( ! is_array( $msg ) && ( $msg = trim( (string) $msg ) ) ) { |
| 186 |
$inner .= sprintf( |
| 187 |
'<div class="notice is-dismissible notice-%s"> |
| 188 |
<p>%s</p> |
| 189 |
</div>', |
| 190 |
sanitize_html_class( $type ), |
| 191 |
wpforo_kses( $msg ) |
| 192 |
); |
| 193 |
} |
| 194 |
} |
| 195 |
} |
| 196 |
echo '<div class="wpf-backend-notices-wrap">' . $inner . '</div>'; |
| 197 |
} |
| 198 |
|
| 199 |
private function frontend( $notices ) { |
| 200 |
$inner = ''; |
| 201 |
foreach( $notices as $type => $notice ) { |
| 202 |
$notice = (array) $notice; |
| 203 |
foreach( $notice as $msg ) { |
| 204 |
if( ! is_array( $msg ) && ( $msg = trim( (string) $msg ) ) ) { |
| 205 |
$inner .= sprintf( PHP_EOL . 'wpforo_notice_show("%1$s", "%2$s");' . PHP_EOL, addslashes( wpforo_kses( $msg ) ), sanitize_html_class( $type ) ); |
| 206 |
} |
| 207 |
} |
| 208 |
} |
| 209 |
?> |
| 210 |
<script type="text/javascript"> |
| 211 |
window.jQuery(document).ready(function () { |
| 212 |
<?php echo $inner ?> |
| 213 |
}); |
| 214 |
</script> |
| 215 |
<?php |
| 216 |
} |
| 217 |
|
| 218 |
public function addonNote() { |
| 219 |
$lastHash = get_option( 'wpforo_addon_note_dismissed' ); |
| 220 |
if( ! $lastHash ) { |
| 221 |
$hash = $this->addonHash(); |
| 222 |
update_option( 'wpforo_addon_note_dismissed', $hash ); |
| 223 |
update_option( 'wpforo_addon_note_first', 'true' ); |
| 224 |
} else { |
| 225 |
$lastHashArray = explode( ',', $lastHash ); |
| 226 |
$currentHash = $this->addonHash(); |
| 227 |
if( $lastHash != $currentHash ) { |
| 228 |
?> |
| 229 |
<div class="updated notice wpforo_addon_note is-dismissible" style="margin-top:10px;"> |
| 230 |
<p style="font-weight:normal; font-size:15px; border-bottom:1px dotted #DCDCDC; padding-bottom:10px; width:95%;"><strong><?php _e( |
| 231 |
'New Addons for Your Forum!', |
| 232 |
'wpforo' |
| 233 |
); ?></strong><br><span style="font-size:14px;"><?php _e( 'Extend your forum with wpForo addons', 'wpforo' ); ?></span></p> |
| 234 |
<div style="font-size:14px;"> |
| 235 |
<?php |
| 236 |
foreach( wpforo_get_addons_info() as $addon ) { |
| 237 |
if( in_array( $addon['title'], $lastHashArray ) ) { |
| 238 |
continue; |
| 239 |
} |
| 240 |
?> |
| 241 |
<div style="display:inline-block; min-width:27%; padding-right:10px; margin-bottom:1px;border-bottom:1px dotted #DCDCDC; border-right:1px dotted #DCDCDC; padding-bottom:10px;"> |
| 242 |
<img src="<?php echo $addon['thumb'] ?>" style="height:40px; width:auto; vertical-align:middle; margin:0 10px; text-decoration:none;"/> <a |
| 243 |
href="<?php echo $addon['url'] ?>" style="text-decoration:none;" target="_blank">wpForo <?php echo $addon['title']; ?></a></div> |
| 244 |
<?php |
| 245 |
} |
| 246 |
?> |
| 247 |
<div style="clear:both;"></div> |
| 248 |
</div> |
| 249 |
<p> <a href="<?php echo admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'addons' ) ) ?>"><?php _e( 'View all Addons', 'wpforo' ); ?> »</a></p> |
| 250 |
</div> |
| 251 |
<script>jQuery(document).on('click', '.wpforo_addon_note .notice-dismiss', function () {jQuery.ajax({ url: ajaxurl, data: { action: 'dismiss_wpforo_addon_note' } });});</script> |
| 252 |
<?php |
| 253 |
} |
| 254 |
} |
| 255 |
} |
| 256 |
|
| 257 |
public function dismissAddonNote() { |
| 258 |
$hash = $this->addonHash(); |
| 259 |
update_option( 'wpforo_addon_note_dismissed', $hash ); |
| 260 |
exit(); |
| 261 |
} |
| 262 |
|
| 263 |
public function dismissAddonNoteOnPage() { |
| 264 |
$hash = $this->addonHash(); |
| 265 |
update_option( 'wpforo_addon_note_dismissed', $hash ); |
| 266 |
} |
| 267 |
|
| 268 |
public function addonHash() { |
| 269 |
$viewed = ''; |
| 270 |
foreach( wpforo_get_addons_info() as $addon ) { |
| 271 |
$viewed .= $addon['title'] . ','; |
| 272 |
} |
| 273 |
|
| 274 |
return $viewed; |
| 275 |
} |
| 276 |
|
| 277 |
public function refreshAddonPage() { |
| 278 |
$lastHash = get_option( 'wpforo_addon_note_dismissed' ); |
| 279 |
$currentHash = $this->addonHash(); |
| 280 |
if( $lastHash != $currentHash ) { |
| 281 |
?> |
| 282 |
<script language="javascript">jQuery(document).ready(function () { |
| 283 |
location.reload(); |
| 284 |
});</script> |
| 285 |
<?php |
| 286 |
} |
| 287 |
} |
| 288 |
|
| 289 |
public function dismissCacheConflict() { |
| 290 |
$excluded = wpforo_get_option( 'wpforo_excluded_cache', '', false ); |
| 291 |
$not_excluded_plugins = WPF()->cache->cache_plugins_status(); |
| 292 |
if( ! empty( $not_excluded_plugins ) ) { |
| 293 |
foreach( $not_excluded_plugins as $plugin ) { |
| 294 |
$excluded .= ',' . $plugin['name']; |
| 295 |
} |
| 296 |
} |
| 297 |
wpforo_update_option( 'wpforo_excluded_cache', trim( (string) $excluded, ',' ) ); |
| 298 |
exit(); |
| 299 |
} |
| 300 |
|
| 301 |
/** |
| 302 |
* Addons Store Promo Box |
| 303 |
* Shows a dismissible info box promoting the new Addons Store page |
| 304 |
* where users can browse, purchase, and manage wpForo addons directly. |
| 305 |
* |
| 306 |
* @since 3.1.8 |
| 307 |
*/ |
| 308 |
public function addonsStorePromo() { |
| 309 |
if( ! current_user_can( 'manage_options' ) ) return; |
| 310 |
|
| 311 |
// Don't show on the addons page itself (they're already there) |
| 312 |
if( function_exists( 'get_current_screen' ) ) { |
| 313 |
$screen = get_current_screen(); |
| 314 |
if( $screen && strpos( $screen->id, 'wpforo-addons' ) !== false ) return; |
| 315 |
} |
| 316 |
|
| 317 |
// Check if user has already dismissed this promo version |
| 318 |
$promo_version = '3.2.0'; |
| 319 |
$dismissed = get_user_meta( get_current_user_id(), 'wpforo_addons_store_promo_dismissed', true ); |
| 320 |
if( $dismissed === $promo_version ) return; |
| 321 |
|
| 322 |
$addons_url = admin_url( 'admin.php?page=wpforo-addons' ); |
| 323 |
$addons = wpforo_get_addons_info(); |
| 324 |
$addon_thumbs = array_slice( array_column( $addons, 'thumb' ), 0, 10 ); |
| 325 |
?> |
| 326 |
<div class="notice notice-info wpforo-addons-store-promo is-dismissible" style="padding: 15px 12px; border-left-color: #00a0d2;"> |
| 327 |
<div style="display: flex; align-items: flex-start; gap: 15px;"> |
| 328 |
<div style="flex-shrink: 0;"> |
| 329 |
<span class="dashicons dashicons-store" style="font-size: 50px; width: 50px; height: 50px; color: #00a0d2;"></span> |
| 330 |
</div> |
| 331 |
<div style="flex: 1;"> |
| 332 |
<p style="font-size: 16px; font-weight: 600; margin: 0 0 6px 0; color: #1d2327;"> |
| 333 |
<?php |
| 334 |
printf( |
| 335 |
__( 'Get Your wpForo Addon Licenses %s', 'wpforo' ), |
| 336 |
'<span style="background: linear-gradient(90deg, #1d2327, #ff6d00); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">' . __( 'Directly in Your Dashboard', 'wpforo' ) . '</span>' |
| 337 |
); |
| 338 |
?> |
| 339 |
</p> |
| 340 |
<p style="font-size: 15px; margin: 0 0 12px 0; color: #50575e;"> |
| 341 |
<?php _e( 'You can now purchase, install, and manage all wpForo addons directly from the Addons page in your WordPress dashboard, no need to visit the gVectors Store. Activate licenses, get automatic updates, and extend your forum with just a few clicks.', 'wpforo' ); ?> |
| 342 |
</p> |
| 343 |
<div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;"> |
| 344 |
<?php foreach( $addon_thumbs as $thumb ) : ?> |
| 345 |
<img src="<?php echo esc_url( $thumb ); ?>" alt="" style="width: 30px; height: 30px; border-radius: 3px; object-fit: cover;" /> |
| 346 |
<?php endforeach; ?> |
| 347 |
<a href="<?php echo esc_url( $addons_url ); ?>" class="button" style="height: 30px; line-height: 28px; padding: 0 22px; font-size: 13px; min-height: 30px; border-color: #0283aa; color: #0283aa;"> |
| 348 |
<?php _e( 'Browse All', 'wpforo' ); ?> → |
| 349 |
</a> |
| 350 |
</div> |
| 351 |
</div> |
| 352 |
</div> |
| 353 |
</div> |
| 354 |
<script> |
| 355 |
jQuery(document).on('click', '.wpforo-addons-store-promo .notice-dismiss', function() { |
| 356 |
jQuery.ajax({ |
| 357 |
url: ajaxurl, |
| 358 |
data: { |
| 359 |
action: 'wpforo_dismiss_addons_store_promo', |
| 360 |
_wpnonce: '<?php echo wp_create_nonce( 'wpforo_dismiss_addons_store_promo' ); ?>' |
| 361 |
} |
| 362 |
}); |
| 363 |
}); |
| 364 |
</script> |
| 365 |
<?php |
| 366 |
} |
| 367 |
|
| 368 |
/** |
| 369 |
* AJAX handler: dismiss the addons store promo for the current user. |
| 370 |
* |
| 371 |
* @since 3.1.8 |
| 372 |
*/ |
| 373 |
public function dismissAddonsStorePromo() { |
| 374 |
check_ajax_referer( 'wpforo_dismiss_addons_store_promo', '_wpnonce' ); |
| 375 |
|
| 376 |
if( ! current_user_can( 'manage_options' ) ) { |
| 377 |
wp_send_json_error( 'Unauthorized', 403 ); |
| 378 |
} |
| 379 |
|
| 380 |
$promo_version = '3.2.0'; |
| 381 |
update_user_meta( get_current_user_id(), 'wpforo_addons_store_promo_dismissed', $promo_version ); |
| 382 |
|
| 383 |
wp_send_json_success(); |
| 384 |
} |
| 385 |
|
| 386 |
} |
| 387 |
|