PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 261011
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v261011
261011 261001 260927 260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 All 192 releases
s2member / src / includes / classes / menu-pages.inc.php

menu-pages.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions 261011, at src/includes/classes/menu-pages.inc.php

1,120 lines 65.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreFile
3 /**
4 * Administrative menu pages.
5 *
6 * Copyright: © 2009-2011
7 * {@link http://websharks-inc.com/ WebSharks, Inc.}
8 * (coded in the USA)
9 *
10 * Released under the terms of the GNU General Public License.
11 * You should have received a copy of the GNU General Public License,
12 * along with this software. In the main directory, see: /licensing/
13 * If not, see: {@link http://www.gnu.org/licenses/}.
14 *
15 * @package s2Member\Menu_Pages
16 * @since 3.5
17 */
18 if(!defined('WPINC')) // MUST have WordPress.
19 exit('Do not access this file directly.');
20
21 if(!class_exists('c_ws_plugin__s2member_menu_pages'))
22 {
23 /**
24 * Administrative menu pages.
25 *
26 * @package s2Member\Menu_Pages
27 * @since 3.5
28 */
29 class c_ws_plugin__s2member_menu_pages
30 {
31 /**
32 * Pre-display errors.
33 *
34 * @package s2Member\Menu_Pages
35 * @since 111209
36 *
37 * @var array
38 */
39 public static $pre_display_errors = array();
40
41 /**
42 * Saves all options from any menu page.
43 *
44 * Can also be self-verified; and configured extensively with function parameters.
45 *
46 * @package s2Member\Menu_Pages
47 * @since 3.5
48 *
49 * @param null|array $new_options Optional. Force feed an array of new options. Defaults to ``$_POST`` vars.
50 * If ``$new_options`` are passed in, be SURE that you've already applied ``stripslashes_deep()``.
51 * @param bool $verified Optional. Defaults to false. If true, ``wp_verify_nonce()`` is skipped in this routine.
52 * @param bool $update_other Optional. Defaults to true. If false, other option-dependent routines will not be processed.
53 * @param bool|array $display_notices Optional. Defaults to true. Can be false, or an array of certain notices that can be displayed.
54 * @param bool|array $enqueue_notices Optional. Defaults to false. Can be true, or an array of certain notices that should be enqueued.
55 * @param bool $request_refresh Optional. Defaults to false. If true, resulting `success` notice will include a link to refresh the menu page.
56 *
57 * @return bool True if all s2Member options were updated successfully, else false.
58 */
59 public static function update_all_options($new_options = NULL, $verified = FALSE, $update_other = TRUE, $display_notices = TRUE, $enqueue_notices = FALSE, $request_refresh = FALSE)
60 {
61 $updated_all_options = FALSE; // Initializing this variable here makes it an available reference-variable to Hooks/Filters.
62
63 foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
64 do_action('ws_plugin__s2member_before_update_all_options', get_defined_vars()); // If you use this Hook, be sure to use ``wp_verify_nonce()``.
65 unset($__refs, $__v); // Housekeeping.
66
67 if($verified || (!empty($_POST['ws_plugin__s2member_options_save']) && ($nonce = $_POST['ws_plugin__s2member_options_save']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-options-save')))
68 {
69 $options = $GLOBALS['WS_PLUGIN__']['s2member']['o']; // Acquire the full existing configuration options array here.
70
71 $new_options = (is_array($new_options)) ? $new_options : ((!empty($_POST) && is_array($_POST)) ? stripslashes_deep($_POST) : array());
72 $new_options = c_ws_plugin__s2member_utils_strings::trim_deep($new_options);
73
74 foreach($new_options as $key => $value) // Find all keys contained within ``$new_options`` matching `^ws_plugin__s2member_`.
75 if(strpos($key, 'ws_plugin__s2member_') === 0) // A relevant ``$new_options`` key matching `^ws_plugin__s2member_`?
76
77 if($key === 'ws_plugin__s2member_configured') // s2Member is now configured (according to these options)?
78 ($GLOBALS['WS_PLUGIN__']['s2member']['c']['configured'] = $value).update_option('ws_plugin__s2member_configured', $value);
79
80 else if(!is_array($value) || (is_array($value) && array_shift($value) === 'update-signal')) // Updating an array?
81 $options[preg_replace('/^'.preg_quote('ws_plugin__s2member_', '/').'/', '', $key)] = $value;
82
83 unset($key, $value); // Unset these utility variables now. This prevents bleeding vars into Hooks/Filters that are of no use.
84
85 foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
86 do_action('ws_plugin__s2member_during_update_all_options', get_defined_vars());
87 unset($__refs, $__v); // Housekeeping.
88
89 //260812 Keep the old s2Get option synchronized for rollback compatibility, while accepting legacy updates during the transition.
90 if(isset($new_options['ws_plugin__s2member_sc_s2get_userid_whitelist']) && !isset($new_options['ws_plugin__s2member_sc_user_fields_whitelist']))
91 $options['sc_user_fields_whitelist'] = $options['sc_s2get_userid_whitelist'];
92 if(isset($options['sc_user_fields_whitelist']))
93 $options['sc_s2get_userid_whitelist'] = $options['sc_user_fields_whitelist'];
94
95 $options = ws_plugin__s2member_configure_options_and_their_defaults(($options = array_merge($options, array('options_version' => (string)($options['options_version'] + 0.001)))));
96 update_option('ws_plugin__s2member_options', $options).((is_multisite() && is_main_site()) ? update_site_option('ws_plugin__s2member_options', $options) : NULL).update_option('ws_plugin__s2member_cache', array());
97
98 if($update_other === TRUE || in_array('auto_eot_system', (array)$update_other)) // Handle the Auto-EOT System now (enable/disable).
99 ($options['auto_eot_system_enabled'] == 1) ? c_ws_plugin__s2member_eot::add_auto_eot_system() : c_ws_plugin__s2member_eot::delete_auto_eot_system();
100
101 if(($display_notices === TRUE || in_array('success', (array)$display_notices)) && ($notice = '<strong>Options saved.'.(($request_refresh) ? ' Please <a href="'.esc_attr($_SERVER['REQUEST_URI']).'">refresh</a>.' : '').'</strong>'))
102 ($enqueue_notices === TRUE || in_array('success', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*') : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice);
103
104 if(empty($_GET['page']) || $_GET['page'] !== 'ws-plugin--s2member-mms-ops') // Do NOT display page-conflict-warnings on the Main Multisite Configuration panel.
105 {
106 if(!$options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>NOTE:</strong> s2Member security restrictions will NOT be enforced until you\'ve configured a Membership Options Page. See: <strong>s2Member → General Options → Membership Options Page</strong>.'))
107 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
108
109 if($options['login_welcome_page'] && $options['login_welcome_page'] === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is the same as your Membership Options Page. Please correct this. See: <strong>s2Member → General Options → Login Welcome Page</strong>.'))
110 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
111
112 if($options['membership_options_page'] && (string)get_option('page_on_front') === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Membership Options Page is currently configured as your Home Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this. See: <strong>WordPress → Reading Options</strong>. Or change: <strong>s2Member → General Options → Membership Options Page</strong>.'))
113 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
114
115 if($options['login_welcome_page'] && (string)get_option('page_on_front') === $options['login_welcome_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is currently configured as your Home Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this. See: <strong>WordPress → Reading Options</strong>. Or change: <strong>s2Member → General Options → Login Welcome Page</strong>.'))
116 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
117
118 if($options['membership_options_page'] && (string)get_option('page_for_posts') === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Membership Options Page is currently configured as your Posts Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Membership Options Page MUST stand alone. Please correct this. See: <strong>WordPress → Reading Options</strong>. Or change: <strong>s2Member → General Options → Membership Options Page</strong>.'))
119 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
120
121 if($options['login_welcome_page'] && (string)get_option('page_for_posts') === $options['login_welcome_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Login Welcome Page is currently configured as your Posts Page (i.e., static page) for WordPress. This causes internal conflicts with s2Member. Your Login Welcome Page MUST stand alone. Please correct this. See: <strong>WordPress → Reading Options</strong>. Or change: <strong>s2Member → General Options → Login Welcome Page</strong>.'))
122 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
123
124 if($options['file_download_limit_exceeded_page'] && $options['file_download_limit_exceeded_page'] === $options['membership_options_page'] && ($display_notices === TRUE || in_array('page-conflict-warnings', (array)$display_notices)) && ($notice = '<strong>s2Member:</strong> Your Download Limit Exceeded Page is the same as your Membership Options Page. Please correct this. See: <strong>s2Member → Download Options</strong>.'))
125 ($enqueue_notices === TRUE || in_array('page-conflict-warnings', (array)$enqueue_notices)) ? c_ws_plugin__s2member_admin_notices::enqueue_admin_notice($notice, '*:*', TRUE) : c_ws_plugin__s2member_admin_notices::display_admin_notice($notice, TRUE);
126 }
127 $updated_all_options = TRUE; // Flag indicating this routine was processed successfully; and that all s2Member options have been updated successfully.
128 }
129 foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
130 do_action('ws_plugin__s2member_after_update_all_options', get_defined_vars());
131 unset($__refs, $__v); // Housekeeping.
132
133 return apply_filters('ws_plugin__s2member_update_all_options', (($updated_all_options) ? TRUE : FALSE), get_defined_vars());
134 }
135
136 /**
137 * Displays shared End-of-Term demotion-role settings on gateway option pages.
138 *
139 * @package s2Member\Menu_Pages
140 * @since 260916.2004
141 *
142 * @return null
143 */
144 public static function eot_demotion_options()
145 {
146 $demotion_to_role = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['eot_demotion_to_role'];
147 $demotion_from = (string)$GLOBALS['WS_PLUGIN__']['s2member']['o']['eot_demotion_from'];
148 $demotion_disabled = ($GLOBALS['WS_PLUGIN__']['s2member']['o']['membership_eot_behavior'] === 'delete');
149
150 //260917.0003 Keep Demote From/To visible but disabled when EOT behavior is Delete; JavaScript mirrors this immediately when the behavior selector changes.
151 echo '<tr class="ws-plugin--s2member-eot-demotion-option-row"'.(($demotion_disabled) ? ' style="opacity:0.5;"' : '').'>'."\n";
152 echo '<th><label for="ws-plugin--s2member-eot-demotion-from">Demote From:</label></th>'."\n";
153 echo '</tr>'."\n";
154 echo '<tr class="ws-plugin--s2member-eot-demotion-option-row"'.(($demotion_disabled) ? ' style="opacity:0.5;"' : '').'>'."\n";
155 echo '<td>'."\n";
156 echo '<select name="ws_plugin__s2member_eot_demotion_from" id="ws-plugin--s2member-eot-demotion-from"'.(($demotion_disabled) ? ' disabled="disabled"' : '').'>'."\n";
157 echo '<option value="s2member_level"'.(($demotion_from === 's2member_level') ? ' selected="selected"' : '').'>s2Member Level role (preserve other WordPress roles)</option>'."\n";
158 echo '<option value="all"'.(($demotion_from === 'all') ? ' selected="selected"' : '').'>All WordPress roles (legacy behavior)</option>'."\n";
159 echo '</select><br />'."\n";
160 echo '<em>These settings are used only when Membership End-of-Term Behavior is set to Demote.</em><br /><br />'."\n";
161 //260916.2004 Explain the fresh-install behavior, legacy compatibility, and capability caveat without treating Subscriber/Level 0 as a role users are demoted from.
162 echo '<em>New installations remove only the user\'s <code>s2member_levelN</code> role at EOT and preserve other WordPress roles; upgraded installations retain the legacy all-role replacement until you change this setting. Preserved roles also preserve their existing capabilities, so roles such as Editor/Author/Contributor or custom roles may continue to grant s2Member access.</em>'."\n";
163 echo '</td>'."\n";
164 echo '</tr>'."\n";
165 echo '<tr class="ws-plugin--s2member-eot-demotion-option-row"'.(($demotion_disabled) ? ' style="opacity:0.5;"' : '').'>'."\n";
166 echo '<th><label for="ws-plugin--s2member-eot-demotion-to-role">Demote To Role:</label></th>'."\n";
167 echo '</tr>'."\n";
168 echo '<tr class="ws-plugin--s2member-eot-demotion-option-row"'.(($demotion_disabled) ? ' style="opacity:0.5;"' : '').'>'."\n";
169 echo '<td>'."\n";
170 //260916.2345 Group Subscriber and s2Member Levels first, then list other registered roles by slug so larger role lists stay predictable and easier to scan.
171 global $wp_roles;
172 if(!is_object($wp_roles))
173 $wp_roles = new WP_Roles();
174 $level_roles = $other_roles = array();
175 foreach((array)$wp_roles->roles as $role_slug => $role_data)
176 {
177 if(in_array($role_slug, array('administrator', 'editor', 'author', 'contributor', 's2member_pending_deletion'), TRUE))
178 continue;
179 $role_name = !empty($role_data['name']) ? translate_user_role($role_data['name']) : $role_slug;
180 if($role_slug === 'subscriber')
181 $level_roles[0] = array($role_slug, $role_name);
182 else if(preg_match('/^s2member_level([1-9][0-9]*)$/', $role_slug, $m))
183 $level_roles[(int)$m[1]] = array($role_slug, $role_name);
184 else
185 $other_roles[$role_slug] = $role_name;
186 }
187 ksort($level_roles, SORT_NUMERIC);
188 ksort($other_roles, SORT_STRING);
189 echo '<select name="ws_plugin__s2member_eot_demotion_to_role" id="ws-plugin--s2member-eot-demotion-to-role"'.(($demotion_disabled) ? ' disabled="disabled"' : '').'>'."\n";
190 if($level_roles)
191 {
192 echo '<optgroup label="s2Member Levels">'."\n";
193 foreach($level_roles as $role)
194 echo '<option value="'.esc_attr($role[0]).'"'.(($demotion_to_role === $role[0]) ? ' selected="selected"' : '').'>'.esc_html($role[1]).' ('.esc_html($role[0]).')</option>'."\n";
195 echo '</optgroup>'."\n";
196 }
197 if($other_roles)
198 {
199 echo '<optgroup label="Other Roles">'."\n";
200 foreach($other_roles as $role_slug => $role_name)
201 echo '<option value="'.esc_attr($role_slug).'"'.(($demotion_to_role === $role_slug) ? ' selected="selected"' : '').'>'.esc_html($role_name).' ('.esc_html($role_slug).')</option>'."\n";
202 echo '</optgroup>'."\n";
203 }
204 echo '</select><br />'."\n";
205 echo '<em>Select the WordPress role to assign when s2Member demotes a member at EOT. Subscriber is the default.</em><br /><br />'."\n";
206 echo '<em>WordPress\'s elevated default roles and s2Member\'s Pending Deletion role are intentionally not available here. Any role selected here keeps the capabilities assigned to it. If those capabilities grant s2Member access, the member may still be able to access protected content after EOT.</em><br /><br />'."\n";
207 echo '<em>Developers who need a role not available here can use <code>ws_plugin__s2member_force_demotion_role</code>; see <a href="https://s2member.com/kb-article/how-do-i-specify-the-demotion-role-upon-eot/" target="_blank" rel="noopener noreferrer external">How do I specify the demotion role upon EOT?</a></em>'."\n";
208 echo '</td>'."\n";
209 echo '</tr>'."\n";
210 do_action('ws_plugin__s2member_after_eot_demotion_options', get_defined_vars());
211 }
212
213 /**
214 * Adds option menus / sub-menus.
215 *
216 * @package s2Member\Menu_Pages
217 * @since 3.5
218 *
219 * @attaches-to ``add_action('admin_menu');``
220 */
221 public static function add_admin_options()
222 {
223 do_action('ws_plugin__s2member_before_add_admin_options', get_defined_vars());
224
225 add_filter('plugin_action_links', 'c_ws_plugin__s2member_menu_pages::_add_settings_link', 10, 2);
226
227 if(apply_filters('ws_plugin__s2member_during_add_admin_options_create_menu_items', TRUE, get_defined_vars()))
228 {
229 if((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) || apply_filters('ws_plugin__s2member_during_add_admin_options_clear_right_side', FALSE, get_defined_vars()))
230 $GLOBALS['WS_PLUGIN__']['s2member']['c']['menu_pages'] = array(); // Clear right side.
231
232 $menu = apply_filters('ws_plugin__s2member_during_add_admin_options_menu_slug', 'ws-plugin--s2member-start', get_defined_vars());
233
234 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_menu_page', TRUE, get_defined_vars()))
235 add_menu_page(((c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? 's2Member (Pro)' : 's2Member'), ((c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? 's2Member (Pro)' : 's2Member'),
236 'create_users', $menu, 'c_ws_plugin__s2member_menu_pages::start_page', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/images/brand-favicon.png');
237
238 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_start_page', TRUE, get_defined_vars()))
239 add_submenu_page($menu, 'Getting Started w/ s2Member', 'Getting Started', 'create_users', 'ws-plugin--s2member-start', 'c_ws_plugin__s2member_menu_pages::start_page');
240
241 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_help_page', !is_multisite () || !c_ws_plugin__s2member_utils_conds::is_multisite_farm () || is_main_site (), get_defined_vars()))
242 add_submenu_page($menu, 'Getting Help w/ s2Member', 'Getting Help', 'create_users', 'ws-plugin--s2member-help', 'c_ws_plugin__s2member_menu_pages::help_page');
243
244 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_divider_1', TRUE, get_defined_vars()))
245 add_submenu_page($menu, '', '<span style="display:block; margin:1px 0 1px -5px; padding:0; height:1px; line-height:1px; background:#CCCCCC;"></span>', 'create_users', '#');
246
247 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_mms_ops_page', (!is_multisite() || is_main_site()), get_defined_vars()))
248 add_submenu_page($menu, 's2Member Multisite Configuration', 'Multisite (Config)', 'create_users', 'ws-plugin--s2member-mms-ops', 'c_ws_plugin__s2member_menu_pages::mms_ops_page');
249
250 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_gen_ops_page', TRUE, get_defined_vars()))
251 add_submenu_page($menu, 's2Member General Options', 'General Options', 'create_users', 'ws-plugin--s2member-gen-ops', 'c_ws_plugin__s2member_menu_pages::gen_ops_page');
252
253 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_res_ops_page', TRUE, get_defined_vars()))
254 add_submenu_page($menu, 's2Member Restriction Options', 'Restriction Options', 'create_users', 'ws-plugin--s2member-res-ops', 'c_ws_plugin__s2member_menu_pages::res_ops_page');
255
256 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_down_ops_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
257 add_submenu_page($menu, 's2Member Download Options', 'Download Options', 'create_users', 'ws-plugin--s2member-down-ops', 'c_ws_plugin__s2member_menu_pages::down_ops_page');
258
259 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_divider_2', TRUE, get_defined_vars()))
260 add_submenu_page($menu, '', '<span style="display:block; margin:1px 0 1px -5px; padding:0; height:1px; line-height:1px; background:#CCCCCC;"></span>', 'create_users', '#');
261
262 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_paypal_ops_page', TRUE, get_defined_vars()))
263 add_submenu_page($menu, 's2Member PayPal Options', 'PayPal Options', 'create_users', 'ws-plugin--s2member-paypal-ops', 'c_ws_plugin__s2member_menu_pages::paypal_ops_page');
264
265 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_paypal_buttons_page', TRUE, get_defined_vars()))
266 add_submenu_page($menu, 's2Member PayPal Buttons', 'PayPal Buttons', 'create_users', 'ws-plugin--s2member-paypal-buttons', 'c_ws_plugin__s2member_menu_pages::paypal_buttons_page');
267
268 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_divider_3', TRUE, get_defined_vars()))
269 add_submenu_page($menu, '', '<span style="display:block; margin:1px 0 1px -5px; padding:0; height:1px; line-height:1px; background:#CCCCCC;"></span>', 'create_users', '#');
270
271 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_trk_ops_page', TRUE, get_defined_vars()))
272 add_submenu_page($menu, 's2Member API / Tracking', 'API / Tracking', 'create_users', 'ws-plugin--s2member-trk-ops', 'c_ws_plugin__s2member_menu_pages::trk_ops_page');
273
274 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_els_ops_page', TRUE, get_defined_vars()))
275 add_submenu_page($menu, 's2Member API / List Servers', 'API / List Servers', 'create_users', 'ws-plugin--s2member-els-ops', 'c_ws_plugin__s2member_menu_pages::els_ops_page');
276
277 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_api_ops_page', TRUE, get_defined_vars()))
278 add_submenu_page($menu, 's2Member API / Notifications', 'API / Notifications', 'create_users', 'ws-plugin--s2member-api-ops', 'c_ws_plugin__s2member_menu_pages::api_ops_page');
279
280 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_scripting_page', TRUE, get_defined_vars()))
281 add_submenu_page($menu, 's2Member API / Scripting', 'API / Scripting', 'create_users', 'ws-plugin--s2member-scripting', 'c_ws_plugin__s2member_menu_pages::scripting_page');
282
283 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_divider_4', TRUE, get_defined_vars()))
284 add_submenu_page($menu, '', '<span style="display:block; margin:1px 0 1px -5px; padding:0; height:1px; line-height:1px; background:#CCCCCC;"></span>', 'create_users', '#');
285
286 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_integrations_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
287 add_submenu_page($menu, 's2Member / Other Integrations', 'Other Integrations', 'create_users', 'ws-plugin--s2member-integrations', 'c_ws_plugin__s2member_menu_pages::integrations_page');
288
289 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_divider_5', TRUE, get_defined_vars()))
290 add_submenu_page($menu, '', '<span style="display:block; margin:1px 0 1px -5px; padding:0; height:1px; line-height:1px; background:#CCCCCC;"></span>', 'create_users', '#');
291
292 if(apply_filters('ws_plugin__s2member_during_add_admin_options_add_logs_page', (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()), get_defined_vars()))
293 add_submenu_page($menu, 's2Member Logs', 'Log Files (Debug)', 'create_users', 'ws-plugin--s2member-logs', 'c_ws_plugin__s2member_menu_pages::logs_page');
294
295 do_action('ws_plugin__s2member_during_add_admin_options_additional_pages', get_defined_vars());
296 }
297 do_action('ws_plugin__s2member_after_add_admin_options', get_defined_vars());
298 }
299
300 /**
301 * Adds network option menus / sub-menus.
302 *
303 * @package s2Member\Menu_Pages
304 * @since 3.5
305 *
306 * @attaches-to ``add_action('network_admin_menu');``
307 */
308 public static function add_network_admin_options()
309 {
310 do_action('ws_plugin__s2member_before_add_network_admin_options', get_defined_vars());
311
312 if(apply_filters('ws_plugin__s2member_during_add_network_admin_options_create_menu_items', TRUE, get_defined_vars()))
313 {
314 if((is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site()) || apply_filters('ws_plugin__s2member_during_add_network_admin_options_clear_right_side', FALSE, get_defined_vars()))
315 $GLOBALS['WS_PLUGIN__']['s2member']['c']['menu_pages'] = array(); // Clear right side.
316
317 $menu = 'ws-plugin--s2member-mms-ops'; // Used below for nesting additional sub-menu pages.
318
319 add_menu_page('s2Member', 's2Member', 'create_users', $menu, 'c_ws_plugin__s2member_menu_pages::mms_ops_page', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/images/brand-favicon.png');
320
321 add_submenu_page($menu, 's2Member Multisite (Configuration)', 'Multisite (Config)', 'create_users', 'ws-plugin--s2member-mms-ops', 'c_ws_plugin__s2member_menu_pages::mms_ops_page');
322
323 do_action('ws_plugin__s2member_during_add_network_admin_options_additional_pages', get_defined_vars());
324 }
325 do_action('ws_plugin__s2member_after_add_network_admin_options', get_defined_vars());
326 }
327
328 /**
329 * A sort of callback function to add the settings link.
330 *
331 * @package s2Member\Menu_Pages
332 * @since 3.5
333 *
334 * @attaches-to ``add_filter('plugin_action_links');``
335 *
336 * @param array $actions Expects an existing array of actions links, passed in by the Filter.
337 * @param string $plugin_file Expects path to a plugin file. We need to test against this for s2Member.
338 *
339 * @return array An array of links, Filtered by this routine.
340 */
341 public static function _add_settings_link($actions = array(), $plugin_file = '')
342 {
343 foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
344 do_action('_ws_plugin__s2member_before_add_settings_link', get_defined_vars());
345 unset($__refs, $__v); // Housekeeping.
346
347 if($plugin_file === $GLOBALS['WS_PLUGIN__']['s2member']['c']['plugin_basename'] && is_array($actions))
348 {
349 $settings = '<a href="'.esc_attr(admin_url('/admin.php?page=ws-plugin--s2member-gen-ops')).'">Settings</a>';
350 array_unshift($actions, apply_filters('ws_plugin__s2member_add_settings_link', $settings, get_defined_vars()));
351
352 foreach(array_keys(get_defined_vars()) as $__v) $__refs[$__v] =& $$__v;
353 do_action('_ws_plugin__s2member_during_add_settings_link', get_defined_vars());
354 unset($__refs, $__v); // Housekeeping.
355 }
356 return apply_filters('_ws_plugin__s2member_add_settings_link', $actions, get_defined_vars());
357 }
358
359 /**
360 * Enqueue scripts for administrative menu pages.
361 *
362 * @package s2Member\Menu_Pages
363 * @since 3.5
364 *
365 * @attaches-to ``add_action('admin_print_scripts');``
366 */
367 public static function add_admin_scripts()
368 {
369 do_action('ws_plugin__s2member_before_add_admin_scripts', get_defined_vars());
370
371 if(!empty($_GET['page']) && preg_match('/ws-plugin--s2member-/', $_GET['page']))
372 {
373 wp_enqueue_script('jquery');
374 wp_enqueue_script('thickbox');
375 wp_enqueue_script('media-upload');
376 wp_enqueue_script('jquery-ui-core');
377 wp_enqueue_script('jquery-sprintf', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/includes/jquery/jquery.sprintf/jquery.sprintf.min.js', array('jquery'), c_ws_plugin__s2member_utilities::ver_checksum());
378 wp_enqueue_script('jquery-ui-effects', $GLOBALS['WS_PLUGIN__']['s2member']['c']['dir_url'].'/src/includes/jquery/jquery.ui-effects/jquery.ui-effects.min.js', array('jquery', 'jquery-ui-core'), c_ws_plugin__s2member_utilities::ver_checksum());
379 //260928.0402 Use WordPress's registered JSON2 compatibility script instead of shipping a duplicate JSON parser.
380 wp_enqueue_script('ws-plugin--s2member-menu-pages', admin_url('admin.php?ws_plugin__s2member_menu_pages_js='.urlencode(mt_rand()), is_ssl() ? 'https' : 'http'), array('jquery', 'thickbox', 'media-upload', 'jquery-sprintf', 'json2', 'jquery-ui-core', 'jquery-ui-effects', 'password-strength-meter'), c_ws_plugin__s2member_utilities::ver_checksum());
381
382 do_action('ws_plugin__s2member_during_add_admin_scripts', get_defined_vars());
383 }
384 do_action('ws_plugin__s2member_after_add_admin_scripts', get_defined_vars());
385 }
386
387 /**
388 * Enqueue styles for administrative menu pages.
389 *
390 * @package s2Member\Menu_Pages
391 * @since 3.5
392 *
393 * @attaches-to ``add_action('admin_print_styles');``
394 */
395 public static function add_admin_styles()
396 {
397 do_action('ws_plugin__s2member_before_add_admin_styles', get_defined_vars());
398
399 if(!empty($_GET['page']) && preg_match('/ws-plugin--s2member-/', $_GET['page']))
400 {
401 wp_enqueue_style('thickbox');
402 wp_enqueue_style('ws-plugin--s2member-menu-pages', admin_url('admin.php?ws_plugin__s2member_menu_pages_css='.urlencode(mt_rand()), is_ssl() ? 'https' : 'http'), array('thickbox'), c_ws_plugin__s2member_utilities::ver_checksum(), 'all');
403
404 do_action('ws_plugin__s2member_during_add_admin_styles', get_defined_vars());
405 }
406 do_action('ws_plugin__s2member_after_add_admin_styles', get_defined_vars());
407 }
408
409 /**
410 * Handles log file downloads.
411 *
412 * @package s2Member\Menu_Pages
413 * @since 120310
414 */
415 public static function log_file_downloader()
416 {
417 if(!current_user_can('create_users')) return;
418 if(is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site())
419 return; // We do NOT provide this functionality on Child Blogs of a Blog Farm Network.
420
421 if(!empty($_GET['ws_plugin__s2member_download_log_file']) && is_string($log_file = $_GET['ws_plugin__s2member_download_log_file']) && strpos($log_file, '..') === FALSE && strpos(basename($log_file), '.') !== 0)
422 if(!empty($_GET['ws_plugin__s2member_download_log_file_v']) && is_string($nonce = $_GET['ws_plugin__s2member_download_log_file_v']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-download-log-file-v'))
423 {
424 $logs_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'];
425
426 $resource = FALSE;
427 $log_file_size = 0;
428 if(is_file($logs_dir.'/'.$log_file) && is_readable($logs_dir.'/'.$log_file) && ($resource = @fopen($logs_dir.'/'.$log_file, 'rb')))
429 if(($log_file_stat = fstat($resource)) !== FALSE)
430 $log_file_size = $log_file_stat['size'];
431
432 @set_time_limit(0);
433 @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
434
435 @ini_set('zlib.output_compression', 0);
436 if(function_exists('apache_setenv'))
437 @apache_setenv('no-gzip', '1');
438
439 while(@ob_end_clean()) ;
440
441 status_header(200); // 200 OK status header.
442
443 header('Content-Encoding: none');
444 header('Accept-Ranges: none');
445 header('Content-Type: text/plain; charset=UTF-8');
446 header('Content-Length: '.$log_file_size);
447 header('Expires: '.gmdate('D, d M Y H:i:s', strtotime('-1 week')).' GMT');
448 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
449 header('Cache-Control: no-cache, must-revalidate, max-age=0');
450 header('Cache-Control: post-check=0, pre-check=0', FALSE);
451 header('Pragma: no-cache');
452
453 header('Content-Disposition: attachment; filename="'.$log_file.'"');
454
455 if(is_resource($resource))
456 {
457 //261006.0145 Stream log downloads in bounded chunks so large logs do not exhaust PHP memory.
458 $chunk_size = max(1, min(2097152, (int)apply_filters('ws_plugin__s2member_file_downloads_chunk_size', 2097152, get_defined_vars())));
459 $bytes_remaining = $log_file_size;
460 while($bytes_remaining > 0 && !feof($resource))
461 {
462 $chunk = fread($resource, min($chunk_size, $bytes_remaining));
463 if($chunk === FALSE || $chunk === '') break;
464 $bytes_remaining -= strlen($chunk);
465 echo $chunk;
466 flush();
467 }
468 fclose($resource);
469 }
470 exit; // Clean exit after serving file.
471 }
472 }
473
474 /**
475 * Handles log file downloads (in ZIP format).
476 *
477 * @package s2Member\Menu_Pages
478 * @since 120310
479 */
480 public static function logs_zip_downloader()
481 {
482 if(!current_user_can('create_users')) return;
483 if(is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site())
484 return; // We do NOT provide this functionality on Child Blogs of a Blog Farm Network.
485
486 if(!empty($_POST['ws_plugin__s2member_logs_download_zip']) && is_string($nonce = $_POST['ws_plugin__s2member_logs_download_zip']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-logs-download-zip'))
487 {
488 $logs_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'];
489 $s2member_logs_zip = $logs_dir.'/'.$_SERVER['HTTP_HOST'].'--s2member-logs.zip';
490
491 if(is_dir($logs_dir)) // Do we have a logs directory?
492 {
493 include_once ABSPATH.'wp-admin/includes/class-pclzip.php';
494
495 if(file_exists($s2member_logs_zip) && is_writable($s2member_logs_zip))
496 unlink($s2member_logs_zip);
497
498 $archive = new PclZip($s2member_logs_zip);
499 $archive->create($logs_dir, PCLZIP_OPT_REMOVE_ALL_PATH);
500 }
501 if(file_exists($s2member_logs_zip))
502 $s2member_logs_zip_size = filesize($s2member_logs_zip);
503 else $s2member_logs_zip_size = 0;
504
505 @set_time_limit(0);
506 @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
507
508 @ini_set('zlib.output_compression', 0);
509 if(function_exists('apache_setenv'))
510 @apache_setenv('no-gzip', '1');
511
512 while(@ob_end_clean()) ;
513
514 status_header(200); // 200 OK status header.
515
516 header('Content-Encoding: none');
517 header('Accept-Ranges: none');
518 header('Content-Type: application/zip');
519 header('Content-Length: '.$s2member_logs_zip_size);
520 header('Expires: '.gmdate('D, d M Y H:i:s', strtotime('-1 week')).' GMT');
521 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
522 header('Cache-Control: no-cache, must-revalidate, max-age=0');
523 header('Cache-Control: post-check=0, pre-check=0', FALSE);
524 header('Pragma: no-cache');
525
526 header('Content-Disposition: attachment; filename="'.basename($s2member_logs_zip).'"');
527
528 if($s2member_logs_zip_size && is_resource($resource = fopen($s2member_logs_zip, 'rb')))
529 {
530 $_bytes_to_read = $s2member_logs_zip_size; // Total bytes we need to read for this file.
531
532 $chunk_size = apply_filters('ws_plugin__s2member_file_downloads_chunk_size', 2097152, get_defined_vars());
533
534 while($_bytes_to_read) // We have bytes to read here.
535 {
536 $_bytes_to_read -= ($_reading = ($_bytes_to_read > $chunk_size) ? $chunk_size : $_bytes_to_read);
537 echo fread($resource, $_reading); // Serve file in chunks (default chunk size is 2MB).
538 flush(); // Flush each chunk to the browser as it is served (avoids high memory consumption).
539 }
540 fclose($resource); // Close file resource handle.
541 unset($_bytes_to_read, $_reading); // Housekeeping.
542 }
543 exit; // Clean exit after serving file.
544 }
545 }
546
547 /**
548 * Archives existing log files and starts fresh with new logs.
549 *
550 * @package s2Member\Menu_Pages
551 * @since 120310
552 */
553 public static function archive_logs_start_fresh()
554 {
555 if(!current_user_can('create_users')) return;
556 if(is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site())
557 return; // We do NOT provide this functionality on Child Blogs of a Blog Farm Network.
558
559 if(!empty($_POST['ws_plugin__s2member_logs_archive_start_fresh']) && is_string($nonce = $_POST['ws_plugin__s2member_logs_archive_start_fresh']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-logs-archive-start-fresh'))
560 {
561 if(is_dir($logs_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir']))
562 foreach(scandir($logs_dir) as $log_file) // Archive existing log files here.
563 {
564 if(preg_match('/\.log$/', $log_file) && stripos($log_file, '-ARCHIVED-') === FALSE)
565 if(is_file($log_dir_file = $logs_dir.'/'.$log_file) && is_writable($log_dir_file))
566 if(!rename($log_dir_file, preg_replace('/\.log$/i', '', $log_dir_file).'-ARCHIVED-'.date('m-d-Y').'-'.time().'.log'))
567 $error = TRUE;
568 }
569 if(!empty($error))
570 c_ws_plugin__s2member_admin_notices::display_admin_notice('Unknown error when attempting to archive log files. Please check directory permissions.', TRUE);
571 else
572 c_ws_plugin__s2member_admin_notices::display_admin_notice('All log files have been archived successfully.');
573 }
574 }
575
576 /**
577 * Deletes existing log files and starts fresh with new logs.
578 *
579 * @package s2Member\Menu_Pages
580 * @since 120312
581 */
582 public static function delete_logs_start_fresh()
583 {
584 if(!current_user_can('create_users')) return;
585 if(is_multisite() && c_ws_plugin__s2member_utils_conds::is_multisite_farm() && !is_main_site())
586 return; // We do NOT provide this functionality on Child Blogs of a Blog Farm Network.
587
588 if(!empty($_POST['ws_plugin__s2member_logs_delete_start_fresh']) && is_string($nonce = $_POST['ws_plugin__s2member_logs_delete_start_fresh']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-logs-delete-start-fresh'))
589 {
590 if(is_dir($logs_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir']))
591 foreach(scandir($logs_dir) as $log_file) // Delete existing log files here.
592 {
593 if(preg_match('/\.log$/', $log_file))
594 if(is_file($log_dir_file = $logs_dir.'/'.$log_file) && is_writable($log_dir_file))
595 if(!unlink($log_dir_file)) $error = TRUE;
596 }
597 if(!empty($error))
598 c_ws_plugin__s2member_admin_notices::display_admin_notice('Unknown error when attempting to delete log files. Please check directory permissions.', TRUE);
599 else
600 c_ws_plugin__s2member_admin_notices::display_admin_notice('All log files have been deleted successfully.');
601 }
602 }
603
604 /**
605 * Builds and handles the Getting Started page.
606 *
607 * @package s2Member\Menu_Pages
608 * @since 3.5
609 */
610 public static function start_page()
611 {
612 do_action('ws_plugin__s2member_before_start_page', get_defined_vars());
613
614 include_once dirname(dirname(__FILE__)).'/menu-pages/start.inc.php';
615
616 do_action('ws_plugin__s2member_after_start_page', get_defined_vars());
617 }
618
619 /**
620 * Builds and handles the Getting Help page.
621 *
622 * @package s2Member\Menu_Pages
623 * @since 151218
624 */
625 public static function help_page()
626 {
627 do_action('ws_plugin__s2member_before_help_page', get_defined_vars());
628
629 include_once dirname(dirname(__FILE__)).'/menu-pages/help.inc.php';
630
631 do_action('ws_plugin__s2member_after_help_page', get_defined_vars());
632 }
633
634 /**
635 * Builds and handles the Main Multisite Options page.
636 *
637 * @package s2Member\Menu_Pages
638 * @since 3.5
639 */
640 public static function mms_ops_page()
641 {
642 do_action('ws_plugin__s2member_before_mms_ops_page', get_defined_vars());
643
644 include_once dirname(dirname(__FILE__)).'/menu-pages/mms-ops.inc.php';
645
646 do_action('ws_plugin__s2member_after_mms_ops_page', get_defined_vars());
647 }
648
649 /**
650 * Builds and handles the General Options page.
651 *
652 * @package s2Member\Menu_Pages
653 * @since 3.5
654 */
655 public static function gen_ops_page()
656 {
657 do_action('ws_plugin__s2member_before_gen_ops_page', get_defined_vars());
658
659 c_ws_plugin__s2member_menu_pages::update_all_options();
660
661 include_once dirname(dirname(__FILE__)).'/menu-pages/gen-ops.inc.php';
662
663 do_action('ws_plugin__s2member_after_gen_ops_page', get_defined_vars());
664 }
665
666 /**
667 * Builds and handles the Restriction Options page.
668 *
669 * @package s2Member\Menu_Pages
670 * @since 3.5
671 */
672 public static function res_ops_page()
673 {
674 do_action('ws_plugin__s2member_before_res_ops_page', get_defined_vars());
675
676 c_ws_plugin__s2member_menu_pages::update_all_options();
677
678 include_once dirname(dirname(__FILE__)).'/menu-pages/res-ops.inc.php';
679
680 do_action('ws_plugin__s2member_after_res_ops_page', get_defined_vars());
681 }
682
683 /**
684 * Builds and handles the Paypal Options page.
685 *
686 * @package s2Member\Menu_Pages
687 * @since 3.5
688 */
689 public static function paypal_ops_page()
690 {
691 do_action('ws_plugin__s2member_before_paypal_ops_page', get_defined_vars());
692
693 c_ws_plugin__s2member_menu_pages::update_all_options();
694
695 $logs_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'];
696
697 if(!is_dir($logs_dir) && is_writable(dirname(c_ws_plugin__s2member_utils_dirs::strip_dir_app_data($logs_dir))))
698 mkdir($logs_dir, 0777, TRUE).clearstatcache();
699
700 $htaccess = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'].'/.htaccess';
701 $htaccess_contents = trim(c_ws_plugin__s2member_utilities::evl(file_get_contents($GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir_htaccess'])));
702
703 if(is_dir($logs_dir) && is_writable($logs_dir) && !file_exists($htaccess))
704 file_put_contents($htaccess, $htaccess_contents).clearstatcache();
705
706 if($GLOBALS['WS_PLUGIN__']['s2member']['o']['gateway_debug_logs']) // Logging enabled?
707 {
708 if(!is_dir($logs_dir)) // If the security-enabled logs directory does not exist yet.
709 c_ws_plugin__s2member_admin_notices::display_admin_notice('The security-enabled logs directory (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($logs_dir)).'</code>) does not exist. Please create this directory manually &amp; make it writable (chmod 777).', TRUE);
710
711 else if(!is_writable($logs_dir)) // If the logs directory is not writable yet.
712 c_ws_plugin__s2member_admin_notices::display_admin_notice('Permissions error. The security-enabled logs directory (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($logs_dir)).'</code>) is not writable. Please make this directory writable (chmod 777).', TRUE);
713
714 if(!file_exists($htaccess)) // If the .htaccess file has not been created yet.
715 c_ws_plugin__s2member_admin_notices::display_admin_notice('The .htaccess protection file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($htaccess)).'</code>) does not exist. Please create this file manually. Inside your .htaccess file, add this:<br /><pre>'.esc_html($htaccess_contents).'</pre>', TRUE);
716
717 else if(!preg_match('/deny from all/i', file_get_contents($htaccess))) // Else if the .htaccess file does not offer the required protection.
718 c_ws_plugin__s2member_admin_notices::display_admin_notice('Unprotected. The .htaccess protection file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($htaccess)).'</code>) does not contain <code>deny from all</code>. Inside your .htaccess file, add this:<br /><pre>'.esc_html($htaccess_contents).'</pre>', TRUE);
719 }
720 include_once dirname(dirname(__FILE__)).'/menu-pages/paypal-ops.inc.php';
721
722 do_action('ws_plugin__s2member_after_paypal_ops_page', get_defined_vars());
723 }
724
725 /**
726 * Builds and handles the Download Options page.
727 *
728 * @package s2Member\Menu_Pages
729 * @since 3.5
730 */
731 public static function down_ops_page()
732 {
733 do_action('ws_plugin__s2member_before_down_ops_page', get_defined_vars());
734
735 c_ws_plugin__s2member_menu_pages::update_all_options();
736
737 if(!empty($_REQUEST['ws_plugin__s2member_cf_options_reset'])
738 && wp_verify_nonce($_REQUEST['ws_plugin__s2member_cf_options_reset'], 'ws-plugin--s2member-cf-options-reset')
739 )
740 {
741 c_ws_plugin__s2member_files_in::reset_aws_cf_config_values(); // A full CloudFront reset.
742 c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon CloudFront configuration reset successfully.');
743 }
744 $files_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['files_dir'];
745
746 $htaccess = $GLOBALS['WS_PLUGIN__']['s2member']['c']['files_dir'].'/.htaccess';
747 $htaccess_contents = trim(c_ws_plugin__s2member_utilities::evl(file_get_contents($GLOBALS['WS_PLUGIN__']['s2member']['c']['files_dir_htaccess'])));
748
749 $no_gzip_htaccess = ABSPATH.'.htaccess'; // Always located in the absolute root path for WordPress.
750 $no_gzip_htaccess_contents = trim(c_ws_plugin__s2member_utilities::evl(file_get_contents($GLOBALS['WS_PLUGIN__']['s2member']['c']['files_no_gzip_htaccess'])));
751
752 if(!c_ws_plugin__s2member_files::no_gzip_rules_in_root_htaccess()) // If s2Member's GZIP exclusions do NOT yet exist in the root `.htaccess` file.
753 c_ws_plugin__s2member_files::write_no_gzip_into_root_htaccess().clearstatcache(); // Handle the root `.htaccess` file now.
754
755 if(!is_dir($files_dir) && is_writable(dirname(c_ws_plugin__s2member_utils_dirs::strip_dir_app_data($files_dir))))
756 mkdir($files_dir, 0777, TRUE).clearstatcache(); // Create this directory structure now.
757
758 if(is_dir($files_dir) && is_writable($files_dir) && !file_exists($htaccess)) // This file does NOT exist yet?
759 file_put_contents($htaccess, $htaccess_contents).clearstatcache(); // Create the `.htaccess` file now.
760
761 if(!c_ws_plugin__s2member_files::no_gzip_rules_in_root_htaccess()) // If s2Member's GZIP exclusions do NOT yet exist in the root `.htaccess` file.
762 c_ws_plugin__s2member_admin_notices::display_admin_notice('Possible GZIP conflict on server. Unable to write GZIP exclusions into root .htaccess file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($no_gzip_htaccess)).'</code>). Please read the panel below: <strong>Preventing GZIP Conflicts</strong>, and add this section yourself:<br /><pre>'.esc_html($no_gzip_htaccess_contents).'</pre>', TRUE);
763
764 if(!is_dir($files_dir)) // If the security-enabled files directory does not exist yet.
765 c_ws_plugin__s2member_admin_notices::display_admin_notice('The security-enabled files directory (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($files_dir)).'</code>) does not exist. Please create this directory manually.', TRUE);
766
767 if(!file_exists($htaccess)) // If the `.htaccess` file has not been created yet.
768 c_ws_plugin__s2member_admin_notices::display_admin_notice('The .htaccess protection file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($htaccess)).'</code>) does not exist. Please create this file manually. Inside your .htaccess file, add this:<br /><pre>'.esc_html($htaccess_contents).'</pre>', TRUE);
769
770 else if(!preg_match('/deny from all/i', file_get_contents($htaccess))) // Else if the `.htaccess` file does not offer the required protection.
771 c_ws_plugin__s2member_admin_notices::display_admin_notice('Unprotected. The .htaccess protection file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($htaccess)).'</code>) does not contain <code>deny from all</code>. Inside your .htaccess file, add this:<br /><pre>'.esc_html($htaccess_contents).'</pre>', TRUE);
772
773 if(!empty($_POST['ws_plugin__s2member_amazon_cf_files_auto_configure_distros']) && ($nonce = $_POST['ws_plugin__s2member_amazon_cf_files_auto_configure_distros']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-amazon-cf-files-auto-configure-distros'))
774 if(($amazon_cf_auto_configure_distros = c_ws_plugin__s2member_files_in::amazon_cf_auto_configure_distros()) && $amazon_cf_auto_configure_distros['success'])
775 c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon CloudFront Distributions auto-configured successfully. Please allow 30 minutes for initial propagation. <strong>Tip:</strong> If you try to stream over the RTMP protocol using something like the <code>[s2Stream /]</code> shortcode, and you keep getting an "ID Not Found" error while using JW Player; please note that it can <em>sometimes</em> take a full 24 hours for RTMP (i.e., streaming distributions) to begin working properly. This is because there are a few initialization routines that must complete on the AWS side when you first integrate with CloudFront. Please be patient.'.(($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_cname']) ? '<br /><em>Downloads Distribution CNAME: <code>'.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_cname']).' &mdash;&raquo; '.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_downloads_dname']).'</code></em>' : '').(($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_cname']) ? '<br /><em>Streaming Distribution CNAME: <code>'.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_cname']).' &mdash;&raquo; '.esc_html($GLOBALS['WS_PLUGIN__']['s2member']['o']['amazon_cf_files_distro_streaming_dname']).'</code></em>' : ''));
776 else // Else there was an error. We need to report this back to the site owner so they can understand what's going on.
777 (c_ws_plugin__s2member_menu_pages::$pre_display_errors['cf_files_auto_configure_distros'] = TRUE).c_ws_plugin__s2member_admin_notices::display_admin_notice('Unable to auto-configure Amazon CloudFront Distributions.<br />Error code: <code>'.esc_html($amazon_cf_auto_configure_distros['code']).'</code>. Error Message: <code>'.esc_html($amazon_cf_auto_configure_distros['message']).'</code>', TRUE);
778
779 if(!empty($_POST['ws_plugin__s2member_amazon_s3_files_auto_configure_acls']) && ($nonce = $_POST['ws_plugin__s2member_amazon_s3_files_auto_configure_acls']) && wp_verify_nonce($nonce, 'ws-plugin--s2member-amazon-s3-files-auto-configure-acls'))
780 if(($amazon_s3_auto_configure_acls = c_ws_plugin__s2member_files_in::amazon_s3_auto_configure_acls()) && $amazon_s3_auto_configure_acls['success'])
781 c_ws_plugin__s2member_admin_notices::display_admin_notice('Amazon S3 ACLs auto-configured successfully.');
782 else // Else there was an error. We need to report this back to the site owner so they can understand what's going on.
783 (c_ws_plugin__s2member_menu_pages::$pre_display_errors['s3_files_auto_configure_acls'] = TRUE).c_ws_plugin__s2member_admin_notices::display_admin_notice('Unable to auto-configure Amazon S3 ACLs.<br />Error code: <code>'.esc_html($amazon_s3_auto_configure_acls['code']).'</code>. Error Message: <code>'.esc_html($amazon_s3_auto_configure_acls['message']).'</code>', TRUE);
784
785 include_once dirname(dirname(__FILE__)).'/menu-pages/down-ops.inc.php';
786
787 do_action('ws_plugin__s2member_after_down_ops_page', get_defined_vars());
788 }
789
790 /**
791 * Builds and handles the API Tracking options page.
792 *
793 * @package s2Member\Menu_Pages
794 * @since 3.5
795 */
796 public static function trk_ops_page()
797 {
798 do_action('ws_plugin__s2member_before_trk_ops_page', get_defined_vars());
799
800 c_ws_plugin__s2member_menu_pages::update_all_options();
801
802 include_once dirname(dirname(__FILE__)).'/menu-pages/trk-ops.inc.php';
803
804 do_action('ws_plugin__s2member_after_trk_ops_page', get_defined_vars());
805 }
806
807 /**
808 * Builds and handles the API List Server options page.
809 *
810 * @package s2Member\Menu_Pages
811 * @since 3.5
812 */
813 public static function els_ops_page()
814 {
815 do_action('ws_plugin__s2member_before_els_ops_page', get_defined_vars());
816
817 c_ws_plugin__s2member_menu_pages::update_all_options();
818
819 include_once dirname(dirname(__FILE__)).'/menu-pages/els-ops.inc.php';
820
821 do_action('ws_plugin__s2member_after_els_ops_page', get_defined_vars());
822 }
823
824 /**
825 * Builds and handles the API Notifications page.
826 *
827 * @package s2Member\Menu_Pages
828 * @since 3.5
829 */
830 public static function api_ops_page()
831 {
832 do_action('ws_plugin__s2member_before_api_ops_page', get_defined_vars());
833
834 c_ws_plugin__s2member_menu_pages::update_all_options();
835
836 include_once dirname(dirname(__FILE__)).'/menu-pages/api-ops.inc.php';
837
838 do_action('ws_plugin__s2member_after_api_ops_page', get_defined_vars());
839 }
840
841 /**
842 * Builds and handles the PayPal Button Generator page.
843 *
844 * @package s2Member\Menu_Pages
845 * @since 3.5
846 */
847 public static function paypal_buttons_page()
848 {
849 do_action('ws_plugin__s2member_before_paypal_buttons_page', get_defined_vars());
850
851 if(!$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_business'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_merchant_id'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password'] || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature'])
852 c_ws_plugin__s2member_admin_notices::display_admin_notice('Please configure <strong>s2Member → PayPal Options</strong> first. Once all of your PayPal Options are configured; including your Email Address, Merchant ID, API Username, Password, and Signature; return to this page &amp; generate your PayPal Button(s).', TRUE);
853
854 include_once dirname(dirname(__FILE__)).'/menu-pages/paypal-buttons.inc.php';
855
856 do_action('ws_plugin__s2member_after_paypal_buttons_page', get_defined_vars());
857 }
858
859 /**
860 * Builds and handles the API Scripting page.
861 *
862 * @package s2Member\Menu_Pages
863 * @since 3.5
864 */
865 public static function scripting_page()
866 {
867 do_action('ws_plugin__s2member_before_scripting_page', get_defined_vars());
868
869 c_ws_plugin__s2member_menu_pages::update_all_options();
870
871 include_once dirname(dirname(__FILE__)).'/menu-pages/scripting.inc.php';
872
873 do_action('ws_plugin__s2member_after_scripting_page', get_defined_vars());
874 }
875
876 /**
877 * Builds and handles the Integrations page.
878 *
879 * @package s2Member\Menu_Pages
880 * @since 3.5
881 */
882 public static function integrations_page()
883 {
884 do_action('ws_plugin__s2member_before_integrations_page', get_defined_vars());
885
886 include_once dirname(dirname(__FILE__)).'/menu-pages/integrations.inc.php';
887
888 do_action('ws_plugin__s2member_after_integrations_page', get_defined_vars());
889 }
890
891 /**
892 * Builds and handles the Logs page.
893 *
894 * @package s2Member\Menu_Pages
895 * @since 120310
896 */
897 public static function logs_page()
898 {
899 do_action('ws_plugin__s2member_before_logs_page', get_defined_vars());
900
901 c_ws_plugin__s2member_menu_pages::update_all_options();
902 c_ws_plugin__s2member_menu_pages::archive_logs_start_fresh();
903 c_ws_plugin__s2member_menu_pages::delete_logs_start_fresh();
904
905 $logs_dir = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'];
906
907 if(!is_dir($logs_dir) && is_writable(dirname(c_ws_plugin__s2member_utils_dirs::strip_dir_app_data($logs_dir))))
908 mkdir($logs_dir, 0777, TRUE).clearstatcache();
909
910 $htaccess = $GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir'].'/.htaccess';
911 $htaccess_contents = trim(c_ws_plugin__s2member_utilities::evl(file_get_contents($GLOBALS['WS_PLUGIN__']['s2member']['c']['logs_dir_htaccess'])));
912
913 if(is_dir($logs_dir) && is_writable($logs_dir) && !file_exists($htaccess))
914 file_put_contents($htaccess, $htaccess_contents).clearstatcache();
915
916 if(!is_dir($logs_dir)) // If the security-enabled logs directory does not exist yet.
917 c_ws_plugin__s2member_admin_notices::display_admin_notice('The security-enabled logs directory (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($logs_dir)).'</code>) does not exist. Please create this directory manually &amp; make it writable (chmod 777).', TRUE);
918
919 else if(!is_writable($logs_dir)) // If the logs directory is not writable yet.
920 c_ws_plugin__s2member_admin_notices::display_admin_notice('Permissions error. The security-enabled logs directory (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($logs_dir)).'</code>) is not writable. Please make this directory writable (chmod 777).', TRUE);
921
922 if(!file_exists($htaccess)) // If the .htaccess file has not been created yet.
923 c_ws_plugin__s2member_admin_notices::display_admin_notice('The .htaccess protection file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($htaccess)).'</code>) does not exist. Please create this file manually. Inside your .htaccess file, add this:<br /><pre>'.esc_html($htaccess_contents).'</pre>', TRUE);
924
925 else if(!preg_match('/deny from all/i', file_get_contents($htaccess))) // Else if the .htaccess file does not offer the required protection.
926 c_ws_plugin__s2member_admin_notices::display_admin_notice('Unprotected. The .htaccess protection file (<code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($htaccess)).'</code>) does not contain <code>deny from all</code>. Inside your .htaccess file, add this:<br /><pre>'.esc_html($htaccess_contents).'</pre>', TRUE);
927
928 if(!$GLOBALS['WS_PLUGIN__']['s2member']['o']['gateway_debug_logs']) // Logging disabled?
929 c_ws_plugin__s2member_admin_notices::display_admin_notice('Logging is currently disabled by your configuration.');
930
931 include_once dirname(dirname(__FILE__)).'/menu-pages/logs.inc.php';
932
933 do_action('ws_plugin__s2member_after_logs_page', get_defined_vars());
934 }
935
936 /**
937 * Registers the Get Help panel across admin pages.
938 *
939 * @package s2Member\Menu_Pages
940 * @since 250824
941 *
942 * @attaches-to ``add_action('admin_menu');``
943 */
944 public static function get_help_panel() {
945 $hooks = array(
946 // Framework
947 'ws_plugin__s2member_during_api_ops_page_before_left_sections_form',
948 'ws_plugin__s2member_during_down_ops_page_before_left_sections_form',
949 'ws_plugin__s2member_during_els_ops_page_before_left_sections_form',
950 'ws_plugin__s2member_during_gen_ops_page_before_left_sections_form',
951 'ws_plugin__s2member_during_help_page_before_left_sections',
952 'ws_plugin__s2member_during_integrations_page_before_left_sections',
953 'ws_plugin__s2member_during_logs_page_before_left_sections',
954 'ws_plugin__s2member_during_paypal_buttons_page_before_left_sections',
955 'ws_plugin__s2member_during_paypal_ops_page_before_left_sections_form',
956 'ws_plugin__s2member_during_res_ops_page_before_left_sections_form',
957 'ws_plugin__s2member_during_scripting_page_before_left_sections',
958 'ws_plugin__s2member_during_start_page_before_left_sections',
959 'ws_plugin__s2member_during_trk_ops_page_before_left_sections_form',
960 // Pro
961 'ws_plugin__s2member_during_authnet_forms_page_before_left_sections',
962 'ws_plugin__s2member_during_authnet_ops_page_before_left_sections_form',
963 'ws_plugin__s2member_during_clickbank_buttons_page_before_left_sections',
964 'ws_plugin__s2member_during_clickbank_ops_page_before_left_sections_form',
965 'ws_plugin__s2member_during_coupon_codes_page_before_left_sections_form',
966 'ws_plugin__s2member_during_import_export_page_before_left_sections',
967 'ws_plugin__s2member_during_mms_ops_page_before_left_sections_form',
968 'ws_plugin__s2member_during_other_gateways_page_before_left_sections_form',
969 'ws_plugin__s2member_during_paypal_forms_page_before_left_sections',
970 'ws_plugin__s2member_during_stripe_forms_page_before_left_sections',
971 'ws_plugin__s2member_during_stripe_ops_page_before_left_sections_form',
972 );
973
974 foreach ($hooks as $hook) {
975 add_action($hook, 'c_ws_plugin__s2member_menu_pages::get_help_panel_load', 1, 1);
976 }
977 }
978
979 /**
980 * Renders the Get Help panel (computes $notice/$draft and includes template).
981 *
982 * @package s2Member\Menu_Pages
983 * @since 250824
984 *
985 * @param array $vars Optional vars passed by the hook (unused).
986 */
987 public static function get_help_panel_load($vars = array()) {
988 if (!current_user_can('manage_options')) return;
989
990 // Build notice (if POST).
991 $notice = array('type' => '', 'text' => '');
992 if (!empty($_POST['s2m_help_nonce']) && isset($_POST['s2m_help_submit']) && wp_verify_nonce($_POST['s2m_help_nonce'], 's2member_get_help')) {
993 $result = c_ws_plugin__s2member_menu_pages::get_help_mail();
994 if (!empty($result['ok'])) {
995 $notice = array('type' => 'success', 'text' => __('Thanks — your message was sent. You’ll receive a copy by email.', 's2member'));
996 $_POST = array(); // clear fields after success so defaults repopulate
997 } else {
998 $msg = isset($result['msg']) ? $result['msg'] : __('Sorry, your message could not be sent. Please try again.', 's2member');
999 $notice = array('type' => 'error', 'text' => $msg);
1000 }
1001 }
1002
1003 // Build draft (preserve POST on error; else defaults from current user).
1004 $current = wp_get_current_user();
1005 $draft = array(
1006 'name' => is_object($current) ? trim($current->display_name) : '',
1007 'email' => is_object($current) ? trim($current->user_email) : '',
1008 'subject' => '',
1009 'details' => '',
1010 'meta' => home_url()."\n"
1011 .'PHP v'.PHP_VERSION
1012 .', WordPress v'.(isset($GLOBALS['wp_version']) ? $GLOBALS['wp_version'] : '')
1013 .', s2Member v'.(defined('WS_PLUGIN__S2MEMBER_VERSION') ? WS_PLUGIN__S2MEMBER_VERSION : '')
1014 .(defined('WS_PLUGIN__S2MEMBER_PRO_VERSION') ? ', Pro v'.WS_PLUGIN__S2MEMBER_PRO_VERSION : ''),
1015 );
1016
1017 if (isset($_POST['name'])) $draft['name'] = wp_strip_all_tags(trim(wp_unslash($_POST['name'])));
1018 if (isset($_POST['email'])) $draft['email'] = sanitize_email(trim(wp_unslash($_POST['email'])));
1019 if (isset($_POST['subject'])) $draft['subject'] = sanitize_text_field(trim(wp_unslash($_POST['subject'])));
1020 if (isset($_POST['details'])) $draft['details'] = trim(wp_unslash($_POST['details']));
1021 if (isset($_POST['meta'])) $draft['meta'] = trim(wp_unslash($_POST['meta']));
1022
1023 // Include template (variables $draft and $notice are in scope for the include).
1024 include_once dirname(dirname(__FILE__)).'/menu-pages/get-help-panel.inc.php';
1025 }
1026
1027 /**
1028 * Sends the Get Help email (simple contact form, Bcc user).
1029 *
1030 * @package s2Member\Menu_Pages
1031 * @since 250824
1032 *
1033 * @return array Array like ['ok' => bool, 'msg' => string].
1034 */
1035 public static function get_help_mail() {
1036 if (!current_user_can('manage_options')) {
1037 return array('ok' => false, 'msg' => __('You do not have permission to send support requests from this page.', 's2member'));
1038 }
1039
1040 $name = isset($_POST['name']) ? wp_strip_all_tags(trim(wp_unslash($_POST['name']))) : '';
1041 $email = isset($_POST['email']) ? sanitize_email(trim(wp_unslash($_POST['email']))) : '';
1042 $subject_in = isset($_POST['subject']) ? sanitize_text_field(trim(wp_unslash($_POST['subject']))) : '';
1043 $details = isset($_POST['details']) ? trim(wp_unslash($_POST['details'])) : '';
1044 $meta = isset($_POST['meta']) ? trim(wp_unslash($_POST['meta'])) : '';
1045
1046 $email = str_replace(array("\r", "\n"), '', $email); // header-injection safety
1047 if (empty($email) || !is_email($email)) {
1048 return array('ok' => false, 'msg' => __('Please provide a valid email address.', 's2member'));
1049 }
1050 if ($details === '') {
1051 return array('ok' => false, 'msg' => __('Please describe how I can help.', 's2member'));
1052 }
1053
1054 // Cap length and normalize body fields
1055 $max_len = 100000;
1056 if (strlen($details) > $max_len) $details = substr($details, 0, $max_len);
1057 if (strlen($meta) > $max_len) $meta = substr($meta, 0, $max_len);
1058
1059 $details = str_replace(array("\r\n", "\r"), "\n", $details);
1060 $meta = str_replace(array("\r\n", "\r"), "\n", $meta);
1061 $message = $details."\n\n".$meta;
1062
1063 // Subject with fallback/prefix
1064 $subject = (string) apply_filters('ws_plugin__s2member_get_help_subject', $subject_in);
1065 $subject = str_replace(array("\r\n", "\r", "\n"), ' ', trim($subject));
1066 $subject = ($subject === '') ? __('s2Member help request', 's2member') : 's2Member help: '.$subject;
1067
1068 // Sanitize display names
1069 $name_hdr = str_replace(array("\r\n", "\r", "\n"), ' ', (string) $name);
1070 $name_hdr = str_replace('"', "'", $name_hdr);
1071
1072 $sitename = c_ws_plugin__s2member_utils_urls::parse_url(home_url(), PHP_URL_HOST);
1073 $sitename = is_string($sitename) ? strtolower($sitename) : '';
1074 if (strpos($sitename, 'www.') === 0) $sitename = substr($sitename, 4);
1075
1076 $default_from_email = 'wordpress@'.$sitename;
1077 $default_from_name = ($name_hdr !== '' ? $name_hdr : get_bloginfo('name'));
1078
1079 // Honor WP and s2 filters, then cleanup
1080 $from_email = (string) apply_filters('wp_mail_from', $default_from_email);
1081 $from_name = (string) apply_filters('wp_mail_from_name', $default_from_name);
1082
1083 $from_email = (string) apply_filters('ws_plugin__s2member_get_help_from_email', $from_email);
1084 $from_name = (string) apply_filters('ws_plugin__s2member_get_help_from_name', $from_name);
1085
1086 $from_email = str_replace(array("\r", "\n"), '', $from_email);
1087 $from_name = str_replace(array("\r\n", "\r", "\n"), ' ', $from_name);
1088 $from_name = str_replace('"', "'", $from_name);
1089
1090 // Build headers
1091 $from_line = '"'.$from_name.'" <'.$from_email.'>';
1092 $user_line = ($name_hdr !== '' ? '"'.$name_hdr.'" ' : '').'<'.$email.'>';
1093
1094 $headers = array('Content-Type: text/plain; charset=UTF-8');
1095 $headers[] = 'From: '.$from_line;
1096 $headers[] = 'Reply-To: '.$user_line;
1097 $headers[] = 'Bcc: '.$user_line;
1098 $headers[] = 'X-s2Member-Help: 1';
1099
1100 $headers = apply_filters('ws_plugin__s2member_get_help_headers', $headers, compact('name','email','subject','details','meta'));
1101
1102 // Recipient
1103 $to = (string) apply_filters('ws_plugin__s2member_get_help_mailbox', '[email protected]');
1104 if (empty($to) || !is_email($to)) {
1105 return array('ok' => false, 'msg' => __('Support mailbox is not configured correctly.', 's2member'));
1106 }
1107
1108 $sent = wp_mail($to, $subject, $message, $headers);
1109
1110 if (!$sent) {
1111 if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
1112 error_log('s2Member Get Help: wp_mail() returned false. To='.$to.' | Subject='.$subject);
1113 }
1114 return array('ok' => false, 'msg' => __('Email could not be sent (mail server issue). Please try again or contact us directly.', 's2member'));
1115 }
1116 return array('ok' => true, 'msg' => __('Thanks — your message was sent. You’ll receive a copy by email.', 's2member'));
1117 }
1118 }
1119 }
1120