You are here

rrssb.module in Ridiculously Responsive Social Sharing Buttons 7

Same filename and directory in other branches
  1. 8.2 rrssb.module
  2. 7.2 rrssb.module

File

rrssb.module
View source
<?php

/**
 * @file
 * Module file for Ridiculously Responsive Social Share Buttons.
 */
require 'rrssb.config.inc';

/**
 * Define the RRSSB library location.
 */
define('RRSSB_LIBRARY_VERSION', '1.10.0');
define('RRSSB_LIBRARY_URI', 'https://github.com/kni-labs/rrssb/archive/' . RRSSB_LIBRARY_VERSION . '.zip');

/**
 * Default value for variable rrssb_image_tokens
 */
define('RRSSB_IMAGE_TOKENS_DEFAULT', '[node:field_image]|[rrssbsite:logo-url]');

/**
 * Implements hook_menu().
 */
function rrssb_menu() {
  $items['admin/config/content/rrssb'] = array(
    'title' => 'Ridiculously Responsive Social Sharing Buttons',
    'description' => 'Configure the Ridiculously Responsive Social Sharing Buttons to select which social buttons you would like to enable.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'rrssb_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer rrssb',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function rrssb_permission() {
  return array(
    'administer rrssb' => array(
      'title' => t('Administer RRSSB'),
      'description' => t('Permission to allow user to configure the Ridiculously Responsive Social Share Buttons.'),
      'restrict access' => TRUE,
    ),
  );
}

/**
 * Implements hook_tokens().
 *
 * These tokens are not advertised in hook_token_info because they are of no use outside this module.
 */
function rrssb_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'rrssb' && !empty($data['rrssb'])) {
    foreach ($tokens as $name => $original) {
      if (isset($data['rrssb'][$name])) {
        $replacements[$original] = $data['rrssb'][$name];
      }
    }
  }

  // This issue https://www.drupal.org/node/823780 has a patch to add site:logo.
  // In the meantime, add our own version.
  if ($type == 'rrssbsite') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'logo-url':
          if ($uri = theme_get_setting('logo')) {
            $replacements[$original] = $uri;
          }
          break;
      }
    }
  }
  return $replacements;
}

/**
 * Implements hook_theme().
 */
function rrssb_theme($existing, $type, $theme, $path) {
  return array(
    'rrssb_config_buttons' => array(
      'render element' => 'element',
    ),
    'rrssb_button_list' => array(
      'render element' => 'element',
    ),
    'rrssb_button' => array(
      'variables' => array(
        'name' => NULL,
        'button' => NULL,
        'rrssb' => NULL,
      ),
    ),
  );
}

/**
 * Theme function for rrssb_config_buttons.
 * Prints the table for the buttons config in the settings form.
 */
function theme_rrssb_config_buttons($variables) {
  $elements = $variables['element'];

  // Allow dragging of rows to set the weights, handled by javascript.
  drupal_add_tabledrag('rrssb_config_buttons', 'order', 'sibling', 'item-row-weight');
  $header = array(
    'label' => t('Button'),
    'enabled' => t('Enabled'),
    // @@TODO Not sure if there is anywhere we can write a description '#description' => t('Username for your page/channel/profile.'),
    'username' => t('Username'),
    'weight' => t('Weight'),
  );
  $rows = array();
  foreach (element_children($elements) as $name) {
    $row = array(
      'class' => array(
        'draggable',
      ),
    );
    foreach ($header as $fieldname => $title) {
      $row['data'][] = drupal_render($elements[$name][$fieldname]);
    }
    $rows[] = $row;
  }
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array(
      'id' => 'rrssb_config_buttons',
    ),
  ));
}

/**
 * Theme function for rrssb_button_list.
 * Prints the list of all buttons.
 */
function theme_rrssb_button_list($variables) {
  $output = '<div class="rrssb-item-list">';
  if ($prefix = variable_get('rrssb_prefix')) {
    $output .= '<span class="rrssb-prefix">' . t($prefix) . '</span>';
  }
  $output .= '<ul class="rrssb-buttons">';
  $output .= drupal_render_children($variables['element']);
  $output .= '</ul></div>';
  return $output;
}

/**
 * Preprocess function for rrssb_button.
 */
function template_preprocess_rrssb_button(&$variables) {

  // Variables are $name, $button, $rrssb
  extract($variables);
  $key = variable_get('rrssb_follow') ? 'follow_url' : 'share_url';
  $rrssb['username'] = $button['username'];
  $variables['link'] = token_replace($button[$key], array(
    'rrssb' => $rrssb,
  ), array(
    'callback' => '_rrssb_urlencode',
  ));
}

/**
 * Helper function to URL encode.
 */
function _rrssb_urlencode(&$replacements, $data, $options) {
  $replacements = array_map('rawurlencode', $replacements);
}

/**
 * Theme function for rrssb_button.
 * Prints a single button.
 */
function theme_rrssb_button($variables) {

  // Variables are $name, $button, $rrssb, $link
  extract($variables);
  $class = $button['popup'] ? 'class="popup"' : '';
  $output = <<<EOM
<li class="rrssb-{<span class="php-variable">$name</span>}"><a href="{<span class="php-variable">$link</span>}" {<span class="php-variable">$class</span>}><span class="rrssb-icon">{<span class="php-variable">$button</span>[<span class="php-string">'svg'</span>]}</span><span class="rrssb-text">{<span class="php-variable">$button</span>[<span class="php-string">'text'</span>]}</span></a></li>
EOM;
  return $output;
}

/**
 * Implements hook_block_info().
 */
function rrssb_block_info() {
  $blocks['rrssb'] = array(
    'info' => t('Ridiculously Responsive Social Share Buttons'),
    'cache' => DRUPAL_CACHE_PER_PAGE,
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function rrssb_block_view($delta = '') {
  $block = array();
  switch ($delta) {
    case 'rrssb':
      $block['subject'] = t('Share this content.');
      $block['content'] = rrssb_get_buttons();
      break;
  }
  return $block;
}

/**
 * Implements hook_form().
 */
function rrssb_form() {
  $all_buttons = rrssb_settings(TRUE);
  $chosen = rrssb_get_chosen();
  $form['rrssb_follow'] = array(
    '#type' => 'select',
    '#title' => t('Select type of buttons'),
    '#options' => array(
      0 => t('Share'),
      1 => t('Follow'),
    ),
    '#default_value' => variable_get('rrssb_follow'),
    '#description' => t('"Share" buttons invite the visitor to share the page from your site onto their page/channel/profile.  "Follow" buttons direct the visitor to your page/channel/profile.'),
  );

  // Create the config for the table of buttons.
  // Drupal handles all the storing to the rrssb_chosen variable automatically, serialising the array.
  // The table layout comes from the theme function.
  $form['rrssb_chosen'] = array(
    '#type' => 'fieldset',
    '#tree' => TRUE,
    '#title' => t('Configure buttons'),
    '#theme' => 'rrssb_config_buttons',
  );
  foreach ($all_buttons as $name => $button) {

    // Determine if this button requires a particular value of rrssb_follow to be valid.
    // This is the case if one or other of the URL as not present.
    // Both URLs absent makes no sense and would be a bug.
    unset($require_follow);
    if (!isset($button['follow_url'])) {
      $require_follow = 0;
    }
    else {
      if (!isset($button['share_url'])) {
        $require_follow = 1;
      }
    }
    $form['rrssb_chosen'][$name]['label'] = array(
      '#type' => 'item',
      '#markup' => $button['text'],
    );
    $form['rrssb_chosen'][$name]['enabled'] = array(
      '#type' => 'checkbox',
      '#default_value' => isset($chosen[$name]['enabled']) ? $chosen[$name]['enabled'] : FALSE,
    );
    if (isset($require_follow)) {

      // Hide entries where there is no corresponding URL.
      $form['rrssb_chosen'][$name]['enabled']['#states'] = array(
        'visible' => array(
          ":input[name='rrssb_follow']" => array(
            'value' => $require_follow,
          ),
        ),
      );
    }
    if (isset($button['follow_url'])) {
      $form['rrssb_chosen'][$name]['username'] = array(
        '#type' => 'textfield',
        '#default_value' => isset($chosen[$name]['username']) ? $chosen[$name]['username'] : '',
        // Hide the username for share URLs where it isn't needed.  Otherwise it is a required field.
        // @@TODO Required field not working.
        '#states' => array(
          'visible' => array(
            ":input[name='rrssb_follow']" => array(
              'value' => 1,
            ),
          ),
          'required' => array(
            ":input[name='rrssb_chosen[{$name}][enabled]']" => array(
              'checked' => TRUE,
            ),
          ),
        ),
      );
    }
    $form['rrssb_chosen'][$name]['weight'] = array(
      '#type' => 'weight',
      '#default_value' => isset($chosen[$name]['weight']) ? $chosen[$name]['weight'] : 0,
      '#delta' => 20,
      '#attributes' => array(
        'class' => array(
          'item-row-weight',
        ),
      ),
    );
  }
  $form['rrssb_prefix'] = array(
    '#type' => 'textfield',
    '#title' => t('Prefix text before the buttons'),
    '#default_value' => variable_get('rrssb_prefix'),
    '#description' => t('Put this text before the buttons.  For example "Follow us" or "Share this page".'),
  );
  $form['rrssb_image_tokens'] = array(
    '#type' => 'textfield',
    '#title' => t('Tokens to use to find images'),
    '#default_value' => variable_get('rrssb_image_tokens', RRSSB_IMAGE_TOKENS_DEFAULT),
    '#description' => t('Enter one or more tokens, separated by |.  These tokens will be tried in turn to determine the image to use in buttons.
      The default value is !default which you can adapt to pick other fields or as desired.', array(
      '!default' => RRSSB_IMAGE_TOKENS_DEFAULT,
    )),
  );
  $form['#submit'][] = 'rrssb_form_submit';
  $form['#validate'][] = 'rrssb_form_validate';
  return system_settings_form($form);
}

/**
 * Verify handler for rrssb_form().
 */
function rrssb_form_validate($form, &$form_state) {
  $follow = $form_state['values']['rrssb_follow'];
  $follow_text = $follow ? t('Follow') : t('Share');
  foreach ($form_state['values']['rrssb_chosen'] as $name => $settings) {
    if ($settings['enabled']) {
      if ($follow && isset($settings['username']) && !$settings['username']) {

        //@@TODO The form highlighting the error in red isn't working.
        form_set_error("rrssb_chosen[{$name}][username]", t('You must set the username to use "Follow" button for !button', array(
          '!button' => $name,
        )));
      }

      // If a button is enabled where there is no URL, we don't count that as an error, just don't show the button (@see rrssb_settings).
    }
  }
}

/**
 * Submit handler for rrssb_form().
 */
function rrssb_form_submit() {

  // Clear cache.
  cache_clear_all('rrssb_buttons', 'cache');
}

/**
 * Implements hook_libraries_info().
 */
function rrssb_libraries_info() {
  $libraries['rrssb'] = array(
    'name' => 'Ridiculously Responsive Social Share Buttons',
    'vendor url' => 'http://kurtnoble.com/labs/rrssb/',
    'download url' => RRSSB_LIBRARY_URI,
    'version arguments' => array(
      'file' => 'package.json',
      'pattern' => '/"version": "([\\d\\.]+)"/',
    ),
    'files' => array(
      'js' => array(
        'js/rrssb.min.js',
      ),
      'css' => array(
        'css/rrssb.css',
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_requirements().
 */
function rrssb_requirements($phase) {

  // Check for library version.  We do a runtime check only, as we don't want to block install - the normal sequence
  // is to install the module and then use the drush command to get the library.
  $requirements = array();
  if ($phase == 'runtime') {
    $requirements['rrssb']['title'] = t('RRSSB library');
    $library = libraries_detect('rrssb');
    if (!$library['installed']) {
      $requirements['rrssb']['value'] = t('Not installed');
      $requirements['rrssb']['severity'] = REQUIREMENT_ERROR;
      $requirements['rrssb']['description'] = '';
    }
    else {
      $requirements['rrssb']['value'] = $library['version'];
      $compare = version_compare($library['version'], RRSSB_LIBRARY_VERSION);
      if ($compare < 0) {
        $requirements['rrssb']['severity'] = REQUIREMENT_ERROR;
        $requirements['rrssb']['description'] = t('Library version is too old.  ');
      }
      else {
        if ($compare > 0) {
          $requirements['rrssb']['severity'] = REQUIREMENT_WARNING;
          $requirements['rrssb']['description'] = t('Library version is newer than has been tested.  ');
        }
        else {
          $requirements['rrssb']['severity'] = REQUIREMENT_OK;
        }
      }
    }
    if ($requirements['rrssb']['severity'] != REQUIREMENT_OK) {
      $requirements['rrssb']['description'] .= t('Please download the RRSSB library using "drush rrssb-plugin" or from !link.', array(
        '!link' => l(RRSSB_LIBRARY_URI, RRSSB_LIBRARY_URI),
      ));
    }
  }
  return $requirements;
}

/**
 * Return the chosen buttons, or the default values if not yet set.
 */
function rrssb_get_chosen() {
  $defaults = array(
    'email' => array(
      'enabled' => TRUE,
      'weight' => -20,
    ),
    'facebook' => array(
      'enabled' => TRUE,
      'weight' => -19,
    ),
    'linkedin' => array(
      'enabled' => TRUE,
      'weight' => -18,
    ),
    'twitter' => array(
      'enabled' => TRUE,
      'weight' => -17,
    ),
    'googleplus' => array(
      'enabled' => TRUE,
      'weight' => -16,
    ),
    'pinterest' => array(
      'enabled' => TRUE,
      'weight' => -15,
    ),
  );
  $chosen = variable_get('rrssb_chosen', $defaults);
  if (!is_array(current($chosen))) {

    // Migrate from old format of variable.
    $weight = -20;
    foreach ($chosen as $name => &$enabled) {
      $enabled = array(
        'enabled' => $enabled ? TRUE : FALSE,
        'weight' => $weight++,
      );
    }
  }
  return $chosen;
}

/**
 * Returns a Drupal render array for the buttons.
 *
 * @param object $node
 *   The node object you are trying to share.
 *
 * @return string
 *   A string of markup for the list of buttons.
 */
function rrssb_get_buttons($node = NULL) {
  if (is_null($node)) {
    $node = menu_get_object();
  }

  // Create an array for how we will map [rrssb:XXX] tokens.  The key is the XXX value and the value is an array
  // of other tokens to try in turn until one works.  For the image token, we allow the user to configure the list
  // of tokens.
  $image_tokens = explode('|', variable_get('rrssb_image_tokens', RRSSB_IMAGE_TOKENS_DEFAULT));
  $config = array(
    'url' => array(
      '[node:url]',
      '[current-page:url]',
    ),
    'title' => array(
      '[node:title]',
      '[current-page:title]',
    ),
    'image' => $image_tokens,
  );

  // Replace tokens.
  foreach ($config as $param => $tokens) {
    foreach ($tokens as $token) {
      $rrssb[$param] = token_replace($token, array(
        'node' => $node,
      ), array(
        'clear' => TRUE,
      ));
      if ($rrssb[$param]) {
        break;
      }
    }
  }

  // If the image returned a comma separated list, just take the first entry.
  list($rrssb['image']) = explode(',', $rrssb['image']);
  $items = array(
    '#theme' => 'rrssb_button_list',
  );
  foreach (rrssb_settings() as $name => $button) {
    $items[] = array(
      '#theme' => 'rrssb_button',
      '#name' => $name,
      '#button' => $button,
      '#rrssb' => $rrssb,
    );
  }

  // Add css and js.
  // Use #attached rather than adding directly else block caching is broken.
  $items['#attached']['libraries_load'][] = array(
    'rrssb',
  );
  return $items;
}

/**
 * Fetch buttons settings.
 *
 * @param boolean $all TRUE: Fetch all buttons.  FALSE: only fetch enabled buttons.
 *
 * @return array Key is button name, value is an array of button config and settings merged.
 * For config values, see hook_rrssb_buttons.
 * Settings values are 'enabled', 'weight', 'username'.
 */
function rrssb_settings($all = FALSE) {

  // The 'all buttons' case is only used on the settings form, so performance isn't so important.
  // Do a static cache, but be careful to use a different entry.  Don't bother with a database cache.
  $buttons =& drupal_static($all ? __FUNCTION__ : __FUNCTION__ . '__all');
  if (isset($buttons)) {
    return $buttons;
  }
  if (!$all && ($cache = cache_get('rrssb_buttons'))) {
    $buttons = $cache->data;
    return $buttons;
  }

  // Get all buttons.
  $chosen = rrssb_get_chosen();
  $defaults = array(
    'enabled' => FALSE,
    'weight' => 0,
    'username' => '',
    'popup' => TRUE,
  );
  $buttons = module_invoke_all('rrssb_buttons');
  drupal_alter('rrssb_buttons', $buttons);
  $iconsDir = libraries_get_path('rrssb') . '/icons';

  // Set some defaults.
  foreach ($buttons as $name => &$button) {

    // Merge in the current config, with suitable defaults and checking.
    if (isset($chosen[$name])) {
      $button += $chosen[$name];
    }
    $button += $defaults;
    $button['username'] = check_plain($button['username']);
    if (!isset($button['svg'])) {

      // Read SVG from file.
      $svgfile = isset($button['svgfile']) ? $button['svgfile'] : "<icons>/{$name}.min.svg";
      $svgfile = str_replace('<icons>', $iconsDir, $svgfile);
      $button['svg'] = file_get_contents($svgfile);
    }

    // Default text to name.
    if (!isset($button['text'])) {
      $button['text'] = $name;
    }
  }

  // Sort buttons by configured weight.
  uasort($buttons, 'drupal_sort_weight');
  if (!$all) {

    // Filter to only include enabled ones with a URL configured.
    $key = variable_get('rrssb_follow') ? 'follow_url' : 'share_url';
    $buttons = array_filter($buttons, function ($button) use ($key) {
      return $button['enabled'] && isset($button[$key]);
    });
    cache_set('rrssb_buttons', $buttons);
  }
  return $buttons;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function rrssb_form_node_type_form_alter(&$form, &$form_state) {
  $form['rrssb'] = array(
    '#type' => 'fieldset',
    '#title' => t('Ridiculously Responsive Social Share Buttons'),
    '#collapsible' => TRUE,
    '#group' => 'additional_settings',
    '#weight' => 20,
    '#attributes' => array(
      'class' => array(
        'rrssb-node-type-settings-form',
      ),
    ),
    '#access' => user_access('administer nodes'),
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'rrssb') . '/rrssb.nodetype.js',
      ),
    ),
  );
  $form['rrssb']['show'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show the social buttons?'),
    '#default_value' => variable_get('rrssb_' . $form['#node_type']->type, 0),
  );
  $form['#submit'][] = 'rrssb_node_type_callback';
}

/**
 * Custom handler to save rrssb info.
 */
function rrssb_node_type_callback($form, &$form_state) {
  variable_set('rrssb_' . $form_state['values']['type'], $form_state['complete form']['rrssb']['show']['#value']);
  field_info_cache_clear();
}

/**
 * Implements hook_node_prepare().
 */
function rrssb_node_prepare($node) {

  // @@TODO: Testing and code read suggests this function may not be needed - remove or add comment to explain.
  if (!isset($node->rrssb)) {
    $node->rrssb = variable_get("rrssb_{$node->type}", 0);
  }
}

/**
 * Implements hook_node_view().
 */
function rrssb_node_view($node, $view_mode, $langcode) {
  if (variable_get('rrssb_' . $node->type) == 1) {
    $node->content['rrssb'] = rrssb_get_buttons($node);
  }
}

/**
 * Implements hook_field_extra_fields().
 */
function rrssb_field_extra_fields() {
  $extra = array();
  foreach (node_type_get_types() as $node_type) {
    if (variable_get('rrssb_' . $node_type->type) == 1) {
      $extra['node'][$node_type->type]['display']['rrssb'] = array(
        'label' => t('Ridiculously Responsive Social Share Buttons'),
        'description' => t('A fake field to display Social buttons'),
        'weight' => 10,
      );
    }
  }
  return $extra;
}

Functions

Namesort descending Description
rrssb_block_info Implements hook_block_info().
rrssb_block_view Implements hook_block_view().
rrssb_field_extra_fields Implements hook_field_extra_fields().
rrssb_form Implements hook_form().
rrssb_form_node_type_form_alter Implements hook_form_FORM_ID_alter().
rrssb_form_submit Submit handler for rrssb_form().
rrssb_form_validate Verify handler for rrssb_form().
rrssb_get_buttons Returns a Drupal render array for the buttons.
rrssb_get_chosen Return the chosen buttons, or the default values if not yet set.
rrssb_libraries_info Implements hook_libraries_info().
rrssb_menu Implements hook_menu().
rrssb_node_prepare Implements hook_node_prepare().
rrssb_node_type_callback Custom handler to save rrssb info.
rrssb_node_view Implements hook_node_view().
rrssb_permission Implements hook_permission().
rrssb_requirements Implements hook_requirements().
rrssb_settings Fetch buttons settings.
rrssb_theme Implements hook_theme().
rrssb_tokens Implements hook_tokens().
template_preprocess_rrssb_button Preprocess function for rrssb_button.
theme_rrssb_button Theme function for rrssb_button. Prints a single button.
theme_rrssb_button_list Theme function for rrssb_button_list. Prints the list of all buttons.
theme_rrssb_config_buttons Theme function for rrssb_config_buttons. Prints the table for the buttons config in the settings form.
_rrssb_urlencode Helper function to URL encode.

Constants

Namesort descending Description
RRSSB_IMAGE_TOKENS_DEFAULT Default value for variable rrssb_image_tokens
RRSSB_LIBRARY_URI
RRSSB_LIBRARY_VERSION Define the RRSSB library location.