WordPress fallback for Bootstrap's CDN without relying on JavaScript? - css

Developing a theme for WordPress using Bootstrap 4 I'm familiar with the suggested approach of coding a <div>:
<div id="bootstrapCssTest" class="hidden"></div>
and checking it with JavaScript:
<script type="text/javascript">
if ($('#bootstrapCssTest').is(':visible') === true) {
$('<link href="/localcopy/css/bootstrap.css" rel="stylesheet" type="text/css" />').appendTo('head');
}
</script>
reference: "How to load local copy of bootstrap css when the cdn server is down?" but I would like a way to test the CSS without JavaScript and enqueue Bootstrap's CSS depending on if the CDN is found. Upon research I read that fopen may not be allowed always on the server level so I opted for get_headers, the code:
function bootstrap_css() {
$bootstrap_cdn = 'https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css';
$cdn_check = #get_headers($bootstrap_cdn);
if ($cdn_check[0] === "HTTP/1.1 200 OK") :
wp_enqueue_style('bootstrap',$bootstrap_cdn,array(),'4.2.1');
function add_cross_origin($html,$handle) {
if ('bootstrap' === $handle) {
return str_replace("media='all'","media='all' integrity='sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS' crossorigin='anonymous'",$html);
}
return $html;
}
add_filter('style_loader_tag','add_cross_origin',10,2);
else :
wp_enqueue_style('bootstrap',get_site_url() . '/css/bootstrap.min.css',false,'4.2.1');
endif;
}
add_action('wp_enqueue_scripts','bootstrap_css');
Is there a better approach to checking wether the Bootstrap CSS CDN is available that doesn't use JavaScript to test? Should I be checking anything beyond the "HTTP/1.1 200 OK"? Would cURL be better to use then get_headers?

After the first time the php script tries accessing the CSS file, the server returns a status code 304. The easiest implementation to check for this would be the following:
function bootstrap_css() {
$bootstrap_cdn = 'https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css';
$cdn_check = #get_headers($bootstrap_cdn);
if (strpos($cdn_check[0], "200") !== false || strpos($cdn_check[0], "304") !== false) :
wp_enqueue_style('bootstrap',$bootstrap_cdn,array(),'4.2.1');
function add_cross_origin($html,$handle) {
if ('bootstrap' === $handle) {
return str_replace("media='all'","media='all' integrity='sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS' crossorigin='anonymous'",$html);
}
return $html;
}
add_filter('style_loader_tag','add_cross_origin',10,2);
else :
wp_enqueue_style('bootstrap',get_site_url() . '/css/bootstrap.min.css',false,'4.2.1');
endif;
}
add_action('wp_enqueue_scripts','bootstrap_css');

Related

admin_enqueue_scripts not loading any file when not logged in

I'm using admin_enqueue_scripts to load my own CSS and JS in the WP-Admin, and I know the JS file is working when logged in, but the CSS file isn't being loaded from what I can see.
I'm checking if logged out, is there any condition that makes this only apply to logged in users? There's nothign to suggest this is the case, and I'm certain my code is right.
Have I missed something here? To me, this should be working, but the style-admin file isn't being loaded on the login page, and there's nothing in the console either.
function sg_assets_admin() {
if(WP_DEBUG === false) {
wp_enqueue_style('sg-style-admin', get_template_directory_uri().'/css/style-admin.min.css');
} else {
wp_enqueue_style('sg-style-admin', get_template_directory_uri().'/css/style-admin.css');
}
wp_enqueue_media();
if(WP_DEBUG === false) {
wp_enqueue_script('sg-script-admin', get_template_directory_uri().'/js/script-admin.min.js');
} else {
wp_enqueue_script('sg-script-admin', get_template_directory_uri().'/js/script-admin.js');
}
}
add_action('admin_enqueue_scripts', 'sg_assets_admin');
It turns out the login page as I am trying to reference doesn't use admin_enqueue_scripts, but login_enqueue_scripts
https://developer.wordpress.org/reference/hooks/login_enqueue_scripts/
function sg_assets_admin() {
if(WP_DEBUG === false) {
wp_enqueue_style('sg-style-admin', get_template_directory_uri().'/css/style-admin.min.css');
} else {
wp_enqueue_style('sg-style-admin', get_template_directory_uri().'/css/style-admin.css');
}
wp_enqueue_media();
if(WP_DEBUG === false) {
wp_enqueue_script('sg-script-admin', get_template_directory_uri().'/js/script-admin.min.js',array('jquery'));
} else {
wp_enqueue_script('sg-script-admin', get_template_directory_uri().'/js/script-admin.js',array('jquery'));
}
}
add_action('admin_enqueue_scripts', 'sg_assets_admin');
Try above modified code

Excluding bootstrap from specific routes in Meteor

I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)

History.js for HTML5 - Hack Needed to Not Break IE7

My goal is to support AJAX history for HTML5 browsers only. However, I would like my site to work with HTML4 browsers, but without AJAX history.
Many of the History.js examples include the following check before performing any operations:
if (!History.enabled) {
// History.js is disabled for this browser.
// This is because we can optionally choose to support HTML4 browsers or not.
return false;
}
This would seem to work except for the fact that older browser such as IE7 do not support native JSON and the History.js plugin requires JSON.parse and JSON.stringify.
The suggested solution is to include json2.js (link). This seems kind of strange to me since HTML5 browsers that support pushState() and popState() should also support native JSON. Also, I do not want to include yet another library that I do not really need. My solution is to conditionally include History.js as follows:
var nativeJSON = (typeof JSON === 'object') && (typeof JSON.parse === 'function') && (typeof JSON.stringify === 'function');
if (nativeJSON) {
/// Include contents of: balupton-history.js-e84ad00\scripts\bundled\html5\jquery.history.js
} else {
window.History = { enabled: false };
}
This seems to work, but feels like a hack. Is there a better way to do this?
EDIT: 7/31/2012
If I do not include history.html4.js it still gives me an error on IE7. It appears that including json2.js is simply a requirement of this plugin at the moment. An improvement could probably be made to silently check for JSON support and disable the plugin if there is none, but for now I have a workaround. Here is a snippit from History.js:
/**
* History.js Core
* #author Benjamin Arthur Lupton <contact#balupton.com>
* #copyright 2010-2011 Benjamin Arthur Lupton <contact#balupton.com>
* #license New BSD License <http://creativecommons.org/licenses/BSD/>
*/
(function(window,undefined){
"use strict";
// ========================================================================
// Initialise
// Localise Globals
var
console = window.console||undefined, // Prevent a JSLint complain
document = window.document, // Make sure we are using the correct document
navigator = window.navigator, // Make sure we are using the correct navigator
sessionStorage = window.sessionStorage||false, // sessionStorage
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
setInterval = window.setInterval,
clearInterval = window.clearInterval,
JSON = window.JSON,
alert = window.alert,
History = window.History = window.History||{}, // Public History Object
history = window.history; // Old History Object
// MooTools Compatibility
JSON.stringify = JSON.stringify||JSON.encode;
JSON.parse = JSON.parse||JSON.decode;
If window.JSON is undefined, referencing window.JSON.stringify will simply cause an error.
The following works for me in IE7 with no errors:
<html>
<head>
<title>Testing</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
// Tell us whether History is enabled
var alertHistory = function() {
alert(History.enabled ? 'enabled' : 'disabled');
}
var nativeJSON = (typeof JSON === 'object') && (typeof JSON.parse === 'function') && (typeof JSON.stringify === 'function');
if (nativeJSON) {
// Native JSON is present, add History.js
var historyJs = document.createElement('script');
historyJs.type = 'text/javascript';
historyJs.src = 'https://raw.github.com/browserstate/history.js/master/scripts/bundled/html5/jquery.history.js';
historyJs.onload = alertHistory;
document.getElementsByTagName("head")[0].appendChild(historyJs);
} else {
window.History = { enabled: false };
alertHistory();
}
</script>
</head>
<body>
</body>
</html>
Here is how I solved it for my case. I wanted to use public CDNs when possible and combine all other JS code for my site into a single include file. Here is what the code looks like.
Page.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Page Title</title>
<!-- JS Files Hosted on CDN(s) -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<!-- Combined Custom JS File -->
<script type="text/javascript" src="js/site.js"></script>
</head>
<body>
</body>
</html>
The single JS include file combines all needed plugins and any custom code needed to run the site.
Site.js
// History.js plugin
var nativeJSON = (typeof JSON === 'object') && (typeof JSON.parse === 'function') && (typeof JSON.stringify === 'function');
if (nativeJSON) {
// contents of browserstate-history.js-e84ad00\scripts\bundled\html5\jquery.history.js
} else {
window.History = { enabled: false };
}
/*
Watermark v3.1.3 (March 22, 2011) plugin for jQuery
http://jquery-watermark.googlecode.com/
Copyright (c) 2009-2011 Todd Northrop
http://www.speednet.biz/
Dual licensed under the MIT or GPL Version 2 licenses.
*/
// contents of jquery.watermark.min.js
// INCLUDE more plugins here
// Custom JS Code here
Chances are I will want to send down at least some custom JS code and this allows me to send it all in 1 payload. From what I understand it is good practice to combine resource files.
EDIT: 2013-06-25
In subsequent projects I have simply included a minified version of json2.js into my combined JS file. Using Google's Closure Compiler you can get it down to about 3K (before HTTP compression) which seems acceptable.

HTML: how to change my website background at each visit?

what's the best approach to change the background of my website at each visit ?
1) write php code, loading a random css file containing the background property
2) write php code, generating different html (and including the background property directly into html code
3) something else ?
thanks
This can be done in your theme's page.tpl.php variable preprocessor. Store the random style in the $_SESSION array to re-use for all pages in the same user session. And append the markup to the $head variable used in the template.
YOURTHEME_preprocess_page(&$variables) {
$style = $_SESSION['YOURTHEME_background_style'];
if (!$style) {
$style = array();
//Generate your random CSS here
$style = "background-image: url('bg-". rand(0,10) .".png')";
$_SESSION['YOURTHEME_background_style'] = $style;
}
$variables['head'] .= '<style type="text/css">body {'. implode("\n", $style) .'}</style>';
}
Usually, $head is placed before $style in the page.tpl.php templaye, so CSS rules from any .css files will overrides your random rule. You may have to use !important in your random CSS to avoid this.
I would probably:
Use hook_user op login to detect the login and then store the background color code in the user object.
In your page template create an inline style for the background color that uses the value stored on the user object. For anonymous users don't do anything and have default defined in a style sheet.
Use a session cookie. Could be set either via js (client side) or something like php (server-side). Here's an example of a js-only solution:
<!doctype html>
<html><head><script>
var backgrounds=['foo.png', 'bar.png', 'hahah.png'];
function setBg () {
var currentBg=readCookie('which_bg');
if (!currentBg) {
currentBg=backgrounds[Math.random()*backgrounds.length|0];
createCookie('which_bg', currentBg);
}
document.body.style.backgroundImage='url('+currentBg+')';
}
// from http://www.quirksmode.org/js/cookies.html
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}
</script></head>
<body onload="setBg();">
...
</body></html>
To change the background image at each page load (not exactly "visit" though), you can use the Drupal module Dynamic Background. For Drupal 7, only the 7.x-2.x branch contains the option for cycling backgrounds randomly. You would install it with:
drush dl dynamic_background-7.x-2.x && drush en dynamic_background
The feature can also be added to the 7.x-1.x branch with a patch, and to the 6.x-1.x branch similarly.

Detecting if YouTube is blocked by company / ISP

We have YouTube videos on a site and want to detect if it is likely that they will not be able to view them due to (mostly likely) company policy or otherwise.
We have two sites:
1) Flex / Flash
2) HTML
I think with Flex I can attempt to download http://youtube.com/crossdomain.xml and if it is valid XML assume the site is available
But with HTML I don't know how to do it. I can't even think of a 'nice hack'.
I like lacker's solution, but yes, it creates a race condition.
This will work and won't create a race contition:
var image = new Image();
image.onload = function(){
// The user can access youtube
};
image.onerror = function(){
// The user can't access youtube
};
image.src = "http://youtube.com/favicon.ico";
You can load an image from youtube using javascript and check its properties. The favicon is tiny and has a consistent url -
var image = new Image();
image.src = "http://youtube.com/favicon.ico";
if (image.height > 0) {
// The user can access youtube
} else {
// The user can't access youtube
}
I think this is slightly better than loading javascript because this won't try to run any code, and while youtube might rename their javascript files, or functions from those files, they are unlikely to ever rename their favicon.
This should work. Basically, it loads a youtube.com javascript file, then checks if a function in that file exists.
<html>
<head>
<script src="http://www.youtube.com/js/account.js"></script>
<script>
function has_you_tube()
{
if(typeof addVideosToQuicklist == 'function')
{
return true;
}
else
{
return false;
}
}
</script>
</head>
<body>
<script>alert( "has_youtube: " + has_you_tube() ); </script>
</body>
</html>
I got stuck on this today and tried the favicon test but it wasnt working in IE. I was using the YouTube Player API Reference for iframe Embeds to embed youtube videos into my site so what I did is perform a check on the player var defined just before the onYouTubeIFrameReady with a delay on the javascript call.
<script> function YouTubeTester() {
if (player == undefined) {
alert("youtube blocked");
}
}
</script>
<script>window.setTimeout("YouTubeTester()", 500);</script>
Seems to work for me. I needed the delay to get it to work in IE.
This worked for me... Its also my first post, hope it helps some one too.
<?php
$v = file_get_contents("https://www.youtube.com/iframe_api");
//Tie counts to a variable
$test = substr_count($v, 'loading');
if ($test > 0)
{ ?>
<iframe>YOUTUBE VIDEO GOES HERE</iframe>
<?php
}
else
{
echo "<br/> no connection";
}
?>

Resources