persona section overhaul — editorial gap-px grid + fresh photography
SECTION REDESIGN: - Killed standalone dashboard image (fake AI laptop, added nothing) - New gap-px grid (signature pattern 2) with border-l-2 accents (pattern 1) - Numbered anchors (01-04) as visual rhythm per brand guide - Wider container: max-w-7xl matches hero width PERSONA CHANGES: - Renamed 'Organisation' -> 'Programme Manager' - Reorder: Charity Manager, Programme Manager, Personal Fundraiser, Volunteer - Updated /for/organisations page content to match PHOTOGRAPHY (4 new images via gemini-3-pro-image-preview): - persona-charity-manager.jpg — hijabi woman at mosque office desk - persona-programme-manager.jpg — man at desk with campaign calendar - persona-fundraiser.jpg — woman on London park bench with phone - persona-volunteer.jpg — young man handing card at charity gala - All optimized: 2.7MB -> 342KB (87% reduction via sharp) - Consistent documentary candid style, 3:2 landscape, warm tones FOOTER: - 'Organisations' -> 'Programme Managers' in nav links
This commit is contained in:
727
screenshots/functions.php.bak
Normal file
727
screenshots/functions.php.bak
Normal file
@@ -0,0 +1,727 @@
|
||||
<?php
|
||||
|
||||
use Manza\CharityRight\Etc;
|
||||
use Manza\CharityRight\Mappers\AppealMapper;
|
||||
use Manza\CharityRight\Theme;
|
||||
|
||||
require_once get_stylesheet_directory() . "/vendor/autoload.php";
|
||||
|
||||
// List of files which will be included.
|
||||
// You can append ! then a comma limited list of class names which must exist to that file.
|
||||
$vlTailwind_includes = array(
|
||||
'/setup.php', // Some theme setup functions
|
||||
'/template-tags.php', // Custom template tags
|
||||
'/pagination.php', // Pagination
|
||||
'/sidebars.php', // Sidebars
|
||||
'/scripts-and-styles.php', // enqueue scripts and styles
|
||||
'/page-titles.php', // page title function
|
||||
'/breadcrumbs.php', // breadcrumbs trail function
|
||||
'/acf.php', // ACF settings
|
||||
'/menus.php', // MENU inits
|
||||
'/ajax.php', // Custom AJAX returning.
|
||||
'/blocks.php', // The custom block registry.
|
||||
'/cpt.php', // Custom post type declarations
|
||||
'/blog-import.php', // Blog post importer. NOTE: Remove this once we're deployed live and happy.
|
||||
'/dashboard.php', // Configure the WP dashboard.
|
||||
'/images.php', // Configure media library images.
|
||||
"/ajax-campaigns.php", // custom AJAX campaign loader
|
||||
"/ajax-blogs.php", // custom AJAX blog loader
|
||||
'/ramadan-challenge.php', // receiving ramadan challenge appeals.
|
||||
'/team365.php', // receiving team365 appeals.
|
||||
);
|
||||
|
||||
foreach ( $vlTailwind_includes as $file ) {
|
||||
$filepath = locate_template( 'includes' . $file );
|
||||
if ( ! $filepath ) {
|
||||
trigger_error( "Error locating /includes{$file} for inclusion", E_USER_ERROR );
|
||||
}
|
||||
require_once $filepath;
|
||||
}
|
||||
|
||||
function buildHeaderTree( array &$elements, $parentId = 0 )
|
||||
{
|
||||
$branch = array();
|
||||
foreach ( $elements as &$element )
|
||||
{
|
||||
if ( $element->menu_item_parent == $parentId )
|
||||
{
|
||||
$children = buildHeaderTree( $elements, $element->ID );
|
||||
if ( $children )
|
||||
$element->wpse_children = $children;
|
||||
|
||||
$branch[$element->ID] = $element;
|
||||
unset( $element );
|
||||
}
|
||||
}
|
||||
return $branch;
|
||||
}
|
||||
|
||||
function searchSocialMedia($name, $array) {
|
||||
foreach ($array as $key => $val) {
|
||||
if ($val['name'] === $name) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
add_image_size( 'cr_card_thumb', 600, 350 , true);
|
||||
add_image_size( 'cr_large_blog_thumb', 600, 480 , true);
|
||||
|
||||
// custom excerpt length
|
||||
function wpdocs_custom_excerpt_length( $length ) {
|
||||
return 30;
|
||||
}
|
||||
add_filter( 'excerpt_length', 'wpdocs_custom_excerpt_length', 999 );
|
||||
|
||||
function new_excerpt_more( $more ) {
|
||||
return '...';
|
||||
}
|
||||
add_filter('excerpt_more', 'new_excerpt_more');
|
||||
|
||||
(new Theme)->run();
|
||||
|
||||
// Force asset URLs to use www to avoid cross-origin script/font blocking.
|
||||
add_filter( 'script_loader_src', function( $src ) {
|
||||
return str_replace( 'https://charityright.org.uk', 'https://www.charityright.org.uk', $src );
|
||||
} );
|
||||
add_filter( 'style_loader_src', function( $src ) {
|
||||
return str_replace( 'https://charityright.org.uk', 'https://www.charityright.org.uk', $src );
|
||||
} );
|
||||
|
||||
// Redirect function from author pages
|
||||
add_action('template_redirect', 'my_custom_disable_author_page');
|
||||
|
||||
function my_custom_disable_author_page() {
|
||||
global $wp_query;
|
||||
|
||||
if ( is_author() ) {
|
||||
// Redirect to homepage, set status to 301 permenant redirect.
|
||||
// Function defaults to 302 temporary redirect.
|
||||
wp_redirect(get_option('home'), 301);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function itsme_disable_feed() {
|
||||
wp_die( __( 'No feed available, please visit the <a href="'. esc_url( home_url( '/' ) ) .'">homepage</a>!' ) );
|
||||
}
|
||||
|
||||
function disable_attachment_pages() {
|
||||
if (is_attachment()) {
|
||||
// Get the parent post ID
|
||||
$parent_post_id = wp_get_post_parent_id(get_the_ID());
|
||||
|
||||
// Redirect to parent post if available, or homepage if not
|
||||
if ($parent_post_id) {
|
||||
wp_redirect(get_permalink($parent_post_id), 301);
|
||||
} else {
|
||||
wp_redirect(home_url(), 301);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
add_action('template_redirect', 'disable_attachment_pages');
|
||||
|
||||
add_action('do_feed', 'itsme_disable_feed', 1);
|
||||
add_action('do_feed_rdf', 'itsme_disable_feed', 1);
|
||||
add_action('do_feed_rss', 'itsme_disable_feed', 1);
|
||||
add_action('do_feed_rss2', 'itsme_disable_feed', 1);
|
||||
add_action('do_feed_atom', 'itsme_disable_feed', 1);
|
||||
add_action('do_feed_rss2_comments', 'itsme_disable_feed', 1);
|
||||
add_action('do_feed_atom_comments', 'itsme_disable_feed', 1);
|
||||
|
||||
add_action('init', function() {
|
||||
// Add rewrite rule for campaign with query parameter
|
||||
add_rewrite_rule(
|
||||
'campaigns/([^/]+)/?$',
|
||||
'index.php?post_type=campaign&name=$matches[1]',
|
||||
'top'
|
||||
);
|
||||
|
||||
// Register your query vars
|
||||
add_filter('query_vars', function($vars) {
|
||||
$vars[] = 'p'; // Using 'p' instead of 'page'
|
||||
return $vars;
|
||||
});
|
||||
});
|
||||
|
||||
// Prevent canonical redirect
|
||||
add_filter('redirect_canonical', function($redirect_url, $requested_url) {
|
||||
if (is_singular('campaign')) {
|
||||
return false;
|
||||
}
|
||||
return $redirect_url;
|
||||
}, 10, 2);
|
||||
|
||||
// donations
|
||||
add_action('rest_api_init', function () {
|
||||
register_rest_route('charityright/v1', '/donations', [
|
||||
'methods' => 'GET',
|
||||
'callback' => 'get_external_donations',
|
||||
'permission_callback' => '__return_true', // Adjust permissions if necessary
|
||||
]);
|
||||
});
|
||||
|
||||
if (!function_exists('wp_money_format')) {
|
||||
function wp_money_format($amount, $currency_symbol = '£', $decimal_places = 2, $thousands_separator = ',', $decimal_separator = '.') {
|
||||
// Ensure the amount is a float
|
||||
$amount = floatval($amount);
|
||||
|
||||
// Format the amount
|
||||
$formatted_amount = number_format($amount, $decimal_places, $decimal_separator, $thousands_separator);
|
||||
|
||||
// Return with the currency symbol
|
||||
return $currency_symbol . $formatted_amount;
|
||||
}
|
||||
}
|
||||
|
||||
function get_external_donations($request) {
|
||||
// Define the appeal ID if necessary, otherwise, pass it from the request
|
||||
$appeal_id = $request->get_param('appeal_id');
|
||||
$page = $request->get_param('page') ?? 1;
|
||||
$per_page = 5; // Define how many donations you want per page
|
||||
|
||||
// External API URL with the appeal_id
|
||||
$url = trim(str_replace("donate", "", get_field("generic_donate_url", "options")), "/") . "/api/v1/appeal-donations/{$appeal_id}?page={$page}&per_page={$per_page}";
|
||||
|
||||
// Fetch donations from the external API
|
||||
$response = wp_remote_get($url);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return new WP_REST_Response([
|
||||
'message' => 'Failed to fetch donations from external API',
|
||||
'error' => $response->get_error_message()
|
||||
], 500);
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (!$data || !isset($data['data']) || !is_array($data['data'])) {
|
||||
return new WP_REST_Response([
|
||||
'message' => 'No donations found or invalid data format'
|
||||
], 404);
|
||||
}
|
||||
|
||||
|
||||
$formattedDonations = array_map(function($donation) {
|
||||
return [
|
||||
'id' => $donation['id'],
|
||||
'donor_name' => $donation['is_anonymous'] ? 'Anonymous' : $donation['donor_name'],
|
||||
'initials' => strtoupper(substr($donation['donor_name'], 0, 1)), // Extract initials
|
||||
'donated_at' => $donation['created_at'], // Pass the raw date, format on frontend
|
||||
'amount' => $donation['amount'],
|
||||
'donor_message' => $donation['donor_message'] ?? ''
|
||||
];
|
||||
}, $data['data']);
|
||||
|
||||
// Return formatted donations and pagination info
|
||||
return new WP_REST_Response([
|
||||
'donations' => $formattedDonations,
|
||||
'pagination' => [
|
||||
'current_page' => $data['current_page'],
|
||||
'last_page' => $data['last_page'],
|
||||
'total' => $data['total']
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
add_action('rest_api_init', function () {
|
||||
register_rest_route('custom/v1', '/delete-post', array(
|
||||
'methods' => 'DELETE',
|
||||
'callback' => 'handle_delete_post',
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => function($param) {
|
||||
return is_string($param); // Ensure slug is a string
|
||||
}
|
||||
),
|
||||
'api_key' => array(
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
register_rest_route('custom/v1', '/update-campaign-preview', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => 'handle_update_campaign_preview',
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'slug' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => function($param) {
|
||||
return is_string($param); // Ensure slug is a string
|
||||
}
|
||||
),
|
||||
'api_key' => array(
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
register_rest_route('custom/v1', '/update-strava-leaderboard', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => 'handle_strava_leaderboard',
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'api_key' => array(
|
||||
'required' => true
|
||||
),
|
||||
'list' => array(
|
||||
'required' => false, // Set to true if `list` is mandatory
|
||||
'validate_callback' => function ($param, $request, $key) {
|
||||
return is_array($param); // Ensure `list` is an array
|
||||
}
|
||||
)
|
||||
)
|
||||
));
|
||||
});
|
||||
|
||||
function handle_delete_post($request) {
|
||||
// Validate API key (you should store this securely)
|
||||
$valid_api_key = 'AKDJSHF8932YRJDFKSAFH120';
|
||||
|
||||
if ($request['api_key'] !== $valid_api_key) {
|
||||
return new WP_Error(
|
||||
'invalid_api_key',
|
||||
'Invalid API key provided.',
|
||||
array('status' => 403)
|
||||
);
|
||||
}
|
||||
|
||||
// Get the slug from the request
|
||||
$slug = $request['slug'];
|
||||
|
||||
// Retrieve the post by slug
|
||||
$post = get_page_by_path($slug, OBJECT, 'campaign');
|
||||
|
||||
if (!$post) {
|
||||
return new WP_Error(
|
||||
'post_not_found',
|
||||
'Post not found',
|
||||
array('status' => 404)
|
||||
);
|
||||
}
|
||||
|
||||
// Attempt to delete the post
|
||||
$deleted = wp_delete_post($post->ID, true);
|
||||
|
||||
if (!$deleted) {
|
||||
return new WP_Error(
|
||||
'delete_failed',
|
||||
'Failed to delete post',
|
||||
array('status' => 500)
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => 'success',
|
||||
'message' => 'Post deleted successfully',
|
||||
'deleted_slug' => $slug
|
||||
);
|
||||
}
|
||||
|
||||
if (!function_exists("number_shorten")) {
|
||||
/**
|
||||
* Shorten long numbers and attaches K, M, B, etc. accordingly
|
||||
* @param int $n
|
||||
* @param int $precision
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function number_shorten( $n, $precision = 1 ) {
|
||||
if ($n < 900) {
|
||||
// 0 - 900
|
||||
$n_format = number_format($n, $precision);
|
||||
$suffix = '';
|
||||
} else if ($n < 900000) {
|
||||
// 0.9k-850k
|
||||
$n_format = number_format($n / 1000, $precision);
|
||||
$suffix = 'K';
|
||||
} else if ($n < 900000000) {
|
||||
// 0.9m-850m
|
||||
$n_format = number_format($n / 1000000, $precision);
|
||||
$suffix = 'M';
|
||||
} else if ($n < 900000000000) {
|
||||
// 0.9b-850b
|
||||
$n_format = number_format($n / 1000000000, $precision);
|
||||
$suffix = 'B';
|
||||
} else {
|
||||
// 0.9t+
|
||||
$n_format = number_format($n / 1000000000000, $precision);
|
||||
$suffix = 'T';
|
||||
}
|
||||
// Remove unecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1"
|
||||
// Intentionally does not affect partials, eg "1.50" -> "1.50"
|
||||
if ( $precision > 0 ) {
|
||||
$dotzero = '.' . str_repeat( '0', $precision );
|
||||
$n_format = str_replace( $dotzero, '', $n_format );
|
||||
}
|
||||
return $n_format . $suffix;
|
||||
}
|
||||
}
|
||||
|
||||
function handle_update_campaign_preview ($request){
|
||||
$existingPost = Etc::findPostByMeta('campaign-preview', 'appeal_id', $request['id']);
|
||||
if ($existingPost) {
|
||||
|
||||
// Update the existing post
|
||||
$postId = wp_update_post([
|
||||
'ID' => $existingPost->ID,
|
||||
'post_title' => $request['name'],
|
||||
'post_name' => $request['slug'],
|
||||
'post_content' => $request['story'],
|
||||
'post_type' => 'campaign-preview'
|
||||
]);
|
||||
|
||||
// Return the updated post object
|
||||
$campaign_preview = get_post($postId);
|
||||
} else {
|
||||
// Create a new post
|
||||
$postId = wp_insert_post([
|
||||
'post_title' => $request['name'],
|
||||
'post_name' => $request['slug'],
|
||||
'post_content' => $request['story'],
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'campaign-preview'
|
||||
]);
|
||||
error_log($postId);
|
||||
|
||||
|
||||
// Add the custom meta field for appeal_id
|
||||
add_post_meta($postId, 'appeal_id', $request['id']);
|
||||
|
||||
// Return the newly created post object
|
||||
$campaign_preview = get_post($postId);
|
||||
}
|
||||
$body = $request->get_body();
|
||||
error_log(print_r(json_decode($body, true), true));
|
||||
|
||||
foreach (AppealMapper::toACF(json_decode($body, true)) as $fieldKey => $fieldValue) {
|
||||
update_field($fieldKey, $fieldValue, $campaign_preview);
|
||||
}
|
||||
|
||||
return $campaign_preview;
|
||||
}
|
||||
|
||||
function handle_strava_leaderboard (WP_REST_Request $request){
|
||||
delete_field('strava_leaderboard', 'option');
|
||||
$list = $request->get_param('list');
|
||||
|
||||
if ($list) {
|
||||
foreach ($list as $item) {
|
||||
$row = array(
|
||||
'athlete_name' => $item['name'],
|
||||
'athlete_image' => $item['image'],
|
||||
'distance' => $item['distance']
|
||||
);
|
||||
|
||||
add_row('strava_leaderboard', $row, 'option');
|
||||
}
|
||||
} else {
|
||||
error_log('No list received.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ajax_words_of_hope_action_submit() : void{
|
||||
$email = $_REQUEST[ "email" ];
|
||||
$name = $_REQUEST[ "name" ];
|
||||
$school = isset($_REQUEST[ "school" ]) ? $_REQUEST[ "school" ] : null;
|
||||
$form = $_REQUEST[ "form" ];
|
||||
$address = $_REQUEST[ "address" ];
|
||||
$message = $_REQUEST[ "message" ];
|
||||
$us_marketing = isset($_REQUEST[ "us_marketing" ]) ? $_REQUEST[ "us_marketing" ] : null;
|
||||
$uk_marketing = isset($_REQUEST[ "uk_marketing" ]) ? $_REQUEST[ "uk_marketing" ] : null;
|
||||
$is_in_us = $_REQUEST[ "is_in_us" ];
|
||||
|
||||
|
||||
$data = array(
|
||||
'email' => $email,
|
||||
'name' => $name,
|
||||
'school' => $school,
|
||||
'form' => $form,
|
||||
'address' => $address,
|
||||
'message' => $message,
|
||||
'us_marketing' => $us_marketing !== "on" && $is_in_us,
|
||||
'uk_marketing' => $uk_marketing == "on" && !$is_in_us,
|
||||
);
|
||||
|
||||
// Set up the arguments for the HTTP request
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'body' => json_encode($data),
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
);
|
||||
|
||||
$base_url = trim(get_field('generic_donate_url', 'options') ?? 'http://crukv2.test/donate/', '/');
|
||||
$wohURL = str_replace("/donate", "/api/word-of-hope/store", $base_url);
|
||||
|
||||
$response = wp_remote_post($wohURL, $args);
|
||||
|
||||
|
||||
// Check for errors in the request
|
||||
if (is_wp_error($response)) {
|
||||
$error_message = $response->get_error_message();
|
||||
error_log("Error making HTTP request: $error_message");
|
||||
wp_send_json_error(array('message' => 'Error making HTTP request.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the response from the external API
|
||||
$response_body = wp_remote_retrieve_body($response);
|
||||
$response_code = wp_remote_retrieve_response_code($response);
|
||||
|
||||
error_log(print_r($response_body, true));
|
||||
if ($response_code === 201) {
|
||||
// Successfully submitted, return success response
|
||||
wp_send_json_success(array('message' => 'Your message has been successfully submitted.'));
|
||||
} else {
|
||||
// API returned an error, return failure response
|
||||
wp_send_json_error(array('message' => 'There was an error submitting your message.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
add_action( "wp_ajax_words_of_hope_action", "ajax_words_of_hope_action_submit" );
|
||||
add_action( "wp_ajax_nopriv_words_of_hope_action", "ajax_words_of_hope_action_submit" );
|
||||
|
||||
// === CR Live Data: Real-time donation updates ===
|
||||
add_action('init', function() {
|
||||
\Manza\CharityRight\Endpoints\CacheBust::register();
|
||||
});
|
||||
|
||||
add_action('wp_enqueue_scripts', function() {
|
||||
if (is_singular('campaign')) {
|
||||
wp_enqueue_script('cr-live', get_template_directory_uri() . '/assets/js/cr-live.js', [], '1.0.0', true);
|
||||
}
|
||||
});
|
||||
// === End CR Live Data ===
|
||||
|
||||
// menu-fix-bust: force cache bust for gute.js
|
||||
add_filter('script_loader_src', function($src, $handle) {
|
||||
if ($handle === 'vl_new_acf_blocks') {
|
||||
$src = add_query_arg('bust', '20260218d', $src);
|
||||
}
|
||||
return $src;
|
||||
}, 10, 2);
|
||||
|
||||
|
||||
// === UTM Parameter Forwarding (2026-02-19) ===
|
||||
add_action('wp_footer', function() {
|
||||
?>
|
||||
<script>
|
||||
(function(){
|
||||
var utmParams = ['utm_source','utm_medium','utm_campaign','utm_term','utm_content','utm_id'];
|
||||
var stored = {};
|
||||
|
||||
// 1. Check URL for UTM params
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var hasNew = false;
|
||||
utmParams.forEach(function(p) {
|
||||
var v = urlParams.get(p);
|
||||
if (v) { stored[p] = v; hasNew = true; }
|
||||
});
|
||||
|
||||
// 2. If new UTMs found, save to localStorage
|
||||
if (hasNew) {
|
||||
localStorage.setItem('cr_utm', JSON.stringify(stored));
|
||||
localStorage.setItem('cr_utm_ts', Date.now());
|
||||
} else {
|
||||
// 3. Load from localStorage (expire after 30 days)
|
||||
try {
|
||||
var s = localStorage.getItem('cr_utm');
|
||||
var ts = localStorage.getItem('cr_utm_ts');
|
||||
if (s && ts && (Date.now() - ts) < 30*86400000) {
|
||||
stored = JSON.parse(s);
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// 4. Nothing stored? Exit
|
||||
if (!Object.keys(stored).length) return;
|
||||
|
||||
// 5. Append UTMs to all links pointing to app.charityright.org.uk
|
||||
function tagLinks() {
|
||||
var links = document.querySelectorAll('a[href*="app.charityright.org.uk"]');
|
||||
links.forEach(function(a) {
|
||||
try {
|
||||
var url = new URL(a.href);
|
||||
var changed = false;
|
||||
Object.keys(stored).forEach(function(k) {
|
||||
if (!url.searchParams.has(k)) {
|
||||
url.searchParams.set(k, stored[k]);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) a.href = url.toString();
|
||||
} catch(e) {}
|
||||
});
|
||||
}
|
||||
|
||||
// Run on load and observe DOM changes
|
||||
tagLinks();
|
||||
var observer = new MutationObserver(function() { tagLinks(); });
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}, 99);
|
||||
// === END UTM Parameter Forwarding ===
|
||||
|
||||
// Add padding offset class for fixed header on pages that need it
|
||||
add_filter('body_class', function($classes) {
|
||||
// Pages with their own header offset handling
|
||||
$excluded_templates = ['page-ramadan.php', 'page-fidya-kaffarah.php'];
|
||||
$template = get_page_template_slug();
|
||||
$template_file = basename(get_page_template());
|
||||
if (!in_array($template, $excluded_templates) && !in_array($template_file, $excluded_templates)) {
|
||||
$classes[] = 'cr-header-offset';
|
||||
}
|
||||
return $classes;
|
||||
});
|
||||
|
||||
// Add crossorigin="anonymous" to third-party scripts for better error reporting
|
||||
add_filter('script_loader_tag', function($tag, $handle, $src) {
|
||||
if (strpos($src, home_url()) !== false || strpos($src, '/wp-') === 0) {
|
||||
return $tag; // Skip local scripts
|
||||
}
|
||||
if (strpos($tag, 'crossorigin') !== false) {
|
||||
return $tag; // Already has crossorigin
|
||||
}
|
||||
return str_replace(' src=', ' crossorigin="anonymous" src=', $tag);
|
||||
}, 10, 3);
|
||||
|
||||
|
||||
// ══════════════════════════════════════════════════════
|
||||
// IN-APP BROWSER FIXES — SITE-WIDE
|
||||
// Fixes conversion-killing issues in Instagram, Facebook,
|
||||
// TikTok, WhatsApp, Snapchat, Twitter, Threads in-app browsers
|
||||
// ══════════════════════════════════════════════════════
|
||||
add_action("wp_head", "cr_inapp_browser_fixes", 1);
|
||||
function cr_inapp_browser_fixes() {
|
||||
?>
|
||||
<style id="cr-inapp-fixes">
|
||||
/* Prevent auto-zoom on input focus — iOS zooms on <16px inputs */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
input, select, textarea { font-size: max(16px, 1em) !important; }
|
||||
}
|
||||
/* Safe area insets for notched devices */
|
||||
body { padding-bottom: env(safe-area-inset-bottom, 0px); }
|
||||
/* Prevent text inflation in WebViews */
|
||||
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
||||
/* Fix rubber-banding scroll issues */
|
||||
html { overscroll-behavior-y: none; }
|
||||
/* 100vh fix — add dvh fallback globally */
|
||||
.min-h-screen, [style*="100vh"] { min-height: 100vh; min-height: 100dvh; }
|
||||
/* Minimum touch targets (WCAG 2.5.8) */
|
||||
a, button, [role="button"], input[type="submit"], input[type="button"] { min-height: 44px; }
|
||||
/* Fix Livewire/Alpine flicker in WebViews */
|
||||
[x-cloak] { display: none !important; }
|
||||
/* In-app browser banner */
|
||||
#cr-inapp-banner {
|
||||
display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 999999;
|
||||
padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px));
|
||||
background: rgba(26,26,46,.97); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(228,34,129,.25);
|
||||
text-align: center; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
#cr-inapp-banner p {
|
||||
color: #fff; font-size: 13px; font-weight: 600; margin: 0 0 10px; line-height: 1.3;
|
||||
}
|
||||
#cr-inapp-banner .cr-iab-open {
|
||||
display: inline-block; padding: 11px 28px;
|
||||
background: #E42281; color: #fff !important; font-size: 13px; font-weight: 700;
|
||||
border-radius: 8px; text-decoration: none !important; min-height: 44px; line-height: 22px;
|
||||
}
|
||||
#cr-inapp-banner .cr-iab-dismiss {
|
||||
display: block; margin: 10px auto 0; background: none; border: none;
|
||||
color: rgba(255,255,255,.35); font-size: 11px; cursor: pointer; padding: 6px;
|
||||
min-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
add_action("wp_body_open", "cr_inapp_browser_banner", 1);
|
||||
function cr_inapp_browser_banner() {
|
||||
?>
|
||||
<div id="cr-inapp-banner" role="complementary" aria-label="Open in browser">
|
||||
<p>For the best experience, open in your browser</p>
|
||||
<a id="cr-iab-open" class="cr-iab-open" href="#">Open in Safari ↗</a>
|
||||
<button class="cr-iab-dismiss" id="cr-iab-dismiss" aria-label="Dismiss">Continue here</button>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
if(sessionStorage.getItem("cr_iab_off")) return;
|
||||
var ua=navigator.userAgent||"";
|
||||
var isInApp=false;
|
||||
// Detect all major in-app browsers
|
||||
if(/FBAN|FBAV/i.test(ua)) isInApp=true; // Facebook
|
||||
if(/Instagram/i.test(ua)) isInApp=true; // Instagram
|
||||
if(/TikTok|BytedanceWebview|musical_ly/i.test(ua)) isInApp=true; // TikTok
|
||||
if(/Snapchat/i.test(ua)) isInApp=true; // Snapchat
|
||||
if(/Twitter|Threads/i.test(ua)) isInApp=true; // Twitter/Threads
|
||||
if(/Line\//i.test(ua)) isInApp=true; // LINE
|
||||
if(/\bwv\b|WebView/i.test(ua)) isInApp=true; // Generic Android WebView
|
||||
if(/LinkedIn/i.test(ua)) isInApp=true; // LinkedIn
|
||||
if(/Pinterest/i.test(ua)) isInApp=true; // Pinterest
|
||||
if(/Telegram/i.test(ua)) isInApp=true; // Telegram
|
||||
// iOS: has iPhone/iPad but NOT Safari = in-app WebView
|
||||
if(!isInApp&&/iPhone|iPad/.test(ua)&&!/Safari/i.test(ua)) isInApp=true;
|
||||
if(!isInApp) return;
|
||||
|
||||
var b=document.getElementById("cr-inapp-banner");
|
||||
var o=document.getElementById("cr-iab-open");
|
||||
var d=document.getElementById("cr-iab-dismiss");
|
||||
if(!b||!o) return;
|
||||
|
||||
var url=window.location.href;
|
||||
if(/iPhone|iPad|iPod/i.test(ua)){
|
||||
o.href=url; o.setAttribute("target","_blank"); o.textContent="Open in Safari ↗";
|
||||
} else {
|
||||
o.href="intent:"+url.replace(/^https?/,"")+"#Intent;scheme=https;end";
|
||||
o.textContent="Open in Chrome ↗";
|
||||
}
|
||||
b.style.display="block";
|
||||
|
||||
if(d) d.addEventListener("click",function(){
|
||||
b.style.display="none";
|
||||
sessionStorage.setItem("cr_iab_off","1");
|
||||
});
|
||||
|
||||
// CRITICAL: Intercept window.open calls and redirect to same-window
|
||||
// This fixes ALL donate buttons site-wide that use window.open
|
||||
var _origOpen = window.open;
|
||||
window.open = function(url, target) {
|
||||
if(url && typeof url === "string" && (url.indexOf("app.charityright.org.uk") > -1 || url.indexOf("/donate") > -1)) {
|
||||
// In-app browser: redirect same-window instead of popup
|
||||
window.location.href = url;
|
||||
return null;
|
||||
}
|
||||
return _origOpen.apply(window, arguments);
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
// ══════════════════════════════════════════════════════
|
||||
// PREFETCH + PRECONNECT — Speed up checkout transition
|
||||
// In-app browsers have cold DNS/TCP so this is critical
|
||||
// ══════════════════════════════════════════════════════
|
||||
add_action("wp_head", "cr_inapp_prefetch_hints", 2);
|
||||
function cr_inapp_prefetch_hints() {
|
||||
?>
|
||||
<link rel="preconnect" href="https://app.charityright.org.uk" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://app.charityright.org.uk">
|
||||
<link rel="preconnect" href="https://js.stripe.com" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://js.stripe.com">
|
||||
<link rel="preconnect" href="https://fonts.bunny.net" crossorigin>
|
||||
<?php
|
||||
}
|
||||
Reference in New Issue
Block a user