Partial Intro to The Daily Blank

That’s meant to be blank like _________ like make your own site that operates like the DS106 Daily Create. I’ve been mumbling about it for like months, and sat down to start tinkering about a week ago.

I think most of the parts are in place, I’d say it is several Greek letters prior to “alpha”, and am writing now to check in as a progress to myself post. Much of what is left is cosmetic and structural layout, documentation, and much more testing.

The idea is that like the Assignment Bank Theme you can create a kind of site that generates any kind of Daily task, assignment, challenge. A Daily [fill in the blank]!

[365] 047
cc licensed ( BY-NC-ND ) flickr photo shared by Corie Howell

Unlike the The Daily Create, you do not have to respond by uploading and tagging to a specific social media site. Whatever you do as a response, you simply tweet to a specific twitter account the site owner creates, and include the link and a hashtag associated with the Daily _______.

This is what I saw as things to figure out, and what I got done last week:

It’s current test site is living now at http://splot.ca/dailyblank and likely will generate PHP error codes as I work on it. I have the current code on github as well.

dasilyblank

I modified the home page template to display just the most recent item (like the Daily Create).

What follow is an excruciatingly long, code-filled post. I’m putting it below the fold, so if you are reading this in one of those antique RSS readers, you might have to click through to see it on my blog (which, I must say, is looking gorgeous in its new clothes).

Daily Blank Options

Like the ds106 Assignment Bank Theme, I use code based on Aliso The Geek’s WordPress Settings API Tutorial to generate an Options screen. My theme adds a menu item to the Dashboard, under Appearance, and to the to Admin bar to link to the screen where you configure your site. This will be organized a bit better, but for now, I just aim getting things working.

First part of options
First part of options

These settings include:

  • A name for the things on your site my Demo is a “Daily Blanl”, for ds106 it might be “Daily Creatr”
  • Twitter account This is the account people will reply to when they want to submit something. I probably will not build in automatic tweeting; but will recommend what we do on The Daily Create, using dlvr to tweet from the site’s RSS feed (auto tweeter plugins either do not manage scheduled posts well OR if they do, they do not allow you to customize the message). For my demo site, I resurrected good old @auntysocial one I used previously to tease my friends. You will want to make some new account to be the “listener”
  • Default Tweet is actually the message I code into the Tweet this buttons on the site.
  • Base Name for Tags is what the site uses to define each “thing” and to form the URLs, tag things internally. For The ds106 Daily create, this would be tdc so Daily Create number 992 is identified by tdc992; in the Daily Blank this would also be the required hash tag, e.g. #tdc992
  • Starting Tag Numbers For the most part, you will just start at 1, but if we used this for a new version of DS106 Daily Create, we could bump the starting point to keep a consistent count system.
  • Front Image Upload or choose via the native WordPress uplaoder the image that is used on the front of the site. Normally this is the most recent post’s featured image, I recoded the template to use the image specified in the options.

The next set of options…

And now, for the rest of the options...
And now, for the rest of the options…
  • When to Publish The time of day when a new item is published. The site allows you to queue in advanced as many of this as you like, they are set to be scheduled posts, meaning they are automatically published to the world when the publish date/time passes.

    Working with time is always insanely complicated. Most people never change their WordPress settings from the default UTC time. I came up with a clever (I think) way to take the input, you can enter a human time like “5 o’clock” or “5pm” or “17:00”. When the options are saved, my script converts that to a computer time (PHP strtotime function) which, if it can figure out what you intend, gives you that time in the current day. To get an internally consisten time, the date function echos it in simple hours:minutes format.


  • $timeofday = strtotime( $input['dailytime'] );

    if ($timeofday) {
    // valid time of day
    $input['dailytime'] = date('H:i', $timeofday);
    } else {
    // uh oh, we are stumped, return an error
    $input['dailytime'] = 'Invalid format! try '13:00' or '1pm' for 1 o'clock';
    }

    The rest of the form is stuff to set up a captcha; this comes into play when I adda form for allowing site visitors to submit a new idea.

    What happens here with this options structure, is I have available in my code, variables for these values, like dailyblank_option('twitteraccount') gives me the name entered as a twitter account or dailyblank_option('dailykind') is the name of the kind of thing.

    See all of the options code on github. It’s called into action in my theme’s functions.php


    add_action( 'init', 'dailyblank_load_theme_options' );

    function dailyblank_load_theme_options() {
    // load theme options Settings

    if ( file_exists( get_stylesheet_directory() . '/class.dailyblank-theme-options.php' ) ) {
    include_once( get_stylesheet_directory() . '/class.dailyblank-theme-options.php' );
    }
    }

    Authorizing and Talking to Twitter API

    This was all completely new ground for me, and getting to where I am was the usual brute force method:

    1. Look for code/plugins.
    2. Figure out what to modify for my site.
    3. Test and break.
    4. If by some miracle this works, go celebrate.
    5. Else Go to [2]

    There are a number of plugins to set up a means to authenticate a twitter account to do stuff on a WordPress site, most are meant for ones that let you tweet out posts. I found Storm Twitter for WordPress which does nothing more than provide a settings page to enter your twitter account credentials, and function you can use in your code to request results from the twitter api.

    You of course need to create a twitter account (where I wasted an hour creating one and never managed to get it set up because I had a typo in my own email. C’est la typo) and login into the developer side of twitter to create an app (look for this in the documentation to be).

    You then need to copy the four strings of alphanumeric gobbledygook to the Twitter Feed Auth screen the plugin adds

    feed-auth

    When you save… nothing happens. You need to create some code that makes the requests to twitter. The Storm Twitter class out of the box was not what I needed; the call it makes to twitter is to get a user’s tweets. I needed mine to get the replies to my twitter account, so I had to edit the Storm Class file near line 154 to change

    [sourcecode lang=”php”]
    $result = $connection->get(‘statuses/user_timeline’, $options);
    [/sourcecode]

    to read

    [sourcecode lang=”php”]
    $result = $connection->get(‘statuses/mentions_timeline’, $options);
    [/sourcecode]

    because I want the mentions my account gets, each time someone tweets to @auntysocial

    But more than that, once I get mentions, I do not want them all. I only want ones that include a URL AND a hash tag that has a form of #thedailyXXXX

    The way I go about this is I create a blank page on my site with a slug name of “debug”, and put my test code in a theme template named page-debug.php. Then I just load that page in my wordpress site, and after stomping out the PHP errors, I debug. And debug. And debug. I use the var_dump() function to look at variables. IN fact, I create my own function to echo this out, putting the results in pre tags makes it readable.


    // handy dandy debug dumper.
    function cogdogbug($var) {
    echo '

    ';
    	var_dump($var);
    	echo '

    ';
    }

    My first effort was to find out what’s returned by the twitter API in the mentions_timeline. The API is well documented and you can find specifically what the timeline call returns. My first step was to send a bunch of tweets to my account, and then experiment with the request via the Storm Twitter class.

    Through trial and much error, I found I had to add a few options to the function that retrieves the tweets

    [sourcecode lang=”php”]
    $tweets = getTweets( dailyblank_option(‘twitteraccount’), 100, array(‘exclude_replies’=>false, ‘trim_user’ => false ) );
    [/sourcecode]

    I am telling getTweets to use the mentions timeline for the account listed in my options page, to look through the 100 most recent items (200 is the max). The last array are the extra API values I need to give it; first is to say I do want replies (the default is not), and not to “trim the user” meaning when I get reference to a user, I want info such as their twitter handle (otherwise all you get is an id).

    Okay, that’s mumbo jump. Basically I am able to ask twitter for info about all the replies (aka mentions) to @auntysopcial, anyone who has included her handle in a tweet.

    What’s in a Tweet?

    Just to blow your mind a bit, while a tweet is limited to 140 characters, a whole lot more stuff is set. This call I am using is returning 5540 characters of data! This showed me where in the results things were that I needed, like the hashtags used in the tweet, the urls used in the tweet, the user name of the person who responded.

      [0]=>
      array(23) {
        ["created_at"]=>
        string(30) "Mon Dec 01 23:14:47 +0000 2014"
        ["id"]=>
        int(539558257367347200)
        ["id_str"]=>
        string(18) "539558257367347200"
        ["text"]=>
        string(77) "@auntysocial #thedaily5 I think you saw my banky thing http://t.co/fUYox67ioM"
        ["source"]=>
        string(82) "Twitter for Websites"
        ["truncated"]=>
        bool(false)
        ["in_reply_to_status_id"]=>
        NULL
        ["in_reply_to_status_id_str"]=>
        NULL
        ["in_reply_to_user_id"]=>
        int(5447672)
        ["in_reply_to_user_id_str"]=>
        string(7) "5447672"
        ["in_reply_to_screen_name"]=>
        string(11) "auntysocial"
        ["user"]=>
        array(41) {
          ["id"]=>
          int(740343)
          ["id_str"]=>
          string(6) "740343"
          ["name"]=>
          string(11) "Alan Levine"
          ["screen_name"]=>
          string(6) "cogdog"
          ["location"]=>
          string(19) "Strawberry, Arizona"
          ["profile_location"]=>
          NULL
          ["description"]=>
          string(153) "Barks about and plays with web tech. Likes photography, guitars, storytelling, blogging, biking, coding, the Who. Hates likes, egos, spammers. Has shots."
          ["url"]=>
          string(22) "http://t.co/ZxC70Ze4em"
          ["entities"]=>
          array(2) {
            ["url"]=>
            array(1) {
              ["urls"]=>
              array(1) {
                [0]=>
                array(4) {
                  ["url"]=>
                  string(22) "http://t.co/ZxC70Ze4em"
                  ["expanded_url"]=>
                  string(21) "http://cogdogblog.com"
                  ["display_url"]=>
                  string(14) "cogdogblog.com"
                  ["indices"]=>
                  array(2) {
                    [0]=>
                    int(0)
                    [1]=>
                    int(22)
                  }
                }
              }
            }
            ["description"]=>
            array(1) {
              ["urls"]=>
              array(0) {
              }
            }
          }
          ["protected"]=>
          bool(false)
          ["followers_count"]=>
          int(8358)
          ["friends_count"]=>
          int(862)
          ["listed_count"]=>
          int(655)
          ["created_at"]=>
          string(30) "Wed Jan 31 21:24:02 +0000 2007"
          ["favourites_count"]=>
          int(232)
          ["utc_offset"]=>
          int(-25200)
          ["time_zone"]=>
          string(7) "Arizona"
          ["geo_enabled"]=>
          bool(true)
          ["verified"]=>
          bool(false)
          ["statuses_count"]=>
          int(56433)
          ["lang"]=>
          string(2) "en"
          ["contributors_enabled"]=>
          bool(false)
          ["is_translator"]=>
          bool(false)
          ["is_translation_enabled"]=>
          bool(false)
          ["profile_background_color"]=>
          string(6) "FFFFFF"
          ["profile_background_image_url"]=>
          string(64) "http://pbs.twimg.com/profile_background_images/88763117/dog2.jpg"
          ["profile_background_image_url_https"]=>
          string(65) "https://pbs.twimg.com/profile_background_images/88763117/dog2.jpg"
          ["profile_background_tile"]=>
          bool(false)
          ["profile_image_url"]=>
          string(79) "http://pbs.twimg.com/profile_images/1772410667/cogdog-watercolor-200_normal.png"
          ["profile_image_url_https"]=>
          string(80) "https://pbs.twimg.com/profile_images/1772410667/cogdog-watercolor-200_normal.png"
          ["profile_banner_url"]=>
          string(55) "https://pbs.twimg.com/profile_banners/740343/1389655067"
          ["profile_link_color"]=>
          string(6) "9D582E"
          ["profile_sidebar_border_color"]=>
          string(6) "D9B17E"
          ["profile_sidebar_fill_color"]=>
          string(6) "EADEAA"
          ["profile_text_color"]=>
          string(6) "333333"
          ["profile_use_background_image"]=>
          bool(true)
          ["default_profile"]=>
          bool(false)
          ["default_profile_image"]=>
          bool(false)
          ["following"]=>
          bool(true)
          ["follow_request_sent"]=>
          bool(false)
          ["notifications"]=>
          bool(true)
        }
        ["geo"]=>
        NULL
        ["coordinates"]=>
        NULL
        ["place"]=>
        NULL
        ["contributors"]=>
        NULL
        ["retweet_count"]=>
        int(0)
        ["favorite_count"]=>
        int(0)
        ["entities"]=>
        array(4) {
          ["hashtags"]=>
          array(1) {
            [0]=>
            array(2) {
              ["text"]=>
              string(9) "thedaily5"
              ["indices"]=>
              array(2) {
                [0]=>
                int(13)
                [1]=>
                int(23)
              }
            }
          }
          ["symbols"]=>
          array(0) {
          }
          ["user_mentions"]=>
          array(1) {
            [0]=>
            array(5) {
              ["screen_name"]=>
              string(11) "auntysocial"
              ["name"]=>
              string(14) "Just a Dog Bot"
              ["id"]=>
              int(5447672)
              ["id_str"]=>
              string(7) "5447672"
              ["indices"]=>
              array(2) {
                [0]=>
                int(0)
                [1]=>
                int(12)
              }
            }
          }
          ["urls"]=>
          array(1) {
            [0]=>
            array(4) {
              ["url"]=>
              string(22) "http://t.co/fUYox67ioM"
              ["expanded_url"]=>
              string(116) "http://cogdog.makes.org/goggles/usernames-remix-of-study-says-scooters-cause-most-toyrelated-injuries-channel-2-news"
              ["display_url"]=>
              string(34) "cogdog.makes.org/goggles/userna…"
              ["indices"]=>
              array(2) {
                [0]=>
                int(55)
                [1]=>
                int(77)
              }
            }
          }
        }
        ["favorited"]=>
        bool(false)
        ["retweeted"]=>
        bool(false)
        ["possibly_sensitive"]=>
        bool(false)
        ["lang"]=>
        string(2) "en"
      }
    

    My function for walking through this had to parse out the items, and process it only if it met the conditions of including a hashtag similar to my basetag and if it had at least 1 URL

    [sourcecode lang=”php”]
    function dailyblank_get_tweets() {
    // fetch the twitter account timeline for replies, grab 100 at a time (200 is max), we want replies and user deets

    $tweets = getTweets( dailyblank_option(‘twitteraccount’), 100, array(‘exclude_replies’=>false, ‘trim_user’ => false ) );

    // set up an array to hold responses
    $new_responses = array();

    // walk through the tweets
    foreach($tweets as $tweet) {

    // array for hashtags
    $hashtags = extract_hashtags( $tweet[‘entities’][‘hashtags’] );

    // We want only replies with hashtags and URLs in ’em
    if ( $hashtags AND $tweet[‘entities’][‘urls’] ) {

    // check for hashtag match against our dailyblank base
    if ( dailyblank_tag_in_hashtags( $hashtags, dailyblank_option(‘basetag’) ) ) {

    // bingo we got a winner;
    // form URL for the tweet
    $t_url = ‘https://twitter.com/’ . $tweet[‘user’][‘screen_name’] . ‘/status/’ . $tweet[‘id_str’];
    // build data array for each response found

    $new_responses[] = array(
    ‘id_str’ => $tweet[‘id_str’], // twitter id for tweet
    ‘tweeter’ => $tweet[‘user’][‘screen_name’], // tweet author
    ‘text’ => $tweet[‘text’], // da tweet
    ‘url’ => $t_url, // full url
    ‘tstamp’ => $tweet[‘created_at’], // timestamp
    ‘tags’ => $hashtags // gimme tags
    );

    }
    }
    }

    // keep track of the last tweet id so we can use the since_id value in the next API call
    update_option( ‘dailyblank_last_tweet’, $tweets[0][‘id_str’] );

    add_dailyblank_responses( $new_responses );
    }
    [/sourcecode]

    The add_dailyblank_responses() function turns each tweet found into a custom content type (described below)- this uses standard built in functions WordPress has to create posts via code

    [sourcecode lang=”php”]
    function add_dailyblank_responses( $responses ) {
    /*
    Utility to add new items to custom post types that represent tweeted responses. Input array includes
    ‘id_str’ => twitter ID
    ‘url’ => link to tweet
    ‘text’ => text of the tweet
    ‘tweeter’ => screen name of tweet author
    ‘tstamp’ => time stamp converted to unix time
    ‘tags’ => simple array of hash tags

    */

    foreach ($responses as $tweet) {

    // Skip existing responses if they match by the slug (post_name) matching twitter ID
    if ( the_slug_exists( $tweet[‘id_str’] ) ) continue;

    // append the tweet username as a tag
    $tweet[‘tags’][] = ‘@’ . $tweet[‘tweeter’];

    // Create post object
    $response_type = array(
    ‘post_type’ => ‘response’,
    ‘post_title’ => $tweet[‘text’], // use the entire tweet as title
    ‘post_name’ => $tweet[‘id_str’], // use twitter id as slug
    ‘post_date_gmt’ => date( ‘Y-m-d H:i:s’, strtotime( $tweet[‘tstamp’] ) ),
    ‘post_date’ => iso8601_gmt_to_local( $tweet[‘tstamp’], $format = ‘Y-m-d H:i:s’),
    ‘post_status’ => ‘publish’,
    ‘post_author’ => 1,
    );

    // Insert the new content type into database, store it’s ID
    $post_id = wp_insert_post( $response_type, $wp_error );

    // add tags to hashtags taxonomy
    wp_set_post_terms( $post_id, $tweet[‘tags’], ‘hashtags’ );

    // add custom field data for the tweet’s URL
    update_post_meta($post_id, ‘tweet_url’, $tweet[‘url’]);

    // add custom field data for the tweet’s URL
    update_post_meta($post_id, ‘tweet_by’, $tweet[‘tweeter’]);
    }
    }

    [/sourcecode]

    The variables in the array sent to wp_insert_post() match the custom content type the site creates (described below). I want to avoid creating duplicates of tweets; I experimenting with adjusting the API to use the capability to check tweets after ones already read, but I did not get it to work.

    Instead, my design for the Response type creates a tweet item identified by its unique twitter id (this is the “slug” or the part of its url name). So my function will skip a tweet if there is already an existing item previously created.

    [sourcecode lang=”php”]
    // find out of post of a $post_type exists based on slug ($post_name)
    //– ht/t http://wordpress.stackexchange.com/a/144439/14945

    global $wpdb;

    $post_exists= $wpdb->get_var(“SELECT count(post_title) FROM $wpdb->posts WHERE post_name like ‘” . $slug. “‘”);

    if ( $post_exists > 0) {
    return true;
    } else {
    return false;
    }
    }
    [/sourcecode]

    Can I get the site to check twitter all by itself?

    There are built in functions in WordPress core to do this; I’m still testing it out. It seems it needs triggers of visits to your site to run the updates. It’s a matter of setting up what are known as “scheduled events” and defining an interval to do this.

    [sourcecode lang=”php”]
    // 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’);
    [/sourcecode]

    I can see that some sites may not have sufficient visitors to trigger this, so I am looking at adding a manual update, as well as creating a URL that can be called from a cron script.

    Storing Tweeted Responses as a Custom Post Type

    Knowing how to create, manage, and use Custom Post types is the huge leap in using WordPress for more than just organizing blog posts. It lets you define kinds of content you can display, search, sort, create, delete that are like posts (with a title, description, etc) that exist independently of posts.

    It is da bomb for wordpress developers.

    I’ve been using them for a few years, and while you can shortcut the process with various plugins, you might get limited to the capabilities of the plugins. I’m an old school code welder; I try where possible to build ’em myself. The Codex has more than sufficient documentation, and when things get sticky, there are searches that find many results in groovy places like Stack Exchange.

    Along with custom content types, you can create custom classification schemes that act like post categories and/or tags- these are custom Taxonomies. I sketched some of this out on a piece of now lost paper, but I knew I needed a content piece that would house the tweets I harvested from twitter.

    Here is what one looks like, I call it a Response

    single response

    This is what I ended up with:

    • Title: I put the text of the tweet in the title.
    • Description: not used! I do not need it.
    • Publish Date: the date/time when it was tweeted
    • Slug or Short Name The twitter id for the tweet, this is unique, and serves well for the part that defines the URL the content generates
    • Custom Fields: I could have made these additional kinds of content, but since they are never seen, using custom fields is good enough. This extra data stored from each tweet:
      • tweet_by the twitter user name of the person who replied
      • tweet_url the full URL for the tweet (used to display it when we show one)

    Here is how that is generated in functions.php:

    [sourcecode lang=”php”]
    register_post_type(
    ‘response’,
    array(
    ‘labels’ => array(
    ‘name’ => __( ‘Responses’),
    ‘singular_name’ => __(‘Response’),
    ‘add_new’ => ‘Add New’,
    ‘add_new_item’ => ‘Add New Response’,
    ‘edit_item’ => ‘Edit Response’,
    ‘new_item’ => ‘New Response’,
    ‘all_items’ => ‘All Responses’,
    ‘view_item’ => ‘View Response’,
    ‘search_items’ => ‘Search Responses’,
    ‘not_found’ => ‘No responses to do found’,
    ‘not_found_in_trash’ => ‘No responses found in Trash’,

    ),
    ‘description’ => __(‘Responses via Twitter’),
    ‘public’ => true,
    ‘show_ui’ => true,
    ‘menu_position’ => 5,
    ‘show_in_nav_menus’ => true,
    ‘supports’ => array(
    ‘title’,
    ‘custom-fields’,
    ),
    ‘has_archive’ => true,
    ‘rewrite’ => true,
    ‘taxonomies’ => array(
    ‘hashtags’,
    ),
    )
    );

    [/sourcecode]

    There are also tags associated with this kind of content, but they are stored separately from the normal blog post tags, this is a custom taxonomy I made called Hashtags. What I am adding to each captured tweet is one of these for each hashtag in the tweet; I also add one (with the “@” in front) for the twitter name on the account. This allows me to create a link that can show all harvested tweets from one user (example).

    This way I have templates made that generate views of all tweets with one of the site’s defined tags, e.g. for thedaily1, but lets me create views for any other hash tags user might use. e.g. ccourses. The latter means those that use the site can ask for other hash tags, e.g. like a course tag.

    Here is how the taxonomy is created functions.php:

    [sourcecode lang=”php”]
    register_taxonomy(
    ‘hashtags’, // Taxonomy name
    array( ‘response’) , // Post Types
    array(
    ‘labels’ => array(
    ‘name’ => __( ‘Hashtags’),
    ‘singular_name’ => __(‘Hashtag’),
    ‘search_items’ => __( ‘Search Hashtags’ ),
    ‘all_items’ => __( ‘All Hashtags’ ),
    ‘edit_item’ => __( ‘Edit Hashtag’ ),
    ‘update_item’ => __( ‘Update Hashtag’ ),
    ‘add_new_item’ => __( ‘Add New Hashtag’ ),
    ‘new_item_name’ => __( ‘New Hashtag’ ),
    ‘separate_items_with_commas’ => __( ‘Separate Hashtags with commas’ ),
    ‘add_or_remove_items’ => __( ‘Add or remove hashtags’ ),
    ‘choose_from_most_used’ => __( ‘Choose from the most used hashtags’ ),
    ‘not_found’ => __( ‘No Hashtags found.’ ),

    ),
    ‘show_ui’ => true,
    ‘show_admin_column’ => true,
    ‘show_tagcloud’ => true,
    ‘hierarchical’ => false,
    )
    );
    }

    [/sourcecode]

    I even got fancy, and modified the code that displays the Responses (equivalent to All Posts) to include columns that display the value of the custom field with the tweeter’s name (linked to their profile in twitter):

    responses view

    This is how I managed to do this.

    [sourcecode lang=”php”]
    // modify the listings to include custom columns
    add_filter( ‘manage_edit-responses_columns’, ‘dailyblank_set_custom_edit_responses_columns’ );
    add_action( ‘manage_responses_posts_custom_column’ , ‘dailyblank_custom_responses_column’, 10, 2 );

    function dailyblank_set_custom_edit_responses_columns( $columns ) {
    // Add a column for the twitter author, insert after the hashtags column
    // — h/t http://wordpress.stackexchange.com/questions/8427/change-order-of-custom-columns-for-edit-panels

    $modcols = array();

    foreach( $columns as $key => $title ) {
    if ($key == ‘date’) {
    // Put the new column before the Date column
    $modcols[‘response’] = ‘Tweeter’;
    }
    $modcols[$key] = $title;
    }

    return $modcols;

    }

    function dailyblank_custom_responses_column( $column, $post_id ) {
    switch ( $column ) {
    case ‘response’ :
    // get the twitter author
    $tweeter = get_post_meta( $post_id, ‘tweet_by’, 1);

    if ( $tweeter ) {
    echo ‘@’ . $tweeter . ‘‘;
    } else {
    echo ‘–‘;
    }
    break;
    }

    }

    [/sourcecode]

    And it means on an individual Daily Blank, at the bottom, I can display all the responses (using the specified tag). This requires a custom loop inside the single.php template.

    What has to happen here is we have to do is get the tag associated with a single post, and run it through a custom query on the response type to get the responses that have the same thing as a Hashtag type. The magic on displaying the tweet as something more than text is to use the twitter URL we stored as a value we pass to the wp_oembed_get() function letting WordPress do the auto-embedding.

    [sourcecode lang=”php”]
    ID, ‘dailyblank_tag’, 1 );

    // query to get responses
    $responses_query = new WP_Query(
    array(
    ‘posts_per_page’ =>’-1′,
    ‘post_type’ => ‘response’,
    ‘hashtags’=> $dailyblank_tag,
    )
    );

    $response_count = $responses_query->post_count;
    $plural = ( $response_count == 1) ? ” : ‘s’;
    $item_counter = 0;
    ?>

    Response Tweeted for this

    have_posts() ) : $responses_query->the_post();?>

    <?php
    // start a new column?
    if ( $item_counter % 3 == 0) {
    echo '

    ‘;
    }

    // bump counter
    $item_counter++;

    echo wp_oembed_get( get_post_meta( $post->ID, ‘tweet_url’, 1 ) );

    if ( $item_counter % 3 == 0 ) echo ‘

    ‘; // — end of row
    ?>

    [/sourcecode]

    Turning an Ordinary Blog Post into a Daily Blank

    Each Daily Blank is a normal blog post. Well, normal enhanced. The extra post editor box I add, allows you to trigger the features:

    • It gives the post a unique tag, the next one in the series. If your most recent one is thedaily345 it makes the next one you make tagged thedaily346.
    • It gives the post a scheduled date to appear, depending on the time of day you set as an option. If thedaily345 is set to publish on December 12, 2014; the new one will be set to publish December 13, 2014.
    • It appends a short instruction string as the directions telling the sitre visitor who to tweet it to, and what tag to use.

    In creating these, we can do them as we do normal blog posts, we start to write them. As I start with a title and the description I want to use, WordPress is already saving it as a draft (notice the URL it is using is based on the title):

    (click image for el biggon version)
    (click image for el biggon version)

    Note the box in the upper right; this is a Custom Meta Box I added to appear on all posts. It has the next tag available. Once you get a new post set up, the first time you click Save Draft. The script called by the custom meta box changes a few things- it adds the tag, changes the short url, sets the scheduled date/time when it will be available and appends the instructions.

    (click the image to see the full one in all it's glory)
    (click the image to see the full one in all it’s glory)

    You can continue to edit as you see fit. It will be saved a a schedule post, meaning visitors cannot see it until the schedule date has passed. Like the DS106 Daily Create, this allows you to create a long queue of things in advance.

    Once published, it appears on the site:

    published daily blank

    Where, if you will visit this one, already has a test tweet I sent it.

    I’ve already saddled this post with way too much code; look for it in functions.php under the area labeled Editing Meta Box – The thing that makes the daily magic. It needed custom functions to find the last tag used, and the date of the last created item.

    Tweaking the Admin Dashboard

    Since the posts are not really posts, my design aesthetic tickled me to figure out how to modify the menu names used in the WordPress Dashboard. Previously, I had used the WordPress Admin Editor plugin. What I find is that 90% of the plugins are front ends for stuff that’s in the core code.

    I found most of what I needed in a post from Revelation Concept. This lets me change the left side menus that normally say “View Posts”, “Add Post”:

    new daily blank add menu w scheduled

    as well as to put it under the top + New menu:

    new menu

    Also, using some help from the WordPress Codex I was able to add a sub menu item to the left menu to link to the Scheduled posts. Here’s yer code, dude!

    [sourcecode lang=”php”]
    // change the name of admin menu items from “New Posts”
    // — h/t http://wordpress.stackexchange.com/questions/8427/change-order-of-custom-columns-for-edit-panels
    // and of course the Codex http://codex.wordpress.org/Function_Reference/add_submenu_page

    add_action( ‘admin_menu’, ‘dailyblank_change_post_label’ );
    add_action( ‘init’, ‘dailyblank_change_post_object’ );
    add_action(‘admin_menu’, ‘dailyblank_scheduled_menu’);

    function dailyblank_change_post_label() {
    global $menu;
    global $submenu;

    $daily_blank_thing = ‘Daily Blank’;

    $menu[5][0] = $daily_blank_thing . ‘s’;
    $submenu[‘edit.php’][5][0] = ‘All ‘ . $daily_blank_thing . ‘s’;
    $submenu[‘edit.php’][10][0] = ‘Add ‘ . $daily_blank_thing;
    $submenu[‘edit.php’][15][0] = $daily_blank_thing .’ Categories’;
    $submenu[‘edit.php’][16][0] = $daily_blank_thing .’ Tags’;
    echo ”;
    }
    function dailyblank_change_post_object() {

    $daily_blank_thing = ‘Daily Blank’;

    global $wp_post_types;
    $labels = &$wp_post_types[‘post’]->labels;
    $labels->name = $daily_blank_thing;
    $labels->singular_name = $daily_blank_thing;
    $labels->add_new = ‘Add ‘ . $daily_blank_thing;
    $labels->add_new_item = ‘Add ‘ . $daily_blank_thing;
    $labels->edit_item = ‘Edit ‘ . $daily_blank_thing;
    $labels->new_item = $daily_blank_thing;
    $labels->view_item = ‘View ‘ . $daily_blank_thing;
    $labels->search_items = ‘Search ‘ . $daily_blank_thing;
    $labels->not_found = ‘No ‘ . $daily_blank_thing . ‘ found’;
    $labels->not_found_in_trash = ‘No ‘ . $daily_blank_thing . ‘ found in Trash’;
    $labels->all_items = ‘All ‘ . $daily_blank_thing;
    $labels->menu_name = $daily_blank_thing;
    $labels->name_admin_bar = $daily_blank_thing;
    }

    function dailyblank_scheduled_menu() {
    add_submenu_page(‘edit.php’, ‘Scheduled Daily Blanks’, ‘Scheduled Daily Blanks’, ‘manage_options’, ‘edit.php?post_status=future&post_type=post’ );
    }
    [/sourcecode]

    Slick, eh?

    What’s Next?

    Things are still in development. And it’s not quite fully tested. On my list is:

    • Add a form so visitors can submit a new Daily Blank like the Daily Create, this come in as Drafts. As a site admin, you can edit, and trigger the activation via the Custom Meta Box. This is pretty easy, I got good in the last project at adding forms that do not need fancy plugins to work.
    • Figure out Ajax Post Loading so any archive or list of responses can have a “show more” button that adds another set. This is new territory for me, and really should be in the assignment bank template to load the responses and tutorials. I am looking to this tutorial to help
    • Add a Header Banner Editor so the theme can have a consistent top mast.
    • Everything Else I cannot Remember. Oh yes, documentation to; which for now looks like:
      docus
    • Everything Else I forgot.

    Are you still reading?

    Hi.

About cogdog

I bark about and play with technology and web stuff. Harmless. I have my shots.

3 thoughts on “Partial Intro to The Daily Blank

  1. Still reading and I’ll read and again:) Great stuff Alan. Some stuff I’ve been dipping my toes in, some stuff I’ve been dreaming about and then a whole lot more. Envelope pushed!
    Wonderful to have all this stuff put in context showing the purpose.

Leave a Reply to John Cancel reply

Your email address will not be published. Required fields are marked *