How to create user personal pages with information from their meta profile fields?

The question:

I asked here how to create virtual pages with information from the user meta profile fields using an external class. I got a good answer and also I have implemented it successfully (thanks to @g-m!). At the same time, the author of answer suggested me that what I want can be done “in a lot simpler and more performant way“, using the core WP functions, just creating a real post type: registering a CPT, maybe not public (so no UI is showed on dashboard), and on user profile saving/updating just created/updated CPT entry.

This is how I created and saved initially some extra user profile fields:

/* Add Extra Fields to the User Profile */
add_action( 'show_user_profile', 'extra_user_profile_fields' );
add_action( 'edit_user_profile', 'extra_user_profile_fields' );

function extra_user_profile_fields( $user ) { ?>
   <h3><?php _e("Academic", "blank"); ?></h3>
   <table class="form-table">
      <!-- Teaching position -->
      <th><label for="teaching_position"><?php _e("Teaching position"); ?></label></th>
      <input type="text" name="teaching_position" id="teaching_position" value="<?php echo esc_attr( get_the_author_meta( 'teaching_position', $user->ID ) ); ?>" class="regular-text" /><br />
      <span class="description"><?php _e("Put here your teaching position"); ?></span>

/* Save Extra User Profile Fields */
add_action( 'personal_options_update', 'save_extra_user_profile_fields' );
add_action( 'edit_user_profile_update', 'save_extra_user_profile_fields' );

function save_extra_user_profile_fields( $user_id ) {
   if ( !current_user_can( 'edit_user', $user_id ) ) { return false; }
   update_user_meta( $user_id, 'teaching_position', $_POST['teaching_position'] );

What I want, is to put on my site a list of department members with links to their personal pages (e.g. with some content from their profiles. Pages must be created automatically, so I do not have to create manually a personal page for each new member. How can this be achieved (except the cited solution)?

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

First of all register the cpt. Probably if you don’t want add the UI for that post type you can set the show_ui to false.

$args = array(
  'label' => 'Members',
  'public' => true,
  'exclude_from_search' => true,
  'show_ui' => false,
  'show_in_nav_menus' => false,
  'show_in_menu' => false,
  'show_in_admin_bar' => false,
  'hierarchical' => false,
  'has_archive' => true,
  'publicly_queryable' => true,
  'rewrite' => true,
  'query_var' => 'member'
register_post_type( 'member', $args );

After that create a function that accept an user id and create the post content

function create_member_page( $user_id = '' ) {
  $user = new WP_User($user_id);
  if ( ! $user->ID ) return '';

  // check if the user whose profile is updating has already a post
  global $wpdb;
  $member_post_exists = $wpdb->get_var( $wpdb->prepare(
   "SELECT ID FROM $wpdb->posts WHERE post_name = %s AND post_type = 'member' and post_status = 'publish'", $user->user_nicename
  ) );

  // you have a custom role for members?
  // you should, because if not all user will have a page, also admin, subscribers...
  // if ( ! in_array('member', $user->roles) ) return '';

  $user_info = array_map( function( $a ){ return $a[0]; }, get_user_meta( $user->ID ) );
  $title = $user_info['first_name'] . ' ' . $user_info['last_name'];
  // of course create the content as you want
  $content = 'This is the page for: ';
  $content .= $user_info['first_name'] . ' ' .$user_info['last_name'];
  $post = array(
    'post_title' => $title,
    'post_name' => $user->user_nicename,
    'post_content' => $content,
    'post_status' => 'publish',
    'post_type' => 'member'
  if ( $member_post_exists ) {
    $post['ID'] = $member_post_exists;
    wp_update_post( $post );
  } else {
    wp_insert_post( $post );

Now you have to run this function on every user creation / updating

add_action( 'personal_options_update', 'create_member_page' );
add_action( 'edit_user_profile_update', 'create_member_page' );

That’s all.

Now you can create your archive-member.php template to show all members, you can get them using WP_Query or get_posts.

And, you can create the template single-member.php to show the member profile, that’s a real page, not a virtual one, for this reason you can use all the WP function with it.

If you want a link to the member page, of course you can use get_permalink($postid); but this is not an intuitive way, you probably prefer to get link from an user id or name, so let’s create anothe function:

function member_permalink ( $user = '' ) {
  if ( ! empty($user) ) {
    if ( is_numeric($user) ) { // user id
      $userObj = get_user($user);
    } else { // user nicename
      $userObj = -1;
  } else {
     $userObj = wp_get_current_user();
     $name = isset($userObj->user_nicename) ? $userObj->user_nicename : '';
  if ( ! isset($name) ) $name = $userObj == -1 ? $user : $userObj->user_nicename;
  global $wpdb;
  $id = $wpdb->get_var( $wpdb->prepare(
    "SELECT ID FROM $wpdb->posts WHERE post_name = %s AND post_type = 'member' AND post_status = 'publish'",
  ) );
  return $id ? get_permalink($id) : '';

This function is flexible, you can use it like: member_permalink($user_id) also you can use it like member_permalink($user_nicename).

If the current logged user is a member, you can use the function like member_permalink() to retrive the url of the current logged-in member.

Note the code is untested, and written here, without syntax highlight, so there are chances for typos…

A side note

Some time ago an user asked if was better generate ‘staff’ content from pages (like present solution) or from user meta fields. In that occasion I opted for second option. That’s because your question is different (virtual pages vs real pages), however read that question / answer can be useful for you.. Find it here.

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment