You are here

socialfield.module in Social field 7

Provides a field for adding social services links.

File

socialfield.module
View source
<?php

/**
 * @file
 * Provides a field for adding social services links.
 */

/**
 * Implements hook_menu().
 */
function socialfield_menu() {
  $items['admin/config/media/socialfield'] = array(
    'title' => 'Social field',
    'description' => 'Social field settings',
    'page callback' => 'socialfield_settings',
    'access arguments' => array(
      'view services',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/config/media/socialfield/add'] = array(
    'title' => 'Add new service',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'socialfield_add_service',
    ),
    'access arguments' => array(
      'create new service',
    ),
    'type' => MENU_LOCAL_ACTION,
  );
  $items['admin/config/media/socialfield/%/edit'] = array(
    'title' => 'Edit service',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'socialfield_edit_service',
      4,
    ),
    'access arguments' => array(
      'edit any service',
    ),
  );
  $items['admin/config/media/socialfield/%/delete'] = array(
    'title' => 'Delete service',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'socialfield_delete_service',
      4,
    ),
    'access arguments' => array(
      'delete any service',
    ),
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function socialfield_permission() {
  return array(
    'view services' => array(
      'title' => t('View services'),
    ),
    'create new service' => array(
      'title' => t('Create new service'),
    ),
    'edit any service' => array(
      'title' => t('Edit any service'),
    ),
    'delete any service' => array(
      'title' => t('Delete any service'),
    ),
  );
}

/**
 * Table that displays all available services.
 */
function socialfield_settings() {
  $header = array(
    t('Name'),
    t('Service'),
    t('Icon class'),
    t('Validation pattern'),
    t('Actions'),
  );
  $rows = array();
  $services = variable_get('socialfield_services');
  foreach ($services as $name => $service) {
    $edit = l(t('edit'), 'admin/config/media/socialfield/' . $name . '/edit');
    $delete = l(t('delete'), 'admin/config/media/socialfield/' . $name . '/delete');
    $rows[] = array(
      $service['name'],
      $name,
      $service['icon'],
      str_replace("\n", '<br />', $service['validation_pattern']),
      $edit . ' ' . $delete,
    );
  }
  $page = array();
  $page['services'] = array(
    '#markup' => theme('table', array(
      'header' => $header,
      'rows' => $rows,
    )),
  );
  return $page;
}

/**
 * Form for adding a new service.
 */
function socialfield_add_service($form, $form_state) {
  $form['service'] = array(
    '#type' => 'textfield',
    '#title' => t('Service machine name'),
  );
  $form['service_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Service name'),
  );
  $form['service_icon'] = array(
    '#type' => 'textfield',
    '#description' => t('Example: icon-facebook'),
    '#title' => t('Service icon class'),
  );
  $form['service_validation_pattern'] = array(
    '#type' => 'textarea',
    '#description' => t('Enter the list of allowed urls separated by new line.<br />Leave empty to allow user input any urls.<br />The "*" character is a wildcard.<br />Example: *facebook.com/* for any page on Facebook site.'),
    '#title' => t('Url validation pattern'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Add'),
  );
  return $form;
}

/**
 * Validates form when adding a new service.
 */
function socialfield_add_service_validate($form, &$form_state) {
  $services = variable_get('socialfield_services');
  if (isset($services[$form_state['values']['service']])) {
    form_set_error('service', t('This service already exist.'));
  }
}

/**
 * Form submit for adding a new service.
 */
function socialfield_add_service_submit($form, $form_state) {
  $values =& $form_state['values'];
  $services = variable_get('socialfield_services');
  $service = array(
    'name' => $values['service_name'],
    'icon' => $values['service_icon'],
    'validation_pattern' => $values['service_validation_pattern'],
  );
  $services[$values['service']] = $service;
  variable_set('socialfield_services', $services);
  drupal_goto('admin/config/media/socialfield');
}

/**
 * Form for editing a service.
 */
function socialfield_edit_service($form, $form_state, $service = NULL) {
  if (!isset($service)) {
    drupal_set_message(t('No service specified.'), 'error');
    drupal_goto('admin/config/media/socialfield');
  }
  $services = variable_get('socialfield_services');
  if (!isset($services[$service])) {
    drupal_set_message(t('The specified service does not exist.'), 'error');
    drupal_goto('admin/config/media/socialfield');
  }
  $form['service'] = array(
    '#type' => 'textfield',
    '#title' => t('Service machine name'),
    '#value' => $service,
    '#disabled' => TRUE,
  );
  $form['service_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Service name'),
    '#default_value' => $services[$service]['name'],
  );
  $form['service_icon'] = array(
    '#type' => 'textfield',
    '#description' => t('Example: icon-facebook'),
    '#title' => t('Service fonticon class'),
    '#default_value' => $services[$service]['icon'],
  );
  $form['service_validation_pattern'] = array(
    '#type' => 'textarea',
    '#description' => t('Enter the list of allowed urls separated by new line.<br />Leave empty to allow user input any urls.<br />The "*" character is a wildcard.<br />Example: *facebook.com/* for any page on Facebook site.'),
    '#title' => t('Url validation pattern'),
    '#default_value' => $services[$service]['validation_pattern'],
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}

/**
 * Form submit for editing a service.
 */
function socialfield_edit_service_submit($form, $form_state) {
  $values =& $form_state['values'];
  $services = variable_get('socialfield_services');
  $service = array(
    'name' => $values['service_name'],
    'icon' => $values['service_icon'],
    'validation_pattern' => $values['service_validation_pattern'],
  );
  $services[$values['service']] = $service;
  variable_set('socialfield_services', $services);
  drupal_goto('admin/config/media/socialfield');
}

/**
 * Form for deleting a service.
 */
function socialfield_delete_service($form, $form_state, $service = NULL) {
  if (!isset($service)) {
    drupal_set_message(t('No service specified.'), 'error');
    drupal_goto('admin/config/media/socialfield');
  }
  $services = variable_get('socialfield_services');
  if (!isset($services[$service])) {
    drupal_set_message(t('The specified service does not exist.'), 'error');
    drupal_goto('admin/config/media/socialfield');
  }
  $form['service'] = array(
    '#type' => 'hidden',
    '#value' => $service,
  );
  $question = t('Are you sure you want to delete @service service?', array(
    '@service' => $service,
  ));
  $path = 'admin/config/media/socialfield';
  return confirm_form($form, $question, $path);
}

/**
 * Form submit for deleting a service.
 */
function socialfield_delete_service_submit($form, $form_state) {
  $values =& $form_state['values'];
  $services = variable_get('socialfield_services');
  unset($services[$values['service']]);
  variable_set('socialfield_services', $services);
  drupal_set_message(t('@service service was successfully deleted.', array(
    '@service' => $values['service'],
  )));
  drupal_goto('admin/config/media/socialfield');
}

/**
 * Implements hook_field_info().
 */
function socialfield_field_info() {
  $used_services = array_keys(variable_get('socialfield_services'));
  $used_services = array_combine($used_services, $used_services);
  $displayed_services = array(
    'twitter',
    'googleplus',
    'facebook',
  );
  $displayed_services = array_combine($displayed_services, $displayed_services);
  $weight_values = range(-10, count($used_services) - 11);
  $weights = array_combine($used_services, $weight_values);
  return array(
    'social_links_field' => array(
      'label' => t('Social links'),
      'description' => t('Social links'),
      'default_widget' => 'socialfield_widget',
      'default_formatter' => 'socialfield_formatter',
      'property_type' => 'socialfield',
      'property_callbacks' => array(
        'socialfield_property_callback_property_info_callback',
      ),
      'instance_settings' => array(
        'used_services' => $used_services,
        'services' => $displayed_services,
        'weights' => $weights,
      ),
    ),
  );
}

/**
 * Field property info callback.
 */
function socialfield_property_callback_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  $name = $field['field_name'];
  $property =& $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
  $property['type'] = 'socialfield';
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
  unset($property['query callback']);
}

/**
 * Implements hook_field_instance_settings_form().
 */
function socialfield_field_instance_settings_form($field, $instance) {
  $social_services = variable_get('socialfield_services');
  $options = array();
  foreach ($social_services as $name => $service) {
    $options[$name] = $service['name'];
  }

  // Removing services from instance settings that were deleted from module settings.
  $used_services =& $instance['settings']['used_services'];
  $used_services = array_intersect($used_services, array_keys($social_services));
  $form = array(
    '#type' => 'container',
    '#process' => array(
      '_socialfield_field_instance_settings_form',
    ),
    '#field' => $field,
    '#instance' => $instance,
    '#options' => $options,
  );
  return $form;
}

/**
 * Instance settings form process callback function.
 */
function _socialfield_field_instance_settings_form($form, &$form_state) {
  $form_state['#used_services'] = $form['#instance']['settings']['used_services'];
  $form_state['#services'] = $form['#instance']['settings']['services'];
  $form_state['#weights'] = $form['#instance']['settings']['weights'];
  asort($form_state['#weights']);
  $services = variable_get('socialfield_services');
  $rows = array();
  foreach ($form_state['#weights'] as $service_name => $weight) {

    // Checking if service was deleted from module configuration.
    if (!isset($services[$service_name])) {
      unset($form_state['#services'][$service_name]);
      unset($form_state['#weights'][$service_name]);
      continue;
    }
    $use_service = in_array($service_name, $form_state['#used_services']);
    $display_service = in_array($service_name, $form_state['#services']);
    $service_markup = '<div class="social-links social-links-instance-settings">' . '<span class="service-' . $service_name . '">' . '<i class="icon ' . $services[$service_name]['icon'] . '"></i></span>' . '<span class="instance-settings-table-service-name">' . ucfirst($service_name) . '</span>' . '</div>';
    $row = array(
      'service_name' => array(
        '#type' => 'hidden',
        '#value' => $service_name,
      ),
      'name' => array(
        '#markup' => $service_markup,
      ),
      'used_services' => array(
        '#type' => 'checkbox',
        '#default_value' => $use_service,
        '#attributes' => array(
          'class' => array(
            'socialfield-table-used-service-checkbox',
          ),
        ),
      ),
      'displayed_services' => array(
        '#type' => 'checkbox',
        '#default_value' => $display_service,
        '#attributes' => array(
          'class' => array(
            'socialfield-table-displayed-service-checkbox',
          ),
        ),
      ),
      'weight' => array(
        '#type' => 'weight',
        '#title' => t('Weight'),
        '#title_display' => 'invisible',
        '#default_value' => $weight,
        '#attributes' => array(
          'class' => array(
            'services-order-weight',
          ),
        ),
      ),
    );
    if ($display_service) {
      $row['used_services']['#attributes']['disabled'] = TRUE;
    }
    $rows[] = $row;
  }
  $module_path = drupal_get_path('module', 'socialfield');

  // Theme this part of the form as a table.
  $form['table'] = array(
    '#theme' => 'socialfield_form_table',
    '#header' => array(
      t('Service'),
      t('Used services'),
      t('Displayed services'),
      t('Weight'),
    ),
    'rows' => $rows,
    '#attributes' => array(
      'id' => 'socialfield-instance-settings-services-table',
    ),
    '#element_validate' => array(
      '_socialfield_validate_services',
    ),
    '#attached' => array(
      'js' => array(
        $module_path . '/js/socialfield.js',
      ),
      'css' => array(
        $module_path . '/css/socialfield.css',
      ),
    ),
  );
  return $form;
}

/**
 * Setting instance settings from table settings.
 */
function _socialfield_validate_services($form, &$form_state) {
  $used_services = $form_state['#used_services'];
  $displayed_services = $form_state['#services'];
  $weights = $form_state['#weights'];

  // Getting new instance settings.
  foreach ($form['rows'] as $key => &$row) {
    if (!is_numeric($key)) {
      continue;
    }
    $service_name = $row['service_name']['#value'];

    // Service checked, wanted to be used.
    if ($row['used_services']['#value']) {
      $used_services[$service_name] = $service_name;
    }
    else {
      unset($used_services[$service_name]);
    }

    // This service was checked to be displayed with icon on widget.
    if ($row['displayed_services']['#value']) {
      $used_services[$service_name] = $service_name;
      $displayed_services[$service_name] = $service_name;
    }
    else {
      unset($displayed_services[$service_name]);
    }
    $weights[$service_name] = $row['weight']['#value'];
  }
  if (!count($displayed_services)) {
    form_set_error('table', t('Select at least one service to display.'));
  }

  // Setting new instance settings.
  $form_state['values']['instance']['settings']['used_services'] = $used_services;
  $form_state['values']['instance']['settings']['services'] = $displayed_services;
  $form_state['values']['instance']['settings']['weights'] = $weights;
  unset($form_state['values']['instance']['settings']['table']);
}

/**
 * Theme callback for the form table.
 */
function theme_socialfield_form_table(&$variables) {

  // Getting the userful values.
  $form = $variables['form'];
  $data = $form['rows'];
  $header = $form['#header'];
  $attributes = $form['#attributes'];

  // Traverse each row and each column in each row.
  $rows = array();
  foreach (element_children($data) as $row_index) {
    $row = array();
    foreach (element_children($data[$row_index]) as $col_index) {
      $element = $data[$row_index][$col_index];
      if (isset($element['#type']) && $element['#type'] == 'hidden') {
        continue;
      }
      $row[] = drupal_render($data[$row_index][$col_index]);
    }
    $rows[] = array(
      'data' => $row,
      'class' => array(
        'draggable',
      ),
    );
  }
  $output = theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => $attributes,
  ));
  drupal_add_tabledrag($attributes['id'], 'order', 'sibling', 'services-order-weight');
  return $output;
}

/**
 * Implements hook_field_is_empty().
 */
function socialfield_field_is_empty($item, $field) {
  return empty($item['service']) || $item['url'] === '';
}

/**
 * Implements hook_field_validate().
 */
function socialfield_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {

  // Clicked remove button. No validation needed.
  if (!isset($items['validate_link']) || !$items['validate_link']) {
    return;
  }
  $services = variable_get('socialfield_services');
  $form_elements = array(
    $field['field_name'],
    $langcode,
    'social_buttons',
  );
  foreach ($items as $delta => &$item) {
    if (!$item['url']) {

      // No url inserted. Nothing to validate here.
      continue;
    }
    if (!valid_url($item['url'], TRUE)) {

      // Checking if the url is valid.
      $form_element = array_merge($form_elements, array(
        'element_' . $delta,
        'url',
      ));
      form_set_error(implode('][', $form_element), t('Not a valid URL.'));
    }
    else {

      // Checking if the url matches the pattern.
      $match = drupal_match_path($item['url'], $services[$item['service']]['validation_pattern']);
      if (!$match) {
        $form_element = array_merge($form_elements, array(
          'element_' . $delta,
          'url',
        ));
        form_set_error(implode('][', $form_element), t("%service link should match the following pattern: %pattern", array(
          '%service' => $services[$item['service']]['name'],
          '%pattern' => $services[$item['service']]['validation_pattern'],
        )));
      }
    }
  }
}

/**
 * Implements hook_field_widget_info().
 */
function socialfield_field_widget_info() {
  return array(
    'socialfield_widget' => array(
      'label' => 'Social links',
      'field types' => array(
        'social_links_field',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
      ),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function socialfield_theme($existing, $type, $theme, $path) {
  $themes = array(
    'socialfield_drag_components' => array(
      'render element' => 'element',
    ),
    'socialfield_formatter' => array(
      'variables' => array(
        'items' => NULL,
        'field' => NULL,
        'label' => NULL,
      ),
    ),
    'socialfield_form_table' => array(
      // The renderable element is the form.
      'render element' => 'form',
    ),
  );
  return $themes;
}

/**
 * Custom theme output for widget.
 */
function theme_socialfield_drag_components($vars) {
  $element = $vars['element'];
  drupal_add_tabledrag('socialfield-table', 'order', 'sibling', 'item-row-weight');
  $services = variable_get('socialfield_services');
  $header = array(
    t('Social links'),
    '',
    '',
    '',
  );
  $rows = array();
  $index = 0;
  for ($i = 0; $i < $element['#num_elements']; $i++) {
    while (!isset($element['element_' . $index])) {

      // There is no element with this index. Moving on to the next possible element.
      $index++;
    }
    $current_element = $element['element_' . $index];
    $rows[] = array(
      'data' => array(
        '<div class="social-links">' . '<span class="service-' . $current_element['#service'] . '">' . '<i class="icon ' . $services[$current_element['#service']]['icon'] . '"></i>' . '</span>' . '</div>',
        drupal_render($current_element['url']),
        drupal_render($current_element['weight']),
        drupal_render($current_element['operation']),
      ),
      'class' => array(
        'draggable',
      ),
      'weight' => $current_element['weight']['#value'],
    );
    $index++;
  }

  // Sorting elements by their weight.
  uasort($rows, 'drupal_sort_weight');
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array(
      'id' => 'socialfield-table',
    ),
  )) . drupal_render($element['add_one_social']) . '<div class="description">' . drupal_render($element['description']) . '</div>';
}

/**
 * Implements hook_field_widget_form().
 */
function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $value_key = key($field['columns']);
  $all_services = variable_get('socialfield_services');

  // Settings to be accessed from ajax callback.
  $form_state['socialfield']['field_data'] = array(
    'field_name' => $field['field_name'],
    'parents' => $element['#field_parents'],
    'langcode' => $langcode,
  );

  // A service (option) from select list was accessed.
  if (isset($form_state['triggering_element']['#value']) && isset($form_state['triggering_element']['#socialfield_add'])) {
    if ($form_state['triggering_element']['#value'] != '_none') {
      $form_state['services'][] = $form_state['triggering_element']['#value'];
    }
  }

  // Remove button was pressed.
  if (isset($form_state['clicked_button']['#name']) && isset($form_state['clicked_button']['#socialfield_delete'])) {
    $element_array = explode('_', $form_state['clicked_button']['#name']);
    $remove_element = (int) end($element_array);
    unset($form_state['values'][$field['field_name']][$langcode][$remove_element]);
    unset($form_state['services'][$remove_element]);
  }

  // Setting number of form elements.
  if (!isset($form_state['services'])) {
    if (count($items)) {

      // Edit page. Showing all saved elements.
      $form_state['num_elements'] = count($items) - 1;
      $form_state['services'] = array();
      foreach ($items as $item) {
        $form_state['services'][] = $item['service'];
      }
    }
    else {

      // Create page. Showing only default elements (aplying instance settings).
      $form_state['num_elements'] = count($instance['settings']['services']) - 1;
      $form_state['services'] = array_values($instance['settings']['services']);
    }
  }

  // Setting the number of services on widget.
  $form_state['num_elements'] = count($form_state['services']);

  // Creating wrapper and setting theme (table) on it.
  $element['social_buttons'] = array(
    '#prefix' => '<div id="social-field-wrapper">',
    '#suffix' => '</div>',
    '#tree' => TRUE,
    '#theme' => 'socialfield_drag_components',
    '#num_elements' => $form_state['num_elements'],
  );

  // Getting maximum weight from existing services.
  $weights = array(
    -11,
  );
  if (!empty($items)) {
    foreach ($items as $item) {
      $weights[] = $item['weight'];
    }
  }
  $weight = max($weights);
  unset($weights);

  // Creating form.
  foreach ($form_state['services'] as $i => $service) {

    // Checking if service was deleted from module settings.
    if (!isset($all_services[$service])) {
      $form_state['num_elements']--;
      $element['social_buttons']['#num_elements']--;
      continue;
    }
    $element['social_buttons']['element_' . $i] = array(
      'url' => array(
        '#type' => 'textfield',
        '#default_value' => isset($items[$i]) && $service == $items[$i]['service'] ? $items[$i]['url'] : '',
        '#value_key' => $value_key,
      ),
      'weight' => array(
        '#type' => 'weight',
        '#title' => t('Weight'),
        '#title_display' => 'invisible',
        '#default_value' => isset($items[$i]['weight']) ? $items[$i]['weight'] : ++$weight,
        '#attributes' => array(
          'class' => array(
            'item-row-weight',
          ),
        ),
      ),
      '#service' => $service,
      'operation' => array(
        '#type' => 'button',
        '#value' => t('Remove'),
        '#name' => 'element_ ' . $i,
        '#socialfield_delete' => TRUE,
        '#validate' => array(),
        '#limit_validation_errors' => array(),
        '#ajax' => array(
          'callback' => 'socialfield_remove_one_element_callback',
          'wrapper' => 'social-field-wrapper',
        ),
      ),
    );
  }

  // Validate on elements.
  $element['#element_validate'] = array(
    'socialfield_service_validate',
  );

  // Getting services for the select list.
  $options = array_keys($all_services);
  $options = array_combine($options, $options);
  if (isset($instance['settings']['used_services'])) {
    $options = array_intersect($options, $instance['settings']['used_services']);
  }
  $options = array_diff($options, $form_state['services']);
  $options = array_map('ucfirst', $options);

  // Select for adding new element (service and url) to form. Done with Ajax Forms.
  $element['social_buttons']['add_one_social'] = array(
    '#type' => 'select',
    '#options' => array(
      '_none' => t('Add more'),
    ) + $options,
    '#socialfield_add' => TRUE,
    '#access' => !empty($options),
    '#validate' => array(),
    '#limit_validation_errors' => array(),
    '#attributes' => array(
      'class' => array(
        'add_one_social_service',
      ),
    ),
    '#ajax' => array(
      'callback' => 'socialfield_add_one_element_callback',
      'wrapper' => 'social-field-wrapper',
    ),
  );

  // Add help description.
  $element['social_buttons']['description'] = array(
    '#markup' => $instance['description'],
  );

  // Attaching css and js files.
  $path = drupal_get_path('module', 'socialfield');
  $element['#attached']['js'][] = $path . '/js/socialfield.js';
  $element['#attached']['css'][] = $path . '/css/socialfield.css';
  return $element;
}

/**
 * Ajax callback for removing one element from widget.
 */
function socialfield_remove_one_element_callback($form, $form_state) {
  $field_name = $form_state['socialfield']['field_data']['field_name'];
  $langcode = $form_state['socialfield']['field_data']['langcode'];
  $parents = $form_state['socialfield']['field_data']['parents'];
  $return_form = drupal_array_get_nested_value($form, $parents);
  return $return_form[$field_name][$langcode]['social_buttons'];
}

/**
 * Ajax callback for adding one element to widget.
 */
function socialfield_add_one_element_callback($form, $form_state) {
  if ($form_state['triggering_element']['#value'] === '_none') {
    return;
  }
  $field_name = $form_state['socialfield']['field_data']['field_name'];
  $langcode = $form_state['socialfield']['field_data']['langcode'];
  $parents = $form_state['socialfield']['field_data']['parents'];
  $return_form = drupal_array_get_nested_value($form, $parents);
  return $return_form[$field_name][$langcode]['social_buttons'];
}

/**
 * Validates widget elements.
 */
function socialfield_service_validate($element, &$form_state) {
  $index = 0;
  $items = array();

  // If remove button was pressed error validation is not necessary.
  $items['validate_link'] = TRUE;
  if (isset($form_state['clicked_button']['#socialfield_delete'])) {
    $items['validate_link'] = FALSE;
  }
  for ($i = 0; $i < $element['social_buttons']['#num_elements']; $i++) {
    while (!isset($element['social_buttons']['element_' . $index])) {

      // There is no element with this index. Moving on to the next possible element.
      $index++;
    }
    $current_element = $element['social_buttons']['element_' . $index];
    $index++;

    // If url doesn`t contain 'http' we concatenate it to url value.
    if ($current_element['url']['#value']) {
      $parsed_url = parse_url($current_element['url']['#value']);
      if (empty($parsed_url['scheme'])) {
        $current_element['url']['#value'] = 'http://' . ltrim($current_element['url']['#value'], '/');
      }
    }
    $items[] = array(
      'service' => $current_element['#service'],
      'url' => $current_element['url']['#value'],
      'weight' => $current_element['weight']['#value'],
    );
  }

  // Changing submitted form values during form validation.
  form_set_value($element, $items, $form_state);
}

/**
 * Implements hook_field_formatter_info().
 */
function socialfield_field_formatter_info() {
  return array(
    'socialfield_formatter' => array(
      'label' => t('Icons'),
      'field types' => array(
        'social_links_field',
      ),
      'settings' => array(
        'link_text' => FALSE,
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function socialfield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {

  // Getting the view mode where our settings are stored.
  $display = $instance['display'][$view_mode];

  // Getting the actual settings.
  $settings = $display['settings'];

  // Initialize the element variable.
  $element = array();
  $element['link_text'] = array(
    '#type' => 'checkbox',
    '#title' => t('Hidden text on link'),
    '#description' => t('Each icon link will contain his service name as a hidden text. This if useful for screen readers.'),
    '#default_value' => $settings['link_text'],
  );
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function socialfield_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $summary = t('Display links as icons.');
  return $summary;
}

/**
 * Implements hook_field_formatter_view().
 */
function socialfield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  if (!$items) {
    return;
  }
  $list_items = array();
  switch ($display['type']) {
    case 'socialfield_formatter':
      uasort($items, 'drupal_sort_weight');
      $services = variable_get('socialfield_services');
      foreach ($items as $item) {

        // Checking if the service was deleted.
        if (!isset($services[$item['service']])) {
          continue;
        }

        // Social link.
        $hidden_text = '';
        if (isset($display['settings']['link_text']) && $display['settings']['link_text']) {

          // Wanting to set hidden text on link.
          $hidden_text = '<span class="element-invisible">' . check_plain($item['service']) . '</span>';
        }
        $link = l('<i class="icon ' . $services[$item['service']]['icon'] . '">' . $hidden_text . '</i>', $item['url'], array(
          'html' => TRUE,
          'attributes' => array(
            'target' => '_blank',
          ),
        ));
        $list_items[] = array(
          'data' => $link,
          'class' => array(
            'field-item',
            'service-' . $item['service'],
          ),
        );
      }

      // Checking if there are links to display.
      if (!$list_items) {
        return;
      }

      // Make list from items.
      $element[0] = array(
        '#theme' => 'socialfield_formatter',
        '#items' => $list_items,
        '#field' => $field,
      );
      $element['#attached']['css'][] = drupal_get_path('module', 'socialfield') . '/css/socialfield.css';
      break;
  }
  return $element;
}

/**
 * Custom theme output for the formatter.
 */
function theme_socialfield_formatter($variables) {
  $label = array();
  $label_class = '';
  if (isset($variables['label']['mode']) && $variables['label']['mode'] != 'hidden') {
    $label = array(
      '#markup' => $variables['label']['label'],
      '#prefix' => '<div class="field-label">',
      '#suffix' => '</div>',
    );
    switch ($variables['label']['mode']) {
      case 'above':
        $label_class = 'field-label-above';
        break;
      case 'inline':
        $label_class = 'field-label-inline';
        break;
      case 'hidden':
        $label_class = 'field-label-hidden';
        break;
    }
  }
  $label = drupal_render($label);
  $list = theme('item_list', array(
    'items' => $variables['items'],
    'attributes' => array(
      'class' => array(
        'field-items',
      ),
    ),
  ));
  $field_classes = array(
    'field',
    'field-name-' . str_replace('_', '-', $variables['field']['field_name']),
    'field-type-' . str_replace('_', '-', $variables['field']['type']),
    $label_class,
    'social-links',
  );
  return '<div class="' . implode(' ', $field_classes) . '">' . $label . $list . '</div>';
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function socialfield_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
  if ($form['#field']['type'] == 'social_links_field') {

    // Hide the cardinality setting on field instance settings for social_links_field fields.
    $form['field']['cardinality']['#default_value'] = FIELD_CARDINALITY_UNLIMITED;
    $form['field']['cardinality']['#disabled'] = TRUE;

    // Hide default value on field instance settings.
    $form['instance']['default_value_widget']['#access'] = FALSE;
  }
}

Functions

Namesort descending Description
socialfield_add_one_element_callback Ajax callback for adding one element to widget.
socialfield_add_service Form for adding a new service.
socialfield_add_service_submit Form submit for adding a new service.
socialfield_add_service_validate Validates form when adding a new service.
socialfield_delete_service Form for deleting a service.
socialfield_delete_service_submit Form submit for deleting a service.
socialfield_edit_service Form for editing a service.
socialfield_edit_service_submit Form submit for editing a service.
socialfield_field_formatter_info Implements hook_field_formatter_info().
socialfield_field_formatter_settings_form Implements hook_field_formatter_settings_form().
socialfield_field_formatter_settings_summary Implements hook_field_formatter_settings_summary().
socialfield_field_formatter_view Implements hook_field_formatter_view().
socialfield_field_info Implements hook_field_info().
socialfield_field_instance_settings_form Implements hook_field_instance_settings_form().
socialfield_field_is_empty Implements hook_field_is_empty().
socialfield_field_validate Implements hook_field_validate().
socialfield_field_widget_form Implements hook_field_widget_form().
socialfield_field_widget_info Implements hook_field_widget_info().
socialfield_form_field_ui_field_edit_form_alter Implements hook_form_FORM_ID_alter().
socialfield_menu Implements hook_menu().
socialfield_permission Implements hook_permission().
socialfield_property_callback_property_info_callback Field property info callback.
socialfield_remove_one_element_callback Ajax callback for removing one element from widget.
socialfield_service_validate Validates widget elements.
socialfield_settings Table that displays all available services.
socialfield_theme Implements hook_theme().
theme_socialfield_drag_components Custom theme output for widget.
theme_socialfield_formatter Custom theme output for the formatter.
theme_socialfield_form_table Theme callback for the form table.
_socialfield_field_instance_settings_form Instance settings form process callback function.
_socialfield_validate_services Setting instance settings from table settings.