You are here

advanced_link.module in Advanced Link 6

Same filename and directory in other branches
  1. 7 advanced_link.module

Defines simple advanced_link widget.

File

advanced_link.module
View source
<?php

/**
 * @file
 * Defines simple advanced_link widget.
 */
define('ADVANCED_LINK_EXTERNAL', 'external');
define('ADVANCED_LINK_INTERNAL', 'internal');
define('ADVANCED_LINK_BOTH', 'both');
define('ADVANCED_LINK_TARGET_DEFAULT', 'default');
define('ADVANCED_LINK_SEARCH_START', 'start');
define('ADVANCED_LINK_SEARCH_CONTAINS', 'contains');

/**
 * Implements hook_perm().
 */
function advanced_link_perm() {
  return array(
    'access advanced_link autocomplete',
  );
}

/**
 * Implements hook_menu().
 */
function advanced_link_menu() {
  $items = array();
  $items['advanced_link/autocomplete'] = array(
    'page callback' => 'advanced_link_autocomplete',
    'access callback' => 'advanced_link_autocomplete_access',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Checks if current user has access to autocomplete.
 */
function advanced_link_autocomplete_access() {
  return user_access('access advanced_link autocomplete') || user_access('administer nodes');
}

/**
 * Menu callback for autocomplete. 
 * 
 * Example: start/string/that/we/searxh/for.
 */
function advanced_link_autocomplete($type_name, $field_name) {

  // Search string is in all args except first and second.
  $args = func_get_args();
  $string = implode('/', array_slice($args, 2));
  $matches = advanced_link_url_load($args[0], $args[1], $string);
  if (!$matches) {
    $matches[''] = t('No matching results.');
  }
  drupal_json($matches);
}

/**
 * Filtering pathes pathes and alias before fetching.
 */
function advanced_link_autocomplete_url_filter_conditions($urls_filter, $query, $collumn) {
  if ($urls_filter) {
    $urls_filter = str_replace("*", "%", $urls_filter);
    $filters = explode("\n", $urls_filter);
    $query .= " AND (";
    foreach ($filters as $filter) {
      $filter = trim($filter);
      $query .= "{$collumn} LIKE '{$filter}' OR ";
    }
    $query = substr_replace($query, '', strlen($query) - 3, 3);
    $query .= ")";
  }
  return $query;
}

/**
 * Loads list of matching URLs.
 */
function advanced_link_url_load($field_name, $type_name, $string = NULL) {
  module_load_include('inc', 'content', 'includes/content.crud');
  $instance = content_field_instance_read(array(
    'field_name' => $field_name,
    'type_name' => $type_name,
  ));
  $search_type = $instance[0]['widget']['urls_search'];
  $urls_filter = isset($instance[0]['widget']['urls_filter']) ? trim($instance[0]['widget']['urls_filter']) : NULL;
  if ($search_type == ADVANCED_LINK_SEARCH_CONTAINS) {

    // Search for urls that contain our string.
    $condition_menu = '%s[^\\%]*$';
    $condition_alias = '%%%s%%';
  }
  else {

    // Search for urls that start string.
    $condition_menu = '^%s[^\\%]*$';
    $condition_alias = '%s%%';
  }
  if ($string) {
    $urls_list = array();
    $urls_list_menu = array();

    // If database connection type is pgsql we should use '~'
    // instead 'REGEXP' in query condition.
    global $db_type;
    if ($db_type == 'pgsql') {
      $operator = '~';
    }
    else {
      $operator = 'REGEXP';
    }

    // $operator use for exclude urls with '%' character.
    $query_text = "SELECT path FROM {menu_router} WHERE path " . $operator . " lower('" . $condition_menu . "')";
    $query_text = advanced_link_autocomplete_url_filter_conditions($urls_filter, $query_text, 'path');
    $query_text .= " ORDER BY path ASC";
    $query = db_query_range($query_text, $string, 0, 20);
    while ($result = db_fetch_object($query)) {
      $urls_list_menu[$result->path] = $result->path;
    }
    $query_text = "SELECT dst FROM {url_alias} WHERE dst LIKE lower('" . $condition_alias . "')";
    $query_text = advanced_link_autocomplete_url_filter_conditions($urls_filter, $query_text, 'dst');
    $query_text .= " ORDER BY dst ASC";
    $query = db_query_range($query_text, $string, 0, 20 - count($urls_list_menu));
    while ($result = db_fetch_object($query)) {
      $urls_list[$result->dst] = $result->dst;
    }
    return $urls_list + $urls_list_menu;
  }
  else {
    return FALSE;
  }
}

/**
 * Implements hook_widget_info().
 */
function advanced_link_widget_info() {
  return array(
    'advanced_link' => array(
      'label' => t('Advanced Link'),
      'field types' => array(
        'link',
      ),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
  );
}

/**
 * Implements hook_widget().
 */
function advanced_link_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  $element = array(
    '#type' => $field['widget']['type'],
    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
    '#title' => $field['widget']['label'],
    '#weight' => $field['widget']['weight'],
    '#description' => $field['widget']['description'],
    '#required' => $field['required'],
    '#field' => $field,
  );
  return $element;
}

/**
 * Provides field validation by additional filtering options.
 */
function advanced_link_validator($element, &$form_state) {
  $urls_allowed = isset($element['#field']['widget']['urls_allowed']) ? $element['#field']['widget']['urls_allowed'] : ADVANCED_LINK_BOTH;
  $urls_filter = isset($element['#field']['widget']['urls_filter']) ? trim($element['#field']['widget']['urls_filter']) : NULL;
  $values = $element['#value'];
  $url = trim($values['url']);
  if ($url) {
    if ($urls_allowed != ADVANCED_LINK_BOTH) {

      // Getting link type - internal or external.
      if ($urls_allowed != link_validate_url($url)) {
        form_set_error($element['#field_name'], t('URLs need to be @url_allowed', array(
          '@url_allowed' => drupal_strtoupper($urls_allowed),
        )));
      }
    }

    // Checking link URL by URL filter patterns.
    if ($urls_filter) {
      $match = drupal_match_path($url, $urls_filter);
      if (!$match) {
        form_set_error($element['#field_name'], t("You can't use such URLs."));
      }
    }
  }
}

/**
 * Implements hook_widget_settings().
 */
function advanced_link_widget_settings($op, $widget) {
  switch ($op) {
    case 'form':
      $form['urls_allowed'] = array(
        '#type' => 'radios',
        '#title' => t('URLs allowed'),
        '#options' => array(
          ADVANCED_LINK_INTERNAL => t('Allowed only internal.'),
          ADVANCED_LINK_EXTERNAL => t('Allowed only external.'),
          ADVANCED_LINK_BOTH => t('Allowed both (external and internal).'),
        ),
        '#default_value' => isset($widget['urls_allowed']) ? $widget['urls_allowed'] : ADVANCED_LINK_BOTH,
        '#description' => t('More filtering options can be found in "URLs filter" textfield.'),
      );
      $filter_description = t("Enter one page per line as Drupal paths or only another external. Note, that filtering applies to any 'URLs allowed' mode.   The '*' character is a wildcard. Example paths are blog for the <em>blog</em> page and blog/* for every personal blog. %front is the front page.", array(
        '%front' => '<front>',
      ));
      $form['urls_filter'] = array(
        '#type' => 'textarea',
        '#title' => t('URLs filter'),
        '#default_value' => isset($widget['urls_filter']) ? $widget['urls_filter'] : '',
        '#description' => $filter_description,
      );
      $form['title_list'] = array(
        '#type' => 'textarea',
        '#title' => t('List of allowed titles'),
        '#default_value' => isset($widget['title_list']) ? $widget['title_list'] : '',
        '#description' => t("Enter the list of allowed titles separated by new line.<br />Leave empty to allow user input any text in title."),
      );
      $form['urls_search'] = array(
        '#type' => 'radios',
        '#title' => t('Suggest urls that'),
        '#options' => array(
          ADVANCED_LINK_SEARCH_START => t('start with specified characters'),
          ADVANCED_LINK_SEARCH_CONTAINS => t('contain specified characters'),
        ),
        '#default_value' => isset($settings['urls_search']) ? $settings['urls_search'] : ADVANCED_LINK_SEARCH_START,
        '#description' => t('Select search type for autocomplete suggestion.'),
      );
      return $form;
    case 'save':
      return array(
        'urls_filter',
        'urls_allowed',
        'title_list',
        'urls_search',
      );
  }
}

/**
 * Implements hook_theme().
 */
function advanced_link_theme() {
  return array(
    'advanced_link' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
  );
}

/**
 * FAPI theme for an individual text elements.
 */
function theme_advanced_link($element) {

  // Prefix single value advanced_link fields with the name of the field.
  if (empty($element['#field']['multiple'])) {
    if (isset($element['url']) && isset($element['title'])) {
      $element['url']['#title'] = $element['#title'] . ' ' . $element['url']['#title'];
      $element['title']['#title'] = $element['#title'] . ' ' . $element['title']['#title'];
    }
    elseif ($element['url']) {
      $element['url']['#title'] = $element['#title'];
    }
  }
  $output = '';
  $output .= '<div class="advanced_link-field-subrow clear-block advanced_link-autocomplete">';
  if (isset($element['title'])) {
    if (trim($element['#field']['widget']['title_list'])) {
      $output .= '<div class="advanced_link-field-title advanced_link-field-column">' . theme('select', $element['title']) . '</div>';
    }
    else {
      $output .= '<div class="advanced_link-field-title advanced_link-field-column">' . theme('textfield', $element['title']) . '</div>';
    }
  }
  $output .= '<div class="advanced_link-field-url' . (isset($element['title']) ? ' advanced_link-field-column' : '') . '">' . theme('textfield', $element['url']) . '</div>';
  $output .= '</div>';
  if (!empty($element['attributes']['target'])) {
    $output .= '<div class="advanced_link-attributes">' . theme('checkbox', $element['attributes']['target']) . '</div>';
  }
  return $output;
}

/**
 * Implements hook_elements().
 */
function advanced_link_elements() {
  $elements = array();
  $elements['advanced_link'] = array(
    '#input' => TRUE,
    '#process' => array(
      'advanced_link_process',
    ),
    '#element_validate' => array(
      'advanced_link_validator',
    ),
  );
  return $elements;
}

/**
 * Process the advanced_link type element before displaying the field.
 *
 * Build the form element. When creating a form using FAPI #process,
 * note that $element['#value'] is already set.
 *
 * The $fields array is in $form['#field_info'][$element['#field_name']].
 */
function advanced_link_process($element, $edit, $form_state, $form) {
  $urls_allowed = $element['#field']['widget']['urls_allowed'];
  $field_name = $element['#field_name'];
  $type_name = $element['#type_name'];
  $field = $form['#field_info'][$element['#field_name']];
  $delta = $element['#delta'];
  $element['url'] = array(
    '#type' => 'textfield',
    '#maxlength' => LINK_URL_MAX_LENGTH,
    '#title' => t('URL'),
    // Increase width of field.
    '#attributes' => array(
      'style' => 'width: 100%',
    ),
    '#description' => $element['#description'],
    '#required' => $delta == 0 && $field['url'] !== 'optional' ? $element['#required'] : FALSE,
    '#default_value' => isset($element['#value']['url']) ? $element['#value']['url'] : NULL,
  );
  if ($urls_allowed != ADVANCED_LINK_EXTERNAL && advanced_link_autocomplete_access()) {
    $element['url']['#autocomplete_path'] = 'advanced_link/autocomplete/' . $element['#field_name'] . '/' . $element['#type_name'];
  }
  if ($field['title'] != 'none' && $field['title'] != 'value') {
    if (isset($field['widget']['title_list']) && trim($field['widget']['title_list'])) {
      $final_array_word = advanced_link_extract_allowed_values($field['widget']['title_list']);
      $element['title'] = array(
        '#type' => 'select',
        '#options' => $final_array_word,
        '#title' => t('Title'),
        '#required' => $delta == 0 && $field['title'] == 'required' ? $field['required'] : FALSE,
        '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL,
      );
    }
    else {
      $element['title'] = array(
        '#type' => 'textfield',
        '#maxlength' => $element['#field']['widget']['title_lenght'],
        '#title' => t('Title'),
        '#required' => $delta == 0 && $field['title'] == 'required' ? $field['required'] : FALSE,
        '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL,
      );
    }
  }

  // Initialize field attributes as an array if it is not an array yet.
  if (!is_array($field['attributes'])) {
    $field['attributes'] = array();
  }

  // Add default atrributes.
  $field['attributes'] += _advanced_link_default_attributes();
  $attributes = isset($element['#value']['attributes']) ? $element['#value']['attributes'] : $field['attributes'];
  if (!empty($field['attributes']['target']) && $field['attributes']['target'] == LINK_TARGET_USER) {
    $element['attributes']['target'] = array(
      '#type' => 'checkbox',
      '#title' => t('Open URL in a New Window'),
      '#return_value' => LINK_TARGET_NEW_WINDOW,
      '#default_value' => $attributes['target'],
    );
  }
  return $element;
}

/**
 * Returns array of default attributes for advanced link.
 */
function _advanced_link_default_attributes() {
  return array(
    'title' => '',
    'target' => ADVANCED_LINK_TARGET_DEFAULT,
    'class' => '',
    'rel' => '',
  );
}

/**
 * Extracts textarea values to array.
 */
function advanced_link_extract_allowed_values($values) {
  $values_as_array = explode("\n", $values);
  $result_values = array(
    '' => '',
  );
  foreach ($values_as_array as $title) {
    $result_values[trim(htmlentities($title))] = trim(htmlentities($title));
  }
  return $result_values;
}

Functions

Namesort descending Description
advanced_link_autocomplete Menu callback for autocomplete.
advanced_link_autocomplete_access Checks if current user has access to autocomplete.
advanced_link_autocomplete_url_filter_conditions Filtering pathes pathes and alias before fetching.
advanced_link_elements Implements hook_elements().
advanced_link_extract_allowed_values Extracts textarea values to array.
advanced_link_menu Implements hook_menu().
advanced_link_perm Implements hook_perm().
advanced_link_process Process the advanced_link type element before displaying the field.
advanced_link_theme Implements hook_theme().
advanced_link_url_load Loads list of matching URLs.
advanced_link_validator Provides field validation by additional filtering options.
advanced_link_widget Implements hook_widget().
advanced_link_widget_info Implements hook_widget_info().
advanced_link_widget_settings Implements hook_widget_settings().
theme_advanced_link FAPI theme for an individual text elements.
_advanced_link_default_attributes Returns array of default attributes for advanced link.

Constants