Most people will never need such a thing in a WordPress site, but outside the boxes of blogging, where WordPress is a Swiss Army Knife, there are situations where you need to run a script or do something on a regular interval. Maybe it’s polling social media. Maybe it’s updating some information about your site. I’ve had my second need recently, so here is the 411 on it.
WARNING: This post has a lot of PHP code. If that makes you break out in shivers, go see a puppy.
My first need for this was when I recast the original ds106 Daily Create into its own wordpress based general theme. The big change was that people would submit their responses as tweets to a specific twitter account, and use hashtags with a certain base hashtag (#tdcXXX for the current DS106 Daily Create).
Okay, so I first figured out how to poll this information from Twitter via it’s API, and got a function to do all the stuff I needed it to (not critical to this post, but I have a bunch of posts about building the DS106 Daily Blank).
In the past I might have rigged up some PHP script and a unix cron job, but that’s alot to ask someone installing this as a theme. Fortunately, I did not need this, because as usual, WordPress has built in code for dealing with scheduled events.
While the example code gives you a sense of what is needed, it did not quite push me over the hump of understanding. More or less you set a “hook” which taps into the WordPress flow of logic to set up a scheduled event, and an “action” that this thing acts upon:
register_activation_hook(__FILE__, 'my_activation'); function my_activation() { wp_schedule_event(time(), 'hourly', 'my_hourly_event'); } add_action('my_hourly_event', 'do_this_hourly'); function do_this_hourly() { // do something every hour }
Unfortunately, I did not seem to include in my code comments where I got the code fragment I modified, but I bet a Canadian Loony not was from StackExchange. But this is the winning working code I used:
// add a scheduler to check for tweets if ( ! wp_next_scheduled( 'dailyblank_hello_twitter' ) ) { $dt = new DateTime(); wp_schedule_event( $dt->getTimestamp(), 'hourly', 'dailyblank_hello_twitter'); } // custom action triggered by event add_action( 'dailyblank_hello_twitter', 'dailyblank_get_tweets', 10, 1); function dailyblank_get_tweets( $show_fb = false ) { // fetch the twitter account timeline for replies, grab 100 at a time (200 is max), we want replies and user details $tweets = getTweets( dailyblank_option('twitteraccount'), 150, array('exclude_replies'=>false, 'trim_user' => false ) ); : : }
If I have my head wrapped around this, I am creating my very own WordPress action called dailyblank_hello_twitter
. My script will create something, an event, with that name and that is run every hour. Once this is set up, it exists as a scheduled event in the site’s database, and its main purpose is to trigger that as an action every hour. For some reason, the example I found suggested that way of using $dt
as a time value; I see other ways of doing it in other code examples, and thwy never work on my site. This one works.
But that alone does nothing, so I have to add an action association that tells WordPress what to do each time there is a dailyblank_hello_twitter
actiobn– and that is to do whatever is in the function dailyblank_get_tweets
(the full function is not needed here to explain the scheduling).
And that’s all you need. I do not think it actually sets an alarm clock and runs every hour; my guess is that all this stuff is triggered when someone visits your site (it might be you too) and WordPress checks itself for a missed timed event.
So that was the first time I needed a scheduled event (that was a bad pun).
The second one is related to the syndication method I use for setting up Connected Courses with the Feed WordPress plugin— and it probably is not a problem for a class sized aggregation hub.
But on ds106, where I cut my teeth on all this stuff, we have over 1300 subscribed feeds. And while I can use the dashboard, there is not exactly a way for people to know if they have already added their blog (and duplicates really gum up the works).
Well I do have a shortcode I use to generate subscribed blogs associated with a certain category (e.g. see the sidebar for http://ds106.us/tag/western106 or http://ds106.us/tag/openonline) but nothing to show someone who is trying to signup if their blog is in the mix (or possibly, as in ds106, it have have been deactivated).
This was a shortcoming on the ds106 blog signup form. One late night in January 2016, I rolled up my sleeves and hacked a script that can generate a listing of all blogs we have in the ds106 syndication bus, whether they are active, and even a link to all posts syndicated from that blog.
The function I wrote for doing it is a bit gnarly call it ds106_update_feedlist()
… I have to use the wordpress get_bookmarks()
function (because Feed WordPress stores all its info as a WordPress link or bookmark), then it has to parse the results a few times to get all the information.
function ds106_update_feedlist() { $out = ""; // arguments for getting FWP feeds from links $args = array( 'orderby' => 'name', 'order' => 'ASC', 'limit' => -1, 'category_name' => 'Contributors', 'hide_invisible' => 0 ); // get them $feeds = get_bookmarks ($args); foreach ( $feeds as $feed ) { // get first line of FWP notes, should have author info $authornotes = strtok($feed->link_notes, "\n"); // search for first integeter in string, should be userid preg_match("|\d+|", $authornotes, $afinds); // we have a id, so we have an author if ( count($afinds) ) { // build output link for author $authorlink = ' (all syndicated posts by ' . get_the_author_meta('user_login', $afinds[0]). ')'; } else { $authorlink = ''; } // now find the tags in the link notes preg_match('/tags: (.+)/', $feed->link_notes, $matches); $feed_tags = ( $matches ) ? substr( str_replace ('\n' , ', ' , $matches[0] ), 5 ) : 'NONE' ; $isactive = ( $feed->link_visible == 'Y' ) ? ' FEED ACTIVE' : ' FEED DEACTIVATED'; $adminlink = ' [*^*]'; $out .= '
'; // get uploads directory, thats where we story the cached filed $upload_dir = wp_upload_dir(); // write the data ('/xxxx/xxxx' is a place in the WordPress uploads // directory I am storing a text file. Not terribly secure. Sue me. file_put_contents ( $upload_dir['basedir'] . '/xxxx/xxxx' , $out); echo 'List of ' . count($feeds) . ' feeds now updated.'; }- ' . $feed->link_name . ' URL: ' . $feed->link_url . ' RSS:
\n"; } $out .= '' . $feed->link_rss . '
' . ' tags: ' . $feed_tags . ' ' . $authorlink . $isactive . $adminlink . "
I thought it would be a lot of overhead to hit the database and process all this stuff each time the page above was accessed, so my idea was to create a function that would do all this work, but generate its output as a text file on the server. Then to view it, the calling page needed to spit out that file’s contents (I did this by making it a shortcode named [feedlist]
).
add_shortcode("feedlist", "ds106_feedlist"); function ds106_feedlist() { //retrieve the most recent listing of all feeds from a static file in the Uploads Directory $upload_dir = wp_upload_dir(); // form string for file path $feedfile = $upload_dir['basedir'] . '/xxxx/xxxx'; // get the content from the cached file $feeds = file_get_contents ( $feedfile ); // nice, show how fresh the data is, adjust it from UTC to EST (oh hard wired code) $out = 'Last updated: ' . date ("F d Y h:i:s a", filemtime( $feedfile ) - 5 * 3600 ) . ' (ET)
'; $out .= $feeds; // show the stuff return $out; }
Regardless, I had everything in place to do the big function, and generate a big wad of static content data in a file.
I decided to add something to the WordPress Admin dashboard to trigger the update- it’s not too hard to add your own stuff to the left side menus, in this case I added it as a submenu to the Tools menu:
This is done by adding an action to the admin menus, which is called each time you see a dashboard screen:
add_action('admin_menu', 'register_feedlist_update_page'); function register_feedlist_update_page() { add_submenu_page( 'tools.php', 'Update Feeds List', 'Update FWP Feeds List', 'manage_options', 'feedlist-update-page', 'ds106_feedlist_callback' ); }
It took a lot of head banging to get this part right. Your function called by the admin_menu action must begin with register_
. The stuff in add_submenu_page
tells wordpress which menu it is part of (“tools.php” is the Tools menu), a title for the page that is generated (“Update Feeds List”), the name that appears on the menu (“Update FWP Feeds List”), the capability for which roles can access the page (any role that can “manage options” which means Administer roles only), the string that becomes part of the URL (the link on the menu becomes “…./wp-admin/tools.php?page=feedlist-update-page”) and what function generates the stuff on the Admin page (a callback function or in this case “ds106_feedlist_callback”).
All this does is add the menu to my dashboard. I need to write ds106_feedlist_callback
to tell it what to put on that page.
Here is my function:
function ds106_feedlist_callback() { // Admin page for updating feed list echo ''; echo ''; }Update Subscribed Feeds List
Updating... '; // trigger call to update the static data file ds106_update_feedlist(); echo '
'; // now echo the results echo 'Currently Subscribed Feeds
'; echo ds106_feedlist(); echo '
The page output sets up the basic wrappers for an admin page content, calls my custom function ds106_update_feedlist()
to run the big ass function update, and the same function used for the shortcode ds106_feedlist()
which merely echoes the static data file, and adds a time stamp on top.
Whew, this all works.
Except– to update the content, I have to manually visit the WordPress Admin dashbaord.
Enter a wordpress scheduled event. This turned out easy, since (a) I had the code above I used for the daily create, and (b) I have the function in place here that updates the static data file.
// add a scheduler to update the feed list if ( ! wp_next_scheduled( 'ds106_kick_feed_updates' ) ) { $dt = new DateTime(); wp_schedule_event( $dt->getTimestamp(), 'hourly', 'ds106_kick_feed_updates'); } // custom action triggered by event add_action( 'ds106_kick_feed_updates', 'ds106_update_feedlist', 10, 1);
So now the public page updates itself, and provides a way for someone to check if their blog has been added to the ds106 syndication bus. It also shows which tags a feeds posts are connected to, e.g. what groups/courses their blog is connected to (I guess I should make those into links).
For admins, this is a quicker way to look for a subscribed blog than the Feed WordPress list in the dashboard, which with 1200 feeds, take a bit of churning to display.
But in this process I learned both how to connect my own custom function to a WordPress admin menu and how to schedule things that need to be run on a regular basis– not likely things too many people night need.
And I will grant any reader a Dedication Badge for wading through all this code. Click there.
Top / Featured Image: I did not search on “schedule” but “timer” hoping to get an image for a device. I hit my image on the first few results from a Google Image Search (limited to images licensed for reuse), and it’s even better when it’s from flickr cause (a) flickr is cool and (b) I can copy the attribution AND download the image from my very own attribution helper.
The image used is a flickr photo by numb3r https://flickr.com/photos/numb3r/2394803508 shared under a Creative Commons (BY-SA) license
Click where?
There. Over there.
Also install wp-crontrol as it is dead handy for testing 🙂
Awesome, I like it. And I like corn and cron.
Damn it. Read the post but didn’t read Pat’s comment until now. Really could have used wp-control last night. 🙂
Always read the comments.