You are here

tweet.module in Tweet 5.2

Builds links to post pages to twitter.

File

tweet.module
View source
<?php

/**
 * @file
 *   Builds links to post pages to twitter.
 */

/**
 * Implementation of hook_help().
 */
function tweet_help($section = '') {
  $output = '';
  switch ($section) {
    case "admin/help#tweet":
      $output = '<p>' . t("This module builds links to post pages to twitter.  See the function tweet_to_twitter() in the module file for an explanation of how to use it.") . '</p>';
      break;
  }
  return $output;
}

/**
 * Implementation of hook_link().
 */
function tweet_link($type, $node = NULL, $teaser = FALSE) {
  if ($type == 'node' && in_array($node->type, variable_get('tweet_types', _tweet_node_types())) && !_tweet_exclude($node->nid)) {
    $title = variable_get('tweet_title', 1);
    if (!$teaser) {
      $link_type = variable_get('tweet_node', 'icon');
      if ($link_type != 'none') {
        $links['tweet'] = _tweet_to_twitter($link_type, '', $node->nid);
        return $links;
      }
    }
    else {
      $link_type = variable_get('tweet_teaser', 'none');
      if ($link_type != 'none') {
        $links['tweet'] = _tweet_to_twitter($link_type, '', $node->nid);
        return $links;
      }
    }
  }
}

/**
 * Implementation of hook_menu().
 */
function tweet_menu($may_cache) {
  $items = array();
  if (!$may_cache) {
    $items[] = array(
      'path' => 'admin/settings/tweet',
      'title' => t('Tweet settings'),
      'description' => t('Allows administrators to adjust certain display settings for Tweet.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'tweet_admin',
      'access' => user_access('access administration pages'),
      'type' => MENU_NORMAL_ITEM,
    );
  }
  return $items;
}

/**
 * Returns a link from _tweet_to_twitter().
 * @see _tweet_to_twitter()
 */
function tweet_to_twitter($type = 'icon', $title = TRUE, $nid = '', $q = '') {
  $array = _tweet_to_twitter($type, $title, $nid, $q);
  return l($array['title'], $array['href'], $array['attributes'], $array['query'], NULL, TRUE, $array['html']);
}

/**
 * Creates a link to post a URL and optionally title to twitter.  Uses the current page by default.
 *
 * @param $type
 *   Specifies what will show up in the link: the twitter icon, the twitter icon and text, or just text.
 *   Pass 'icon' to show just the icon, 'icon_text' to show the icon and text, and 'text' to show just the text.
 *   Required if display options for nodes are set to 'none' on the settings page.
 * @param $format
 *   A string representing the tweet text, optionally with the case-insensitive tokens [url] and [title].
 *   If not passed, the format from the settings page will be used.
 * @param $nid
 *   The NID of the node for which the twitter link should be constructed.
 * @param $q
 *   The absolute URL of the page for which the twitter link should be constructed.
 *   If this is not the current page, the title must be set manually, or it will be incorrect.
 * @see _tweet_make_url()
 * @see _tweet_get_title()
 * @see _tweet_process()
 * @see tweet_to_twitter()
 * @see tweet_link()
 * @return
 *   A themed link to post the specified or current page to twitter.
 */
function _tweet_to_twitter($type = '', $format = '', $nid = '', $q = '') {
  if ($nid && !$q) {
    $q = url('node/' . $nid, array(
      'absolute' => TRUE,
    ));
  }
  $url = _tweet_make_url($q);
  $title = _tweet_get_title($nid);
  if (!$format) {
    $format = variable_get('tweet_format', '[url] [title]');
  }
  $tweet = _tweet_process($format, array(
    '[url]' => $url,
    '[title]' => $title,
  ));
  $path = 'http://twitter.com/home';
  $text = t('Post to Twitter');
  $image_location = drupal_get_path('module', 'tweet') . '/icon.png';
  global $base_url;
  $image = '<img src="' . $base_url . base_path() . check_plain(variable_get('tweet_image', $image_location)) . '" alt="' . $text . '" title="' . $text . '" />';
  if (!$type) {

    //Note that $type can be 'none', in which case nothing shows up.
    $type = variable_get('tweet_node', 'icon');
  }
  if ($type == 'icon') {
    $show = $image;
  }
  else {
    if ($type == 'icon_text') {
      $show = $image . ' ' . $text;
    }
    else {
      if ($type == 'text') {
        $show = $text;
      }
    }
  }
  $attributes = array(
    'class' => 'tweet',
    'rel' => 'nofollow',
  );
  if (variable_get('tweet_new_window', 'target') == 'target') {
    $attributes['target'] = '_blank';
  }
  else {
    if (variable_get('tweet_new_window', 'target') == 'js') {
      $attributes['onClick'] = "window.open('{$path}?status={$tweet}','twitter','')";
      $path = $_GET['q'];
      $tweet = 'sent';
    }
  }
  return array(
    'title' => $show,
    'href' => $path,
    'attributes' => $attributes,
    'query' => 'status=' . $tweet,
    'html' => TRUE,
  );
}

/**
 * Determines what will be in the tweet itself.
 *
 * @param $format
 *   A string containing the text of the tweet before it gets processed.
 * @param $tokens
 *   An associative array where keys represent text that will be replaced by their value in $format.
 * @see _tweet_to_twitter()
 * @return
 *   The URL-ready tweet text.
 */
function _tweet_process($format = '', $tokens = array()) {
  if (!$format) {
    $format = variable_get('tweet_format', '[url] [title]');
  }
  foreach ($tokens as $search => $replace) {
    $format = str_ireplace($search, $replace, $format);
  }
  $format = drupal_urlencode($format);

  //The #, &, and / characters get double-encoded by drupal_urlencode, but they must appear single-encoded for Twitter to recognize them.

  //Spaces are manually encoded to plus signs for clarity of whitespace at the end of the tweet.
  $format = str_replace(array(
    '%2523',
    '%2526',
    '%252F',
    '%20',
  ), array(
    '%23',
    '%26',
    '%2F',
    '+',
  ), $format);
  return $format;
}

/**
 * Returns the title of the node for which the NID was passed or the current page.
 * Note that there is no good way to get the page title for a page that is not the current page.
 * We assume the title is the same as the title of the node if a node is being viewed, but this is often not the case when certain modules are being used.
 * In this case, it is recommended that you manually pass the title to tweet_to_twitter().
 *
 * @param $nid
 *   The NID of the node for which to return the title.  If not passed, uses the current page.
 * @see _tweet_to_twitter()
 * @return
 *   The title of the node for the NID passed or the title of the current page.
 */
function _tweet_get_title($nid = '') {
  if ($nid) {
    $node = node_load(array(
      'nid' => $nid,
    ));
    $title = $node->title;
  }
  else {
    $title = drupal_get_title();
  }
  if (drupal_strlen($title) > 120) {
    $title = drupal_substr($title, 0, 119) . '';
  }
  return $title;
}

/**
 * Retrieves and beautifies the abbreviated URL.
 *
 * @param $q
 *   The URL of the page for which to create the abbreviated URL.  If not passed uses the current page.
 * @see _tweet_to_twitter()
 * @see _tweet_get_url()
 * @return
 *   An abbreviated URL.
 */
function _tweet_make_url($q = '') {
  if (!$q) {
    $q = url($_GET['q'], array(
      'absolute' => TRUE,
    ));
  }
  $cached = cache_get($q);
  if ($cached->data) {
    return $cached->data;
  }
  $url = _tweet_get_url($q);

  //If the primary service fails, try the secondary service.
  if (!$url) {
    $url = _tweet_get_url($q, variable_get('tweet_service_backup', 'TinyURL'));

    //If the secondary service fails, use the original URL.
    if (!$url) {
      $url = $q;
    }
  }

  //Replace "http://" with "www." if the URL is abbreviated because it's shorter.
  if ($url != $q) {
    $url = drupal_substr($url, 7);
    $url = 'www.' . $url;
  }
  $expire = time() + 60 * 60 * 24 * 7 * 3;
  cache_set($q, 'cache', $url, $expire);
  return $url;
}

/**
 * Gets an abbreviated URL using either CURL or PHP from the appropriate service.
 * Times out after three (3) seconds.
 *
 * @param $original
 *   The URL of the page for which to retrieve the abbreviated URL.
 * @param $service
 *   The service to use to abbreviate the URL.
 *   For services available by default, see tweet_tweet_service().
 * @see _tweet_make_url()
 * @return
 *   An abbreviated URL.
 */
function _tweet_get_url($original, $service = '') {
  if (!$service) {
    $service = variable_get('tweet_service', 'is.gd');
  }
  $skip = FALSE;
  $services = module_invoke_all('tweet_service', $original);
  foreach ($services as $name => $api) {
    if ($service == $name) {
      if (is_string($api)) {
        $url = $api . $original;
        break;
      }
      else {
        if (is_array($api) && $api['custom'] == FALSE) {
          $url = $api['url'] . $original;
          break;
        }
        else {
          if (is_array($api) && $api['custom'] == TRUE) {
            $url = $api['url'];
            $skip = TRUE;
          }
        }
      }
    }
  }

  //If the service isn't found, use the original.
  if (!$url) {
    return $original;
  }
  if (variable_get('tweet_method', 'curl') == 'php' && !$skip) {
    $context = stream_context_create(array(
      'http' => array(
        'timeout' => 3,
      ),
    ));
    $contents = file_get_contents($url, 0, $context);
  }
  else {
    if (variable_get('tweet_method', 'curl') == 'curl' && !$skip) {
      $c = curl_init();
      curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 3);
      curl_setopt($c, CURLOPT_URL, $url);
      $contents = curl_exec($c);
      curl_close($c);
    }
    else {
      $contents = $url;
    }
  }
  if ($contents && drupal_substr($contents, 0, 7) == 'http://') {
    return $contents;
  }
  $method = drupal_strtoupper(variable_get('tweet_method', 'curl'));
  if ($skip) {
    $method = t('A custom method');
  }
  watchdog('tweet', t('%method failed to return an abbreviated URL from %service.', array(
    '%method' => $method,
    '%service' => $service,
  )), WATCHDOG_NOTICE, $url);
  return FALSE;
}

/**
 * Implementation of hook_tweet_service().
 */
function tweet_tweet_service($original) {
  return array(
    'hex.io' => 'http://hex.io/api-create.php?url=',
    'idek.net' => 'http://idek.net/c.php?idek-api=true&idek-ref=drupal_tweet_module&idek-url=',
    'is.gd' => 'http://is.gd/api.php?longurl=',
    'lin.cr' => 'http://lin.cr/?mode=api&full=1&l=',
    'ri.ms' => 'http://ri.ms/api-create.php?url=',
    'th8.us' => 'http://th8.us/api.php?url=',
    'TinyURL' => 'http://tinyurl.com/api-create.php?url=',
    'tr.im' => 'http://api.tr.im/api/trim_simple?url=',
  );
}

/**
 * Excludes certain Node IDs from displaying links.
 *
 * @param $nid
 *   The NID to check for exclusion.
 * @return
 *   TRUE if the node should be excluded, or FALSE if it should not.
 */
function _tweet_exclude($nid) {
  $exclude = explode(',', variable_get('tweet_exclude', ''));
  $excludes = array();
  foreach ($exclude as $check) {
    $excludes[] = trim($check);
  }
  if (!empty($excludes)) {
    if (in_array($nid, $excludes)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Settings page.
 */
function tweet_admin() {
  $form['node_type'] = array(
    '#type' => 'select',
    '#title' => t('Type of link to show on nodes'),
    '#default_value' => variable_get('tweet_node', 'icon'),
    '#options' => array(
      'icon' => 'icon',
      'icon_text' => 'icon_text',
      'text' => 'text',
      'none' => 'none',
    ),
  );
  $form['teaser_type'] = array(
    '#type' => 'select',
    '#title' => t('Type of link to show on teasers'),
    '#default_value' => variable_get('tweet_teaser', 'none'),
    '#options' => array(
      'icon' => 'icon',
      'icon_text' => 'icon_text',
      'text' => 'text',
      'none' => 'none',
    ),
  );
  $form['tweet_new_window'] = array(
    '#type' => 'radios',
    '#title' => t('Open Twitter'),
    '#default_value' => variable_get('tweet_new_window', 'target'),
    '#options' => array(
      0 => t('In same window'),
      'target' => t('In new window with target="_blank" (not XHTML 1.0 Strict compliant)'),
      'js' => t('In new window with JavaScript'),
    ),
  );
  $methods = array();
  if (function_exists('file_get_contents')) {
    $methods['php'] = t('PHP');
  }
  if (function_exists('curl_exec')) {
    $methods['curl'] = t('cURL');
  }
  if (empty($methods)) {
    if (variable_get('tweet_method', 'curl') != 'none') {
      variable_set('tweet_method', 'none');
    }
    $form['tweet_method'] = array(
      '#type' => 'radios',
      '#title' => t('Method'),
      '#description' => '<p>' . t('The method to use to retrieve the abbreviated URL.') . '</p>' . '<p><strong>' . t('Your PHP installation does not support the URL abbreviation feature of the Tweet module.') . '</strong> ' . t('You must compile PHP with either the cURL library or the file_get_contents() function to use this option.') . '</p>',
      '#default_value' => 'none',
      '#options' => array(
        'none' => t('None'),
      ),
      '#disabled' => TRUE,
    );
    $form['tweet_service'] = array(
      '#type' => 'radios',
      '#title' => t('Service'),
      '#description' => t('The service to use to create the abbreviated URL.'),
      '#default_value' => 'none',
      '#options' => array(
        'none' => t('None'),
      ),
    );
    $form['tweet_service_backup'] = array(
      '#type' => 'radios',
      '#title' => t('Backup Service'),
      '#description' => t('The service to use to create the abbreviated URL if the primary service is down.'),
      '#default_value' => 'none',
      '#options' => array(
        'none' => t('None'),
      ),
    );
  }
  else {
    $form['tweet_method'] = array(
      '#type' => 'radios',
      '#title' => t('Method'),
      '#description' => t('The method to use to retrieve the abbreviated URL.'),
      '#default_value' => variable_get('tweet_method', 'curl'),
      '#options' => $methods,
    );
    $all_services = module_invoke_all('tweet_service', $original);
    $services = array();
    foreach ($all_services as $key => $value) {
      $services[$key] = $key;
    }
    $services['none'] = t('None');
    $form['tweet_service'] = array(
      '#type' => 'select',
      '#title' => t('Service'),
      '#description' => t('The service to use to create the abbreviated URL.'),
      '#default_value' => variable_get('tweet_service', 'is.gd'),
      '#options' => $services,
    );
    $form['tweet_service_backup'] = array(
      '#type' => 'select',
      '#title' => t('Backup Service'),
      '#description' => t('The service to use to create the abbreviated URL if the primary service is down.'),
      '#default_value' => variable_get('tweet_service_backup', 'TinyURL'),
      '#options' => $services,
    );
  }
  $node_types = variable_get('tweet_types', array());

  //If all types are selected, un-select them, because the system will still save the result as all selected and it looks better.
  if ($node_types == _tweet_node_types()) {
    $node_types = array();
  }
  $form['types'] = array(
    '#type' => 'select',
    '#multiple' => TRUE,
    '#title' => t('Node types on which to display link'),
    '#description' => t('If no types are selected, the link will appear on all types.  To stop links from appearing on all nodes, choose "none" in the teaser and node display options above.'),
    '#default_value' => $node_types,
    '#options' => _tweet_node_types(),
  );
  $image_location = drupal_get_path('module', 'tweet') . '/icon.png';
  $form['tweet_image'] = array(
    '#type' => 'textfield',
    '#title' => t('Image'),
    '#description' => t('Enter the URL for the image you want to show up if you allow images to appear in your links, relative to your Drupal installation.  Ex.: sites/all/modules/tweet/icon.png'),
    '#default_value' => variable_get('tweet_image', $image_location),
  );
  $form['tweet_exclude'] = array(
    '#type' => 'textfield',
    '#title' => t('Exclude nodes'),
    '#description' => t('Enter the NIDs of nodes which should not have Tweet links, separated by commas.'),
    '#default_value' => variable_get('tweet_exclude', ''),
  );
  $form['tweet_format'] = array(
    '#type' => 'textfield',
    '#title' => t('Format'),
    '#description' => t('Manipulate the elements of the tweet by changing their order, removing them, or adding them (like hashtags).  You can use the case-insensitive tokens [url] and [title].'),
    '#maxlength' => 140,
    '#default_value' => variable_get('tweet_format', '[url] [title]'),
  );
  return system_settings_form($form);
}

/**
 * Validation handler for tweet_admin().
 * @see tweet_admin()
 * @see tweet_admin_validate()
 */
function tweet_admin_validate($form_id, $form_values) {
  if ($form_values['tweet_service'] == $form_values['tweet_service_backup'] && $form_values['tweet_service_backup'] != 'none') {
    form_set_error('tweet_service_backup', t('You must select a backup abbreviation service that is different than your primary service.'));
  }
  if ($form_values['tweet_service'] == 'none' && $form_values['tweet_service_backup'] != 'none') {
    drupal_set_message(t('You have selected a backup URL abbreviation service, but no primary service.  Your URLs will not be abbreviated with these settings.'));
  }
}

/**
 * Submit handler for tweet_admin().
 * @see tweet_admin()
 * @see tweet_admin_validate()
 */
function tweet_admin_submit($form_id, $form_values) {
  variable_set('tweet_node', $form_values['node_type']);
  variable_set('tweet_teaser', $form_values['teaser_type']);
  variable_set('tweet_new_window', $form_values['tweet_new_window']);
  variable_set('tweet_method', $form_values['tweet_method']);
  variable_set('tweet_service', $form_values['tweet_service']);
  variable_set('tweet_service_backup', $form_values['tweet_service_backup']);
  variable_set('tweet_image', $form_values['tweet_image']);
  variable_set('tweet_exclude', $form_values['tweet_exclude']);
  variable_set('tweet_format', $form_values['tweet_format']);

  //If no types are selected, assign all types.
  if ($form_values['types'] == array()) {
    $form_values['types'] = _tweet_node_types();
  }
  variable_set('tweet_types', $form_values['types']);

  //Clear the general cache because changed settings may mean that different URLs should be used.
  cache_clear_all('*', 'cache', TRUE);
  drupal_set_message(t('The configuration options have been saved.'));
}

/**
 * Helper function to provide node types in the format array(TYPE => TYPE).
 */
function _tweet_node_types() {
  $a = array_keys(node_get_types());
  $return = drupal_map_assoc($a);
  return $return;
}

Functions

Namesort descending Description
tweet_admin Settings page.
tweet_admin_submit Submit handler for tweet_admin().
tweet_admin_validate Validation handler for tweet_admin().
tweet_help Implementation of hook_help().
tweet_link Implementation of hook_link().
tweet_menu Implementation of hook_menu().
tweet_to_twitter Returns a link from _tweet_to_twitter().
tweet_tweet_service Implementation of hook_tweet_service().
_tweet_exclude Excludes certain Node IDs from displaying links.
_tweet_get_title Returns the title of the node for which the NID was passed or the current page. Note that there is no good way to get the page title for a page that is not the current page. We assume the title is the same as the title of the node if a node is being…
_tweet_get_url Gets an abbreviated URL using either CURL or PHP from the appropriate service. Times out after three (3) seconds.
_tweet_make_url Retrieves and beautifies the abbreviated URL.
_tweet_node_types Helper function to provide node types in the format array(TYPE => TYPE).
_tweet_process Determines what will be in the tweet itself.
_tweet_to_twitter Creates a link to post a URL and optionally title to twitter. Uses the current page by default.