You are here

follow.module in Follow 5

Same filename and directory in other branches
  1. 8.2 follow.module
  2. 6 follow.module
  3. 7.2 follow.module
  4. 7 follow.module

Allows users to add links to their social network profiles.

File

follow.module
View source
<?php

/**
 * @file
 *   Allows users to add links to their social network profiles.
 */
define('FOLLOW_NAME', 0);
define('FOLLOW_ME', 1);
define('FOLLOW_US', 2);

/**
 * Implementation of hook_help().
 */
function follow_help($path) {
  switch ($path) {
    case 'follow':
    case 'admin/settings/follow':
      return t('Please copy and paste the url for your public profile or page for each service you would like to display in the block. Links need to match the domain of the service in question.');
  }
}

/**
 * Implementation of hook_menu().
 */
function follow_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/follow',
      'title' => t('Site follow links'),
      'description' => t('Add sitewide follow links'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'follow_links_form',
      ),
      'access' => user_access('edit site follow links'),
    );
  }
  else {
    $items[] = array(
      'path' => 'user/' . arg(1) . '/follow',
      'title' => 'My follow links',
      'description' => 'edit follow links',
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'follow_links_form',
        1,
      ),
      'access' => user_access('follow_links_user_access'),
      'type' => MENU_LOCAL_TASK,
    );

    // Add the Follow CSS.
    drupal_add_css(drupal_get_path('module', 'follow') . '/follow.css');
  }
  return $items;
}

/**
 * Access callback for user follow links editing.
 */
function follow_links_user_access($uid) {
  return ($GLOBALS['user']->uid == $uid && user_access('edit own follow links') || user_access('edit any user follow links')) && $uid > 0;
}

/**
 * Implementation of hook_perm().
 */
function follow_perm() {
  return array(
    'edit own follow links',
    'edit site follow links',
    'edit any user follow links',
    'administer follow',
    'change follow link titles',
  );
}

/**
 * Implementation of hook_block().
 */
function follow_block($op = 'list', $delta = 0, $edit = array()) {
  $function = 'follow_block_' . $op;
  if (function_exists($function)) {
    return $function($delta, $edit);
  }
}

/**
 * Implementation of hook_block_list().
 */
function follow_block_list($delta = 0, $edit = array()) {
  $blocks['site'] = array(
    'info' => t('Follow Site'),
    // We need to cache per role so the edit/configure links display only if user
    // has access.
    'cache' => BLOCK_CACHE_PER_ROLE,
  );
  $blocks['user'] = array(
    'info' => t('Follow User'),
    // Here we need to cache per user so the edit link only shows for the associated
    // account.
    'cache' => BLOCK_CACHE_PER_USER,
  );
  return $blocks;
}

/**
 * Implementation of hook_block_configure().
 */
function follow_block_configure($delta = '', $edit = array()) {
  switch ($delta) {
    case 'site':
      $form['follow_title'] = array(
        '#type' => 'radios',
        '#title' => t('Default block title'),
        '#default_value' => variable_get('follow_site_block_title', FOLLOW_NAME),
        '#options' => array(
          FOLLOW_NAME => t('Follow @name on', array(
            '@name' => variable_get('site_name', 'Drupal'),
          )),
          FOLLOW_ME => t('Follow me on'),
          FOLLOW_US => t('Follow us on'),
        ),
      );
      $form['follow_user'] = array(
        '#type' => 'checkbox',
        '#title' => t('User pages'),
        '#description' => t('Should this block display on user profile pages?'),
        '#default_value' => variable_get('follow_site_block_user', TRUE),
      );
      return $form;
    case 'user':
      $form['follow_title'] = array(
        '#type' => 'radios',
        '#title' => t('Default block title'),
        '#default_value' => variable_get('follow_user_block_title', FOLLOW_NAME),
        '#options' => array(
          FOLLOW_NAME => t('Follow [username] on'),
          FOLLOW_ME => t('Follow me on'),
        ),
      );
      return $form;
  }
}

/**
 * Implementation of hook_block_save().
 */
function follow_block_save($delta = '', $edit = array()) {
  variable_set('follow_' . $delta . '_block_title', $edit['follow_title']);
  if ($delta == 'site') {
    variable_set('follow_site_block_user', $edit['follow_user']);
  }
}

/**
 * Implementation of hook_block_view().
 */
function follow_block_view($delta = '', $edit = array()) {
  switch ($delta) {
    case 'site':
      if (($content = _follow_block_content()) && (variable_get('follow_site_block_user', TRUE) || !(arg(0) == 'user' && is_numeric(arg(1))))) {
        return array(
          'subject' => _follow_block_subject(),
          'content' => $content,
        );
      }
      break;
    case 'user':
      $uid = arg(1);
      if (arg(0) == 'user' && is_numeric($uid) && ($content = _follow_block_content($uid))) {
        return array(
          'subject' => _follow_block_subject($uid),
          'content' => $content,
        );
      }
      break;
  }
}

/**
 * Helper function to build the block title.
 *
 * @param $uid
 *   The uid of the user account.  Defaults to the site form, $uid = 0.
 */
function _follow_block_subject($uid = 0) {
  return follow_link_title($uid) . ':';
}

/**
 * Helper function to create a link or block title.
 *
 * @param $uid
 *   The uid of the user account.  Defaults to the site form, $uid = 0.
 */
function follow_link_title($uid = 0) {

  // Check to see if we have a valid username.
  if ($uid) {
    $setting = variable_get('follow_user_block_title', FOLLOW_NAME);

    // Special handling for usernames.
    if ($setting == FOLLOW_NAME) {
      $account = user_load(array(
        'uid' => $uid,
      ));

      // Set plain to TRUE for realname module support.
      return t('Follow !name on', array(
        '!name' => theme('username', $account),
      ));
    }
    return t('Follow me on');
  }
  switch (variable_get('follow_site_block_title', FOLLOW_NAME)) {
    case FOLLOW_NAME:
      return t('Follow @name on', array(
        '@name' => variable_get('site_name', 'Drupal'),
      ));
    case FOLLOW_ME:
      return t('Follow me on');
    case FOLLOW_US:
      return t('Follow us on');
  }
}

/**
 * Helper function to build the block content display.
 *
 * @param $uid
 *   The uid of the user account.  Defaults to the site form, $uid = 0.
 */
function _follow_block_content($uid = 0) {
  $output = '';
  if ($links = follow_links_load($uid)) {
    $output = theme('follow_links', array(
      'links' => $links,
      'networks' => follow_networks_load($uid),
    ));
    $output .= _follow_block_config_links($uid);
  }
  return $output;
}

/**
 * Theme function to output a list of links.
 *
 * @param $links
 *   An array of follow link objects.
 * @param $networks
 *   An array of network names, keys are machine names, values are visible titles.
 *
 * @ingroup themeable
 */
function theme_follow_links($variables) {
  $links = $variables['links'];
  $networks = $variables['networks'];
  $output = '<div class="follow-links clearfix">';
  foreach ($links as $link) {
    $title = isset($link->title) ? $link->title : '';
    if (empty($title)) {
      $title = $networks[$link->name]['title'];
    }
    $output .= theme('follow_link', array(
      'link' => $link,
      'title' => $title,
    ));
  }
  $output .= '</div>';
  return $output;
}

/**
 * Theme function to print an individual link.
 *
 * @param $link
 *   A follow link object.
 * @param $title
 *   The translated title of the social network.
 *
 * @ingroup themable
 */
function theme_follow_link($variables) {
  $link = $variables['link'];
  $title = $variables['title'];
  $classes = array();
  $classes[] = 'follow-link';
  $classes[] = "follow-link-{$link->name}";
  $classes[] = $link->uid ? 'follow-link-user' : 'follow-link-site';
  $attributes = array(
    'class' => implode(' ', $classes),
    'title' => follow_link_title($link->uid) . ' ' . $title,
  );
  return l($title, $link->path, $attributes) . "\n";
}

/**
 * Outputs a list of configuration links for users with appropriate permissions
 *
 * @param $uid
 *   The uid of the user account.  Defaults to the site form, $uid = 0.
 * @return
 *   A string containing the links, output with theme_links().
 */
function _follow_block_config_links($uid) {
  $links = array();
  if ($uid == 0 && user_access('edit site follow links')) {
    $links['follow_edit'] = array(
      'title' => t('Edit'),
      'href' => 'admin/settings/follow',
      'query' => drupal_get_destination(),
    );
  }
  elseif (follow_links_user_access($uid)) {
    $links['follow_edit'] = array(
      'title' => t('Edit'),
      'href' => 'user/' . $uid . '/follow',
      'query' => drupal_get_destination(),
    );
  }
  if (user_access('administer blocks')) {
    $links['follow_configure'] = array(
      'title' => t('Configure'),
      'href' => $uid ? 'admin/build/block/configure/follow/user' : 'admin/build/block/configure/follow/site',
      'query' => drupal_get_destination(),
    );
  }
  return theme('links', $links, array(
    'class' => 'links inline',
  ));
}

/**
 * The form for editing follow links.
 *
 * @param $uid
 *   The uid of the user account.  Defaults to the site form, $uid = 0.
 *
 * @ingroup forms
 */
function follow_links_form($uid = 0) {
  $form = array();
  $form['uid'] = array(
    '#type' => 'hidden',
    '#value' => $uid,
  );
  $form['follow_links']['#tree'] = TRUE;
  $form['follow_links']['#theme'] = 'follow_links_form';
  $links = follow_links_load($uid);
  $networks = follow_networks_load($uid, TRUE);

  // Put all our existing links at the top, sorted by weight.
  if (is_array($links)) {
    foreach ($links as $name => $link) {
      $title = $networks[$name]['title'];
      $form['follow_links'][$name] = _follow_links_form_link($link, $title, $uid);

      // Unset this specific network so we don't add the same one again below.
      unset($networks[$name]);
    }
  }

  // Now add all the empty ones.
  foreach ($networks as $name => $info) {
    $link = new stdClass();
    $link->name = $name;
    $form['follow_links'][$name] = _follow_links_form_link($link, $info['title'], $uid);
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

/**
 * Helper function to create an individual link form element.
 */
function _follow_links_form_link($link, $title, $uid) {
  $elements = array(
    '#tree' => TRUE,
  );
  $elements['name'] = array(
    '#value' => $title,
  );
  if (isset($link->lid)) {
    $elements['lid'] = array(
      '#type' => 'hidden',
      '#value' => $link->lid,
    );
    $elements['weight'] = array(
      '#type' => 'weight',
      '#default_value' => $link->weight,
    );
  }
  $elements['url'] = array(
    '#type' => 'textfield',
    '#follow_network' => $link->name,
    '#follow_uid' => $uid,
    '#default_value' => isset($link->url) ? $link->url : '',
    '#element_validate' => array(
      'follow_url_validate',
    ),
  );

  // Provide the title of the link only if the link URL is there and the user
  // has the appropriate access.
  $elements['title'] = array(
    '#type' => 'textfield',
    '#default_value' => isset($link->title) ? $link->title : '',
    '#size' => 15,
    '#access' => user_access('change follow link titles') && !empty($link->url),
  );
  return $elements;
}

/**
 * Like drupal_http_build_query() but without urlencodings.
 */
function follow_build_query(array $query, $parent = '') {
  $params = array();
  foreach ($query as $key => $value) {
    $key = $parent ? $parent . '[' . $key . ']' : $key;

    // Recurse into children.
    if (is_array($value)) {
      $params[] = follow_build_query($value, $key);
    }
    elseif (!isset($value)) {
      $params[] = $key;
    }
    else {
      $params[] = $key . '=' . $value;
    }
  }
  return implode('&', $params);
}

/**
 * Build a url for use in the form.
 */
function follow_build_url($path, $options) {
  $url = $path;
  if (!empty($options['query'])) {
    $url .= (strpos($path, '?') !== FALSE ? '&' : '?') . follow_build_query($options['query']);
  }
  if (!empty($options['fragment'])) {
    $url .= '#' . $options['fragment'];
  }
  return $url;
}

/**
 * Split a Drupal path or external link into path and options like a menu link.
 */
function follow_parse_url($url) {
  $parsed_url = parse_url($url);
  $defaults = array(
    'scheme' => '',
    'host' => '',
    'port' => '',
    'path' => '/',
    'query' => '',
    'fragment' => '',
  );
  $parsed_url += $defaults;
  $options = array(
    'query' => array(),
    'fragment' => $parsed_url['fragment'],
  );

  // Parse the query string into an array.
  parse_str($parsed_url['query'], $options['query']);
  if ($parsed_url['scheme']) {
    $parsed_url['scheme'] .= '://';
  }

  // Throw away port for now.
  $path = $parsed_url['scheme'] . $parsed_url['host'] . $parsed_url['path'];
  return array(
    'path' => $path,
    'options' => $options,
  );
}

/**
 * Build a regex to validate the url based on a known service url.
 */
function follow_build_url_regex($network_info) {
  if (!empty($network_info['domain'])) {

    // An external link.
    return '@^https?://([a-z0-9\\-_.]+\\.|)' . str_replace('.', '\\.', $network_info['domain']) . '/@i';
  }

  // An internal link should not have ':'.
  return '@^[^:]+$@';
}

/**
 * Submit handler for the follow_links_form.
 */
function follow_links_form_submit($form_id, $form_state) {
  $links = $form_state['follow_links'];
  foreach ($links as $name => $link) {
    $link = (object) $link;
    $link->url = trim($link->url);

    // Check to see if there's actually a link
    if (empty($link->url)) {

      // If there's an lid, delete the link.
      if (isset($link->lid)) {
        follow_link_delete($link->lid);
      }

      // Continue to the next link.
      continue;
    }
    else {
      $link->uid = $form_state['uid'];
      $link->name = $name;
      follow_link_save($link);
    }
  }
}

/**
 * Theme the drag-and-drop form.
 *
 * Arranges records in a table, and adds the css and js for draggable sorting.
 *
 * @ingroup themeable
 * @ingroup forms
 */
function theme_follow_links_form($form) {
  $rows = array();
  $disabled_rows = array();
  foreach (element_children($form['follow_links']) as $key) {
    $row = array();
    if (isset($form['follow_links'][$key]['weight'])) {
      $row[] = drupal_render($form['follow_links'][$key]['lid']) . drupal_render($form['follow_links'][$key]['name']);
      $row[] = drupal_render($form['follow_links'][$key]['url']);
      if (user_access('change follow link titles')) {
        $row[] = drupal_render($form['follow_links'][$key]['title']);
      }

      // Now, render the weight row.
      $form['follow_links'][$key]['weight']['#attributes']['class'][] = 'follow-links-weight';
      $row[] = drupal_render($form['follow_links'][$key]['weight']);

      // Add the new row to our collection of rows, and give it the 'draggable' class.
      $rows[] = array(
        'data' => $row,
        'class' => array(
          'draggable',
        ),
      );
    }
    else {
      $disabled_rows[] = array(
        drupal_render($form['follow_links'][$key]['name']),
        drupal_render($form['follow_links'][$key]['url']),
      );
    }
  }

  // Render a list of header titles, and our array of rows, into a table.
  $header = array(
    t('Name'),
    t('URL'),
  );
  if (user_access('change follow link titles')) {
    $header[] = t('Customized Name');
  }
  $header[] = t('Weight');
  $disabled_header = array(
    t('Name'),
    t('URL'),
  );
  $output = '';
  if (count($rows)) {
    $output .= theme('table', $header, $rows, array(
      'id' => 'follow-links-weighted-form',
    ));
  }
  if (count($disabled_rows)) {
    $output .= theme('table', $disabled_header, $disabled_rows);
  }
  $output .= drupal_render($form);
  return $output;
}

/**
 * Loader function for individual links.
 *
 * @param $uid
 *   An int containing the uid of the user. uid 0 pulls the site follow links.
 * @return
 *   A single link in array format, or FALSE if none matched the incoming ID.
 */
function follow_links_load($uid = 0) {
  $links = array();
  $sql = "SELECT * FROM {follow_links} WHERE uid = %d ORDER BY weight ASC";
  $result = db_query($sql, $uid);
  while ($link = db_fetch_object($result)) {
    $link->options = unserialize($link->options);
    $link->url = follow_build_url($link->path, $link->options);
    $links[$link->name] = $link;
  }
  return $links;
}

/**
 * Inserts a new link, or updates an existing one.
 *
 * @param $link
 *   A link object to be saved.
 */
function follow_link_save($link) {
  $parsed = follow_parse_url($link->url);
  $link->path = $parsed['path'];
  $link->options = $parsed['options'];
  $name = isset($link->name) ? $link->name : '';
  $uid = isset($link->uid) ? $link->uid : 0;
  $path = isset($link->path) ? $link->path : '';
  $options = serialize(isset($link->options) ? $link->options : array());
  $title = isset($link->title) ? $link->title : '';
  $weight = isset($link->weight) ? $link->weight : 0;
  if (isset($link->lid)) {
    db_query("UPDATE {follow_links} set name = '%s', uid = %d, path = '%s', options = '%s', weight = %d, title = '%s' WHERE lid = %d", $name, $uid, $path, $options, $weight, $title, $link->lid);
  }
  else {
    db_query("INSERT INTO {follow_links} (name, uid, path, options, weight, title) VALUES ('%s', %d, '%s', '%s', %d, '%s')", $name, $uid, $path, $options, $weight, $title);
  }
  return $link;
}

/**
 * Deletes a link, given its unique ID.
 *
 * @param $lid
 *   An int containing the ID of a link.
 */
function follow_link_delete($lid) {
  db_query('DELETE FROM {follow_links} WHERE lid = %d', $lid);
}

/**
 * Loads all follow networks
 *
 * @param $reset
 *   Boolean.  If TRUE, flushes the follow networks cache.
 *
 * @return
 *   An array of network names, keys are machine names, values are visible titles.
 */
function follow_networks_load($uid, $reset = FALSE) {
  static $networks = array();

  // Clear cache if $reset is TRUE;
  if ($reset) {
    $networks = array();
  }

  // Return presets if the array is populated.
  if (empty($networks[$uid])) {

    // We call hook_follow_networks_alter() to allow other modules to create
    // or alter networks.
    $networks[$uid] = follow_default_networks($uid);
    module_invoke_all('follow_networks_alter', $networks);
  }
  return $networks[$uid];
}

/**
 * Implementation of hook_follow_networks().
 *
 * @return
 *   An array of network names, keys are machine names, values are visible titles.
 */
function follow_default_networks($uid) {
  $networks = array(
    'facebook' => array(
      'title' => t('Facebook'),
      'domain' => 'facebook.com',
    ),
    'virb' => array(
      'title' => t('Virb'),
      'domain' => 'virb.com',
    ),
    'myspace' => array(
      'title' => t('MySpace'),
      'domain' => 'myspace.com',
    ),
    'twitter' => array(
      'title' => t('Twitter'),
      'domain' => 'twitter.com',
    ),
    'picasa' => array(
      'title' => t('Picasa'),
      'domain' => 'picasaweb.google.com',
    ),
    'flickr' => array(
      'title' => t('Flickr'),
      'domain' => 'flickr.com',
    ),
    'youtube' => array(
      'title' => t('YouTube'),
      'domain' => 'youtube.com',
    ),
    'vimeo' => array(
      'title' => t('Vimeo'),
      'domain' => 'vimeo.com',
    ),
    'bliptv' => array(
      'title' => t('blip.tv'),
      'domain' => 'blip.tv',
    ),
    'lastfm' => array(
      'title' => t('last.fm'),
      'domain' => 'last.fm',
    ),
    'linkedin' => array(
      'title' => t('LinkedIn'),
      'domain' => 'linkedin.com',
    ),
    'delicious' => array(
      'title' => t('Delicious'),
      'domain' => 'delicious.com',
    ),
    'tumblr' => array(
      'title' => t('Tumblr'),
      'domain' => 'tumblr.com',
    ),
  );
  if ($uid == 0) {
    $networks['this-site'] = array(
      'title' => t('This site (RSS)'),
      'domain' => '',
    );
  }
  return $networks;
}

Functions

Namesort descending Description
follow_block Implementation of hook_block().
follow_block_configure Implementation of hook_block_configure().
follow_block_list Implementation of hook_block_list().
follow_block_save Implementation of hook_block_save().
follow_block_view Implementation of hook_block_view().
follow_build_query Like drupal_http_build_query() but without urlencodings.
follow_build_url Build a url for use in the form.
follow_build_url_regex Build a regex to validate the url based on a known service url.
follow_default_networks Implementation of hook_follow_networks().
follow_help Implementation of hook_help().
follow_links_form The form for editing follow links.
follow_links_form_submit Submit handler for the follow_links_form.
follow_links_load Loader function for individual links.
follow_links_user_access Access callback for user follow links editing.
follow_link_delete Deletes a link, given its unique ID.
follow_link_save Inserts a new link, or updates an existing one.
follow_link_title Helper function to create a link or block title.
follow_menu Implementation of hook_menu().
follow_networks_load Loads all follow networks
follow_parse_url Split a Drupal path or external link into path and options like a menu link.
follow_perm Implementation of hook_perm().
theme_follow_link Theme function to print an individual link.
theme_follow_links Theme function to output a list of links.
theme_follow_links_form Theme the drag-and-drop form.
_follow_block_config_links Outputs a list of configuration links for users with appropriate permissions
_follow_block_content Helper function to build the block content display.
_follow_block_subject Helper function to build the block title.
_follow_links_form_link Helper function to create an individual link form element.

Constants

Namesort descending Description
FOLLOW_ME
FOLLOW_NAME @file Allows users to add links to their social network profiles.
FOLLOW_US