'."\n"; echo '
'."\n"; echo '
'."\n"; c_ws_plugin__s2member_menu_pages_tb::display(); echo '
'."\n"; echo '

General Options

'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_before_left_sections_form", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo ''."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_before_left_sections", get_defined_vars()); if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_uninstall", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_uninstall", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Plugin Deletion Safeguards (highly recommended)

'."\n"; echo '

By default, s2Member will retain all of it\'s Roles, Capabilities, and your Configuration Options when/if you delete s2Member from the Plugins Menu in WordPress. However, if you would like for s2Member to erase itself completely, please choose: No (upon deletion, erase all data/options). See also: s2Member Uninstall Instructions

'; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_uninstall", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Recommended setting: (Yes, safeguard all data/options)'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_uninstall", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_security", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_security", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo ''."\n"; //260810 Clarify that s2Member always has an encryption secret and that this separate key is optional. echo '

Security Encryption Key (optional)

'."\n"; echo '

s2Member uses an encryption secret to encrypt and validate certain sensitive values. If you leave this setting blank, s2Member automatically uses the WordPress salts via wp_salt(). You can instead configure a separate Security Encryption Key for s2Member, giving you independent control over this secret and making it easier to preserve across site moves or restores when you keep the same key.

'."\n"; echo '

Once a site is in use, avoid changing its effective encryption key unless you intend to invalidate or recover data encrypted with a previous key. Existing Specific Post/Page Access Links, Registration Access Links, registration/tracking cookies, pending transaction-return data, and values encrypted through s2Member\'s encryption API may depend on it. If you configure a key here, keep a secure backup of it.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_security", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; //260810 Make the configured-key guidance explicit without implying that a separate key is required. echo (!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key"]) ? '
If you configure a separate key, use at least 60 characters. It may contain letters, numbers, spaces, and punctuation; up to 256 characters.
Ex: '.esc_html(strtoupper(c_ws_plugin__s2member_utils_strings::random_str_gen(64))).''."\n" : ''; echo (count($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sec_encryption_key_history"]) > 1) ? '
Click here for a history of your last 10 configured Security Encryption Keys.'."\n" : ''; echo '
'."\n"; echo '
'."\n"; //260810 Explain how the configured key fits into s2Member's normal key resolution and what key changes affect. echo '

How this Key is Used:

'."\n"; echo '

For normal encryption/decryption, a custom key supplied by code takes precedence. Otherwise s2Member uses the Security Encryption Key configured above; if this setting is blank, it falls back to the WordPress salts via wp_salt().

'."\n"; echo '

Many encrypted values are short-lived, but some can remain valid much longer. Changing the effective key will invalidate existing data that was encrypted with the previous key, which can include Specific Post/Page Access Links, Registration Access Links, registration and tracking cookies, pending transaction-return data, and values created through s2Member\'s encryption API. If this setting is blank, changing the WordPress salts will likewise invalidate data encrypted with the previous salts.

'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_security", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_localhost_info", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_localhost_info", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Localhost WAMP/MAMP Installs (are you a developer?)

'."\n"; echo '

If you\'re developing your site in a localhost environment, running something like WAMP/MAMP, or EasyPHP, please add this line to your /wp-config.php file: define("LOCALHOST", true);.

'."\n"; echo '

This lets s2Member know definitively that your site is in a localhost environment. s2Member will adjust itself accordingly, maximizing functionality during your developement. s2Member can usually auto-detect this, but in cases where your localhost installation runs on something other than 127.0.0.1/localhost, you need to tell s2Member definitively, by adding that line to your /wp-config.php file. For instance, s2Member needs to know when your server IP is the same as all User IPs.

'."\n"; echo '

Once your site goes live, please remove the line. If you\'re already on a live server connected to the web, please ignore this section.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_localhost_info", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_localhost_info", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_lazy_load", (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site() || is_super_admin()), get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_lazy_load", get_defined_vars()); $open_performance_panel = !empty($_GET['s2member-open-panel']) && in_array((string)$_GET['s2member-open-panel'], array('frontend-static-assets', 'dynamic-asset-loader'), TRUE); echo '
'."\n"; echo '
'."\n"; echo '

CSS/JS Lazy Loading (Client-Side Libraries)

'."\n"; echo '

By default, s2Member will load it\'s CSS/JS libraries on every page of your site. However, you may wish to enable lazy-loading here (i.e., only load when absolutely necessary).

'."\n"; echo '

Tip: Do you need s2Member\'s CSS/JS on every page? If not, you can turn lazy-loading on. If you need s2Member\'s CSS/JS on a given Post/Page, you can insert an HTML comment into the Post/Page content like this: <!--s2member-->. If a Post/Page contains the word s2member or an [s2* Shortcode, this will automatically trigger s2Member\'s lazy-load routine (no matter what you configure here). Thus, it\'s an easy way to force s2Member to load it\'s CSS/JS on specific Posts/Pages where you deem this necessary. There is also a WordPress filter available: add_filter("ws_plugin__s2member_lazy_load_css_js", "__return_true"); for developers. This could be incorporated into more dynamic scenarios.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_lazy_load", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; //260904.2014 Dynamic delivery applies independently of static delivery, so give its loader choice a separate section. echo '
'."\n"; echo '

Dynamic CSS/JS Loader

'."\n"; echo '

When s2Member needs to generate CSS or JavaScript dynamically, this controls the preferred dynamic route. If Static CSS/JS Delivery is enabled but current hooks or configuration specifically require the normal WordPress environment, s2Member uses the Full WordPress Dynamic Loader for compatibility instead.

'."\n"; echo ''."\n"; echo ''."\n"; $dynamic_asset_loader = (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['dynamic_asset_loader']) && $GLOBALS['WS_PLUGIN__']['s2member']['o']['dynamic_asset_loader'] === 'wordpress') ? 'wordpress' : 's2o'; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; //260910.0709 Keep the familiar s2member-o.php identity visible, explain the lighter WordPress load for new users, and link the two KB references needed when security rules block direct access to that plugin PHP file. echo 'The s2Member-Only Dynamic Loader uses a lighter WordPress load for better performance through s2member-o.php. See s2Member-Only Mode. Because this PHP file is inside the s2Member plugin directory, some server or security configurations need to explicitly allow web access to it. If it returns a 403, 503, or 500 error, see Mod Security (Odd 403, 503, 500 Errors). The Full WordPress Dynamic Loader uses the site\'s normal WordPress request as a compatibility alternative.'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; //260904.2014 Keep static CSS/JS delivery and its related optimizations together in a clearly named section. echo '
'."\n"; echo '

CSS/JS Delivery & Optimization (Beta)

'."\n"; echo '

These beta options build CSS and JavaScript files ahead of time under the WordPress uploads directory, so the web server can serve them directly without starting PHP and WordPress for each request. Framework and Pro files remain separate by default, with optional combining and minification for further optimization.

'."\n"; echo '

After enabling these options, test your membership, registration, profile, and payment pages. If static delivery cannot be used, s2Member switches to dynamic delivery automatically. Compatibility requirements that need normal WordPress hooks use the Full WordPress Dynamic Loader; Asset Health explains the active route and reason.

'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; $static_js_text = (isset($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_js_text']) && $GLOBALS['WS_PLUGIN__']['s2member']['o']['static_js_text'] === 'page') ? 'page' : 'static'; $static_js_page_text_supported = c_ws_plugin__s2member_utils_assets::static_js_page_text_supported(); //260913.2140 Link Asset Health guidance to the full setting row and leave room above it so the setting label remains visible below the WordPress admin bar. echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; //260912.0522 Expose the frontend-asset wait as a 1–60-second diagnostic control; changing it must not alter when the CSS/JS itself is enqueued or executed. $asset_health_wait_seconds = (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['asset_health_wait_seconds'])) ? max(1, min(60, (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['asset_health_wait_seconds'])) : 3; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Generates timestamped Framework CSS and, when Pro is active, a separate Pro/gateway stylesheet by default. Generation failures or incompatible custom CSS hooks use legacy dynamic CSS.'."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Used only with Static CSS Delivery. Original source files remain readable and unchanged.'."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Generates timestamped Framework JavaScript and, when Pro is active, a separate Pro/gateway script by default. Personal/member details always remain page-specific and are never stored in static files. WordPress 4.2–4.4 automatically keep dynamic JavaScript.'."\n"; echo '
'."\n"; echo '

Some s2Member messages and labels are shown or updated by JavaScript—for example, form validation and password feedback—so JavaScript needs access to that text too.

'."\n"; echo '
'."\n"; //260913.2111 Explain that page-loaded text can also preserve Static JS when request-specific values would otherwise require Dynamic delivery. echo 'Static files: best for sites that use one language throughout; the text is cached with the generated JavaScript and does not add the larger text block to every page. WordPress page: use this for multilingual sites or when page-specific JavaScript values vary by request; those values stay with the page so the same external JavaScript can remain static whenever no other compatibility requirement needs Dynamic delivery. Personal/member details are always loaded with the page and are never stored in static files.'."\n"; if(!$static_js_page_text_supported) echo '

Load with each WordPress page is unavailable: update s2Member Pro to a version that supports page-loaded JavaScript text. If your site needs different JavaScript text between languages before updating Pro, set Static JS Delivery to No so WordPress generates the JavaScript dynamically.

'."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Used only with Static JS Delivery. s2Member generates from readable JavaScript sources; developers do not maintain parallel minified files.'."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Applies when s2Member Pro is active. Separate delivery is the default; combining is an optional optimization that serves one generated CSS file and/or one generated JavaScript file instead of separate Framework and Pro files.'."\n"; echo '
'."\n"; echo '
'."\n"; echo 'After the page finishes loading, s2Member waits this many seconds before checking that its frontend CSS and JavaScript have loaded and become active. This helps avoid false warnings when caching or optimization tools delay CSS/JS execution. Default: 3 seconds. Allowed range: 1–60.'."\n"; echo '
'."\n"; $static_assets_enabled = !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_css']) || !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_js']); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
Refresh Static Assets
'."\n"; echo '
'."\n"; echo ''.(($static_assets_enabled) ? 'Creates new timestamps immediately for every active generated file. Separate Framework/Pro files keep independent build timestamps, while combined mode uses one timestamp per enabled asset type. Save option changes before using this button.' : 'Enable Static CSS Delivery or Static JS Delivery and save the options before using this button.').''."\n"; //260907.2203 Point site owners to the CSS/JS operational history from the asset controls. echo '

When s2Member logging is enabled, important static and dynamic CSS/JS events such as generation, refreshes, delivery problems, fallbacks, and recovery are recorded in css-js.log. Routine page loads are not logged.

'."\n"; echo '
'."\n"; //260904.0649 Keep Refresh tied to saved settings: disable it when no static type is active and while related controls have unsaved changes. //260911.1834 Refresh is an explicit admin recovery action; follow a completed server-side rebuild attempt with the existing trusted browser recheck so the displayed Health state updates without another click. echo ''."\n"; $asset_health = c_ws_plugin__s2member_utils_assets::frontend_asset_health(TRUE); $asset_health_http = get_option('ws_plugin__s2member_asset_http_health', array()); $asset_health_log = get_option('ws_plugin__s2member_assets_health_log', array()); $asset_health_last_generation = 0; $asset_health_pending_rebuild = FALSE; $asset_health_age = function($timestamp) { $seconds = max(0, time() - (int)$timestamp); if($seconds < MINUTE_IN_SECONDS) return '<1 min ago'; if($seconds < HOUR_IN_SECONDS) return floor($seconds / MINUTE_IN_SECONDS).' min ago'; if($seconds < DAY_IN_SECONDS) return floor($seconds / HOUR_IN_SECONDS).' hr ago'; return floor($seconds / DAY_IN_SECONDS).' d ago'; }; foreach(array('css', 'js') as $_asset_type) if(!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_'.$_asset_type])) foreach(c_ws_plugin__s2member_utils_assets::static_asset_ids($_asset_type, 'all') as $_asset_id) { $_asset_build = abs((int)c_ws_plugin__s2member_utils_assets::static_asset_build($_asset_id)); if($_asset_build > $asset_health_last_generation) $asset_health_last_generation = $_asset_build; } $asset_health_last_generation_label = (!$static_assets_enabled) ? 'Not in use' : (($asset_health_last_generation > 0) ? $asset_health_age($asset_health_last_generation) : 'Not generated yet'); //260912.1956 Loader rows depend on the complete trusted route check, so report that timestamp instead of a newer cheap partial probe when available. $asset_health_last_check = (!empty($asset_health_http['full_checked'])) ? (int)$asset_health_http['full_checked'] : ((!empty($asset_health_http['checked'])) ? (int)$asset_health_http['checked'] : 0); $asset_health_last_check_label = ($asset_health_last_check > 0) ? $asset_health_age($asset_health_last_check) : 'Not checked yet'; $asset_health_last_issue_time = 0; $asset_health_last_issue_result = ''; $asset_health_last_issue_label = ''; $asset_health_last_issue_detail = ''; $asset_health_last_issue_page_id = 0; $asset_health_last_issue_page_path = ''; if(!empty($asset_health_log['last_issue']) && is_array($asset_health_log['last_issue'])) { //260911.1806 Prefer the dedicated historical snapshot so natural recovery and busy healthy traffic do not erase the useful cause from Last issue. $asset_health_last_issue_time = (!empty($asset_health_log['last_issue']['time'])) ? (int)$asset_health_log['last_issue']['time'] : 0; $asset_health_last_issue_result = (!empty($asset_health_log['last_issue']['result'])) ? (string)$asset_health_log['last_issue']['result'] : ''; $asset_health_last_issue_label = (!empty($asset_health_log['last_issue']['label'])) ? (string)$asset_health_log['last_issue']['label'] : ''; $asset_health_last_issue_detail = (!empty($asset_health_log['last_issue']['detail'])) ? (string)$asset_health_log['last_issue']['detail'] : ''; $asset_health_last_issue_page_id = (!empty($asset_health_log['last_issue']['page_id'])) ? (int)$asset_health_log['last_issue']['page_id'] : 0; $asset_health_last_issue_page_path = (!empty($asset_health_log['last_issue']['page_path'])) ? (string)$asset_health_log['last_issue']['page_path'] : ''; } else if(empty($asset_health_log['last_issue_cleared_at']) && !empty($asset_health_log['last_10_asset_loads']) && is_array($asset_health_log['last_10_asset_loads'])) foreach(array_reverse($asset_health_log['last_10_asset_loads']) as $_asset_health_load) if(!empty($_asset_health_load['time']) && !empty($_asset_health_load['result']) && (string)$_asset_health_load['result'] !== 'okay') { $asset_health_last_issue_time = (int)$_asset_health_load['time']; $asset_health_last_issue_result = (string)$_asset_health_load['result']; break; } $asset_health_last_issue_label = str_replace(array('Full WordPress Dynamic ', 's2Member-Only Dynamic ', 'JavaScript'), array('WordPress ', 's2Member-Only ', 'JS'), $asset_health_last_issue_label); $asset_health_last_issue = 'None'; if($asset_health_last_issue_time > 0 && $asset_health_last_issue_result !== '') { //260911.1834 Self-repaired missing files are historical issues, not degraded scored loads. $_asset_health_result_labels = array('late' => 'late', 'fallback' => 'fallback', 'failed' => 'failed', 'fallback-unavailable' => 'fallback unavailable', 'repaired' => 'static asset missing'); $_asset_health_result_label = (isset($_asset_health_result_labels[$asset_health_last_issue_result])) ? $_asset_health_result_labels[$asset_health_last_issue_result] : $asset_health_last_issue_result; $asset_health_last_issue = $asset_health_age($asset_health_last_issue_time).' — '.(($asset_health_last_issue_label !== '') ? $asset_health_last_issue_label.' ' : '').$_asset_health_result_label; } $asset_health_last_issue_page_label = ''; $asset_health_last_issue_page_url = ''; if($asset_health_last_issue_page_id > 0) { $_asset_health_page_title = (string)get_the_title($asset_health_last_issue_page_id); $asset_health_last_issue_page_label = (($_asset_health_page_title !== '') ? $_asset_health_page_title.' ' : '').'(#'.$asset_health_last_issue_page_id.')'; $asset_health_last_issue_page_url = (string)get_permalink($asset_health_last_issue_page_id); } if($asset_health_last_issue_page_path !== '') { $asset_health_last_issue_page_label .= (($asset_health_last_issue_page_label !== '') ? ' — ' : '').$asset_health_last_issue_page_path; if($asset_health_last_issue_page_url === '') { //260913.0454 REQUEST_URI already includes any WordPress subdirectory, so append it to the site origin instead of duplicating the home path. $_asset_health_home_parts = c_ws_plugin__s2member_utils_urls::parse_url(home_url('/')); if(is_array($_asset_health_home_parts) && !empty($_asset_health_home_parts['scheme']) && !empty($_asset_health_home_parts['host'])) $asset_health_last_issue_page_url = $_asset_health_home_parts['scheme'].'://'.$_asset_health_home_parts['host'].((!empty($_asset_health_home_parts['port'])) ? ':'.(int)$_asset_health_home_parts['port'] : '').'/'.ltrim($asset_health_last_issue_page_path, '/'); } } unset($_asset_type, $_asset_id, $_asset_build, $_asset_health_load, $_asset_health_result_labels, $_asset_health_result_label, $_asset_health_page_title, $_asset_health_home_parts); //260910.0638 New Health Status section for easier review. echo '
'."\n"; echo '

CSS/JS Asset Health: '.esc_html($asset_health['status_label']).'

'."\n"; echo ''."\n"; //260911.0627 Keep healthy rows compact: the status light carries health while the value names the active delivery method; only exceptions add status text. foreach($asset_health['rows'] as $_asset_health_row) { $_asset_health_pending = ((string)$_asset_health_row['status_label'] === 'Pending rebuild'); $_asset_health_url = (!empty($_asset_health_row['url'])) ? (string)$_asset_health_row['url'] : ''; $_asset_health_delivery = (!empty($_asset_health_row['delivery'])) ? (string)$_asset_health_row['delivery'] : ''; $_asset_health_exception = ''; if((string)$_asset_health_row['status_label'] !== 'Healthy' && (string)$_asset_health_row['status_label'] !== 'Using dynamic fallback') { $_asset_health_exception_labels = array('Rebuild issue' => 'Rebuild issue', 'Fallback check failed' => 'Check failed', 'Delivery check failed' => 'Check failed'); $_asset_health_exception = (isset($_asset_health_exception_labels[$_asset_health_row['status_label']])) ? $_asset_health_exception_labels[$_asset_health_row['status_label']] : (string)$_asset_health_row['status_label']; } $_asset_health_value = $_asset_health_delivery.(($_asset_health_exception !== '') ? ' — '.$_asset_health_exception : ''); $_asset_health_not_generated = ($_asset_health_delivery === 'Static' && $_asset_health_url === '' && $_asset_health_exception === '' && !$_asset_health_pending); if($_asset_health_not_generated) $_asset_health_value .= ' — Not generated yet'; $_asset_health_light = ($_asset_health_pending || $_asset_health_not_generated) ? 'disabled' : (string)$_asset_health_row['status']; $_asset_health_light_label = ($_asset_health_not_generated) ? 'Not generated yet' : (string)$_asset_health_row['status_label']; if($_asset_health_pending || $_asset_health_not_generated) $asset_health_pending_rebuild = TRUE; //260911.1924 Both neutral waiting states benefit from the shared automatic-rebuild explanation. //260911.1924 A Pending rebuild URL names the previous generation, so hiding [open] avoids implying that it is the asset waiting to be created. echo ''."\n"; } echo ''."\n"; echo ''."\n"; $_asset_health_last_issue_clear = ($asset_health_last_issue_time > 0) ? ' [clear]' : ''; $_asset_health_last_issue_page = ($asset_health_last_issue_page_label !== '') ? '
Page: '.esc_html($asset_health_last_issue_page_label).(($asset_health_last_issue_page_url !== '') ? ' [open]' : '') : ''; echo ''."\n"; echo '
'.esc_html($_asset_health_row['label']).':'.esc_html($_asset_health_value).((!$_asset_health_pending && $_asset_health_url !== '') ? '[open]' : '').((!$_asset_health_pending && ($_asset_health_row['status'] !== 'healthy' || $_asset_health_delivery === 'Dynamic required') && (string)$_asset_health_row['detail'] !== '') ? '
'.wp_kses_post($_asset_health_row['detail']).'' : '').'
Last generation:'.esc_html($asset_health_last_generation_label).'
Last trusted check:'.esc_html($asset_health_last_check_label).'
Last issue'.$_asset_health_last_issue_clear.':'.esc_html($asset_health_last_issue).(($asset_health_last_issue_detail !== '') ? '
'.esc_html($asset_health_last_issue_detail) : '').$_asset_health_last_issue_page.'
'."\n"; unset($_asset_health_row, $_asset_health_pending, $_asset_health_not_generated, $_asset_health_light, $_asset_health_light_label, $_asset_health_url, $_asset_health_delivery, $_asset_health_exception, $_asset_health_exception_labels, $_asset_health_value); if($asset_health_pending_rebuild) //260911.1924 Keep neutral waiting states easy to understand; normal admin use now also provides an automatic recovery opportunity before frontend traffic is needed. echo '

* Static assets marked Not generated yet or Pending rebuild will be created or rebuilt automatically on a later admin or frontend page-load, or immediately with Refresh Static Assets.

'."\n"; //260910.0818 Recheck gives immediate confirmation after an admin fixes routing/access; preserve this panel/anchor so the refreshed result returns here. echo '

'."\n"; echo ''."\n"; $asset_health_latest_issues = (!empty($asset_health['latest_issues']) && is_array($asset_health['latest_issues'])) ? $asset_health['latest_issues'] : array(); $asset_health_latest_issues_blocks = array(); $_asset_health_result_labels = array('late' => 'Late', 'fallback' => 'Fallback', 'failed' => 'Failed', 'fallback-unavailable' => 'Fallback unavailable', 'repaired' => 'Static asset missing / repaired'); foreach($asset_health_latest_issues as $_asset_health_issue) { if(!is_array($_asset_health_issue)) continue; $_issue_result = (!empty($_asset_health_issue['result'])) ? (string)$_asset_health_issue['result'] : 'issue'; $_issue_result_label = (isset($_asset_health_result_labels[$_issue_result])) ? $_asset_health_result_labels[$_issue_result] : ucfirst(str_replace('-', ' ', $_issue_result)); $_issue_label = (!empty($_asset_health_issue['label'])) ? (string)$_asset_health_issue['label'] : 'Asset'; $_issue_lines = array($_issue_label.' — '.$_issue_result_label); if(!empty($_asset_health_issue['detail'])) $_issue_lines[] = (string)$_asset_health_issue['detail']; //260913.0305 Keep Latest Issues compact and copyable: show the full queryless frontend URL, then one precise occurrence timestamp per line. $_issue_page_path = (!empty($_asset_health_issue['page_path'])) ? (string)$_asset_health_issue['page_path'] : ''; $_issue_page_url = ''; if($_issue_page_path !== '') { $_issue_home_parts = c_ws_plugin__s2member_utils_urls::parse_url(home_url('/')); if(is_array($_issue_home_parts) && !empty($_issue_home_parts['scheme']) && !empty($_issue_home_parts['host'])) $_issue_page_url = $_issue_home_parts['scheme'].'://'.$_issue_home_parts['host'].((!empty($_issue_home_parts['port'])) ? ':'.(int)$_issue_home_parts['port'] : '').'/'.ltrim($_issue_page_path, '/'); } if($_issue_page_url !== '') $_issue_lines[] = 'Page: '.$_issue_page_url; $_issue_count = (!empty($_asset_health_issue['count'])) ? max(1, (int)$_asset_health_issue['count']) : 1; $_issue_times = array(); if(!empty($_asset_health_issue['times']) && is_array($_asset_health_issue['times'])) foreach($_asset_health_issue['times'] as $_issue_time) if((int)$_issue_time > 0) $_issue_times[] = date_i18n('Y-m-d H:i:s', (int)$_issue_time); if($_issue_times) { if($_issue_count > count($_issue_times)) $_issue_lines[] = 'Seen '.number_format_i18n($_issue_count).' times. Latest '.number_format_i18n(count($_issue_times)).' occurrences:'; else $_issue_lines[] = 'Seen '.number_format_i18n($_issue_count).' '.(($_issue_count === 1) ? 'time' : 'times').':'; $_issue_lines = array_merge($_issue_lines, $_issue_times); } else $_issue_lines[] = 'Seen '.number_format_i18n($_issue_count).' '.(($_issue_count === 1) ? 'time.' : 'times.'); $asset_health_latest_issues_blocks[] = implode("\n", $_issue_lines); } $asset_health_latest_issues_text = ($asset_health_latest_issues_blocks) ? implode("\n\n", $asset_health_latest_issues_blocks) : 'No Asset Health issues are currently recorded.'; echo '

Latest Issues'.(($asset_health_latest_issues) ? ' [clear]' : '').'

'."\n"; echo ''."\n"; echo '

s2Member keeps the latest 10 distinct Asset Health issues here until cleared. Repeated occurrences of the same issue on the same page are grouped together and keep up to 10 recent occurrence times. Clearing Last issue or Latest Issues does not change Current Health or its recent scoring history. Recheck Asset Health safely resets recent scoring history after current delivery and its fallback verify healthy. Enable s2Member logging for more detailed CSS/JavaScript troubleshooting history.

'."\n"; //260913.0056 Keep explicit historical clearing separate from Recheck; neither clear action changes rolling Health or current delivery state. echo ''."\n"; unset($asset_health_latest_issues, $asset_health_latest_issues_blocks, $asset_health_latest_issues_text, $_asset_health_issue, $_asset_health_result_labels, $_issue_result, $_issue_result_label, $_issue_label, $_issue_lines, $_issue_page_path, $_issue_page_url, $_issue_home_parts, $_issue_count, $_issue_times, $_issue_time); //260911.2004 Explain Asset Health in site-owner language, with scores first so the relationship between each result/status and its score is easy to scan. echo '

How is this health status calculated? (click here)

'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '

No-Cache Headers Behavior (beta)

'."\n"; echo '

s2Member can render different output depending on logged-in state, access level, conditionals (e.g., [s2If]), and nonce-based forms. If a cache stores a page generated under one context and serves it to another, visitors may see incorrect content or stale nonces. These options control when s2Member sends HTTP no-cache headers (Cache-Control: no-cache) to reduce that risk while allowing caching where appropriate.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_no_cache_headers", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Choose when s2Member should send HTTP no-cache headers.'."\n"; echo '
    '."\n"; echo '
  • Always: Sends no-cache headers very early on every page request. This is the safest behavior when member-conditional output could appear anywhere (content, widgets, theme templates, menus) and caching is not configured to vary correctly. It can reduce performance because it prevents caching for guests site-wide.
  • '."\n"; echo '
  • Selective: Enables selective behavior via the long-standing ws_plugin__s2member_no_cache_headers_selective filter used by customizations/plugins to change the default. Headers are still decided very early, actually too early to reliably reflect decisions made while a page renders (for example, when [s2If] or other shortcodes execute). This may improve caching for guests, but it can miss some runtime no-cache triggers in edge cases. Use this for backward compatibility if you already rely on it and understand the tradeoffs.
  • '."\n"; echo '
  • Evaluative (beta): Evaluates the page later, after s2Member shortcodes and other runtime checks have affected the page, resulting in better informed guest caching. No-cache headers are then sent at WordPress\' standard header-sending stage. Because this mode sends no-cache headers later than Always or Selective modes, it may expose “headers already sent” issues caused by another plugin or theme outputting content too early. Output added in widgets, menus, or theme templates may still require using Always or Selective mode.
  • '."\n"; echo '
'."\n"; //260308 Sub-setting UI for no-cache debug header; label-based heading without extra divider. echo '

Enable no-cache debug header?

'."\n"; echo '

'."\n"; echo '    '."\n"; echo '
'."\n"; echo 'This adds a Server-Timing header to help troubleshoot why no-cache headers are being applied or not. This header includes the following variables:'."\n"; echo '

'."\n"; echo '
    '."\n"; echo '
  • mode = Behavior mode in effect: always, selective, evaluative.
  • '."\n"; echo '
  • no_cache = 1 means forced no-cache via filter; 0 means not forced.
  • '."\n"; echo '
  • selective = 1 means selective behavior enabled; 0 means not enabled.
  • '."\n"; echo '
  • headers = 1 means s2Member decided this request shouldn\'t be cached; 0 means it didn\'t.
  • '."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_lazy_load", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_s_badge_wp_footer_code", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_s_badge_wp_footer_code", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_s_badge_wp_footer_code", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_email_config", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_email_config", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Email From: '.esc_html('"Name"
').'

'."\n"; echo '

This is the name/address that will appear in outgoing email notifications sent by the s2Member plugin.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_email_from_name_config", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'We recommend that you use the name of your site here.'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Example: support@your-domain.com. Please read this important note.'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Ex: mailto:support@your-domain.com (mailto link)
'."\n"; echo 'Or: '.esc_html(home_url("/contact-us/")).''."\n"; echo '
'."\n"; // New table for HTML Emails option. echo '
'."\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '
' . "\n"; echo '' . "\n"; echo '
' . "\n"; echo '    ' . "\n"; echo '
' . "\n"; echo 'This enables s2Member to send HTML emails instead of just plain text. It applies to all emails you can edit in the s2Member options.' . "\n"; echo '
' . "\n"; echo '
'."\n"; echo '

New User Email Configuration

'."\n"; echo ''."\n"; echo (empty($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["pluggables"]["wp_new_user_notification"])) ? '

Conflict warning: You have another theme or plugin installed that is preventing s2Member from controlling this aspect of your installation. When the pluggable function wp_new_user_notification() is handled by another plugin, it\'s not possible for s2Member to allow customization of New User Emails. This is NOT a major issue. In fact, in some cases, it might be desirable. That being said, if you DO want to use s2Member\'s customization of New User Emails, you will need to deactivate one plugin at a time until this conflict warning goes away.

'."\n" : ''; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_emails", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '

New User Email Message (click to customize)

'."\n"; echo '

This email is sent to new Users/Members who did not set a Custom Password during registration.

'."\n"; if($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["custom_reg_password"]) echo '

↑ NOTE: You currently have Custom Passwords enabled in your s2Member Registration/Profile Field options. Therefore, this email is not going to be sent; i.e., it is only sent to users who need it for the purpose of obtaining their password.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_email", get_defined_vars()); echo ''."\n"; echo '
'."\n"; echo '

Administrative: New User Notification (click to customize)

'."\n"; echo '

This email notification is sent to you, each time a new User/Member registers.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_new_user_admin_email", get_defined_vars()); echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_email_config", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_open_registration", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_open_registration", get_defined_vars()); if(is_multisite() && is_main_site()) // A Multisite Network, and we're on the Main Site? { echo '
'."\n"; echo '
'."\n"; echo '

Open Registration / Free Subscribers (optional)

'."\n"; echo '

On the Main Site of a Multisite Network, the settings for Open Registration are consolidated into the s2Member → Multisite (Config) panel.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_open_registration", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; } else // Else we display this section normally. No special considerations are required in this case. { echo '
'."\n"; echo '
'."\n"; echo '

Open Registration / Free Subscribers (optional)

'."\n"; echo '

s2Member supports Free Subscribers (at Level #0), along with four Primary Levels [1-4] of paid Membership. If you want your visitors to be capable of registering absolutely free, you will want to "allow" Open Registration. Whenever a visitor registers without paying, they\'ll automatically become a Free Subscriber at Level #0.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_open_registration", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'If you set this to Yes, you\'re unlocking /wp-login.php?action=register. When a visitor registers without paying, they\'ll automatically become a Free Subscriber at Level #0. The s2Member software reserves Level #0; to be used only for Free Subscribers. All other Membership Levels [1-4] require payment.'."\n"; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '

BuddyPress: BuddyPress will use its own Registration Form here.
s2Member integrates with BuddyPress, and the above setting will control Open Regisration for BuddyPress too.
'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; } do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_open_registration", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_membership_levels", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_membership_levels", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Membership Levels (required, please customize these)

'."\n"; echo '

The default Membership Levels are labeled generically; feel free to modify them as needed. s2Member supports Free Subscribers (at Level #0), along with several Primary Roles for paid Membership (i.e., Levels 1-4), created by the s2Member plugin.'.((!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? ' s2Member also supports unlimited Custom Capability Packages (see s2Member → API Scripting → Custom Capabilities)' : '').'. That being said, you don\'t have to use all of the Membership Levels if you don\'t want to. To use only 1 or 2 of these Levels, just design your Membership Options Page, so it only includes Payment Buttons for the Levels being used.

'."\n"; echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '

TIP: Unlimited Membership Levels are only possible with s2Member Pro. However, Custom Capabilities are possible in all versions of s2Member, including the free version. Custom Capabilities are a great way to extend s2Member in creative ways. If you\'re an advanced site owner, a theme designer, or a web developer integrating s2Member for a client, please check your Dashboard, under: s2Member → API Scripting → Custom Capabilities. We also recommend this video tutorial.

'."\n" : ''; echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '

See also: These KB articles: s2Member Roles/Capabilities and Simple Shortcode Conditionals.

'."\n" : ''; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_membership_levels", get_defined_vars()); echo ''."\n"; echo ''."\n"; for($n = 0; $n <= $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["levels"]; $n++) { echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; } echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'This is the Label for Level #'.$n.(($n === 0) ? ' (Free Subscribers)' : ' Members').'.
'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '    
'."\n"; echo 'This affects your administrative Dashboard only (i.e., your list of Users).
s2Member can force WordPress to use your Labels instead of referencing Roles by `s2Member Level #`. If this is your first installation of s2Member, we suggest leaving this set to no until you\'ve had a chance to get acclimated with s2Member\'s functionality. In fact, many site owners choose to leave this off, because they find it less confusing when Roles are referred to by their s2Member Level #.'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo '

The button to the right, is a nifty tool, which allows you to reset s2Member\'s internal Roles and Capabilities that integrate with WordPress. If you, or a developer working with you, has made attempts to alter the default internal Role/Capability sets that come with s2Member, and you need to reset them back to the way s2Member expects them to be, please use this tool. Attn Developers: it is also possible lock-in your modified Roles/Capabilities with an s2Member Filter. Please see this KB article for details.

'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_membership_levels", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_login_registration", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_login_registration", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_login_registration", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_custom_reg_fields", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_custom_reg_fields", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Custom Registration/Profile Fields (optional, for further customization)

'."\n"; echo '

Some fields are already built-in by default. The defaults are: *Username*, *Email*, *First Name*, *Last Name*.

'."\n"; echo '

Custom Fields will appear in your Standard Registration Form, and in User/Member Profiles:
('.esc_html(c_ws_plugin__s2member_utils_urls::wp_register_url()).')

'."\n"; echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '

The Main Site of a Multisite Blog Farm uses this Form. s2Member supports Custom Fields here too.
('.esc_html(c_ws_plugin__s2member_utils_urls::wp_signup_url()).')

'."\n" : ''; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '

BuddyPress: BuddyPress will use its own Registration Form here.
s2Member can integrate your Custom Fields with BuddyPress too, please see options below.

'."\n" : ''; echo '

Regarding Registration: Custom Fields do not appear during repeat registration and/or checkout attempts (i.e., they do not appear for any user that is currently logged into the site). Please make sure that you test registration and/or checkout forms while not logged in (i.e., please test as a first-time customer). Existing users, members, customers may update Custom Fields by editing their Profile.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_custom_reg_fields", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; // This is filled by JavaScript routines. echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Recommended setting (Yes). It\'s usually a good idea to leave this on.'."\n"; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '
* Has no affect on BuddyPress registration form (BuddyPress always collects a full Name field).'."\n" : ''; echo (c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '
* s2Member Pro (Checkout) Forms always require a First/Last Name for billing.'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '
* Has no affect on BuddyPress registration form (BuddyPress always uses its full Name field).'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '

Note: Custom Passwords are easier for users. However, this has an impact on site security. Enabling Custom Passwords disables the New User Notification email that contains a password-setup link for each user. In other words, enabling Custom Passwords effectively disables any sort of email verification procedure, because the user is allowed to set their password during registration, instead of doing that via email confirmation—which is the default WordPress behavior.

'."\n"; echo (is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && is_main_site()) ? '

* For security purposes, Custom Passwords are not possible on the Main Site of a Blog Farm. [?]

'."\n" : ''; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '

* Does not affect BuddyPress registration form (always yes with BuddyPress registration).

'."\n" : ''; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo 'Minimum length: characters.'."\n"; echo '    Minimum strength: '."\n"; echo '

Tip: Minimum length and password strength also impact profile updates, so it\'s a good idea to configure these even if you\'re not using Custom Passwords during registration.

'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'To force personal email addresses, provide a comma-delimited list of email users to reject. [?]
'."\n"; echo 'Ex: info,help,admin,webmaster,hostmaster,sales,support,spam
'."\n"; echo 'See: this article for a more complete list.'."\n"; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '
* Affects BuddyPress registration form too.'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; foreach(array("profile-view" => "Yes, integrate with BuddyPress Public Profiles.", "registration" => "Yes, integrate with BuddyPress Registration Form.", "profile" => "Yes, integrate with BuddyPress Profile Editing Panel.") as $ws_plugin__s2member_temp_s_value => $ws_plugin__s2member_temp_s_label) echo '
'."\n"; echo '
'."\n"; echo (!c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '* BuddyPress is not installed; which is perfectly OK. BuddyPress is not a requirement.'."\n" : '* The options above, make it possible to integrate Custom Registration/Profile Fields (i.e., those configured with s2Member) into BuddyPress as well. However, if you configure Profile Fields with BuddyPress, those will NOT be integrated with s2Member. Therefore, if you need Custom Registration/Profile Fields to work with both s2Member and with BuddyPress, please configure them with s2Member.'; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_custom_reg_fields", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_sc_s2get_shortcode", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_sc_s2get_shortcode", get_defined_vars()); //260812 This whitelist is shared by s2Get and s2Member-List; legacy hook and element names are retained for backward compatibility. echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '

Shortcode User Fields Whitelist (optional)

'."\n"; echo '

Enter a comma-separated list of user fields that these shortcodes may display from a user account other than the person currently viewing the page. This applies to [s2Get user_id="" /] and [s2Member-List show_fields="" /].

'."\n"; echo '

Only allow fields that are appropriate to reveal about other user accounts wherever these shortcodes are used. If either shortcode tries to display an unlisted field from another user account, that cross-user field value will be blocked and s2Member will show administrators a security notice with the fields that need review.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_sc_s2get_shortcode", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Examples: display_name, avatar, first_name, last_name, full_name'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_sc_s2get_shortcode", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_login_welcome_page", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_login_welcome_page", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_login_welcome_page", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_membership_options_page", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_membership_options_page", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Membership Options Page (required, please customize this)

'."\n"; echo '

Please create and/or choose an existing Page that showcases your Membership Options. This special Page is where you\'ll insert the Payment Button(s) generated for you by s2Member (or Pro-Forms; if you\'re running s2Member Pro). This Page serves as your lead-in signup page (i.e., you\'ll give visitors one or more registration options here, and they\'ll pay for the option they choose).

'."\n"; echo '

Your Membership Options Page should detail all of the features that come with Membership to your site, and provide a Payment Button (or Pro-Form; if you\'re running s2Member Pro) for each Level of access you plan to offer. This is also the Page that anyone could be redirected to (by s2Member), should they attempt to access an area of your site that requires access to something they\'re currenty not allowed to view.

'."\n"; echo '

Tip: If you allow Open Registration (i.e., Free Subscribers), you might want to place a link on your Membership Options Page, which points directly to your free Registration Form, instead of routing a Customer through your Payment Gateway first. It\'s a matter of preference though. For further details, please check the section above: s2Member → General Options → Open Registration.

'."\n"; echo c_ws_plugin__s2member_utils_conds::bp_is_installed() ? '

BuddyPress: Even with BuddyPress, s2Member still needs a Membership Options Page. This is where your Payment Buttons (or Pro-Forms; if you\'re running s2Member Pro) will go, giving people the ability to pay you. And again, this is also the Page that anyone could be redirected to (by s2Member), should they attempt to access an area of your site that requires access to something they\'re currenty not allowed to view.

'."\n" : ''; echo '

Always Public: This Page must be public at all times. In fact, s2Member will not allow this Page to be restricted in any way. Note: for technical reasons, your Membership Options Page cannot be set to your Front Page (i.e., your Home Page); or your Posts Page (i.e., your main Blog page). Please create a separate (dedicated) Page in WordPress, and then designate it as your Membership Options Page below.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_membership_options_page", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Please choose a Page that provides Users with a way to sign up. We recommend the following title: Membership Signup'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'See also: Dashboard → s2Member → API / Scripting → Membership Options Page / Variables
'."\n"; echo 'Recommended setting: (Yes, enable MOP Vars)'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_membership_options_page", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_profile_modifications", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_profile_modifications", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Giving Members The Ability To Modify Their Profile

'."\n"; echo '

s2Member can be configured to redirect Members away from the default Profile Editing Panel that is built into WordPress. When/if a Member attempts to access the default Profile Editing Panel, they\'ll instead be redirected to the Login Welcome Page that you\'ve configured with s2Member. Why would I redirect away from the default Profile Editing Panel? Unless you\'ve made some drastic modifications to your WordPress installation, the default Profile Editing Panel that comes with WordPress is not suited for any sort of public access.

'."\n"; echo '

So instead of using this default Profile Editing Panel, s2Member provides you (the site owner) with a special Shortcode: [s2Member-Profile /]. You can insert this into your Login Welcome Page, or any Post/Page for that matter (even into a Text Widget). This Shortcode produces an Inline Profile Editing Form that supports all aspects of s2Member, including Password changes; and any Custom Registration/Profile Fields that you\'ve configured with s2Member. Alternatively, you can send your Members to a special Stand-Alone version. The stand-alone version makes it possible for you to open it up in a popup window, or embed it into your Login Welcome Page using an IFRAME. Code samples below.

'."\n"; echo (c_ws_plugin__s2member_utils_conds::bp_is_installed()) ? '

BuddyPress: BuddyPress already provides Users/Members with a Profile Editing Panel, powered by your theme. If you\'ve configured Custom Registration/Profile Fields with s2Member, you can also enable s2Member\'s Profile Field integration with BuddyPress (recommended). For further details, see: s2Member → General Options → Registration/Profile Fields.

'."\n" : ''; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_profile_modifications", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Recommended setting (Yes). Note: When this is set to (Yes), s2Member will take an initiative to further safeguard ALL /wp-admin/ areas of your installation; not just the Default Profile Panel.'."\n"; echo '
'."\n"; echo '
'."\n"; echo '

Shortcode (copy/paste), for an Inline Profile Modification Form:
'."\n"; echo '

'."\n"; echo '

Stand-Alone (copy/paste), for popup window:

'."\n"; echo '

/", c_ws_plugin__s2member_utils_strings::esc_refs(home_url("/?s2member_profile=1")), file_get_contents(dirname(__FILE__)."/code-samples/current-user-profile-modification-page-url-2-ops.x-php"))).'" onclick="this.select ();" />

'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_profile_modifications", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_gen_ops_page_during_left_sections_display_url_shortening", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_before_url_shortening", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

URL Shortening Service API (Preference)

'."\n"; echo '

In a few special cases, long URLs generated by s2Member (i.e., those containing encrypted authentication details) will be shortened. A shortened URL prevents issues with very long links becoming corrupted by a Customer\'s email application. For instance, the Signup Confirmation Email that s2Member sends out to a new paying Customer may contain a link which is shortened to prevent corruption by email applications. By default, s2Member uses its own built-in short links, stored temporarily on your site. You can also choose a third-party URL shortener, configure a custom shortener, or disable shortening.

'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_during_url_shortening", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; if (c_ws_plugin__s2member_utils_conds::pro_is_installed()) { echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; } echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'If you choose Bitly, enter your Bitly oAuth Access Token'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Your own custom URL (i.e., GET request), with %%s2_long_url%% Replacement Code. [details]
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_gen_ops_page_during_left_sections_after_url_shortening", get_defined_vars()); } do_action("ws_plugin__s2member_during_gen_ops_page_after_left_sections", get_defined_vars()); echo '

'."\n"; echo '
'."\n"; echo '
'."\n"; c_ws_plugin__s2member_menu_pages_rs::display(); echo '
'."\n"; echo ''."\n"; } } } new c_ws_plugin__s2member_menu_page_gen_ops ();