You are here

avatar_selection.module in Avatar Selection 7

The Avatar Selection module allows the user to pick an avatar image from a list already loaded by an administrative user, and to the administrator to disable uploading other avatar files by the user.

File

avatar_selection.module
View source
<?php

/**
 * @file
 * The Avatar Selection module allows the user to pick an avatar image from a
 * list already loaded by an administrative user, and to the administrator to
 * disable uploading other avatar files by the user.
 */

/**
 * Implements hook_help().
 */
function avatar_selection_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/help#avatar_selection':
      $output .= '<p>' . t('Allows the user to pick an avatar from a list.') . '</p>';
      return $output;
    case 'admin/modules#description':
      return t('Allows the user to pick an avatar from a list.');
    case 'admin/config/people/avatar_selection/images':
      return t('Upload images to display as a user avatar.  These images can be anything you like, but it is recommended that you maintain a uniform icon size for all of your avatars.  Maximum dimensions are 85x85 and the maximum size is 30 kB.');
  }
}

/**
 * Implements hook_permission().
 *
 * Define the permissions this module uses.
 */
function avatar_selection_permission() {
  return array(
    'administer avatar selection' => array(
      'title' => t('administer avatar selection'),
      'description' => t('Allows uploading and deleting avatar images and altering the configuration settings.'),
    ),
    'access avatars' => array(
      'title' => t('access avatars'),
      'description' => t('Allows viewing the avatar selection list and selecting an avatar from it.'),
    ),
    'upload avatar in profile' => array(
      'title' => t('upload avatar in profile'),
      'description' => t('Allows uploading an avatar image even if uploads are disabled in the module settings.'),
    ),
  );
}

/**
 * Implements hook_node_access().
 *
 * Define what does each permission means :
 * 'view' is included in the 'access avatars' permission; while 'create',
 * 'update' and 'delete' are included in the 'administrer avatar selection'.
 *
 * @param $op
 *   The action the user wants to do after the function checks the permission.
 * @param $node
 *   The node where the specific permission is requested.
 * @return
 *   The access needed to perform a certain operation.
 */
function avatar_selection_node_access($node, $op, $account) {
  if ($op == 'view') {
    return user_access('access avatars');
  }
  elseif ($op == 'create' || $op == 'update' || $op == 'delete') {
    return user_access('administer avatar selection');
  }
}

/**
 * Implements hook_menu().
 */
function avatar_selection_menu() {
  $items = array();
  $items['admin/config/people/avatar_selection'] = array(
    'title' => 'Avatar Selection',
    'description' => 'Allows the user to upload and delete avatars.',
    'file' => 'avatar_selection.admin.inc',
    'page callback' => 'avatar_selection_settings_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer avatar selection',
    ),
  );
  $items['admin/config/people/avatar_selection/config'] = array(
    'title' => 'Configure',
    'description' => 'Allows the user to configure avatar settings.',
    'file' => 'avatar_selection.admin.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'avatar_selection_config_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer avatar selection',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/config/people/avatar_selection/upload'] = array(
    'title' => 'Upload',
    'description' => 'Allows the user to upload avatars.',
    'file' => 'avatar_selection.admin.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'avatar_selection_upload_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer avatar selection',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => -9,
  );
  $items['admin/config/people/avatar_selection/edit'] = array(
    'title' => 'Manage avatars',
    'description' => 'Allows the user to modify or delete an avatar from a list.',
    'file' => 'avatar_selection.admin.inc',
    'page callback' => 'avatar_selection_roles_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer avatar selection',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/config/people/avatar_selection/edit/role/%'] = array(
    'title' => 'Manage avatars',
    'description' => 'Allows the user to modify or delete an avatar from a list.',
    'file' => 'avatar_selection.admin.inc',
    'page callback' => 'avatar_selection_roles_page',
    'page arguments' => array(
      'op' => 'role',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer avatar selection',
    ),
  );
  if (module_exists('og')) {
    $items['admin/config/people/avatar_selection/edit/og/%'] = array(
      'title' => 'Manage avatars',
      'description' => 'Allows the user to modify or delete an avatar from a list.',
      'file' => 'avatar_selection.admin.inc',
      'page callback' => 'avatar_selection_roles_page',
      'page arguments' => array(
        'op' => 'og',
      ),
      'access callback' => 'user_access',
      'access arguments' => array(
        'administer avatar selection',
      ),
    );
  }
  return $items;
}

/**
 * Implements hook_form_alter().
 *
 * Create the form structure for adding an avatar in the user registration page.
 *
 * @param &$form
 *   General reference used in drupal, defining the structure & the fields of
 *   a form.
 * @param $form_state
 *   General variable, used to control the processing of the form.
 * @param $form_id
 *   The default is 'user_register'; hold the page where the function is being
 *   used.
 * @return
 *   Return the structure of the form.
 */
function avatar_selection_form_user_register_form_alter(&$form, &$form_state, $form_id = 'user_register_form') {

  // If user pictures aren't enabled, nothing to do here.
  if (!variable_get('user_pictures', 0)) {
    return;
  }
  $anon_user = drupal_anonymous_user();

  // See if user has access to avatars.
  $disable_upload = variable_get('avatar_selection_disable_user_upload', 0) && !user_access('upload avatar in profile');
  if (!user_access('access avatars')) {

    // If uploads also disabled, remove the field altogether.
    if ($disable_upload) {
      unset($form['picture']);
    }

    // To allow random avatars to be assigned on registration.
    $form['#validate'][] = 'avatar_selection_validate_user_avatar';
    return $form;
  }

  // We find out the current page number.
  $page = 0;
  if (isset($_GET['page']) && is_numeric($_GET['page'])) {
    $page = $_GET['page'];
  }
  $force_choose = module_exists('reg_with_pic') ? FALSE : variable_get('avatar_selection_force_user_avatar_reg', 0);
  $avatars_per_page = variable_get('avatar_selection_avatar_per_page', 30);
  $selects = _avatar_selection_image_list($anon_user, '', 0, $page * $avatars_per_page, $avatars_per_page);
  if (count($selects['avatars'])) {
    drupal_add_css(drupal_get_path('module', 'avatar_selection') . '/avatar_selection.css');
    $upload = 1;
    if (empty($form['picture']) || !is_array($form['picture']) || !$form['picture']['#access']) {
      $upload = 0;

      // I.e. Not provided by 'register with picture' contributed module.
      $form['picture'] = array(
        '#type' => 'fieldset',
        '#title' => t('Picture'),
        '#weight' => 1,
        '#access' => TRUE,
      );
    }
    $form['picture']['#access'] = TRUE;
    drupal_add_js(drupal_get_path('module', 'avatar_selection') . '/js/avatar_selection_pager.js');
    $form['picture']['select_avatar'] = array(
      '#type' => 'radios',
      '#title' => $upload == 0 ? t('Select an avatar') : t('Or simply select an icon'),
      '#description' => $upload ? '' : t('Your virtual face or picture.'),
      '#options' => $selects['avatars'],
      '#required' => $force_choose ? TRUE : FALSE,
      '#attributes' => array(
        'class' => array(
          'user-avatar-select',
        ),
      ),
      '#prefix' => '<div id="avatar-selection-loading"></div>',
      '#suffix' => theme('avatar_selection_pager', array(
        'total' => $selects['total'],
        'limit' => $avatars_per_page,
      )),
    );
    $form['#validate'][] = 'avatar_selection_validate_user_avatar';
  }
  else {
    $form['#validate'][] = 'avatar_selection_validate_user_avatar';
  }
  $js_file = drupal_get_path('module', 'avatar_selection') . '/js/avatar_selection.js';
  drupal_add_js($js_file);
  return $form;
}

/**
 * Implements hook_form_alter().
 *
 * Create the form structure for adding an avatar in the user profile page.
 *
 * @param &$form
 *   General reference used in drupal, defining the structure & the fields of
 *   a form.
 * @param $form_state
 *   General variable, used to control the processing of the form.
 * @param $form_id
 *   The default is 'user_profile_form'; holds the form name.
 * @return
 *   Return the structure of the form.
 */
function avatar_selection_form_user_profile_form_alter(&$form, &$form_state, $form_id = 'user_profile_form') {

  // If user pictures aren't enabled, nothing to do here.
  if (!variable_get('user_pictures', 0) || empty($form['picture'])) {
    return;
  }
  $user = user_load($form['#user']->uid);

  // See if user has access to avatars.
  $disable_upload = variable_get('avatar_selection_disable_user_upload', 0) && !user_access('upload avatar in profile');
  if (!user_access('access avatars')) {

    // If uploads also disabled, remove the field altogether.
    if ($disable_upload) {
      unset($form['picture']);
    }
    return;
  }

  // We find out the current page number.
  $page = 0;
  if (isset($_GET['page']) && is_numeric($_GET['page'])) {
    $page = $_GET['page'];
  }
  if (is_array($form['picture'])) {
    drupal_add_css(drupal_get_path('module', 'avatar_selection') . '/avatar_selection.css');
    $force_choose = variable_get('avatar_selection_force_user_avatar', 0);

    // If upload support has been disabled, remove the ability to upload
    // pictures.
    if ($disable_upload) {
      unset($form['picture']['picture_upload']);
      if ($force_choose) {
        unset($form['picture']['picture_delete']);
      }

      // Don't force a selection if the user already has a picture.
      $force_choose = is_object($user->picture) ? FALSE : $force_choose;
    }
    else {

      // Picture uploads allowed, so don't force a selection.
      $force_choose = FALSE;
    }

    // Show selection options.
    $avatars_per_page = variable_get('avatar_selection_avatar_per_page', 30);
    $selects = _avatar_selection_image_list($user, '', 0, $page * $avatars_per_page, $avatars_per_page);
    if (count($selects['avatars']) && isset($form['picture']['#type'])) {
      drupal_add_js(drupal_get_path('module', 'avatar_selection') . '/js/avatar_selection_pager.js');
      $form['picture']['select_avatar'] = array(
        '#type' => 'radios',
        '#title' => $disable_upload ? t('Select an avatar') : t('Or simply select an icon'),
        '#description' => $disable_upload ? t('Your virtual face or picture.') : '',
        '#options' => $selects['avatars'],
        '#required' => $force_choose ? TRUE : FALSE,
        '#attributes' => array(
          'class' => array(
            'user-avatar-select',
          ),
        ),
        '#prefix' => '<div id="avatar-selection-loading"></div>',
        '#suffix' => theme('avatar_selection_pager', array(
          'total' => $selects['total'],
          'limit' => $avatars_per_page,
        )),
      );
      $form['picture']['#access'] = TRUE;
      $form['#validate'][] = 'avatar_selection_validate_user_avatar';
    }
    else {
      $form['#validate'][] = 'avatar_selection_validate_user_avatar';
    }
    $js_file = drupal_get_path('module', 'avatar_selection') . '/js/avatar_selection.js';
    drupal_add_js($js_file);
  }
  return $form;
}

/**
 * Validate and upload the user's picture.
 *
 * @param &$form
 *   General reference used in drupal, defining the structure & the fields of a
 *   form.
 * @param &$form_state
 *   General reference, used to control the processing of the form.
 */
function avatar_selection_validate_user_avatar(&$form, &$form_state) {

  // If required, validate the uploaded picture.
  $validators = array(
    'file_validate_is_image' => array(),
    'file_validate_image_resolution' => array(
      variable_get('user_picture_dimensions', '85x85'),
    ),
    'file_validate_size' => array(
      variable_get('user_picture_file_size', 30) * 1024,
    ),
  );
  $file = file_save_upload('picture_upload', $validators);

  // No file upload, but avatar selection set.
  if (!$file && !empty($form_state['values']['select_avatar'])) {
    $path = file_build_uri('avatar_selection') . '/';

    // Load file.
    $file = file_load($form_state['values']['select_avatar']);
    $file->status = 0;
    $file = file_copy($file, 'temporary://' . $file->filename);
    $form_state['values']['picture_upload'] = $file;
  }
  elseif (!$file && variable_get('avatar_selection_set_random_default', FALSE)) {
    if (!empty($form_state['user']->uid)) {
      $account = $form_state['user'];
    }
    else {
      $account = drupal_anonymous_user();
    }
    $account->original = entity_load_unchanged('user', $account->uid);
    if (empty($account->original->picture) || isset($form_state['values']['picture_delete']) && $form_state['values']['picture_delete'] == 1) {
      $random_file = avatar_selection_get_random_image($account->original);
      if ($random_file) {
        $random_file->status = 0;
        $random_file = file_copy($random_file, 'temporary://' . $random_file->filename);
        $form_state['values']['picture_upload'] = $random_file;
      }
    }
  }
}

/**
 * Implements hook_entity_presave().
 *
 * Handle saving of selected avatar. This function is not used if reg_with_pic
 * module is enabled to avoid duplication.
 */
function avatar_selection_entity_presave($account, $type) {

  // Only handle new users that have an uploaded picture.
  // user_save() will handle old users.
  if ($type != 'user' || !$account->is_new || empty($account->picture) || !is_object($account->picture) || module_exists('reg_with_pic')) {
    return;
  }
  $picture = $account->picture;
  $info = image_get_info($picture->uri);
  $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');

  // Set the new uid since it is typically not set at this point. user_save()
  // will use this uid if set so no problems setting it early here.
  if (empty($account->uid)) {
    $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')
      ->fetchField());
  }

  // Prepare the pictures directory.
  file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
  $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']);

  // Move the temporary file into the final location.
  if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
    $picture->status = FILE_STATUS_PERMANENT;
    $account->picture = file_save($picture);
    file_usage_add($picture, 'user', 'user', $account->uid);
  }

  // Update user record with picture fid.
  if (isset($account->picture->fid)) {
    $account->picture = $account->picture->fid;
  }
}

/**
 * Implements hook_user_insert().
 */
function avatar_selection_user_insert(&$edit, $account, $category) {
  if (!empty($edit['select_avatar'])) {
    $usage = new stdClass();
    $usage->uid = $account->uid;
    $usage->fid = $edit['select_avatar'];
    drupal_write_record('avatar_selection_usage', $usage);
  }
}

/**
 * Implements hook_user_update().
 */
function avatar_selection_user_update(&$edit, $account, $category) {
  if (!empty($edit['select_avatar'])) {
    $usage = new stdClass();
    $usage->uid = $account->uid;
    $usage->fid = $edit['select_avatar'];
    $has_rows = (bool) db_query_range('SELECT 1 FROM {avatar_selection_usage} WHERE uid = :uid', 0, 1, array(
      ':uid' => $account->uid,
    ))
      ->fetchField();
    if ($has_rows) {
      drupal_write_record('avatar_selection_usage', $usage, 'uid');
    }
    else {
      drupal_write_record('avatar_selection_usage', $usage);
    }
  }
}

/**
 * Implements hook_user_delete().
 */
function avatar_selection_user_delete($account) {
  db_delete('avatar_selection_usage')
    ->condition('uid', $account->uid)
    ->execute();
}

/**
 * Implements hook_user_cancel().
 */
function avatar_selection_user_cancel($edit, $account, $method) {
  db_delete('avatar_selection_usage')
    ->condition('uid', $account->uid)
    ->execute();
}

/**
 * Select a random avatar picture for a certain user.
 *
 * @param $user
 *   User object.
 * @return
 *   Return the file object of the image to be used as an avatar.
 */
function avatar_selection_get_random_image($user) {
  $avatars = _avatar_selection_image_list($user);
  if ($avatars['total'] > 0) {
    $fid = array_rand($avatars['avatars'], 1);
    if ($fid) {
      $avatar = file_load($fid);
      return $avatar;
    }
  }
  return FALSE;
}

/**
 * Get the list of avatars available to a certain user.
 *
 * @param $user
 *   User object (optional).
 * @param $set_type
 *   Set type, can be 'role' or 'og' (optional).
 * @param $set_id
 *   The unique identifier of the set (optional).
 * @param $from
 *   The offset.
 * @param $count
 *   Number of avatars to return.
 * @return
 *   Return an array with the list of avatars for the current user, together
 *   with the number of avatars.
 */
function _avatar_selection_image_list($user = '', $set_type = '', $set_id = 0, $from = 0, $count = 0) {
  $avatars = array();
  $url = file_build_uri('avatar_selection');
  file_prepare_directory($url);
  $total = 0;

  // Prepare base query.
  $count_query = db_select('avatar_selection', 'avs')
    ->fields('avs', array(
    'fid',
  ))
    ->distinct();
  $query = db_select('avatar_selection', 'avs')
    ->fields('avs', array(
    'avatar',
    'fid',
    'name',
    'weight',
  ))
    ->distinct()
    ->orderBy('weight', 'ASC')
    ->orderBy('name', 'ASC')
    ->orderBy('avatar', 'ASC');

  // If we're searching on a particular role - join avatar_selection_roles table.
  if ($set_type == 'role') {
    if ($set_id) {
      $count_avsr_alias = $count_query
        ->join('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid AND avsr.rid = :rid', array(
        ':rid' => $set_id,
      ));
      $avsr_alias = $query
        ->join('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid AND avsr.rid = :rid', array(
        ':rid' => $set_id,
      ));
    }
    else {
      $count_avsr_alias = $count_query
        ->leftJoin('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid');
      $count_query
        ->isNULL("{$count_avsr_alias}.rid");
      $avsr_alias = $query
        ->leftJoin('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid');
      $query
        ->isNULL("{$avsr_alias}.rid");
    }
  }
  elseif ($set_type == 'og') {
    if ($set_id) {
      $count_avs_og_alias = $count_query
        ->join('avatar_selection_og', 'avs_og', 'avs.aid = avs_og.aid AND avs_og.ogid = :ogid', array(
        ':ogid' => $set_id,
      ));
      $avs_og_alias = $query
        ->join('avatar_selection_og', 'avs_og', 'avs.aid = avs_og.aid AND avs_og.ogid = :ogid', array(
        ':ogid' => $set_id,
      ));
    }
    else {
      $count_avs_og_alias = $count_query
        ->leftJoin('avatar_selection_og', 'avs_og', 'avs.aid = avs_og.aid');
      $count_query
        ->isNULL("{$count_avs_og_alias}.ogid");
      $avs_og_alias = $query
        ->leftJoin('avatar_selection_og', 'avs_og', 'avs.aid = avs_og.aid');
      $query
        ->isNULL("{$avs_og_alias}.ogid");
    }
  }
  elseif (!empty($user)) {

    // Set up some variables.
    $user_roles = array_keys($user->roles);
    $user_og = array();
    if (module_exists('og') && !empty($user->group_audience)) {
      $og_groups = $user->group_audience;
      if (!empty($og_groups) && is_array($og_groups)) {
        foreach ($og_groups as $key => $groups) {
          foreach ($groups as $group) {
            $user_og[] = $group['gid'];
          }
        }
      }
    }

    // Organic groups enabled - join avatar_selection_og table.
    if (module_exists('og') && !empty($user_og)) {
      if (variable_get('avatar_selection_distinctive_avatars', FALSE) || $user->uid != 1) {
        $count_avs_og_alias = $count_query
          ->leftJoin('avatar_selection_og', 'avs_og', 'avs.aid = avs_og.aid');
        $count_query
          ->condition(db_or()
          ->isNULL("{$count_avs_og_alias}.ogid")
          ->condition("{$count_avs_og_alias}.ogid", $user_og, 'IN'));
        $avs_og_alias = $query
          ->leftJoin('avatar_selection_og', 'avs_og', 'avs.aid = avs_og.aid');
        $query
          ->condition(db_or()
          ->isNULL("{$avs_og_alias}.ogid")
          ->condition("{$avs_og_alias}.ogid", $user_og, 'IN'));
      }
    }

    // Distinct avatars are enabled - join avatar_selection_usage and avatar_selection_roles tables.
    if (variable_get('avatar_selection_distinctive_avatars', FALSE)) {
      $count_usage_alias = $count_query
        ->leftJoin('avatar_selection_usage', 'u', 'u.fid = avs.fid');
      $count_query
        ->isNull('u.fid');
      $count_avsr_alias = $count_query
        ->leftJoin('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid');
      $count_query
        ->condition(db_or()
        ->isNULL("{$count_avsr_alias}.rid")
        ->condition("{$count_avsr_alias}.rid", $user_roles, 'IN'));
      $usage_alias = $query
        ->leftJoin('avatar_selection_usage', 'u', 'u.fid = avs.fid');
      $query
        ->isNull('u.fid');
      $avsr_alias = $query
        ->leftJoin('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid');
      $query
        ->condition(db_or()
        ->isNULL("{$avsr_alias}.rid")
        ->condition("{$avsr_alias}.rid", $user_roles, 'IN'));
    }
    elseif ($user->uid != 1) {
      $count_avsr_alias = $count_query
        ->leftJoin('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid');
      $count_query
        ->condition(db_or()
        ->isNULL("{$count_avsr_alias}.rid")
        ->condition("{$count_avsr_alias}.rid", $user_roles, 'IN'));
      $avsr_alias = $query
        ->leftJoin('avatar_selection_roles', 'avsr', 'avs.aid = avsr.aid');
      $query
        ->condition(db_or()
        ->isNULL("{$avsr_alias}.rid")
        ->condition("{$avsr_alias}.rid", $user_roles, 'IN'));
    }
  }

  // Execute the queries.
  $total = $count_query
    ->countQuery()
    ->execute()
    ->fetchField();
  $count = $count ? $count : $total;
  $result = $query
    ->range($from, $count)
    ->execute();

  // Theme images.
  if (module_exists('image')) {
    $imagecache_preset = variable_get('avatar_selection_imagecache_preset', 0);
  }
  foreach ($result as $avatar) {
    $filepath = $url . '/' . $avatar->avatar;
    if (module_exists('image') && $imagecache_preset && file_valid_uri($filepath)) {
      $avatars[$avatar->fid] = theme('image_style', array(
        'style_name' => $imagecache_preset,
        'path' => $filepath,
        'alt' => $avatar->name,
        'title' => $avatar->name,
      ));
    }
    else {
      $avatars[$avatar->fid] = theme('image', array(
        'path' => $filepath,
        'alt' => $avatar->name,
        'title' => $avatar->name,
      ));
    }
  }
  $selects['avatars'] = $avatars;
  $selects['total'] = $total;
  return $selects;
}

/**
 * Implements hook_file_download().
 *
 * Ensure that user pictures (avatars) are always downloadable.
 *
 * @param $file
 *   The path to the file that will be checked if downloadable.
 * @return
 *   An array containing the file mime type and size, generated by the array()
 *   function, if everything is fine.  NULL, if the file is not downloadable.
 */
function avatar_selection_file_download($file) {
  if (user_access('access content')) {
    $data = explode('/', $file);
    $icon = array_pop($data);
    $folder = implode('/', $data);
    if ('avatar_selection' == $folder) {
      $info = image_get_info(file_prepare_directory($file));
      return array(
        'Content-type: ' . $info['mime_type'],
        'Content-length: ' . $info['file_size'],
      );
    }
    else {
      return NULL;
    }
  }
}

/**
 * Implements hook_theme().
 */
function avatar_selection_theme() {
  return array(
    'avatar_selection_pager' => array(
      'variables' => array(
        'total' => 10,
        'limit' => 10,
        'js_file' => NULL,
      ),
    ),
    'avatar_selection_pager_link' => array(
      'variables' => array(
        'text' => NULL,
        'url' => NULL,
        'page' => 0,
      ),
    ),
  );
}

/**
 * Output themed pager navigation.
 *
 * @param $total
 *   Total number of elements to navigate through.
 * @param $limit
 *   Maximum number of elements to show on one page.
 * @return
 *   HTML formatted pager navigation.
 */
function theme_avatar_selection_pager($variables) {
  $total = $variables['total'];
  $limit = $variables['limit'];
  $path = $_GET['q'];
  $total_pages = ceil($total / $limit);
  $current_page = isset($_GET['page']) ? $_GET['page'] : 0;

  // Calculate various markers within this pager piece:
  // Middle is used to 'center' pages around the current page.
  $middle_page = 5;
  $first_page = $current_page - $middle_page + 1;
  $last_page = $current_page + $middle_page + 1;
  $output = '<div class="avatar-selection-pager-nav">';
  if ($current_page > 0) {
    $output .= theme('avatar_selection_pager_link', array(
      'text' => t('« first'),
      'url' => $path,
      'page' => 0,
    ));
    $output .= theme('avatar_selection_pager_link', array(
      'text' => t('‹ previous'),
      'url' => $path,
      'page' => $current_page - 1,
    ));
  }
  $i = $first_page;

  // Adjust 'center' if at end of query.
  if ($last_page > $total_pages) {
    $i = $i + ($total_pages - $last_page);
    $last_page = $total_pages;
  }

  // Adjust 'center' if at start of query.
  if ($i <= 0) {
    $last_page = $last_page + (1 - $i);
    $i = 1;
  }

  // When there is more than one page, create the pager list.
  if ($total_pages > 1) {
    for (; $i <= $last_page && $i <= $total_pages; $i++) {
      if ($i < $current_page + 1) {
        $output .= theme('avatar_selection_pager_link', array(
          'text' => $i,
          'url' => $path,
          'page' => $i - 1,
        ));
      }
      if ($i == $current_page + 1) {
        $output .= '<strong class="pager-current">' . $i . '</strong>';
      }
      if ($i > $current_page + 1) {
        $output .= theme('avatar_selection_pager_link', array(
          'text' => $i,
          'url' => $path,
          'page' => $i - 1,
        ));
      }
    }
  }
  if ($current_page + 1 < $total_pages) {
    $output .= theme('avatar_selection_pager_link', array(
      'text' => t('next ›'),
      'url' => $path,
      'page' => $current_page + 1,
    ));
    $output .= theme('avatar_selection_pager_link', array(
      'text' => t('last »'),
      'url' => $path,
      'page' => $total_pages - 1,
    ));
  }
  $output .= '</div>';
  return $output;
}

/**
 * Output themed pager navigation link.
 *
 * @param $text
 *   Link (human-readable) text.
 * @param $path
 *   Current page.
 * @param $page
 *   Page number to display.
 * @return
 *   HTML formatted link for pager.
 */
function theme_avatar_selection_pager_link($variables) {
  $text = $variables['text'];
  $path = $variables['url'];
  $page = $variables['page'];
  $url = url($path);
  $output = l($text, $path, array(
    'query' => array(
      'page' => $page,
    ),
  ));
  return $output;
}