The Simplest WordPress User Access Log Ever

Those of use who develop using pods often find we use it for everything. So here’s a quick tip on using PodsCMS to create a custom user access log.

Step 1: Set up the Pod

I’m assuming you’ve already installed/activate both the PodsCMS and Pods UI plugins. If not, please do so before starting.

Create a new Pod called “logins”.

By default each pod is created with a name and slug field. We’re going to use the name field but you can delete the slug field.

Then you’ll need to create a field for “date”. Of course, Pods stores the date any entry is created in a field called “created” which you can access from within Pods Templates. But it still makes sense to have a date field in the Pod itself, if nothing else for the sake of a clear data model.

So once you have added the date field your Pod will look like this:

Step 2: Add function

Now just add the following code to your functions.php file.

function mpv_add_access_log_entry($user) {
$user = explode('|',$user);
$log = new PodAPI(); 
$params = array('datatype'=>'logins');
$params['columns'] = array('name'=>$user[0],'date'=>date('Y-m-d H:i:s'));

This function uses the Pod API to insert a row in the logins table. Alternatively you can use the $wpdb class and do something like this:

global $wpdb;
$wpdb->insert($wpdb->prefix.'pod_tbl_logins',array('name'=> $user[0],'date'=> date('Y-m-d H:i:s')));

The only trouble with going this route is that in order to use the pods admin interface to manage the data, you’ll also need to add a row to the wp_pod table. This will change with Pods 2.0 so there’s no need to demonstrate. But I strongly recommend using the PodsAPI class as it will make sure to implement best practices and in 2.0 it will use the $wpdb class anyway.

So that’s it. To create an exportable report of the logins just install the “Exports and Reports” plugin. Or you can use PodsUI to create a custom interface.

Have fun.

Add tag-like functionality to your pod inputs

Yet another update: The code below expects the name of your pick field to correspond to the name of the related pod type ( i.e. a pickfield name “genre” should relate to a pod called “genre” , as opposed to “file_type”).

Warning: My code highlighter is gimping up the code a little. If you plan to copy/paste, please download the text file instead of copying straight from the webpag.

Try the DEMO
(login using guest/guest).

Update: The demo was down yesterday because I was playing with some code. Sorry about that.

Update 2:
Also note that if this input helper isn’t working on your site make sure that you’ve enqueued the “suggest” script which ships automatically with WordPress. Do this by adding the following line of code to your functions.php file:

if (!wp_script_is('suggest', 'queue') && !wp_script_is('suggest', 'to_do') && !wp_script_is('suggest', 'done'))  {

So this isn’t really a helper so I’m not going to add it to the PodsCMS helper gallery. However, I think it’s a useful bit of code for pods developers.

The problem: You prefer using pods for developing content that needs lots of relationships managed seamlessly. Post types and tags just won’t cut it. But man, you miss that slick tag UI that WordPress has where you just start typing, hit enter and POW the tag is added. Pods doesn’t have anything like this natively.

The solution:

First create a new input helper and paste in the following code:


This helper creates an input field and calls up some jQuery magic to make it auto-complete. But in order for this magic to work you need to add a function to your functions.php (or a plugin file if that’s the case) and hook it to the wp_ajax_{$action} hook. For more about this hook check Gary Cao’s article.

function ajax_pod_search() {
if(isset($_GET['q'])) {
$str = $_GET['q'];
if(strlen($str) < 2) { die; }
$pod = $_GET['datatype'];
global $wpdb;
$query = $wpdb->prepare("SELECT name FROM {$wpdb->prefix}pod_tbl_{$pod} WHERE name LIKE '%s'", '%'.like_escape($str).'%'); 
$results = $wpdb->get_col($query);
echo join( $results, "n" );

Now simply deploy the input_helper on your chosen multi-pick field pod.

But wait! It’s not saving your pod items. That’s because PodsCMS expects to receive a list of comma-separated pod ids to register the relationship. But our jQuery is outputting the name instead.

So we now need to add a pre-save helper to manage migrating tag names to ids.

save_pod_item(array('datatype'=>$field, 'columns' => array('name'=>$name)));
} else {
$pid = (is_array($pid)) ? $pid[0] : $pid;
$vals[] = $pid; 
} else {
$pid = pod_check($field, $name, 'name'); 
  $new_tag = new PodAPI();
  $pid = $new_tag->save_pod_item(array('datatype'=>$field, 'columns' => array('name'=>$name)));
} else {
$pid = (is_array($pid)) ? $pid[0] : $pid;
$vals = $pid; 
$val_str = implode(',',$vals);
$columns[$field]['value'] = $val_str;

Note that we have to tell the helper the field to which we want it to apply. We do this by setting the $field variable. If you are going to use the input helper on multiple fields, you’ll want to create a pre-save helper for each field. This helper will iterate over the submitted pod names, check to see if that pod exists already and getting its id. If the pod name does not already exist, the helper will create it.

Note that this helper makes use of a function called “pod_check”. You will need to add this function to your functions.php file as well.

function pod_check($datatype, $value='', $field='id') {
    global $wpdb;
    $datatype = pods_sanitize($datatype);
    $field = pods_sanitize($field);
    $return = $wpdb->get_col($wpdb->prepare("SELECT `id` FROM `{$wpdb->prefix}pod_tbl_{$datatype}` WHERE `$field` = ".('id'==$field?"%d":"%s"),array($value)));
    if (true == $return && 0 < $return) {
    return $return;
    } else {
    return false;

This function actually does the work of looking up the pod id.

And that's it. You can make any pod into a "tag" using this approach. But keep in mind that if you are using this field to relate a pods with required fields, you will need to modify the pre-save helper to add those required fields.

Using WordPress Pods to Manage Custom Data

Last week I posted about relating a WordPress post to your Pods data. But I realize, for a lot of people, the question is still: Why use Pods to manage your custom data in the first place?

There are a ton of benefits to using PodsCMS. Here’s a thread worth reading on the forum. But I thought it might be helpful to see an example.

Let’s go back to our related books tutorial. Why couldn’t we do this using custom fields and custom post types? That would be simple enough. We’d first create a post type called “books”. Then we’d need to create a meta box that would add the post ID of the related book to our post.

No big deal. We could easily query the related books lik so (I am not testing this code, so don’t try and reuse. This is purely for example):

$bookid = get_post_meta($post->ID,'related_books', false); 
$args = array('post__in' => $bookid, 'post_type' => 'books');
$books = new WP_query($args);

Then of course, we’d use a while loop:

while($books->have_posts()) : $books->the_post();

Pretty simple. But what happens when we start adding complexity? What if we want to feature books that are either related by ID, or have a certain author?

Let’s assume for the moment that we’re using a custom field to save the author name in the books post type. This scenario is a little more challenging. The query above is restrictive to books that have been selected as related. But since we want to do an “OR” query we’re probably going to need a custom select query:

global $wpdb;
$id = $post->ID; //the current post id
$query = "SELECT DISTINCT p.ID FROM wp_posts p INNER JOIN wp_postmeta AS m ON p.ID =  m.post_id WHERE p.post_type = 'books' AND p.post_status = 'publish' AND ( (m.meta_key = 'book_author' AND m.meta_value = 'William Shakespeare') OR p.ID IN(SELECT DISTINCT meta_value FROM wp_postmeta WHERE meta_key = 'related_book' AND post_id = '$id') )";
$results = $wpdb->get_col($query); 

In theory this custom select query gets us an array of post IDs we can then pass on to the standard WP query using the post__in argument we used above. Notice that just a modest increase in the complexity of the request leads to major increase in the complexity of the query.

But what if we are using a post_type for authors and need all the authors who have written certain kinds of books. Say we have a category “childrens books” and we want to get a list of all authors who have written a children’s book. This is where WordPress starts to get pretty convoluted. We could use a custom select query, like we did before. But we could also combine some WP queries.

//get the books in the childrens book category
$cbooks = get_posts('post_type=books&category_name=Childrens Books');
foreach($cbooks as $cbook) {
  $cids[] = $cbook->ID;
$authors = query_posts(array('post_type'=>'author','post__in'=>$cids)); 

This is a a pretty inefficient use of code and it only get worse. Imagine needing to see a list of books in a certain category, with a certain author, with a certain number of pages, that was written in 2010.

You’re using custom fields for the publication year and the pages, a custom post_type for author and a category for the genre. Think of the mental power that will go into putting this query together. You’ll have to do a custom select query.

global $wpdb;
$query = "
INNER JOIN wp_term_relationships AS tr ON tr.object_id = p.ID
INNER JOIN wp_term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
INNER JOIN wp_terms AS t ON t.term_id = tt.term_id
INNER JOIN wp_postmeta AS m1 ON m1.post_id = p.ID AND m1.meta_key = 'related_author'
INNER JOIN wp_postmeta AS m2 ON m2.post_id = m1.post_id AND m2.meta_key = 'publication_year'
INNER JOIN wp_postmeta AS m3 ON m3.post_id = m2.post_id AND m3.meta_key = 'num_pages'
WHERE p.post_status = 'publish' AND p.post_type = 'books'
AND t.slug = 'childrens-books' 
AND m1.meta_value = 'William Shakespeare'
AND m2.meta_value = '2010'
AND m3.meta_value > 200
ORDER BY p.post_title ASC
$results = $wpdb->get_col($query);

I’m not even sure if this query would work, but the query that would work would look a lot like this.

So you get the point. WordPress has no method of simplifying complex queries.

If you are the client and know exactly how you plan to use your site then this isn’t such a big worry. But if you’re a developer working for a client using WordPress as a CMS, you don’t know what the client might eventually request. Nevertheless, it’s your job to be able to meet that request.

Pods is a CMS framework built specifically to deal with these complexities. Let’s perform the query above using the Pods approach.

$pod = new Pod('books');
$pod->findRecords('name ASC',-1, " = 'William Shakespeare' AND t.year = '2010' AND t.pages > 200 AND cat.slug = 'childrens-books'"); 
echo $pod->showTemplate('book_list');

Seriously, that’s it. And of course, you could substitute “OR” for any of the “AND” operators above to get a more inclusive result.

Pods gets a lot of criticism for being a WordPress hack. If you can do what you want to do with WordPress why introduce an unnecessary plugin? I hope I’ve demonstrated here that the advantages to using the PodsCMS Framework are enormous in terms of time and mental energy saved.

And it’s key to remember that Pods is a development Framework, not really a plugin. It can be used to manage minor amounts of custom data in your WordPress posts or a complex web of related content types that are outside of the Posts system altogether.

Down and Dirty WordPress Pods Related Content

Update: Looking at this now, I realized there are some advantages to taking a slightly different approach.

This thread at asks how to relate posts to Pods content. This is something that will get easier in future versions of pods. But the beauty of Pods is that it’s already pretty damn easy if you are familiar with how WordPress manages custom fields.

Getting Down!

So let’s say you have a pod called “books” and you want to be able to relate blog posts to the books you are organizing via pods. I’m going to assume for the sake of this tutorial that you already know how to create Pods content and so you’ll have no problem with the first step:

1. Books pod:

2. Posts pod.

Next you’ll want to set up a pod to relate to posts, you might call it “posts”, or “post_relations”, or “post_meta”:

Again, if you’re familiar with Pods this should be pretty straight-forward. Though you might ask why are we creating a pod called “posts” when it’s the books we want to relate to posts. Well we could just relate the books to posts. But the trouble is relating the other way, posts -> books, gets difficult without a pod to store all the relationships. Plus, we might want to add more “meta” fields to our posts pod that we can also associate with a post. Basically, if we’re using pods, we might as well take advantage of the flexibility.

Let’s also create a pod field called books and make it a pick field related to the books pod.

So in our posts pod, we have a pick column relating to posts and one relating to books.

3. Multidirectional?

We can now return to the book pod and set up a “posts” field there to relate to the posts pod as a bidirectional field.

Why not relate to the posts directly? Again, we want to leverage pods to give us as many options as possible. As long as one pod as is related to posts, we’re “plugged” in so to speak.

4. Create the metabox function:

So here’s where it gets a little more difficult … but still not too bad. We’ll need to create a function that will build the content of our meta box. Add this function to your functions.php.

function books_related_pods() {
global $post;
echo '
'; echo ''; echo '
'; }

Notice that we’re creating one Pods object as ‘$old’ so that we can access the existing values. Then we’re creating a second as “options” which we use to build our select menu options.

5. Create a meta box callback function.
Now we need a function to build the metabox itself. This function will be hooked into the WordPress API. This is an easy one. If you want to know more about meta boxes, read the WordPress Codex article.

function books_related_mb() {
add_meta_box( 'pods_related_mb', "Related Books", 'books_related_pods', 'post', 'normal', 'high', '' );

Note that the third parameter sets the “callback” function for the meta box. In this case we are using function we created above. Also note that the fourth parameter is where you specify the post_type on which you wish to use the meta box.

6. Hook into the WordPress API:

Just add a line in your functions.php:


So at this point you should be able to go to your post screen and see the new meta box. If you can’t your doing something wrong. But we still need to save the meta box.

6. Save the pods!

Now you’ll see the advantage of the seemingly complicated pods set up. All we have to do is find the post’s related pod and add the values using the Pods API.

function books_related_save_pod($post_id) {
if( !wp_is_post_revision( $post_id ) && !wp_is_post_autosave( $post_id ) ) {
        if(isset($_POST['related_books'])) {
$vals = implode(',',$_POST['related_books']);
$pod = new Pod('posts');
$pod->findRecords('id DESC',1,"post.ID = '$post_id'" );
while($pod->fetchRecord()) { 
$pid = $pod->get_pod_id();
if($pid) {

//if a pod exists, save the values
$api = new PodAPI();
$params = array(); 
$params['datatype'] = 'posts';
$params['pod_id'] = $pid;
$params['columns']['books'] = $vals;
$params = pods_sanitize($params);

} else {

$new = new PodAPI(); 
$params = array();
$params['datatype'] = 'posts';
$params['columns']['name'] = $_POST['post_title']; // You must set a name when saving a new pod
$params['columns']['post'] = $post_id;
$params['columns']['books'] = $vals;
$params = pods_sanitize($params);


So there you have it. Down and dirty.