PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260917
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260917
260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 All 189 releases
← All changes | src/includes/classes/utils-assets.inc.php +179 -40 260913260917 View file →
@@ -1219,11 +1219,12 @@
1219 1219
1220 1220 /**
1221 1221 * Returns the page-level Okay/Fallback result for the delivery routes selected by WordPress.
1222 1222 *
1223 - * A normal configured route is Okay. WordPress Dynamic delivery is Fallback when it was selected
1224 - * only because requested static delivery or the selected s2member-o.php route could not
1225 - * be used. Browser activation is checked separately by the frontend activation monitor.
1223 + * A normal configured route is Okay. Compatibility-required Full WordPress Dynamic delivery is
1224 + * also Okay because it is the correct route for the current request/configuration. WordPress Dynamic
1225 + * is Fallback only when a requested route unexpectedly could not be used. Browser activation is
1226 + * checked separately by the frontend activation monitor.
1226 1227 *
1227 1228 * @package s2Member\Utilities
1228 1229 * @since 260910.0630
1229 1230 *
@@ -1238,9 +1239,14 @@
1238 1239 $type = (!empty($expectation['type'])) ? (string)$expectation['type'] : '';
1239 1240 if(!in_array($type, array('css', 'js'), TRUE))
1240 1241 continue;
1241 1242 if(!empty($expectation['delivery']) && $expectation['delivery'] === 'dynamic-wordpress' && (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_'.$type]) || $selected_s2o))
1243 + {
1244 + //260913.2001 Static delivery can be intentionally incompatible with current hooks/configuration; successful required Dynamic delivery is the correct route, not a degraded fallback.
1245 + if(!empty($expectation['dynamic_required']) && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_'.$type]))
1246 + continue;
1242 1247 return 'fallback';
1248 + }
1243 1249 }
1244 1250 return 'okay';
1245 1251 }
1246 1252
@@ -1287,9 +1293,9 @@
1287 1293 return array();
1288 1294 }
1289 1295
1290 1296 /**
1291 - * Returns Okay/Fallback/Failed for the site's currently configured delivery using trusted failure state.
1297 + * Returns Okay/Fallback/Failed for the site's currently required delivery using trusted failure state.
1292 1298 *
1293 1299 * @package s2Member\Utilities
1294 1300 * @since 260910.0630
1295 1301 *
@@ -1309,26 +1315,32 @@
1309 1315 {
1310 1316 $type_rating = 4;
1311 1317 if(!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_'.$type]))
1312 1318 {
1313 - $fallback = !empty($local_health['location']);
1314 - foreach(self::static_asset_ids($type, 'all') as $id)
1319 + $dynamic_requirement = self::static_type_dynamic_requirement($type);
1320 + if(!empty($dynamic_requirement['required']))
1321 + $type_rating = (!empty($failures['dynamic:dynamic_'.$type])) ? 1 : 4; //260913.2001 Compatibility-required Dynamic delivery is the intended route; only failure of that route degrades Health.
1322 + else
1315 1323 {
1316 - $state = self::static_asset_build($id);
1317 - $definition = self::static_asset_definition($id, FALSE);
1318 - $generation_failure = get_transient('ws_plugin__s2member_static_asset_failure_'.str_replace('.', '_', $id));
1319 - if(empty($definition['ok']) || ($generation_failure && $state <= 0) || isset($local_health[$id]))
1320 - $fallback = TRUE;
1321 - if($state > 0 && !empty($location['ok']))
1324 + $fallback = !empty($local_health['location']);
1325 + foreach(self::static_asset_ids($type, 'all') as $id)
1322 1326 {
1323 - $base = substr($id, 0, -strlen('.'.$type));
1324 - $url = $location['url'].'/'.$base.'-'.$state.'.'.$type;
1325 - if(!empty($failures['static:'.$id]) && !empty($failures['static:'.$id]['url']) && (string)$failures['static:'.$id]['url'] === $url)
1327 + $state = self::static_asset_build($id);
1328 + $definition = self::static_asset_definition($id, FALSE);
1329 + $generation_failure = get_transient('ws_plugin__s2member_static_asset_failure_'.str_replace('.', '_', $id));
1330 + if(empty($definition['ok']) || ($generation_failure && $state <= 0) || isset($local_health[$id]))
1326 1331 $fallback = TRUE;
1332 + if($state > 0 && !empty($location['ok']))
1333 + {
1334 + $base = substr($id, 0, -strlen('.'.$type));
1335 + $url = $location['url'].'/'.$base.'-'.$state.'.'.$type;
1336 + if(!empty($failures['static:'.$id]) && !empty($failures['static:'.$id]['url']) && (string)$failures['static:'.$id]['url'] === $url)
1337 + $fallback = TRUE;
1338 + }
1327 1339 }
1340 + if($fallback)
1341 + $type_rating = (!empty($failures['fallback:dynamic_'.$type])) ? 1 : 2;
1328 1342 }
1329 - if($fallback)
1330 - $type_rating = (!empty($failures['fallback:dynamic_'.$type])) ? 1 : 2;
1331 1343 }
1332 1344 else if($selected_s2o)
1333 1345 {
1334 1346 $s2o_problem = !is_file(self::s2o_file_path()) || (!empty($failures['s2o']));
@@ -1395,8 +1407,12 @@
1395 1407 $location = self::static_assets_location(FALSE);
1396 1408 if(!empty($location['ok']))
1397 1409 foreach(array('css' => 'static_css', 'js' => 'static_js') as $type => $option)
1398 1410 if(!empty($GLOBALS['WS_PLUGIN__']['s2member']['o'][$option]))
1411 + {
1412 + $dynamic_requirement = self::static_type_dynamic_requirement($type);
1413 + if(!empty($dynamic_requirement['required']))
1414 + continue; //260913.2001 Intentionally inactive static files are not trusted-probe targets while compatibility requires Dynamic delivery.
1399 1415 foreach(self::static_asset_ids($type, 'all') as $id)
1400 1416 {
1401 1417 $build = self::static_asset_build($id);
1402 1418 if($build <= 0)
@@ -1424,8 +1440,9 @@
1424 1440 }
1425 1441 $targets['static:'.$id] = $target;
1426 1442 }
1427 1443 }
1444 + }
1428 1445
1429 1446 if($full)
1430 1447 {
1431 1448 //260910.0818 A full trusted check includes the actual dynamic response and its available WordPress fallback so Fallback can be distinguished from Failed instead of assuming a fallback works.
@@ -1447,11 +1464,13 @@
1447 1464 );
1448 1465
1449 1466 if(!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_'.$type]))
1450 1467 {
1451 - $target_id = 'fallback:'.$health_id;
1468 + $dynamic_requirement = self::static_type_dynamic_requirement($type);
1469 + //260913.2001 Probe compatibility-required Full WordPress Dynamic as the active route; genuine static failures keep the existing fallback probe identity.
1470 + $target_id = (!empty($dynamic_requirement['required'])) ? 'active:'.$health_id : 'fallback:'.$health_id;
1452 1471 $wordpress_target['id'] = $target_id;
1453 - $wordpress_target['failure_id'] = $target_id;
1472 + $wordpress_target['failure_id'] = (!empty($dynamic_requirement['required'])) ? 'dynamic:'.$health_id : $target_id;
1454 1473 $wordpress_target['failure_url'] = $wordpress_url;
1455 1474 $targets[$target_id] = $wordpress_target;
1456 1475 }
1457 1476 else if($selected_s2o)
@@ -1696,11 +1715,12 @@
1696 1715 * @param string $url Public URL emitted on this page.
1697 1716 * @param string $delivery `static`, `dynamic-s2member-o`, or `dynamic-wordpress`.
1698 1717 * @param int $build Static build timestamp, or zero for dynamic delivery.
1699 1718 * @param string $issue_detail Optional reason a preferred route fell back before this response was selected.
1719 + * @param bool $dynamic_required Whether Full WordPress Dynamic delivery is intentionally required for compatibility.
1700 1720 * @return null
1701 1721 */
1702 - public static function register_page_asset_expectations($asset_id = '', $type = '', $url = '', $delivery = '', $build = 0, $issue_detail = '')
1722 + public static function register_page_asset_expectations($asset_id = '', $type = '', $url = '', $delivery = '', $build = 0, $issue_detail = '', $dynamic_required = FALSE)
1703 1723 {
1704 1724 $type = strtolower((string)$type);
1705 1725 $url = (string)$url;
1706 1726 if(!in_array($type, array('css', 'js'), TRUE) || !$url || !in_array($delivery, array('static', 'dynamic-s2member-o', 'dynamic-wordpress'), TRUE))
@@ -1721,8 +1741,10 @@
1721 1741 'tag_value' => $tag_value,
1722 1742 'activation_tag' => self::activation_tag_snippet($id, $type, $tag_value),
1723 1743 //260911.1806 Server-side fallback context is not sent to the browser; it only supplies a useful Last issue snapshot for the page that selected fallback.
1724 1744 'issue_detail' => substr(wp_strip_all_tags((string)$issue_detail), 0, 240),
1745 + //260913.2001 Server-only compatibility context keeps intentional Full WordPress Dynamic delivery Healthy without changing the compact browser expectation/signature.
1746 + 'dynamic_required' => (bool)$dynamic_required,
1725 1747 );
1726 1748 $expectation['signature'] = self::asset_runtime_expectation_signature($expectation);
1727 1749 self::$page_asset_expectations[$id] = $expectation;
1728 1750 return;
@@ -2431,9 +2453,9 @@
2431 2453
2432 2454 //260903.0544 Normal requests only check whether current hooks/configuration permit static delivery; source assembly and filesystem work wait until a build is actually needed.
2433 2455 $compatibility = self::static_asset_definition($id, FALSE);
2434 2456 if(empty($compatibility['ok']))
2435 - return self::$static_asset_cache[$id] = array('ok' => FALSE, 'url' => '', 'build' => abs(self::static_asset_build($id)), 'error' => (string)$compatibility['error']);
2457 + return self::$static_asset_cache[$id] = array('ok' => FALSE, 'url' => '', 'build' => abs(self::static_asset_build($id)), 'error' => (string)$compatibility['error'], 'dynamic_required' => !empty($compatibility['dynamic_required']));
2436 2458
2437 2459 $state = self::static_asset_build($id);
2438 2460 $dirty = $state < 0;
2439 2461 $active_build = abs($state);
@@ -2567,9 +2589,9 @@
2567 2589 foreach(self::static_asset_ids($type, 'all') as $id)
2568 2590 {
2569 2591 $assets[$id] = self::ensure_static_asset($id, $force);
2570 2592 if(empty($assets[$id]['ok']))
2571 - return array('ok' => FALSE, 'assets' => $assets, 'error' => (string)$assets[$id]['error']);
2593 + return array('ok' => FALSE, 'assets' => $assets, 'error' => (string)$assets[$id]['error'], 'dynamic_required' => !empty($assets[$id]['dynamic_required']));
2572 2594 }
2573 2595 return array('ok' => (bool)$assets, 'assets' => $assets, 'error' => '');
2574 2596 }
2575 2597
@@ -2657,21 +2679,28 @@
2657 2679 if(empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_css']) && empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_js']))
2658 2680 return self::$static_assets_health_cache = array();
2659 2681
2660 2682 $missing = array();
2683 + $checked_types = array();
2684 + foreach(array('css' => 'static_css', 'js' => 'static_js') as $type => $option)
2685 + if(!empty($GLOBALS['WS_PLUGIN__']['s2member']['o'][$option]))
2686 + {
2687 + $dynamic_requirement = self::static_type_dynamic_requirement($type);
2688 + if(empty($dynamic_requirement['required']))
2689 + $checked_types[] = $type; //260913.2001 Only currently active static routes participate in local static-file health.
2690 + }
2661 2691 $location = self::static_assets_location(FALSE);
2662 - if(empty($location['ok']))
2692 + if($checked_types && empty($location['ok']))
2663 2693 $missing['location'] = $location['error'];
2664 - else
2665 - foreach(array('css' => 'static_css', 'js' => 'static_js') as $type => $option)
2666 - if(!empty($GLOBALS['WS_PLUGIN__']['s2member']['o'][$option]))
2667 - foreach(self::static_asset_ids($type, 'all') as $id)
2668 - {
2669 - $build = self::static_asset_build($id);
2670 - $base = substr($id, 0, -strlen('.'.$type));
2671 - if($build > 0 && !is_file($location['dir'].'/'.$base.'-'.$build.'.'.$type))
2672 - $missing[$id] = 'Expected static asset '.$id.' is missing.';
2673 - }
2694 + else if(!empty($location['ok']))
2695 + foreach($checked_types as $type)
2696 + foreach(self::static_asset_ids($type, 'all') as $id)
2697 + {
2698 + $build = self::static_asset_build($id);
2699 + $base = substr($id, 0, -strlen('.'.$type));
2700 + if($build > 0 && !is_file($location['dir'].'/'.$base.'-'.$build.'.'.$type))
2701 + $missing[$id] = 'Expected static asset '.$id.' is missing.';
2702 + }
2674 2703
2675 2704 //260907.2203 Log only local-health transitions so recurring admin checks do not repeat the same event.
2676 2705 $previous = get_option('ws_plugin__s2member_static_asset_health', array());
2677 2706 $previous = is_array($previous) ? $previous : array();
@@ -2756,8 +2785,9 @@
2756 2785 $selected_s2o = empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['dynamic_asset_loader']) || $GLOBALS['WS_PLUGIN__']['s2member']['o']['dynamic_asset_loader'] !== 'wordpress';
2757 2786 $dynamic_normal = array('css' => FALSE, 'js' => FALSE);
2758 2787 $wp_loader_active = array('css' => FALSE, 'js' => FALSE);
2759 2788 $wp_loader_fallback = array('css' => FALSE, 'js' => FALSE);
2789 + $wp_loader_required = array('css' => FALSE, 'js' => FALSE);
2760 2790
2761 2791 foreach(array('css' => 'CSS', 'js' => 'JS') as $type => $type_label)
2762 2792 {
2763 2793 $static_requested = !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['static_'.$type]);
@@ -2792,8 +2822,31 @@
2792 2822 continue;
2793 2823 }
2794 2824
2795 2825 $ids = self::static_asset_ids($type, 'all');
2826 + $dynamic_requirement = self::static_type_dynamic_requirement($type);
2827 + if(!empty($dynamic_requirement['required']))
2828 + {
2829 + //260913.2001 A compatibility-required Dynamic route is expected delivery, not an Orange fallback; Full WordPress remains mandatory so the triggering hooks/configuration are present.
2830 + $wp_loader_active[$type] = TRUE;
2831 + $wp_loader_fallback[$type] = FALSE;
2832 + $wp_loader_required[$type] = TRUE;
2833 + $delivery_url = self::wordpress_dynamic_asset_url();
2834 + $delivery_url = ($type === 'css') ? add_query_arg(array('ws_plugin__s2member_css' => '1', 'qcABC' => '1'), $delivery_url) : add_query_arg(array('ws_plugin__s2member_js_w_globals' => (defined('WS_PLUGIN__S2MEMBER_API_CONSTANTS_MD5') ? WS_PLUGIN__S2MEMBER_API_CONSTANTS_MD5 : '1'), 'qcABC' => '1'), $delivery_url);
2835 + $failed = !empty($failures['dynamic:dynamic_'.$type]);
2836 + $status = ($failed) ? 'error' : 'healthy';
2837 + $status_label = ($failed) ? 'Delivery check failed' : 'Healthy';
2838 + //260913.2111 The requirement detail now carries concise, actionable guidance; do not append a generic compatibility sentence to every row.
2839 + $detail = (string)$dynamic_requirement['detail'];
2840 + if($failed)
2841 + {
2842 + $detail .= ' The required Full WordPress Dynamic Loader could not be loaded or confirmed active.';
2843 + $error_notice_items['delivery:'.$type] = $type_label.' requires Full WordPress Dynamic delivery, but that route could not be loaded or verified.';
2844 + }
2845 + $rows[] = array('label' => $type_label.' Delivery', 'delivery' => 'Dynamic required', 'status' => $status, 'status_label' => $status_label, 'detail' => $detail, 'url' => $delivery_url);
2846 + continue;
2847 + }
2848 +
2796 2849 $fallback = FALSE;
2797 2850 $fallback_reasons = array();
2798 2851 $states = array();
2799 2852 $generation_failures = array();
@@ -2952,9 +3005,12 @@
2952 3005 {
2953 3006 $event = self::asset_runtime_health_event('dynamic_'.$type);
2954 3007 $status = $event['status'];
2955 3008 $status_label = $event['label'];
2956 - $detail = ($is_fallback) ? 'Full WordPress Dynamic Loader is currently serving this asset as fallback.' : 'Full WordPress Dynamic Loader is the configured delivery route.';
3009 + if(!empty($wp_loader_required[$type]))
3010 + $detail = 'Full WordPress Dynamic Loader is the active compatibility-required route.';
3011 + else
3012 + $detail = ($is_fallback) ? 'Full WordPress Dynamic Loader is currently serving this asset as fallback.' : 'Full WordPress Dynamic Loader is the configured delivery route.';
2957 3013 if($event['detail'])
2958 3014 $detail .= ' '.$event['detail'];
2959 3015 }
2960 3016 else
@@ -3514,8 +3570,43 @@
3514 3570 wp_send_json_success(array('recorded' => self::record_asset_runtime_suspicions($missing, $load)));
3515 3571 }
3516 3572
3517 3573 /**
3574 + * Returns whether an enabled static asset type intentionally requires dynamic delivery for compatibility.
3575 + *
3576 + * A source/build/filesystem failure is not an intentional requirement and remains a real fallback condition.
3577 + *
3578 + * @package s2Member\Utilities
3579 + * @since 260913.2001
3580 + *
3581 + * @param string $type `css` or `js`.
3582 + * @return array Requirement state and site-owner detail.
3583 + */
3584 + protected static function static_type_dynamic_requirement($type = '')
3585 + {
3586 + $type = strtolower((string)$type);
3587 + if(!in_array($type, array('css', 'js'), TRUE))
3588 + return array('required' => FALSE, 'detail' => '');
3589 +
3590 + $required = FALSE;
3591 + $details = array();
3592 + foreach(self::static_asset_ids($type, 'all') as $id)
3593 + {
3594 + $definition = self::static_asset_definition($id, FALSE);
3595 + if(!empty($definition['ok']))
3596 + continue;
3597 + if(empty($definition['dynamic_required']))
3598 + return array('required' => FALSE, 'detail' => '');
3599 + $required = TRUE;
3600 + if(!empty($definition['health_detail']))
3601 + $details[] = (string)$definition['health_detail'];
3602 + else if(!empty($definition['error']))
3603 + $details[] = (string)$definition['error'];
3604 + }
3605 + return array('required' => $required, 'detail' => implode(' ', array_unique($details)));
3606 + }
3607 +
3608 + /**
3518 3609 * Returns the source definition for one currently enabled/compatible generated frontend asset file.
3519 3610 *
3520 3611 * @package s2Member\Utilities
3521 3612 * @since 260903.0525
@@ -3547,9 +3638,20 @@
3547 3638 $hook_dynamic = has_action('ws_plugin__s2member_during_css');
3548 3639 $hook_dynamic = (bool)apply_filters('ws_plugin__s2member_dynamic_css_required', $hook_dynamic, get_defined_vars());
3549 3640
3550 3641 if($framework_dynamic || $hook_dynamic)
3551 - return array('ok' => FALSE, 'sources' => array(), 'minify' => FALSE, 'error' => 'Current CSS hooks require legacy dynamic assets');
3642 + {
3643 + //260913.2001 Explain which compatibility condition makes Dynamic delivery intentional instead of collapsing all such cases into a generic fallback error.
3644 + $reasons = array();
3645 + if(has_action('ws_plugin__s2member_before_css'))
3646 + $reasons[] = 'the "ws_plugin__s2member_before_css" hook has a customization';
3647 + if(isset($GLOBALS['wp_filter']['all']))
3648 + $reasons[] = 'WordPress\'s global "all" hook is active';
3649 + if($hook_dynamic)
3650 + $reasons[] = (has_action('ws_plugin__s2member_during_css')) ? 'the "ws_plugin__s2member_during_css" hook has a customization that must remain dynamic' : 'the "ws_plugin__s2member_dynamic_css_required" filter requires dynamic CSS';
3651 + $error = 'Static CSS cannot be used with the current request/configuration because '.implode('; ', array_unique($reasons)).'. Full WordPress Dynamic Loader is required so the current hooks and customizations remain available.';
3652 + return array('ok' => FALSE, 'sources' => array(), 'minify' => FALSE, 'error' => $error, 'dynamic_required' => TRUE);
3653 + }
3552 3654 if(!$include_sources)
3553 3655 return array('ok' => TRUE, 'sources' => array(), 'minify' => !empty($o['static_css_minify']), 'error' => '');
3554 3656
3555 3657 $sources = ($pro_file) ? array() : array(array('file' => $c['dir'].'/src/includes/s2member.css', 'preserve_header' => TRUE));
@@ -3571,13 +3673,13 @@
3571 3673 $page_text = self::static_js_text_delivery() === 'page';
3572 3674 if($page_text && !self::static_js_page_text_supported())
3573 3675 return array('ok' => FALSE, 'sources' => array(), 'minify' => FALSE, 'error' => 'Loading JavaScript text with each WordPress page requires a current s2Member Pro version');
3574 3676
3677 + $js_api_constants_enabled = (bool)apply_filters('ws_plugin__s2member_js_api_constants_enable', FALSE);
3575 3678 if($page_text)
3576 3679 {
3577 3680 //260906.2049 Text and other page-specific values resolve in the normal HTML request, so they do not make the external JavaScript dynamic.
3578 - $framework_dynamic = apply_filters('ws_plugin__s2member_js_api_constants_enable', FALSE)
3579 - || has_action('ws_plugin__s2member_before_js_w_globals') || isset($GLOBALS['wp_filter']['all']);
3681 + $framework_dynamic = $js_api_constants_enabled || has_action('ws_plugin__s2member_before_js_w_globals') || isset($GLOBALS['wp_filter']['all']);
3580 3682 }
3581 3683 else
3582 3684 {
3583 3685 $site_locale = (string)get_option('WPLANG');
@@ -3584,10 +3686,9 @@
3584 3686 if(!$site_locale && defined('WPLANG'))
3585 3687 $site_locale = (string)WPLANG;
3586 3688 $site_locale = ($site_locale) ? $site_locale : 'en_US';
3587 3689 $current_locale = (function_exists('determine_locale')) ? (string)determine_locale() : (string)get_locale();
3588 - $framework_dynamic = apply_filters('ws_plugin__s2member_js_api_constants_enable', FALSE)
3589 - || has_action('ws_plugin__s2member_before_js_w_globals') || $current_locale !== $site_locale || has_filter('ws_plugin__s2member_files_dir')
3690 + $framework_dynamic = $js_api_constants_enabled || has_action('ws_plugin__s2member_before_js_w_globals') || $current_locale !== $site_locale || has_filter('ws_plugin__s2member_files_dir')
3590 3691 || has_filter('ws_plugin__s2member_min_password_length') || has_filter('ws_plugin__s2member_min_password_strength_code') || has_filter('ws_plugin__s2member_min_password_strength_score')
3591 3692 || isset($GLOBALS['wp_filter']['all']);
3592 3693 }
3593 3694
@@ -3596,9 +3697,47 @@
3596 3697 if($hook_dynamic && !$page_text && self::static_js_builtin_pro_callbacks_supported())
3597 3698 $hook_dynamic = FALSE; //260906.2049 Older Pro releases can still use static-file text when their JavaScript hook contains only known built-ins.
3598 3699
3599 3700 if($framework_dynamic || $hook_dynamic)
3600 - return array('ok' => FALSE, 'sources' => array(), 'minify' => FALSE, 'error' => 'Current JavaScript hooks/configuration require dynamic assets');
3701 + {
3702 + //260913.2111 Separate page-specific values that JavaScript Text Delivery can move into the page from requirements that still need Full WordPress Dynamic JS.
3703 + $text_delivery_reasons = array();
3704 + $other_reasons = array();
3705 + if($js_api_constants_enabled)
3706 + $other_reasons[] = 'full s2Member JavaScript API constants are enabled by the "ws_plugin__s2member_js_api_constants_enable" filter';
3707 + if(has_action('ws_plugin__s2member_before_js_w_globals'))
3708 + $other_reasons[] = 'the "ws_plugin__s2member_before_js_w_globals" hook has a customization';
3709 + if(!$page_text && isset($current_locale, $site_locale) && $current_locale !== $site_locale)
3710 + $text_delivery_reasons[] = 'the current page language differs from the site default ('.$current_locale.' vs '.$site_locale.')';
3711 + foreach(array(
3712 + 'ws_plugin__s2member_files_dir' => 'the s2Member files directory is generated dynamically',
3713 + 'ws_plugin__s2member_min_password_length' => 'the minimum password length is generated dynamically',
3714 + 'ws_plugin__s2member_min_password_strength_code' => 'the password-strength code is generated dynamically',
3715 + 'ws_plugin__s2member_min_password_strength_score' => 'the password-strength score is generated dynamically',
3716 + ) as $filter => $reason)
3717 + if(!$page_text && has_filter($filter))
3718 + $text_delivery_reasons[] = $reason.' (via hook '.$filter.')';
3719 + if(isset($GLOBALS['wp_filter']['all']))
3720 + $other_reasons[] = 'WordPress\'s global "all" hook is active';
3721 + if(has_filter('ws_plugin__s2member_pro_available_gateways'))
3722 + $other_reasons[] = 'the available Pro gateways are filtered dynamically by "ws_plugin__s2member_pro_available_gateways"';
3723 + if($hook_dynamic && has_action('ws_plugin__s2member_during_js_w_globals') && !self::static_js_builtin_pro_callbacks_supported())
3724 + $other_reasons[] = 'the "ws_plugin__s2member_during_js_w_globals" hook contains a custom, reordered, or unsupported callback';
3725 + if($hook_dynamic && !$text_delivery_reasons && !$other_reasons)
3726 + $other_reasons[] = 'the "ws_plugin__s2member_dynamic_js_required" filter explicitly requires Dynamic JS';
3727 +
3728 + $text_delivery_reasons = array_unique($text_delivery_reasons);
3729 + $other_reasons = array_unique($other_reasons);
3730 + $reasons = array_merge($text_delivery_reasons, $other_reasons);
3731 + $error = 'Static JavaScript requires Dynamic delivery because '.implode('; ', $reasons).'. Full WordPress Dynamic Loader is required so the current hooks, values, and customizations remain available.';
3732 + if($text_delivery_reasons && !$other_reasons)
3733 + $health_detail = ucfirst(implode('; ', $text_delivery_reasons)).'. To keep the external JavaScript static, set <a href="#ws-plugin--s2member-static-js-text-setting">JavaScript Text Delivery</a> to "Load JavaScript text with each WordPress page".';
3734 + else if($text_delivery_reasons && $other_reasons)
3735 + $health_detail = 'Some page-specific JavaScript values require Dynamic JS: '.implode('; ', $text_delivery_reasons).'. Setting <a href="#ws-plugin--s2member-static-js-text-setting">JavaScript Text Delivery</a> to "Load JavaScript text with each WordPress page" lets pages affected only by those values keep using Static JS. Pages where another detected requirement applies will still use Full WordPress Dynamic JS: '.implode('; ', $other_reasons).'.';
3736 + else
3737 + $health_detail = 'Static JavaScript requires Dynamic delivery because '.implode('; ', $other_reasons).'. Full WordPress Dynamic Loader is used so the required hooks and customizations remain available.';
3738 + return array('ok' => FALSE, 'sources' => array(), 'minify' => FALSE, 'error' => $error, 'health_detail' => $health_detail, 'dynamic_required' => TRUE);
3739 + }
3601 3740 if($page_text)
3602 3741 {
3603 3742 $data_map_signature = self::static_js_data_map_signature($id);
3604 3743 if(empty($data_map_signature['ok']))