| @@ -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'])) |