<?php

/**
 * @file
 * Bring customizations to the default search box
 *
 * Adds node types and taxonomy options to the search form
 */

define('CUSTOM_SEARCH_LABEL_DEFAULT', t('Search this site'));
define('CUSTOM_SEARCH_SUBMIT_TEXT_DEFAULT', t('Search'));
define('CUSTOM_SEARCH_TYPE_SELECTOR_LABEL_DEFAULT', t('Search for'));
define('CUSTOM_SEARCH_MAX_LENGTH_DEFAULT', 128);
define('CUSTOM_SEARCH_SIZE_DEFAULT', 15);
define('CUSTOM_SEARCH_ALL_TEXT_DEFAULT', t('-Any-'));
define('CUSTOM_SEARCH_CRITERIA_OR_LABEL_DEFAULT', t('Containing any of the words'));
define('CUSTOM_SEARCH_CRITERIA_PHRASE_LABEL_DEFAULT', t('Containing the phrase'));
define('CUSTOM_SEARCH_CRITERIA_NEGATIVE_LABEL_DEFAULT', t('Containing none of the words'));
define('CUSTOM_SEARCH_FILTER_LABEL_DEFAULT', t('Filter the results'));
define('CUSTOM_SEARCH_PATHS_SELECTOR_LABEL_DEFAULT', t('Customize your search'));
define('CUSTOM_SEARCH_PATHS_TERMS_SEPARATOR_DEFAULT', '+');

/**
 * Includes
 */
module_load_include('inc', 'custom_search', 'includes/forms');
module_load_include('inc', 'custom_search', 'includes/apachesolr_search');
module_load_include('inc', 'custom_search', 'includes/google_appliance');
module_load_include('inc', 'custom_search', 'includes/luceneapi_node');

/**
 * Implementation of hook_menu().
 */
function custom_search_menu() {
  $items['admin/settings/custom_search'] = array(
    'title'             => 'Custom Search',
    'description'       => 'Customize the default search, change labels, default texts, ordering, and display content types and taxonomy selectors.',
    'page callback'     => 'drupal_get_form',
    'page arguments'    => array('custom_search_admin'),
    'access arguments'  => array('administer custom search'),
    'file'              => 'custom_search.admin.inc',
  );
  $items['admin/settings/custom_search/settings'] = array(
    'title'             => 'Settings',
    'description'       => 'Change the labels, the default texts and the ordering of elements.',
    'access arguments'  => array('administer custom search'),
    'type'              => MENU_DEFAULT_LOCAL_TASK,
    'weight'            => 0,
  );
  $items['admin/settings/custom_search/content'] = array(
    'title'             => 'Content',
    'description'       => 'Select the content types to present as search options in the search block.',
    'page arguments'    => array('custom_search_content_admin'),
    'access arguments'  => array('administer custom search'),
    'file'              => 'custom_search.admin.inc',
    'type'              => MENU_LOCAL_TASK,
    'weight'            => 1,
  );
  $items['admin/settings/custom_search/results'] = array(
    'title'             => 'Results page',
    'description'       => 'Customize the search results page.',
    'page arguments'    => array('custom_search_results_admin'),
    'access arguments'  => array('administer custom search'),
    'file'              => 'custom_search.admin.inc',
    'type'              => MENU_LOCAL_TASK,
    'weight'            => 4,
  );
  return $items;
}

/**
 * Implementation of hook_menu_alter().
 * (needed for compatibility with Search Ranking)
 */
function custom_search_menu_alter(&$callbacks) {
  // Panels integration
  if ($callbacks['search/node/%menu_tail']['page callback'] == 'page_manager_search_page') return;

  $callbacks['search/node/%menu_tail']['page callback'] = 'search_view';
  $callbacks['search/node/%menu_tail']['file'] = 'search.pages.inc';
  $callbacks['search/node/%menu_tail']['file path'] = drupal_get_path('module', 'search');
}

/**
 * Implementation of hook_perm().
 */
function custom_search_perm() {
  return array('administer custom search', 'use custom search');
}

/**
 * Implementation of hook_form_alter().
 */
function custom_search_form_alter(&$form, $form_state, $form_id) {

  // Filter the form_id value to identify all the custom blocks
  $form_id_processed = $form_id;
  $delta = '';
  for ($a = 1 ; $a <= variable_get('custom_search_blocks_number', 1) ; $a++) {
    if ($form_id == 'custom_search_blocks_form_' . $a) {
      $form_id_processed = 'custom_search_blocks_form';
      $delta = 'blocks_' . $a . '_';
    }
  }

  switch ($form_id_processed) {

    case 'search_form':

      if (isset($form['advanced'])) {

        // Criteria
        if (!variable_get('custom_search_advanced_or_display', TRUE)) $form['advanced']['keywords']['or']['#type'] = 'hidden';
        if (!variable_get('custom_search_advanced_phrase_display', TRUE)) $form['advanced']['keywords']['phrase']['#type'] = 'hidden';
        if (!variable_get('custom_search_advanced_negative_display', TRUE)) $form['advanced']['keywords']['negative']['#type'] = 'hidden';

        // Taxonomy
        if (module_exists('taxonomy')) {
          $vocabularies = taxonomy_get_vocabularies();
          foreach ($vocabularies as $voc) {
            if (!variable_get('custom_search_advanced_voc' . $voc->vid . '_display', TRUE)) unset($form['advanced']['category']['#options'][$voc->name]);
          }
          if (!count($form['advanced']['category']['#options'])) unset($form['advanced']['category']['#type']);
        }

        // Content Types
        $names = array_keys(node_get_types('names'));
        foreach ($names as $name) {
          if (!variable_get('custom_search_advanced_type_' . $name . '_display', TRUE)) unset($form['advanced']['type']['#options'][$name]);
        }
        if (!count($form['advanced']['type']['#options'])) unset($form['advanced']['type']['#type']);

        if (!variable_get('custom_search_results_search', TRUE)) {
          // If basic search is hidded, original search terms are imported into advanced search.
          $original_keys = $form['basic']['inline']['keys']['#default_value'];
          $temp_keys = explode(' ', $original_keys);
          foreach ($temp_keys as $value) {
            if (drupal_substr($value, 0, 5) != 'type:' && drupal_substr($value, 0, 9) != 'category:') $keys[] = $value;
          }
          $form['advanced']['keywords']['or']['#default_value'] = implode(' ', $keys);
          if (!isset($GLOBALS['custom_search_nb_results']) || (isset($GLOBALS['custom_search_nb_results']) && !$GLOBALS['custom_search_nb_results'])) $form['advanced']['#collapsed'] = FALSE;
          $form['basic']['#prefix'] = '<div class="element-invisible">';
          $form['basic']['#suffix'] = '</div>';
        }

        $form['advanced']['#collapsible'] = variable_get('custom_search_results_advanced_search_collapsible', TRUE);
        $form['advanced']['#collapsed'] = variable_get('custom_search_results_advanced_search_collapsed', TRUE);

        if (!variable_get('custom_search_results_advanced_search', TRUE)) $form['advanced']['#type'] = 'hidden';

      }

    break;

    case 'search_theme_form':
    case 'search_block_form':
    case 'custom_search_blocks_form':
      if (user_access('use custom search')) {

        // Title.
        $form[$form_id]['#title'] = t(variable_get('custom_search_' . $delta . 'label', CUSTOM_SEARCH_LABEL_DEFAULT));
        if (!variable_get('custom_search_' . $delta . 'label_visibility', TRUE)) $form[$form_id]['#post_render'] = array('_custom_search_hide_label');

        // Search box.
        $form[$form_id]['#default_value'] = t(variable_get('custom_search_' . $delta . 'text', ''));
        $form[$form_id]['#weight'] = variable_get('custom_search_' . $delta . 'search_box_weight', 0);
        if (!isset($form[$form_id]['#attributes']['class'])) $form[$form_id]['#attributes']['class'] = '';
        $form[$form_id]['#attributes']['class'] .= ' custom-search-default-value custom-search-box';
        $form[$form_id]['#size'] = variable_get('custom_search_' . $delta . 'size', CUSTOM_SEARCH_SIZE_DEFAULT);
        $form[$form_id]['#maxlength'] = variable_get('custom_search_' . $delta . 'max_length', CUSTOM_SEARCH_MAX_LENGTH_DEFAULT);

        // Default text.
        $form['default_text'] = array(
          '#type'           => 'hidden',
          '#default_value'  => t(variable_get('custom_search_' . $delta . 'text', '')),
          '#attributes'     => array('class' => 'default-text'),
        );

        // CSS
        drupal_add_css(drupal_get_path('module', 'custom_search') . '/custom_search.css', 'module');

        // Criterion
        $criteria = array('or' => 6, 'phrase' => 7, 'negative' => 8);
        foreach ($criteria as $c => $w) {
          if (variable_get('custom_search_' . $delta . 'criteria_' . $c . '_display', FALSE)) {
            $form['custom_search_criteria_' . $c] = array(
              '#type'       => 'textfield',
              '#title'      => variable_get('custom_search_' . $delta . 'criteria_' . $c . '_label', constant('CUSTOM_SEARCH_CRITERIA_' . strtoupper($c) . '_LABEL_DEFAULT')),
              '#size'       => 15,
              '#maxlength'  => 255,
              '#weight'     => variable_get('custom_search_' . $delta . 'criteria_' . $c . '_weight', $w),
            );
          }
        }

        // Content type & other searches.
        // Content types.
        $toptions = array();
        $types = array_keys(array_filter(variable_get('custom_search_' . $delta . 'node_types', array())));
        if (count($types)) {
          $names = node_get_types('names');
          if (count($types) > 1 || variable_get('custom_search_' . $delta . 'any_force', FALSE)) $toptions['c-all'] = variable_get('custom_search_' . $delta . 'type_selector_all', CUSTOM_SEARCH_ALL_TEXT_DEFAULT);
          foreach ($types as $type) {
            $toptions['c-' . $type] = $names[$type];
          }
        }
        $options = array();
        // Other searches.
        $others = array_keys(array_filter(variable_get('custom_search_' . $delta . 'other', array())));
        // If content types and other searches are combined, make an optgroup.
        if (count($others) && count($toptions) && variable_get('custom_search_' . $delta . 'type_selector', 'select') == 'select') {
          $content = module_invoke('node', 'search', 'name');
          $options[$content] = $toptions;
        }
        else {
          $options = $toptions;
        }
        foreach (module_implements('search') as $module) {
          if ($module != 'node' && $name = module_invoke($module, 'search', 'name')) {
            if (in_array($module, $others)) $options['o-' . $module] = $name;
          }
        }
        if (count($options)) {
          $selector_type = variable_get('custom_search_' . $delta . 'type_selector', 'select');
          if ($selector_type == 'selectmultiple') {
            $selector_type = 'select';
            $multiple = TRUE;
          }
          else $multiple = FALSE;
          $form['custom_search_types'] = array(
            '#type'           => $selector_type,
            '#multiple'       => $multiple,
            '#title'          => variable_get('custom_search_' . $delta . 'type_selector_label', CUSTOM_SEARCH_TYPE_SELECTOR_LABEL_DEFAULT),
            '#options'        => $options,
            '#default_value'  => ((variable_get('custom_search_' . $delta . 'type_selector', 'select') == 'checkboxes') ? array('c-all') : 'c-all'),
            '#attributes'     => ($selector_type != 'radios' && $selector_type != 'checkboxes') ? array('class' => 'custom-search-selector custom-search-types') : array(),
            '#prefix'         => ($selector_type == 'radios' || $selector_type == 'checkboxes') ? '<div class="custom-search-selector custom-search-types">' : '',
            '#suffix'         => ($selector_type == 'radios' || $selector_type == 'checkboxes') ? '</div>' : '',
            '#weight'         => variable_get('custom_search_' . $delta . 'content_types_weight', 1),
            '#validated'      => TRUE,
          );

          // If there's only one type, hide the selector
          if (count($others) + count($types) == 1 && !variable_get('custom_search_' . $delta . 'any_force', FALSE)) {
            $form['custom_search_types']['#type'] = 'hidden';
            $form['custom_search_types']['#default_value'] = key(array_slice($options, count($options)-1));
          }

          if (!variable_get('custom_search_' . $delta . 'type_selector_label_visibility', TRUE)) $form['custom_search_types']['#post_render'] = array('_custom_search_hide_label');
        }

        // Custom paths
        if (variable_get('custom_search_' . $delta . 'paths', '') != '') {
          $options = array();
          $lines = explode("\n", variable_get('custom_search_' . $delta . 'paths', ''));
          foreach ($lines as $line) {
            $temp = explode('|', $line);
            $options[$temp[0]] = (count($temp) >= 2) ? t($temp[1]) : '';
          }
          if (count($options) == 1) {
            $form['custom_search_paths'] = array(
              '#type'           => 'hidden',
              '#default_value'  => key($options),
            );
          }
          else {
            $form['custom_search_paths'] = array(
              '#type'           => variable_get('custom_search_' . $delta . 'paths_selector', 'select'),
              '#title'          => variable_get('custom_search_' . $delta . 'paths_selector_label', CUSTOM_SEARCH_PATHS_SELECTOR_LABEL_DEFAULT),
              '#options'        => $options,
              '#default_value'  => key($options),
              '#weight'         => variable_get('custom_search_' . $delta . 'custom_paths_weight', 9),
            );
            if (!variable_get('custom_search_' . $delta . 'paths_selector_label_visibility', TRUE)) $form['custom_search_paths']['#post_render'] = array('_custom_search_hide_label');
          }
        }

        // Submit button.
        $form['submit']['#value'] = t(variable_get('custom_search_' . $delta . 'submit_text', CUSTOM_SEARCH_SUBMIT_TEXT_DEFAULT));

        if (variable_get('custom_search_' . $delta . 'image_path', '') != '') {
          $form['submit']['#type'] = 'image_button';
          $form['submit']['#src'] = variable_get('custom_search_' . $delta . 'image_path', '');
          $form['submit']['#name'] = 'op';
          $form['submit']['#attributes'] = array('alt' => variable_get('custom_search_' . $delta . 'submit_text', CUSTOM_SEARCH_SUBMIT_TEXT_DEFAULT), 'class' => 'custom-search-button');
        }
        elseif ($form['submit']['#value'] == '') $form['submit']['#attributes'] = array('style' => 'display:none;');

        $form['submit']['#weight'] = variable_get('custom_search_' . $delta . 'submit_button_weight', 3);

        // Popup
        $form['popup'] = array(
          '#type'       => 'fieldset',
          '#weight'     => 1 + variable_get('custom_search_' . $delta . 'search_box_weight', 0),
          '#attributes' => array('class' => 'custom_search-popup'),
        );
        if (!empty($form['custom_search_types']) && variable_get('custom_search_' . $delta . 'content_types_region', 'block') == 'popup') {
          $form['popup']['custom_search_types'] = $form['custom_search_types'];
          unset($form['custom_search_types']);
        }
        if (!empty($form['custom_search_paths']) && variable_get('custom_search_' . $delta . 'custom_paths_region', 'block') == 'popup') {
          $form['popup']['custom_search_paths'] = $form['custom_search_paths'];
          unset($form['custom_search_paths']);
        }
        foreach ($criteria as $c => $w) {
          if (variable_get('custom_search_' . $delta . 'criteria_' . $c . '_display', FALSE) && variable_get('custom_search_' . $delta . 'criteria_' . $c . '_region', 'block') == 'popup') {
            $form['popup']['custom_search_criteria_' . $c] = $form['custom_search_criteria_' . $c];
            unset($form['custom_search_criteria_' . $c]);
          }
        }

        // Form attributes
        $form['#attributes']['class'] = 'search-form';
        $form['#submit'][] = 'custom_search_submit';
        $form['form_token']['#default_value'] = drupal_get_token($form_id);

      }
    break;
  }
}

/**
 * Alter the search to respect the search modes selected.
 */
function custom_search_submit($form, &$form_state) {
  $delta = (isset($form_state['values']['delta'])) ? 'blocks_' . $form_state['values']['delta'] . '_' : '' ;
  variable_set('custom_search_delta', $delta); // save for later use (exclusion & refresh)
  $type = 'node';
  $keys = $form_state['values'][$form_state['values']['form_id']];

  $types = $form_state['values']['custom_search_types'];
  if (!is_array($types)) $types = array($types);
  $types = array_map('_custom_search_filter_keys', array_filter($types));

  if (module_exists('taxonomy')) {
    $terms = array();
    $vocabularies = taxonomy_get_vocabularies();
    foreach ($vocabularies as $voc) {
        $vterms = $form_state['values']['custom_search_vocabulary_' . $voc->vid];
        if (!is_array($vterms)) $vterms = array($vterms);
        $terms = array_merge($terms, $vterms);
    }
    $terms = array_map('_custom_search_filter_keys', array_values(array_filter($terms)));
    // if one or more -Any- is selected, delete them
    while (($index = array_search('all', $terms)) !== FALSE) array_splice($terms, $index, 1);
  }

  $search_types = module_implements('search');
  if (in_array(current($types), $search_types)) $type = current($types);
  else {
    if (trim($form_state['values']['custom_search_criteria_or']) != '') $keys .= ' ' . str_replace(' ', ' OR ', trim($form_state['values']['custom_search_criteria_or']));
    if (trim($form_state['values']['custom_search_criteria_negative']) != '') $keys .= ' -' . str_replace(' ', ' -', trim($form_state['values']['custom_search_criteria_negative']));
    if (trim($form_state['values']['custom_search_criteria_phrase']) != '') $keys .= ' "' . trim($form_state['values']['custom_search_criteria_phrase']) . '"';
    $original_keywords = $keys;
    if (count($types)) {
      // If a content type is selected, and it's not -Any-, search for that type.
      if (!in_array('all', $types)) $keys = search_query_insert($keys, 'type', implode(',', $types));
      // If -Any- is selected and -Any- is set to restrict the search, grab the content types.
      elseif (variable_get('custom_search_' . $delta . 'any_restricts', FALSE)) {
        $restricted_types = array_keys(array_filter(variable_get('custom_search_' . $delta . 'node_types', array())));
        $keys = search_query_insert($keys, 'type', implode(',', $restricted_types));
      }
    }
    if (module_exists('taxonomy') && count($terms)) {
      $keys = search_query_insert($keys, 'category', implode(',', $terms));
    }
  }

  // Integrates other search modules
  if (module_exists('apachesolr_search') && variable_get('apachesolr_search_make_default', 0)) {
    $search_path = _custom_search_apachesolr_search(array(
      'keywords'  => $original_keywords,
      'types'     => $types,
      'terms'     => (!empty($terms)) ? $terms : array(),
    ));
  }
  elseif (module_exists('google_appliance')) {
    $search_path = _custom_search_google_appliance_search(array(
      'keys'  => $keys,
    ));
  }
  elseif (module_exists('luceneapi_node') && variable_get('luceneapi:default_search', 0)) {
    $search_path = _custom_search_lucenapi_search(array(
      'keywords'  => $original_keywords,
      'types'     => $types,
      'terms'     => (!empty($terms)) ? $terms : array(),
    ));
  }
  else {
    $search_path = array(
      'path'  => 'search/' . $type . '/' . $keys,
      'query' => '',
    );
  }

  // Build a custom path if needed
  if (isset($form_state['values']['custom_search_paths']) && $form_state['values']['custom_search_paths'] != '') {
    $custom_path = str_replace('[key]', $form_state['values'][$form_state['values']['form_id']], $form_state['values']['custom_search_paths']);
    if (strpos($form_state['values']['custom_search_paths'], '[terms]') !== FALSE) $custom_path = str_replace('[terms]', (count($terms)) ? implode($form_state['values']['custom_search_paths_terms_separator'], $terms) : '', $custom_path);
    $form_state['redirect'] = $custom_path;
    // check for external path. If not, add base path
    if (drupal_substr($custom_path, 0, 4) != 'http') $form_state['redirect'] = url($form_state['redirect'], array('absolute' => TRUE));
  }
  else $form_state['redirect'] = url($search_path['path'], array('query' => $search_path['query'], 'absolute' => TRUE));

}

/*
 * Rewrite the sql query to exclude content types
 */
function custom_search_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
  if ($query == '' && $primary_table == 'n' && $primary_field = 'nid' && empty($args)) {
    $excluded_types = array_filter(variable_get('custom_search_' . variable_get('custom_search_delta', '') . 'node_types_excluded', array()));
    if (!empty($excluded_types)) {
      $where = " n.type NOT IN ('". join("','", $excluded_types) ."') ";
      return array('where' => $where);
    }
  }
}

/**
 * Implementation of hook_init().
 */
function custom_search_init() {
  if (user_access('use custom search')) {
    drupal_add_js(drupal_get_path('module', 'custom_search') . '/js/custom_search.js');
    drupal_add_js(array('custom_search' => array(
      'form_target' => variable_get('custom_search_target', '_self'),
      'solr' => (module_exists('apachesolr_search') && variable_get('apachesolr_search_make_default', 0)) ? 1 : 0,
    )), 'setting');
  }
}

/**
 * Implementation of hook_theme().
 */
function custom_search_theme() {
  $path = drupal_get_path('module', 'custom_search') . '/theme';
  $custom_search_theme_array = array(
    'custom_search_javascript'  => array('arguments' => array()),
    'custom_search_sort_form' => array(
      'path'      => $path,
      'template'  => 'custom_search-sort-form',
      'arguments' => array('form' => NULL),
    ),
    'search_results' => array(
      'arguments' => array('results' => NULL, 'type' => NULL),
      'path'      => $path,
      'file'      => 'custom_search.pages.inc',
      'template'  => 'custom_search-results',
    ),
    'search_result' => array(
      'arguments' => array('result' => NULL, 'type' => NULL),
      'path'      => $path,
      'file'      => 'custom_search.pages.inc',
      'template'  => 'custom_search-result',
    ),
  );

  // Panels integration
  $search_results_page_callback = menu_get_item('search/node/%menu_tail');
  if ($search_results_page_callback['page_callback'] == 'page_manager_search_page') {
    unset($custom_search_theme_array['search_results'], $custom_search_theme_array['search_result']);
  }

  return $custom_search_theme_array;
}

/**
 * Filter the types.
 */
function _custom_search_filter_keys($val) {
  return (strlen($val) > 2 && $val[1] == '-') ? drupal_substr($val, 2) : $val;
}

/**
 * Remove the label by adding a class.
 */
function _custom_search_hide_label($content, $elements) {
  if (strpos($content, '<label class') !== FALSE) $content = str_replace('<label class="', '<label class="element-invisible ', $content);
  else $content = str_replace('<label', '<label class="element-invisible"', $content);
  return $content;
}

/**
 * Function used to filter node_type array to only filter those that are configured in Custom Search Form
 */
function custom_search_filter_array($value = FALSE) {
  return $value !== 0;
}
