| @@ -94,8 +94,74 @@ | ||
| 94 | 94 | do_action('ws_plugin__s2member_after_display_admin_notice', get_defined_vars()); |
| 95 | 95 | } |
| 96 | 96 | |
| 97 | 97 | /** |
| 98 | + * Displays a branded s2Member administrative notice. | |
| 99 | + * | |
| 100 | + * @package s2Member\Admin_Notices | |
| 101 | + * @since 260904.1923 | |
| 102 | + * | |
| 103 | + * @param string $title Notice title. | |
| 104 | + * @param string $message Main notice message. | |
| 105 | + * @param bool $error Optional. True for an error notice; otherwise an informational notice. | |
| 106 | + * @param string $dismiss_url Optional explicit URL that dismisses the current notice incident. | |
| 107 | + */ | |
| 108 | + public static function display_branded_notice($title = '', $message = '', $error = FALSE, $dismiss_url = '') | |
| 109 | + { | |
| 110 | + $title = trim((string)$title); | |
| 111 | + $message = trim((string)$message); | |
| 112 | + $dismiss_url = trim((string)$dismiss_url); | |
| 113 | + if(!$message) | |
| 114 | + return; | |
| 115 | + | |
| 116 | + $_logo_url = $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/images/logo-square-big.png'; | |
| 117 | + $_notice_class = ($error) ? 'notice notice-error' : 'notice notice-info'; //260910.0818 Use WordPress's standard error style for the immediate Red problem and the quieter informational style for the persistent Orange review notice. | |
| 118 | + $_dismiss = (($dismiss_url !== '') ? '<a href="'.esc_url($dismiss_url).'" title="Dismiss this incident" style="position:absolute; top:8px; right:10px; text-decoration:none;">Dismiss</a>' : ''); //260910.0709 The caller owns the nonce-protected incident URL so dismissal follows incident identity, not mutable notice text. | |
| 119 | + | |
| 120 | + //260909.2021 Keep incident dismissal explicit so changing notice text cannot accidentally make a persistent problem look like a new one. | |
| 121 | + echo '<div class="'.esc_attr($_notice_class).'" style="position:relative; margin:0 0 15px 2px !important; padding:8px '.(($dismiss_url !== '') ? '60px' : '8px').' 8px 8px !important;">'.$_dismiss.'<table cellspacing="0" cellpadding="0"><tr><td style="vertical-align:top; padding:0 10px 0 0;"><img src="'.esc_url($_logo_url).'" alt="" width="40" height="40" style="border:0;" /></td><td style="vertical-align:top;">'.(($title !== '') ? '<strong>'.esc_html($title).'</strong><br />' : '').wp_kses_post($message).'</td></tr></table></div>'; | |
| 122 | + } | |
| 123 | + | |
| 124 | + /** | |
| 125 | + * Warns administrators when Pro is too old to contain the current Pro updater behavior. | |
| 126 | + * | |
| 127 | + * @package s2Member\Admin_Notices | |
| 128 | + * @since 260917.0425 | |
| 129 | + * | |
| 130 | + * @attaches-to `add_action('admin_notices');` | |
| 131 | + * @attaches-to `add_action('user_admin_notices');` | |
| 132 | + * @attaches-to `add_action('network_admin_notices');` | |
| 133 | + */ | |
| 134 | + public static function outdated_pro_notice() | |
| 135 | + { | |
| 136 | + if(!current_user_can('update_plugins') || !defined('WS_PLUGIN__S2MEMBER_PRO_VERSION') || !defined('WS_PLUGIN__S2MEMBER_VERSION')) | |
| 137 | + return; | |
| 138 | + | |
| 139 | + //260917.0425 v260913 introduced the current background Pro updater flow; Framework owns this fallback warning only for older Pro releases. | |
| 140 | + $_current_updater_version = '260913'; | |
| 141 | + if(version_compare(WS_PLUGIN__S2MEMBER_PRO_VERSION, $_current_updater_version, '>=') || !version_compare(WS_PLUGIN__S2MEMBER_PRO_VERSION, WS_PLUGIN__S2MEMBER_VERSION, '<')) | |
| 142 | + return; | |
| 143 | + | |
| 144 | + $_account_url = 'https://s2member.com/account/'; | |
| 145 | + //260917.2113 s2Member versions begin with yymmdd; show the installed Pro version's approximate age so administrators can immediately see how far behind it is. | |
| 146 | + $_pro_release_age = ''; | |
| 147 | + if(preg_match('/^(\d{2})(\d{2})(\d{2})/', WS_PLUGIN__S2MEMBER_PRO_VERSION, $_pro_version_parts)) | |
| 148 | + { | |
| 149 | + $_pro_release_timestamp = mktime(0, 0, 0, (int) $_pro_version_parts[2], (int) $_pro_version_parts[3], 2000 + (int) $_pro_version_parts[1]); | |
| 150 | + if($_pro_release_timestamp) | |
| 151 | + $_pro_release_age = human_time_diff($_pro_release_timestamp, current_time('timestamp')); | |
| 152 | + } | |
| 153 | + | |
| 154 | + //260917.1937 Keep this urgent notice compact and skimmable: short paragraphs, prominent version age/security risk, and a clearly separated update action. | |
| 155 | + $_message = '<p style="line-height:1.3em; margin:.3em 0;"><strong>Your s2Member Pro v'.esc_html(WS_PLUGIN__S2MEMBER_PRO_VERSION).($_pro_release_age ? ' is '.esc_html($_pro_release_age).' old and' : '').' is missing important security fixes</strong>.</p>'; | |
| 156 | + $_message .= '<p style="line-height:1.3em; margin:.3em 0;"><em>Please install the latest ZIP from WP Admin > Plugins > Add Plugin > Upload Plugin.</em></p>'; | |
| 157 | + $_update_button = '<a class="button button-primary" style="margin-top:.3em; background:darkred; border-color:darkred;" href="'.esc_url($_account_url).'" target="_blank" rel="external noopener">Download the Latest s2Member Pro Now</a>'; | |
| 158 | + | |
| 159 | + //260917.1937 Keep this Framework-owned warning persistent and red; include the action in the message instead of the helper's review slot so no extra <br> is inserted before it. | |
| 160 | + c_ws_plugin__s2member_admin_notices::display_security_notice($_message.$_update_button, '', array(), '', 'notice-error'); | |
| 161 | + } | |
| 162 | + | |
| 163 | + /** | |
| 98 | 164 | * Displays a branded s2Member security notice. |
| 99 | 165 | * |
| 100 | 166 | * @package s2Member\Admin_Notices |
| 101 | 167 | * @since 260813 |
| @@ -103,10 +169,11 @@ | ||
| 103 | 169 | * @param string $message Main notice message. |
| 104 | 170 | * @param string $review Review prompt shown above the items. |
| 105 | 171 | * @param array $items Notice items, with safe HTML allowed. |
| 106 | 172 | * @param string $dismiss_url Optional dismissal URL. |
| 173 | + * @param string $notice_class Optional WordPress notice severity class. | |
| 107 | 174 | */ |
| 108 | - public static function display_security_notice($message = '', $review = '', $items = array(), $dismiss_url = '') | |
| 175 | + public static function display_security_notice($message = '', $review = '', $items = array(), $dismiss_url = '', $notice_class = 'notice-warning') | |
| 109 | 176 | { |
| 110 | 177 | $message = trim((string)$message); |
| 111 | 178 | $review = trim((string)$review); |
| 112 | 179 | $items = (array)$items; |
| @@ -112,8 +179,11 @@ | ||
| 112 | 179 | $items = (array)$items; |
| 113 | 180 | if(!$message) |
| 114 | 181 | return; |
| 115 | 182 | |
| 183 | + //260917.0513 Preserve the existing warning style by default, while allowing especially urgent security notices to use WordPress's stronger error styling. | |
| 184 | + $_notice_class = (($notice_class === 'notice-error') ? 'notice notice-error' : 'notice notice-warning'); | |
| 185 | + | |
| 116 | 186 | $_items = array(); |
| 117 | 187 | foreach($items as $_item) |
| 118 | 188 | if(is_string($_item) && trim($_item) !== '') |
| 119 | 189 | $_items[] = '<em>• '.wp_kses_post($_item).'</em>'; |
| @@ -119,9 +189,12 @@ | ||
| 119 | 189 | $_items[] = '<em>• '.wp_kses_post($_item).'</em>'; |
| 120 | 190 | |
| 121 | 191 | $_logo_url = $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/images/logo-square-big.png'; |
| 122 | 192 | $_dismiss = (($dismiss_url !== '') ? '<a href="'.esc_url($dismiss_url).'" title="Dismiss until detected again" style="position:absolute; top:8px; right:10px; text-decoration:none;">Dismiss</a>' : ''); |
| 123 | - echo '<div class="notice notice-warning" style="position:relative; margin:0 0 15px 2px !important; padding:8px 60px 8px 8px !important;">'.$_dismiss.'<table cellspacing="0" cellpadding="0"><tr><td style="vertical-align:top; padding:0 10px 0 0;"><img src="'.esc_url($_logo_url).'" alt="" width="40" height="40" style="border:0;" /></td><td style="vertical-align:top;"><strong>s2Member Security Notice</strong><br />'.wp_kses_post($message).(($review !== '') ? '<br />'.wp_kses_post($review) : '').(($_items) ? '<br />'.implode('<br />', $_items) : '').'</td></tr></table></div>'; | |
| 193 | + | |
| 194 | + //260917.1937 Give urgent red security notices a stronger heading without changing the existing presentation of normal yellow security notices. | |
| 195 | + $_title = (($notice_class === 'notice-error') ? '<h2 style="margin:0 0 .3em; color:darkred;">s2Member Security Notice</h2>' : '<strong>s2Member Security Notice</strong><br />'); | |
| 196 | + echo '<div class="'.esc_attr($_notice_class).'" style="position:relative; margin:0 0 15px 2px !important; padding:8px 60px 8px 8px !important;">'.$_dismiss.'<table cellspacing="0" cellpadding="0"><tr><td style="vertical-align:top; padding:0 10px 0 0;"><img src="'.esc_url($_logo_url).'" alt="" width="40" height="40" style="border:0;" /></td><td style="vertical-align:top;">'.$_title.wp_kses_post($message).(($review !== '') ? '<br />'.wp_kses_post($review) : '').(($_items) ? '<br />'.implode('<br />', $_items) : '').'</td></tr></table></div>'; | |
| 124 | 197 | } |
| 125 | 198 | |
| 126 | 199 | /** |
| 127 | 200 | * Records a shortcode user field that is not approved for cross-user display. |
| @@ -224,10 +297,10 @@ | ||
| 224 | 297 | unset($_details, $_item, $_post_id, $_edit_link, $_post_title); |
| 225 | 298 | |
| 226 | 299 | $_settings_url = add_query_arg('s2member-open-panel', 'shortcode-user-fields-whitelist', admin_url('/admin.php?page=ws-plugin--s2member-gen-ops')).'#ws-plugin--s2member-shortcode-user-fields-whitelist'; |
| 227 | 300 | $_dismiss_url = wp_nonce_url(add_query_arg('s2member-dismiss-shortcode-user-fields-notice', '1', admin_url()), 's2member-dismiss-shortcode-user-fields-notice'); |
| 228 | - $_message = 'Some s2Member shortcodes use user fields that are not in <em><a href="'.esc_url($_settings_url).'">s2Member → General Options → Shortcode User Fields Whitelist</a></em>'; | |
| 229 | - c_ws_plugin__s2member_admin_notices::display_security_notice($_message, 'Review the fields below and allow the ones that are okay for other users to see:', $_field_items, $_dismiss_url); | |
| 301 | + $_message = 'Some s2Member shortcodes attempted to display user fields from other accounts that are not in <em><a href="'.esc_url($_settings_url).'">s2Member → General Options → Shortcode User Fields Whitelist</a></em>. Those cross-user field values were blocked.'; | |
| 302 | + c_ws_plugin__s2member_admin_notices::display_security_notice($_message, 'Review the blocked fields below and allow the ones that are okay for other users to see:', $_field_items, $_dismiss_url); | |
| 230 | 303 | } |
| 231 | 304 | |
| 232 | 305 | /** |
| 233 | 306 | * Processes all administrative notices. |
| @@ -257,9 +330,9 @@ | ||
| 257 | 330 | { |
| 258 | 331 | //250510 Fixed for PHP 8.1+: safely normalize on_pages before foreach |
| 259 | 332 | $notice = (array)$notice; |
| 260 | 333 | $notice['on_pages'] = empty($notice['on_pages']) ? array('*') : (array)$notice['on_pages']; |
| 261 | - foreach($notice['on_pages'] as $page) | |
| 334 | + foreach($notice['on_pages'] as $page) | |
| 262 | 335 | { |
| 263 | 336 | if(!preg_match('/^(.+?)\:/', $page)) // NO prefix? |
| 264 | 337 | $page = 'blog:'.ltrim($page, ':'); // `blog:` |
| 265 | 338 | |