PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260927
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260927
260927 260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 All 190 releases
← All changes | src/includes/classes/admin-notices.inc.php +68 -12 260913 → 260927 View file →
@@ -121,8 +121,47 @@
121 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 122 }
123 123
124 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 &gt; Plugins &gt; Add Plugin &gt; 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 + /**
125 164 * Displays a branded s2Member security notice.
126 165 *
127 166 * @package s2Member\Admin_Notices
128 167 * @since 260813
@@ -130,10 +169,11 @@
130 169 * @param string $message Main notice message.
131 170 * @param string $review Review prompt shown above the items.
132 171 * @param array $items Notice items, with safe HTML allowed.
133 172 * @param string $dismiss_url Optional dismissal URL.
173 + * @param string $notice_class Optional WordPress notice severity class.
134 174 */
135 - 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')
136 176 {
137 177 $message = trim((string)$message);
138 178 $review = trim((string)$review);
139 179 $items = (array)$items;
@@ -139,8 +179,11 @@
139 179 $items = (array)$items;
140 180 if(!$message)
141 181 return;
142 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 +
143 186 $_items = array();
144 187 foreach($items as $_item)
145 188 if(is_string($_item) && trim($_item) !== '')
146 189 $_items[] = '<em>&bull;&nbsp; '.wp_kses_post($_item).'</em>';
@@ -146,9 +189,12 @@
146 189 $_items[] = '<em>&bull;&nbsp; '.wp_kses_post($_item).'</em>';
147 190
148 191 $_logo_url = $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/images/logo-square-big.png';
149 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>' : '');
150 - 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>';
151 197 }
152 198
153 199 /**
154 200 * Records a shortcode user field that is not approved for cross-user display.
@@ -233,28 +279,38 @@
233 279 return;
234 280 }
235 281 update_option('ws_plugin__s2member_shortcode_user_fields_transition_fields', $_fields, FALSE);
236 282
237 - // Build a useful field list with a separate entry for each detected shortcode location.
238 - $_field_items = array();
283 + //260921.2025 Keep this notice compact by listing unique fields once and aggregating the pages where they were detected.
284 + $_field_names = $_post_links = array();
239 285 foreach($_fields as $_details)
240 286 {
241 - $_item = esc_html($_details['field']).' — ['.esc_html($_details['shortcode']).']';
287 + $_field_key = strtolower((string)$_details['field']);
288 + $_field_names[$_field_key] = (string)$_details['field'];
289 +
242 290 $_post_id = (!empty($_details['post_id'])) ? (int)$_details['post_id'] : 0;
243 - if($_post_id > 0 && ($_edit_link = get_edit_post_link($_post_id, '')))
291 + if($_post_id > 0 && !isset($_post_links[$_post_id]) && ($_edit_link = get_edit_post_link($_post_id, '')))
244 292 {
245 293 $_post_title = get_the_title($_post_id);
246 294 $_post_title = ($_post_title !== '') ? $_post_title : '(no title)';
247 - $_item .= ' — <a href="'.esc_url($_edit_link).'">'.esc_html($_post_title).' (#'.$_post_id.')</a>';
295 + $_post_links[$_post_id] = '<a href="'.esc_url($_edit_link).'">'.esc_html($_post_title).'</a>';
248 296 }
249 - $_field_items[] = $_item;
250 297 }
251 - unset($_details, $_item, $_post_id, $_edit_link, $_post_title);
298 + unset($_details, $_field_key, $_post_id, $_edit_link, $_post_title);
252 299
253 300 $_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';
254 301 $_dismiss_url = wp_nonce_url(add_query_arg('s2member-dismiss-shortcode-user-fields-notice', '1', admin_url()), 's2member-dismiss-shortcode-user-fields-notice');
255 - $_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>';
256 - 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);
302 + $_field_is_singular = (count($_field_names) === 1);
303 + $_message = ($_field_is_singular ? 'The following user field needs' : 'The following user fields need').' to be whitelisted. See: <em><a href="'.esc_url($_settings_url).'">s2Member → General Options → Shortcode User Fields Whitelist</a></em>';
304 + $_review = '<span style="background:#ffebcd; padding:0 3px;"><em>'.esc_html(implode(', ', array_values($_field_names))).'</em></span>';
305 + if($_post_links)
306 + {
307 + $_post_links = array_values($_post_links);
308 + $_last_post_link = (count($_post_links) > 1) ? array_pop($_post_links) : '';
309 + $_used_on = $_last_post_link ? implode(', ', $_post_links).', and '.$_last_post_link : $_post_links[0];
310 + $_review .= '<br /><em>Used on: '.$_used_on.'.</em>';
311 + }
312 + c_ws_plugin__s2member_admin_notices::display_security_notice($_message, $_review, array(), $_dismiss_url);
257 313 }
258 314
259 315 /**
260 316 * Processes all administrative notices.
@@ -284,9 +340,9 @@
284 340 {
285 341 //250510 Fixed for PHP 8.1+: safely normalize on_pages before foreach
286 342 $notice = (array)$notice;
287 343 $notice['on_pages'] = empty($notice['on_pages']) ? array('*') : (array)$notice['on_pages'];
288 - foreach($notice['on_pages'] as $page)
344 + foreach($notice['on_pages'] as $page)
289 345 {
290 346 if(!preg_match('/^(.+?)\:/', $page)) // NO prefix?
291 347 $page = 'blog:'.ltrim($page, ':'); // `blog:`
292 348