Intercept WordPress Admin Dashboard for Alerts - plugins

How can I make a plugin send an alert to one's dashboard at the top right below where the WordPress upgrade notice might normally appear?
I imagine I need some kind of add_filter() or add_action() call in the plugins' code.

Insert the following code in either a functions.php of your theme, or in a plugin. It's a sample that you can adapt and take further. In this demo, I wanted to demonstrate where you could post a message saying that the user needs to update a plugin.
<? function addDashboardAlert() { ?>
<style type="text/css">
.alert {
padding-top:4px;
padding-bottom:6px;
padding-left:302px;
background-color:#ebfbff;
border-bottom:1px solid #CCC;
display:none;
}
</style>
<script type="text/javascript">
$j = jQuery;
$j().ready(function(){ //when page has fully loaded
$j('h2:contains("Dashboard")').parent().prev().after('<div id="my-plugin-alert" class="alert">X Plugin 2.0 is available. Upgrade Now!</div>');
setTimeout("$j('#my-plugin-alert').fadeIn('slow');clearTimeout();",1000);
});
</script>
<? } add_action('admin_head','addDashboardAlert'); ?>
First, it intercepts admin_head to insert some Javascript. In the Javascript, since we know that current WordPress's include jQuery, but loads it with the .noConflict() option, then we can assign the jQuery to $j to keep it short in our code. When the page has fully loaded, it looks for an H2 that contains "Dashboard", meaning the Dashboard page. It then traverses up the DOM a little to a nice slot where it can insert the alert and then adds one there. It concludes with a professional fadeIn() call.

Related

What's the "right" way to add Google Web Auth to Svelte/Sapper?

Giving Svelte/Sapper a look and am curious what the right way to add something like Google Sign-In for Websites to my app.
I have everything working from the example code they give you from the site above, but I've done it by adding the onsuccess "onSignIn" function to the template.html file which doesn't seem like the right way to do this.
Inside src/routes/template.html
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script>
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
console.log("ID: " + profile.getId());
}
</script>
Inside src/components/Nav.svelte
<div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div>
The code above works fine because onSignIn has access to window, but it seems like I should be able to add this to the Nav component where the button itself lives. Is there a preferred way to handle something like this?
If anyone is looking for something similar, I found this git repo that I was able to get started and modify to suit my needs github.com/beyonk-adventures/svelte-social-auth.

app_offline.htm file does not work

I have been battling with this for ages now but I just can not get it to work.
Every blog/site I have been too says there is nothing you need to do in IIS but this cannot be correct as there are multiple website configurations such as Application, virtual directory, simple php/asp websites, vitual.
Can someone please explain to me what the setup needs to look like in IIS7.
I have:
Checked the file spelling: app_offline.htm
Made sure the file was at least 512 bytes (saw this on a random site)
Made sure it is in fact in the root of the application/website
Checked that I can browse directly to the file
Made sure the application pool framework was set to v2.0 or v4.0
Made sure the above application pool was assigned to my website
Tried this in a new website in IIS where the app_offline.htm was the only file in the root.
I have multiple websites set up that I have tested with, namely:
MVC3 Web Application
PHP Simple Website
Classic ASP Simple Website
Webforms Website
Webforms Application
Virtual folders in the above sites
Applications within the above sites
All of the above are working, and placing the app_offline.htm does absolutely nothing.
Please can someone provide some clarity.
I recently had the same issue with the app_offline file and the real problem I had was that windows was set to hide known file extensions. So when the file app_offline.htm was created I thought that the name was correct, but windows was hiding the extension .txt.
Create a web.config file with following content
<?xml version="1.0"?>
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
Hope that helps.
I was also struggling a little bit with this issue.
Along with the fore-mentioned criteria in the other answers. It turns out that the file type MUST be specified specifically .htm NOT .html.
I had the same issue, and although I couldn't solve it, I found a reasonable workaround. I added the same file, but named "appoffline.htm" to the root directory and leave it there permanently.
When I need to take application offline, I use the IIS HTTP Redirection setting for the website to redirect all incoming requests to appoffline.htm (make sure to tick "Redirect all requests to exact destination").
So I never did find a solution to this problem but I did find an alternative to what I was trying to achieve.
Basically, I wanted to show a specific "offline" page per app which would show when the site was offline. Here is what I did...
I created a website I called "_offline" in IIS. I then added a generic "catch all" binding for Port: 80 and left the host name blank. You may need to disable your current default website before this binding will be accepted.
Create an index.html page and put whatever content in there you want to show and shove it as the default page for "_offline". I'll include a bit of script below that works pretty well.
Now you can test by turning off your website, you should see your new index page. If you can't turn off the website, add a binding in your hosts file to anything like "testdomain.com" and point that to your server. Entering that in your browser should then show your offline page.
Just bare in mind, this page will show any time your IIS can not find an active website at the address coming in. Depending on your setup, this may or may not be acceptable in which case you should not use this method.
Now for my index page. I put some javascript in to determine which site the user is trying to reach, then reveal a portion of the html. I also have a countdown that runs and tries to refresh the page every 10 seconds.
Anyway, not the ideal result, but it works.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width = device-width">
<title>Under Maintenance</title>
</head>
<style>
body{
color:#014795;
}
.container{
max-width:650px;
margin:auto;
font-size:18px;
font-family:Arial, Helvetica, sans-serif;
padding:20pt;
text-align:center
}
#logo img {
max-width:100%;
}
a {
color: inherit;
text-decoration: none;
}
a:hover {
color: inherit;
text-decoration: none;
}
</style>
<body>
<table class="container">
<tr>
<td>
<span id="logo"></span>
<p>This site is currently under maintenance and will be available shortly.</p>
<p>We apologize for any inconvenience caused.</p>
<table style="text-align:left;margin:auto;">
<tr><td>Telephone:</td><td>+27 11 11 1111</td></tr>
<tr><td>Fax:</td><td>+27 11 111 2222</td></tr>
<tr><td>Email:</td><td>support#fubar.com</td></tr>
</table>
<p>We will automatically try to reconnect you in <span id="timeleft"></span> seconds</p>
</td>
</tr>
</table>
<script type="text/javascript">
var refreshEvery = 10;
var currentSec = 0;
var timer = setInterval(function() {
currentSec++;
if (currentSec >= refreshEvery) {
clearInterval(timer);
location.reload();
}
document.getElementById("timeleft").innerHTML = "" + (refreshEvery - currentSec);
}, 1000)
document.getElementById("timeleft").innerHTML = "" + (refreshEvery - currentSec);
// Use this site to create a base64 image http://www.base64-image.de/step-1.php
if (document.domain.indexOf("stacksnippets") >= 0) {
// Cusomise the site here, you can also show hide html content.
document.body.style.backgroundColor = "black";
document.body.style.color = "white";
} else {
// put default stuff here
}
</script>
</body>
</html>
Try to start out with a fresh and simple app_offline.htm file, like
<html><body>offline</body></html>
to see if that fixes the problem. In my case the problem was that the file encoding of the app_offline.htm file was "UTF-8 with BOM" rather than plain UTF-8.
The following handler also needs to be present:
ExtensionlessUrlHandler-Integrated-4.0
Make sure that in IIS Manager, on the website's Properties, the Application name has been created. Properties > Directory > Application settings > Application name.
Tested in IIS V6.0
None of the solutions above worked for us.
When testing on the server with same file but renamed (app_offline.TEMPLATE.htm) the server loaded the htm page without issue.
This was resolved by removing the large-ish Base64 encoded image from the file. (used as a background-image)
The app_offline.htm file still uses a Base64 encoded favicon and company logo image.

Easiest way to consume text returned from a REST service

I need to display on my Web page a simple text string returned from a REST service. I am currently using an XMLHttpRequest:
<div id="returnedText"></div>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.status == 200 && xhr.readyState == 4) {
document.getElementById("returnedText").innerHTML=xhr.responseText;
}
};
xhr.open("GET",url,true);
xhr.send(null);
</script>
Isn't there a lighter way? I considered using a script tag but the Web service in question doesn't support JSONP. I also did a naive attempt with an iframe (putting the REST url as src) but it didn't work.
I did another attempt with iframes and actually this works fine:
<iframe src="url"></iframe>
Where url is the REST service call.
I must have done something wrong the first time (maybe an authentication issue).
Well the iframe route is clunky since you'd be loading the REST response into it and then reaching into it via JS to get the response. What's more, it would cause a visible load in the browser's address bar area. AJAX came along to do away with the iframe hack :)
JSON-P requires about as much setup as AJAX and if your server doesn't support the callback, that's a none starter.
AJAX needn't be thought heavy. Kick it into its own utility function or, even better, use a library, which makes requests like these do'able in one line. jQuery example:
$.get('some/path').done(function(response) { /* do something */ });

How to architecture a webapp using jquery-mobile and knockoutjs

I would like to build a mobile app, brewed from nothing more but html/css and JavaScript. While I have a decent knowledge of how to build a web app with JavaScript, I thought I might have a look into a framework like jquery-mobile.
At first, I thought jquery-mobile was nothing more then a widget framework which targets mobile browsers. Very similar to jquery-ui but for the mobile world. But I noticed that jquery-mobile is more than that. It comes with a bunch of architecture and let's you create apps with a declarative html syntax. So for the most easy thinkable app, you wouldn't need to write a single line of JavaScript by yourself (which is cool, because we all like to work less, don't we?)
To support the approach of creating apps using a declarative html syntax, I think it's a good take to combine jquery-mobile with knockoutjs. Knockoutjs is a client-side MVVM framework that aims to bring MVVM super powers known from WPF/Silverlight to the JavaScript world.
For me MVVM is a new world. While I have already read a lot about it, I have never actually used it myself before.
So this posting is about how to architecture an app using jquery-mobile and knockoutjs together. My idea was to write down the approach that I came up with after looking at it for several hours, and have some jquery-mobile/knockout yoda to comment it, showing me why it sucks and why I shouldn't do programming in the first place ;-)
The html
jquery-mobile does a good job providing a basic structure model of pages. While I am well aware that I could have my pages to be loaded via ajax afterwards, I just decided to keep all of them in one index.html file. In this basic scenario we are talking about two pages so that it shouldn't be too hard to stay on top of things.
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<link rel="stylesheet" href="libs/jquery-mobile/jquery.mobile-1.0a4.1.css" />
<link rel="stylesheet" href="app/base/css/base.css" />
<script src="libs/jquery/jquery-1.5.0.min.js"></script>
<script src="libs/knockout/knockout-1.2.0.js"></script>
<script src="libs/knockout/knockout-bindings-jqm.js" type="text/javascript"></script>
<script src="libs/rx/rx.js" type="text/javascript"></script>
<script src="app/App.js"></script>
<script src="app/App.ViewModels.HomeScreenViewModel.js"></script>
<script src="app/App.MockedStatisticsService.js"></script>
<script src="libs/jquery-mobile/jquery.mobile-1.0a4.1.js"></script>
</head>
<body>
<!-- Start of first page -->
<div data-role="page" id="home">
<div data-role="header">
<h1>Demo App</h1>
</div><!-- /header -->
<div data-role="content">
<div class="ui-grid-a">
<div class="ui-block-a">
<div class="ui-bar" style="height:120px">
<h1>Tours today (please wait 10 seconds to see the effect)</h1>
<p><span data-bind="text: toursTotal"></span> total</p>
<p><span data-bind="text: toursRunning"></span> running</p>
<p><span data-bind="text: toursCompleted"></span> completed</p>
</div>
</div>
</div>
<fieldset class="ui-grid-a">
<div class="ui-block-a"><button data-bind="click: showTourList, jqmButtonEnabled: toursAvailable" data-theme="a">Tour List</button></div>
</fieldset>
</div><!-- /content -->
<div data-role="footer" data-position="fixed">
<h4>by Christoph Burgdorf</h4>
</div><!-- /header -->
</div><!-- /page -->
<!-- tourlist page -->
<div data-role="page" id="tourlist">
<div data-role="header">
<h1>Bar</h1>
</div><!-- /header -->
<div data-role="content">
<p>Back to home</p>
</div><!-- /content -->
<div data-role="footer" data-position="fixed">
<h4>by Christoph Burgdorf</h4>
</div><!-- /header -->
</div><!-- /page -->
</body>
</html>
The JavaScript
So let's come to the fun part - the JavaScript!
When I started to think about layering the app, I have had several things in mind (e.g. testability, loose coupling). I'm going to show you how I decided to split of my files and comment things like why did I choose one thing over another while I go...
App.js
var App = window.App = {};
App.ViewModels = {};
$(document).bind('mobileinit', function(){
// while app is running use App.Service.mockStatistic({ToursCompleted: 45}); to fake backend data from the console
var service = App.Service = new App.MockedStatisticService();
$('#home').live('pagecreate', function(event, ui){
var viewModel = new App.ViewModels.HomeScreenViewModel(service);
ko.applyBindings(viewModel, this);
viewModel.startServicePolling();
});
});
App.js is the entry point of my app. It creates the App object and provides a namespace for the view models (soon to come). It listenes for the mobileinit event which jquery-mobile provides.
As you can see, I'm creating a instance of some kind of ajax service (which we will look at later) and save it to the variable "service".
I also hook up the pagecreate event for the home page in which I create an instance of the viewModel that gets the service instance passed in. This point is essential to me. If anybody thinks, this should be done differently, please share your thoughts!
The point is, the view model needs to operate on a service (GetTour/, SaveTour etc.). But I don't want the ViewModel to know any more about it. So for example, in our case, I'm just passing in a mocked ajax service because the backend hasn't been developed yet.
Another thing I should mention is that the ViewModel has zero knowledge about the actual view. That's why I'm calling ko.applyBindings(viewModel, this) from within the pagecreate handler. I wanted to keep the view model seperated from the actual view to make it easier to test it.
App.ViewModels.HomeScreenViewModel.js
(function(App){
App.ViewModels.HomeScreenViewModel = function(service){
var self = {}, disposableServicePoller = Rx.Disposable.Empty;
self.toursTotal = ko.observable(0);
self.toursRunning = ko.observable(0);
self.toursCompleted = ko.observable(0);
self.toursAvailable = ko.dependentObservable(function(){ return this.toursTotal() > 0; }, self);
self.showTourList = function(){ $.mobile.changePage('#tourlist', 'pop', false, true); };
self.startServicePolling = function(){
disposableServicePoller = Rx.Observable
.Interval(10000)
.Select(service.getStatistics)
.Switch()
.Subscribe(function(statistics){
self.toursTotal(statistics.ToursTotal);
self.toursRunning(statistics.ToursRunning);
self.toursCompleted(statistics.ToursCompleted);
});
};
self.stopServicePolling = disposableServicePoller.Dispose;
return self;
};
})(App)
While you will find most knockoutjs view model examples using an object literal syntax, I'm using the traditional function syntax with a 'self' helper objects. Basically, it's a matter of taste. But when you want to have one observable property to reference another, you can't write down the object literal in one go which makes it less symmetric. That's one of the reason why I'm choosing a different syntax.
The next reason is the service that I can pass on as a parameter as I mentioned before.
There is one more thing with this view model which I'm not sure if I did choose the right way. I want to poll the ajax service periodically to fetch the results from the server. So, I have choosen to implement startServicePolling/stopServicePolling methods to do so. The idea is to start the polling on pageshow, and stop it when the user navigates to different page.
You can ignore the syntax which is used to poll the service. It's RxJS magic. Just be sure I'm polling it and update the observable properties with the returned result as you can see in the Subscribe(function(statistics){..}) part.
App.MockedStatisticsService.js
Ok, there is just one thing left to show you. It's the actual service implementation. I'm not going much into detail here. It's just a mock that returns some numbers when getStatistics is called. There is another method mockStatistics which I use to set new values through the browsers js console while the app is running.
(function(App){
App.MockedStatisticService = function(){
var self = {},
defaultStatistic = {
ToursTotal: 505,
ToursRunning: 110,
ToursCompleted: 115
},
currentStatistic = $.extend({}, defaultStatistic);;
self.mockStatistic = function(statistics){
currentStatistic = $.extend({}, defaultStatistic, statistics);
};
self.getStatistics = function(){
var asyncSubject = new Rx.AsyncSubject();
asyncSubject.OnNext(currentStatistic);
asyncSubject.OnCompleted();
return asyncSubject.AsObservable();
};
return self;
};
})(App)
Ok, I wrote much more as I initially planned to write. My finger hurt, my dogs are asking me to take them for a walk and I feel exhausted. I'm sure there are plenty things missing here and that I put in a bunch of typos and grammer mistakes. Yell at me if something isn't clear and I will update the posting later.
The posting might not seem as an question but actually it is! I would like you to share your thoughts about my approach and if you think it's good or bad or if I'm missing out things.
UPDATE
Due to the major popularity this posting gained and because several people asked me to do so, I have put the code of this example on github:
https://github.com/cburgdorf/stackoverflow-knockout-example
Get it while it's hot!
Note: As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live().
I'm working on the same thing (knockout + jquery mobile). I'm trying to write a blog post about what I've learned but here are some pointers in the meantime. Remember that I'm also trying to learn knockout/jquery mobile.
View-Model and Page
Only use one (1) view-model object per jQuery Mobile-page. Otherwise you can get problems with click-events that are triggered multiple times.
View-Model and click
Only use ko.observable-fields for view-models click-events.
ko.applyBinding once
If possible: only call ko.applyBinding once for every page and use ko.observable’s instead of calling ko.applyBinding multiple times.
pagehide and ko.cleanNode
Remember to clean up some view-models on pagehide.
ko.cleanNode seems to disturb jQuery Mobiles rendering - causing it to re-render the html. If you use ko.cleanNode on a page you need to remove data-role’s and insert the rendered jQuery Mobile html in the source code.
$('#field').live('pagehide', function() {
ko.cleanNode($('#field')[0]);
});
pagehide and click
If you are binding to click-events - remember to clean up .ui-btn-active. The easiest way to accomplish this is using this code snippet:
$('[data-role="page"]').live('pagehide', function() {
$('.ui-btn-active').removeClass('ui-btn-active');
});

How to use coffeescript with Sinatra

I'm trying to get coffeescript working with Sinatra. I'm new to both technologies so this is probably something silly. My problem seems to be that the coffeescript compiles to javascript but doesn't execute on page, instead appearing as html.
#sinatra app
require 'coffee-script'
get "/test.js" do
coffee :hello
end
#hello.coffee
alert "hello world"
#My page (/test.js) doesn't execute the js - just displays the code
#On screen in the browser I get this:
(function() {
alert("hello world");
}).call(this);
#In the HTML I get this within the body tags
<pre style="word-wrap: break-word; white-space: pre-wrap;">(function() {
alert('hello world!');
}).call(this);
</pre>
Hmm... it looks like your example is based on this Sinatra documentation. But for some reason, Sinatra is trying to serve the .js file as HTML, and is preprocessing it accordingly. Are you by any chance setting content_type elsewhere in your application? Try changing your code to
get "/test.js" do
content_type "text/javascript"
coffee :hello
end
You could also try a completely different approach, using either Rack::Coffee or Barista to compile your CoffeeScript to JavaScript automatically at the Rack level. That might be easier if you have a large number of CoffeeScript files anyway.
Edit: After posting the above, it struck me that I'm probably just misinterpreting your markup. Is what you see when you load the page test.js in your browser just
alert('hello world!');
? If so, everything is working fine. JavaScript is only going to run in your browser when it's in an HTML page between <script> tags, or referenced via <script src="test.js"></script>. So in addition to your existing code, add
get "/alert", :provides => 'html' do
'<script type=src="test.js"></script>'
end
then open that alert address in your browser, and the script should run.
From sinatra-coffee-script-template
I was just looking for the same setup.
require 'rubygems'
require 'bundler/setup'
require 'sinatra'
require 'coffee-script'
get '/' do
erb :index
end
get '/application.js' do
coffee :application
end
then in application.coffee
$(document).ready ->
$('button:first').click ->
$('body').toggleClass 'dark', 'light'
index.erb
<h1>I'm living the dream</h1>
<button>Click me</button>
layout.erb
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Sinatra Coffee-Script Template</title>
<style type="text/css">
.dark {
background: #2F2F2F;
color: #7F7F7F;
}
.light {
background: #EEEEEE;
color: #2F2F2F;
}
</style>
</head>
<body>
<%= yield %>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script src="/javascripts/listeners.js" type="text/javascript"></script>
</body>
</html>
I usually just setup a watcher on my CoffeeScript files while developing coffee -wc dirname/ and then deploy the compiled JS files to production. It's not ideal, but it's less complicated in some ways and removes the dependency on Node.JS from my production server (which in my case is Heroku.)
Use a gem like sinatra-asset-snack (https://rubygems.org/gems/sinatra-asset-snack) or even better, use a bootstrap to start your project so you don't have to worry about setting up all the plumbing (https://github.com/benkitzelman/sinatra-backbone-bootstrap)