Commit 4f95bc05 authored by Manish Kumar's avatar Manish Kumar
Browse files

D8 initial commit

parent ae898c52
Loading
Loading
Loading
Loading
+0 −448
Original line number Diff line number Diff line
@@ -4,451 +4,3 @@
 * @file
 * Json autocomplete Module file.
 */

/**
 * Constant used across the module.
 */
const JSON_AUTOCOMPLETE_ADMIN_SETTINGS = 'admin/config/json-autocomplete';
const JSON_AUTOCOMPLETE_ADMIN_CONFIG = 'admin/config/json-autocomplete/config';
const JSON_AUTOCOMPLETE_ADMIN_SETTINGS_URL = 'admin/config/json-autocomplete/config/settings';
const JSON_AUTOCOMPLETE_FIELD_LOG_REPORT = 'admin/config/json-autocomplete/config/field-log';
const JSON_AUTOCOMPLETE_REBUILD_JSON = 'admin/config/json-autocomplete/rebuild/%';
const JSON_AUTOCOMPLETE_URL = 'json/autocomplete';
const JSON_AUTOCOMPLETE_WIDGET_TAXONOMY_TERM_REFERNCE = 'json_taxonomy_autocomplete';
const JSON_AUTOCOMPLETE_SELECTED_FILE_SYSTEM = 'json_taxonomy_stream_wrapper';
const JSON_AUTOCOMPLETE_UPDATE_ON_INSERT = 'json_taxonomy_update_on_insert';
const JSON_AUTOCOMPLETE_UPDATE_ON_UPDATE = 'json_taxonomy_update_on_update';
const JSON_AUTOCOMPLETE_UPDATE_ON_DELETE = 'json_taxonomy_update_on_delete';
const JSON_AUTOCOMPLETE_NUMBER_OF_RESULTS = 'json_taxonomy_number_of_results';

/**
 * Implements hook_help().
 */
function json_autocomplete_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/help#json_autocomplete':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('This module provides enhanced auto complete widget which is better in performance as compare to default auto complete, as it name suggests that searching based on json, so instead of get result from database it searches from json and gives fast results with better user experience.') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_permission().
 */
function json_autocomplete_permission() {
  return array(
    'administer json autocomplete' => array(
      'title' => t('Administer json autocomplete configuration'),
      'description' => t('Administer json autocomplete configuration'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function json_autocomplete_menu() {
  $items[JSON_AUTOCOMPLETE_ADMIN_SETTINGS] = array(
    'title' => 'JSON AUTOCOMPLETE',
    'description' => 'Json autocomplete configurations',
    'position' => 'right',
    'weight' => -15,
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array('administer json autocomplete'),
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
  );
  $items[JSON_AUTOCOMPLETE_ADMIN_CONFIG] = array(
    'title' => 'Json autocomplete configurations',
    'description' => 'Json autocomplete configurations',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('json_autocomplete_settings_form'),
    'access arguments' => array('administer json autocomplete'),
    'file' => 'json_autocomplete.admin.inc',
  );
  $items[JSON_AUTOCOMPLETE_ADMIN_SETTINGS_URL] = array(
    'title' => 'Json autocomplete configurations',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );
  $items[JSON_AUTOCOMPLETE_FIELD_LOG_REPORT] = array(
    'title' => 'Json autocomplete fields',
    'description' => 'Display used field as json autocomplete and it\'s file status',
    'page callback' => 'json_autocomplete_field_status_report',
    'access arguments' => array('administer json autocomplete'),
    'type' => MENU_LOCAL_TASK,
    'file' => 'json_autocomplete.admin.inc',
  );
  $items[JSON_AUTOCOMPLETE_REBUILD_JSON] = array(
    'title' => 'Rebuild json field',
    'page callback' => 'json_autocomplete_rebuild_json_field',
    'page arguments' => array(4),
    'access arguments' => array('administer json autocomplete'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'json_autocomplete.admin.inc',
  );
  $items[JSON_AUTOCOMPLETE_URL] = array(
    'title' => 'Json autocomplete',
    'page callback' => 'json_autocomplete_callback',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
    'file' => 'json_autocomplete.pages.inc',
  );
  return $items;
}

/**
 * Implements hook_field_widget_info().
 */
function json_autocomplete_field_widget_info() {
  return array(
    JSON_AUTOCOMPLETE_WIDGET_TAXONOMY_TERM_REFERNCE => array(
      'label' => t('Enhanced autocomplete (json based)'),
      'field types' => array('taxonomy_term_reference'),
      'settings' => array(
        'size' => 60,
        'match_operator' => 'STARTS_WITH',
        'json_autocomplete_path' => 'json/autocomplete/taxonomy',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function json_autocomplete_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  if ($instance['widget']['type'] == JSON_AUTOCOMPLETE_WIDGET_TAXONOMY_TERM_REFERNCE) {
    $tags = array();
    foreach ($items as $item) {
      $tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
    }

    $search_type = $instance['widget']['settings']['match_operator'] == 'STARTS_WITH' ? 1 : 0;
    $element += array(
      '#type' => 'textfield',
      '#default_value' => taxonomy_implode_tags($tags),
      '#autocomplete_path' => $instance['widget']['settings']['json_autocomplete_path'] . '/' . $field['field_name'] . '/' . $search_type,
      '#size' => $instance['widget']['settings']['size'],
      '#maxlength' => 1024,
      '#element_validate' => array('taxonomy_autocomplete_validate'),
    );
  }

  return $element;
}

/**
 * Implements hook_field_widget_settings_form().
 */
function json_autocomplete_field_widget_settings_form($field, $instance) {
  $widget = $instance['widget'];
  $settings = $widget['settings'] + field_info_widget_settings($widget['type']);

  $form = array();

  if ($widget['type'] == JSON_AUTOCOMPLETE_WIDGET_TAXONOMY_TERM_REFERNCE) {
    $form['match_operator'] = array(
      '#type' => 'select',
      '#title' => t('Autocomplete matching'),
      '#default_value' => $settings['match_operator'],
      '#options' => array(
        'STARTS_WITH' => t('Starts with'),
        'CONTAINS' => t('Contains'),
      ),
      '#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of nodes.'),
    );
    $form['size'] = array(
      '#type' => 'textfield',
      '#title' => t('Size of textfield'),
      '#default_value' => $settings['size'],
      '#element_validate' => array('_element_validate_integer_positive'),
      '#required' => TRUE,
    );
  }

  return $form;
}

/**
 * Implements hook_form_alter().
 */
function json_autocomplete_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'field_ui_widget_type_form' && array_key_exists(JSON_AUTOCOMPLETE_WIDGET_TAXONOMY_TERM_REFERNCE, $form['basic']['widget_type']['#options'])) {
    form_load_include($form_state, 'pages.inc', 'json_autocomplete');
    $form['#validate'][] = 'json_autocomplete_field_ui_widget_type_form_validate';
    $form['#submit'][] = 'json_autocomplete_field_ui_widget_type_form_submit';
  }
}

/**
 * Implements hook_taxonomy_term_insert().
 */
function json_autocomplete_taxonomy_term_insert($term) {
  json_autocomplete_update_json_file($term, 'taxonomy_term_reference', 'insert');
}

/**
 * Implements hook_taxonomy_term_update().
 */
function json_autocomplete_taxonomy_term_update($term) {
  json_autocomplete_update_json_file($term, 'taxonomy_term_reference', 'update');
}

/**
 * Implements hook_taxonomy_term_delete().
 */
function json_autocomplete_taxonomy_term_delete($term) {
  json_autocomplete_update_json_file($term, 'taxonomy_term_reference', 'delete');
}

/**
 * Update json file status into database.
 *
 * @param int $status
 *   Status of file.
 * @param string $field_name
 *   Field name.
 */
function json_autocomplete_change_json_file_status($status, $field_name) {
  return db_update('json_autocomplete_fields')
    ->fields(array(
      'status' => $status,
    ))
    ->condition('field_name', $field_name, '=')
    ->execute();
}

/**
 * Implements hook_cron().
 */
function json_autocomplete_cron() {
  json_autocomplete_json_rebuild();
}

/**
 * Rebuid json file for one or all fields.
 *
 * @param string $field_name
 *   Field machine name.
 */
function json_autocomplete_json_rebuild($field_name = NULL) {
  module_load_include('inc', 'json_autocomplete', 'json_autocomplete.pages');
  $query = db_select('json_autocomplete_fields')->fields('json_autocomplete_fields', array('field_name', 'field_type'))->condition('field_type', 'taxonomy_term_reference');
  if ($field_name) {
    $query->condition('field_name', $field_name);
  }
  $result = $query->execute()->fetchAll();
  if (count($result)) {
    foreach ($result as $value) {
      json_autocomplete_save_file($value->field_type, $value->field_name, TRUE);
      json_autocomplete_change_json_file_status(0, $value->field_name);
    }
  }
}

/**
 * Update json file.
 *
 * @param object $term
 *   Term object.
 * @param string $field_type
 *   Field type.
 * @param string $op
 *   Operation performed.
 */
function json_autocomplete_update_json_file($term, $field_type, $op) {
  $result = db_select('json_autocomplete_fields')->fields('json_autocomplete_fields', array('field_name'))->condition('field_type', $field_type)->execute()->fetchAll();
  if ($field_type == 'taxonomy_term_reference') {
    json_autocomplete_update_taxonomy_term_refernce_json_file($term, $op, $result);
  }
}

/**
 * Update taxonomy term reference fields json.
 *
 * @param object $term
 *   Term object.
 * @param string $op
 *   Operation performed.
 * @param array $fields
 *   An array of json fields.
 */
function json_autocomplete_update_taxonomy_term_refernce_json_file($term, $op, array $fields) {
  if (count($fields)) {
    foreach ($fields as $value) {
      $field = field_info_field($value->field_name);
      $vocabularies = taxonomy_vocabulary_get_names();
      $vids = array();
      foreach ($field['settings']['allowed_values'] as $tree) {
        $vids[] = $vocabularies[$tree['vocabulary']]->vid;
      }
      $need_to_update = $need_to_delete = FALSE;
      if (in_array($term->vid, $vids)) {
        switch ($op) {
          case 'insert':
            $need_to_update = variable_get(JSON_AUTOCOMPLETE_UPDATE_ON_INSERT, 0);
            break;

          case 'update':
            $need_to_update = variable_get(JSON_AUTOCOMPLETE_UPDATE_ON_UPDATE, 0);
            break;

          case 'delete':
            $need_to_delete = variable_get(JSON_AUTOCOMPLETE_UPDATE_ON_DELETE, 0);
            break;
        }
        if ($need_to_update || $need_to_delete) {
          $wrapper = file_stream_wrapper_get_instance_by_uri(json_autocomplete_get_default_file_system() . '://' . $value->field_name . '_json_autocomplete.json');
          $path = $wrapper->realpath();
          $json = json_decode(file_get_contents($path), TRUE);
          if ($need_to_delete) {
            unset($json[$term->tid]);
          }
          else {
            $json[$term->tid] = $term->name;
          }
          file_put_contents($path, json_encode($json));
          json_autocomplete_change_json_file_status(0, $value->field_name);
        }
        else {
          json_autocomplete_change_json_file_status(1, $value->field_name);
        }
      }
    }
  }
}

/**
 * Gets all available stream  wrapper.
 *
 * @return array
 *   Array of all available stream wrapper.
 */
function json_autocomplete_get_available_stream_wrapper() {
  $stream_wrappers = array();
  foreach (file_get_stream_wrappers() as $key => $value) {
    if ($key == 'temporary') {
      continue;
    }
    $stream_wrappers[$key] = $value['name'];
  }
  return $stream_wrappers;
}

/**
 * Gets the default file system for json file.
 */
function json_autocomplete_get_default_file_system() {
  $default_file_system = array_key_exists('private', json_autocomplete_get_available_stream_wrapper()) ? 'private' : '';
  return variable_get(JSON_AUTOCOMPLETE_SELECTED_FILE_SYSTEM, $default_file_system);
}

/**
 * Update json field log into database.
 *
 * @param string $op
 *   Operation to be performed.
 * @param string $widget_type
 *   Widget type.
 * @param string $field_name
 *   Field name.
 * @param int $fid
 *   Fid of file.
 * @param int $status
 *   Status of json file.
 */
function json_autocomplete_update_field_log($op, $widget_type, $field_name, $fid = 0, $status = 0) {
  if (empty($op) || empty($widget_type) || empty($field_name)) {
    return;
  }
  switch ($op) {
    case 'insert':
      db_merge('json_autocomplete_fields')
        ->key(array('field_name' => $field_name, 'field_type' => $widget_type))
        ->fields(array(
          'field_name' => $field_name,
          'field_type' => $widget_type,
          'status' => $status,
          'fid' => $fid,
        ))
        ->execute();
      break;

    case 'delete';
      db_delete('json_autocomplete_fields')
        ->condition('field_name', $field_name)
        ->condition('field_type', $widget_type)
        ->execute();
      break;
  }
}

/**
 * Save a file to drupal system.
 *
 * @param string $data
 *   A string containing the contents of the file.
 * @param string $destination
 *   Destination of file.
 * @param string $replace
 *   Flag whether need to replace or rename.
 *
 * @return bool
 *   Return whether file being saved or not.
 */
function json_autocomplete_file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME, $cron = FALSE) {
  global $user;

  // Update user to administrator on cron and drush command.
  if ($cron && !$user->uid) {
    if (!$user = user_load(1)) {
      $uid = db_query("select users.uid from {users} INNER JOIN {users_roles} ON users.uid = users_roles.uid INNER JOIN {role_permission} ON role_permission.rid = users_roles.rid WHERE role_permission.permission LIKE 'administer site configuration' LIMIT 1")->fetchField();
      $user = user_load($uid);
    }
  }

  if (empty($destination)) {
    $destination = file_default_scheme() . '://';
  }
  if (!file_valid_uri($destination)) {
    watchdog('file', 'The data could not be saved because the destination %destination is invalid. This may be caused by improper use of json_autocomplete_file_save_data() or a missing stream wrapper.', array('%destination' => $destination));
    drupal_set_message(t('The data could not be saved, because the destination is invalid. More information is available in the system log.'), 'error');
    return FALSE;
  }

  if ($uri = file_unmanaged_save_data($data, $destination, $replace)) {
    // Create a file object.
    $file = new stdClass();
    $file->fid = NULL;
    $file->uri = $uri;
    $file->filename = drupal_basename($uri);
    $file->filemime = file_get_mimetype($file->uri);
    $file->uid = $user->uid;
    $file->status = FILE_STATUS_PERMANENT;
    // If we are replacing an existing file re-use its database record.
    if ($replace == FILE_EXISTS_REPLACE) {
      $existing_files = file_load_multiple(array(), array('uri' => $uri));
      if (count($existing_files)) {
        $existing = reset($existing_files);
        $file->fid = $existing->fid;
        $file->filename = $existing->filename;
      }
    }
    // If we are renaming around an existing file (rather than a directory),
    // use its basename for the filename.
    elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
      $file->filename = drupal_basename($destination);
    }

    return file_save($file);
  }
  return FALSE;
}