| |

User Taxonomies – Categorize Your WordPress Users

UPDATE, MAY 2020

This post is now six years old, and technology has moved on. Today, we would take a different approach to building a directory of WordPress Users. 

Please read our new post describing how to use CiviCRM to create and customize a public, organized display of user profiles. This is Tadpole’s specialty and we’re happy to talk with you about it – feel free to reach out.

The following should only be read for reference – there are better, easier ways to do this stuff now :-)

Original article

This article is a demonstration of how to implement user taxonomies on a WordPress website. It is the subject of a WPNYC Meetup presentation I am giving tonight! The code used in this project can be found on Github, and is freely available to all.

Recently we rebuilt ArtsWestchester.org. The project had the following requirements:

  • The site has two kinds of users: Artists, and Cultural Organizations
  • All users have their own page, which displays their blogs posts, events, and bio. “Artist” profiles and “Cultural Organization” profiles display slightly different information
  • The site displays users in two directories, each with their own different categories. For example, Artists have categories like Dance, Music, and Film; Cultural Organizations have categories like Libraries, Museums, and Galleries.

A visitor to the site can browse through either the Artist Directory or the Cultural Organizations Directory:

screenshot of navigation menu | user taxonomies for wordpress
Menu options for the directories

Each directory lists categories and each category has its own icon:

screenshot of artists directory | customer user taxonomy for wordpress
The Artist Directory
WordPress user taxonomy by Tadpole Collective
The Cultural Organizations directory

Clicking into a category icon leads to a list of user profiles associated with the category:

screenshot
Artists under the Literary category
screenshot
Cultural Organizations under the Galleries category

And clicking an individual entry brings you to the user profile page:

screenshot
An artist profile
screenshot
A Cultural Organization profile

If you have some experience with WordPress, you may have noticed a pattern. The “category” pages here are strikingly similar to the usual WordPress category archive pages, which list all the posts under a category (e.g. “mywebsite.com/category/news/”). The “directory” pages are analogous to a page that lists all the categories on your website, with each category name being linked to its corresponding category page.

The difference is that we’re categorizing users instead of posts. Weird? A little. But WordPress provides for this. In its way of thinking, “posts” and “users” are sort of the same. They are both “objects” – meaning, they are both things that have a defined structure and certain qualities. As developers, we can take these objects and do things with them – like assign them to categories and list them all out on a page.

Taxonomies

Categories and Tags have a fancy name: Taxonomies. This just means ways of organizing things. All the categories on your WordPress website are collectively known as a taxonomy: the “category” taxonomy. All the tags form the “tag” taxonomy. Individual categories or tags are called “terms”. So every taxonomy is a collection of terms. If your WordPress blog has 17 categories, technically, you have 17 terms in the category taxonomy.

Many of you will be aware that WordPress offers us the ability to create custom taxonomies in addition to Categories and Tags. These are most often applied to posts, or to custom post types. For example, you might create a custom taxonomy (“tax” for short) called “Meal”, with the terms “Breakfast”, “Brunch”, “Lunch”, “Dinner”, and “Dessert”. Then you could organize your posts (presumably for a food blog) for easier navigation and understanding by your users. You might create another tax called Ingredients, with terms like Sugar, Flour, and Wheat, to create another way for users to find the content most relevant to them.

What we decided to do on this project was to create a custom taxonomy attached to the user object, as opposed to the post object. WordPress supports this, although it’s not as straightforward as a custom tax for posts. You have to do a little more manual work. But it works, and with increased usage (should that happen), I’m sure we’ll see increased support from WP core for this method.

Why a custom tax for users?

When we listened to the requirements for the project, it sounded to me exactly like categorizing users. But before we jumped into the idea, we considered a couple of other options.

  • BuddyPress. We considered using the amazing social media plugin BuddyPress. This is a powerful application that instantly gives you forums, groups, messaging, user profiles and more. It’s awesome, but for this project it was too much. Although the users are part of the same community, it’s not really a “social” site – users have a relationship with ArtsWestchester, but the site is not geared to facilitate relationships amongst themselves. Users don’t associate with one another (no groups) or send each other messages, or post forum topics. So we felt BuddyPress was overkill for this.
  • Multisite. Since each user has a blog, maybe multisite was the way to go. This would allow us to create a whole website for every user and have them related to each by domain (e.g. mydomain.artswestchester.org). But users don’t really need a whole website; they only need one page for their profile and ability to create posts and events. And we knew the existing website already had thousands of users, and that not all users would be allowed to create blogs. Multisite made it too complex, so we ruled it out (incidentally we did build a multisite for the client, but all users and everything we’re talking about today are on one site, so for our purposes we can consider this a single site installation).
  • Custom Post Types. We could have created CPTs called “Artists” and “Cultural Organizations”, and categorized those. But this wouldn’t be tied to any user functionality – how could we grant roles and permissions to CPTs? How could we attribute blog posts to a post? There is probably a way to do this using post associations, but it sounded like too much hassle to recreate what WordPress already does out of the box: give users logins and the ability to write posts.

User taxonomies gave us the flexibility to divide users in categories and build archive pages to display them in directories. We’d work with the user object so that people could create accounts and have logins and be assigned to roles and publish blog posts in a traditional manner. Our primary task was to display users in organized lists, and build profile pages pulling in their self-created content. So we went with this.

Building the Foundation

Like many a WordPress project before it, this one began with a Justin Tadlock tutorial. Although that article is two and a half years old – a lifetime in software development – when we began the project it was fresher, and to be honest it still holds plenty of relevance today. It’s likely that WP core has improved upon some of the quirks mentioned in this post, and we welcome feedback from anyone on which parts of our implementation could use adjustment here in 2014 and beyond.

Justin takes us through several steps to form the basis for your custom user taxonomy:

  • Register the taxonomy against the user object using register_taxonomy()
  • Correct the term update count mechanism, as WordPress will only update the term count automatically for posts, and needs a helper function to do it for users.
  • Create the Manage Terms page and adjust its terminology (again, because WP doesn’t do this automatically)
  • Create the mechanism to assign terms to users and save those terms (again, same reason)

As we further researched this, we found a developer named Damian Gostomski had taken all the “missing” core functions from the above list and abstracted them into a class, then built a plugin out of it. The plugin has not been updated for a couple years, and so displays the usual warning about that. We didn’t use the plugin, but we borrowed the class he built for it and made our own, which is working well in WP 3.8.3.

Our plugin is a single file, available here on Github. Since Justin explained all of these functions in his post, I won’t repeat it all here.

Theme Files

That one file is a plugin, which of course belongs in /wp-content/plugins/, but the other files in this demo are all part of the theme, and therefore go into /wp-content/themes/yourtheme/. There are 3 necessary files:

  • author.php. This is the standard WordPress user profile page, available with many themes including all default WP themes since Twenty Ten. Typically it just lists all blog posts by a user. Here we’ll do a bit more with it.
  • taxonomy-{taxonomy}.php. In our case, as we’ve created two taxonomies, we have taxonomy-artist.php and taxonomy-culturalorg.php. This is because we have named our taxonomies ‘artist’ and ‘culturalorg’ in the plugin above. These templates will build the archive pages, for example https://artswestchester.org/cultural-organizations/directory/arts-council/
  • page-{id}.php. This is what we’ll use for the directory pages, where we list all the terms under a taxonomy. This is just a standard specialized page template, targeting a specific page on your site (you can also use a custom page template if you prefer). In our example, we use page-274.php and page-1059.php.

Let’s go through each of these in a bit of detail.

author.php

Here we have a single file which will be used to display all user profiles, and it’s time to admit that I wasn’t telling the whole truth when I said there were two kinds of users – there are actually four. Artists break down into plain “artists” and “artist members”, the latter of which are dues-paying members of the organization who get to create blog posts and events. Regular, nonpaying “artists” can have a basic profile only. There are also Teaching Artists, which for various reasons we don’t need to go into here, we created as its own taxonomy rather than a term within the Artist taxonomy. And finally, a subset of Cultural Organizations are known as Affiliates.

Since these various roles have slight differences in their profile information, we use if-then statements in the template to show or hide different fields according to role.

The first thing we do in the file is determine what type of user we’re displaying, by looking up the slug:

$curauth = (get_query_var('author_name')) ? get_user_by('slug', get_query_var('author_name')) : get_userdata(get_query_var('author'));

Here we define the variable $curauth to be the current author (or user). We’ll use this variable throughout the file when we need to pull user data, for example when we want to display the user’s name a few lines down:

echo $curauth->display_name;

For each piece of content that is displayed only for certain users, we’ll determine the role of the user before deciding to display it or not. We’ll repeat this technique throughout the template:

  • First, get the ID of our current user by referring back to $curauth
  • Using get_user_meta(), we’ll look to see if the user is assigned to any of the roles that display this particular content
  • If the user is so assigned, show the content

As an example, in lines 27-40, we’re trying to determine if the user is a Cultural Org or an Affiliate. If they are either of these, we will show the terms they are associated with under the ‘culturalorg’ taxonomy. In lines 42-55, we look to see if the user is an Artist or Artist Member, in which case we’ll display the terms from the ‘artist’ taxonomy.

We display the list of associated terms using wp_get_object_terms(). This is similar to wp_get_post_terms() except that it’s not as specific – we can use it for any object, not just posts.

Later in the template, we want to display the user’s blog posts. This is similar to what you would normally find in the author.php file – just a new WP_Query to form a loop of the user’s posts (lines 211-221).

taxonomy-artist.php

I’ll use this file as an example, but all of this applies to taxonomy-culturalorg.php as well.

This template will create an archive page for every term in the ‘artist’ taxonomy. We will use get_objects_in_term() to return a list of all user IDs assigned to the term in question. So when we’re on the Literary page, this returns a list of users associated with the term Literary.

Once we have a list of user IDs, we set up a foreach loop to display the information we want for each user. Plug the user ID into a get_user_meta call – or in some cases, get_author_meta – and output your content. In our case, we’re displaying a profile image (we created a custom field for this, but you could also use the avatar), display name, the categories the user belongs to, the artist statement (another custom field), and a link to the full profile page (see lines 68-98).

You may have noticed that this looks more complicated than it needs to be. One of the things we learned while building this was that we didn’t have many options for controlling the order in which the users are displayed. When we call for the list of users for the relevant terms, get_objects_in_term() only allows for either ASC or DESC order – in our case, that means ascending or descending order of user ID, which isn’t very useful here. Because the client wanted them in alphabetical order by display name, we resorted to SQL statements (see line 55).

page-1059.php

Again, we’ll stick to just the artist example – the same applies to cultural orgs in page-274.php.

This page is the one folks will land on when they click “Artist Directory” on the main menu: https://artswestchester.org/directory/.

This is an actual WordPress page called “Artist Directory” with the slug /directory/. There is some content written on this page (“There are over 1,000 artists living and working in Westchester County…”) so we need to make sure that our template outputs the actual page content before getting into the category listings. Basically, this is the template for a page of posts – except, in our case, it’s a page of users.

There’s an additional piece here, in that we have associated an image with each term using a great little plugin called Taxonomy Images. This allows us to upload images to each term via the dashboard very simply. To display the images, we’re using apply_filters according to the plugin author’s instructions, and specifying the taxonomy we want. Then it’s just a matter of another foreach loop outputting each term image as a list item linking back to the term archive. Et voilà!

Feedback

Let me know what you think! I will update this post based on questions and input from the meetup tonight.