You are here

search_by_page_paths.module in Search by Page 8

Module file for Search by Page Paths, a sub-module for Search by Page.

Allows you to add Drupal pages, such as Views pages, to Search by Page.

File

search_by_page_paths/search_by_page_paths.module
View source
<?php

/**
 * @file
 * Module file for Search by Page Paths, a sub-module for Search by Page.
 *
 * Allows you to add Drupal pages, such as Views pages, to Search by Page.
 * @ingroup search_by_page
 */
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Utility\Html;
use Drupal\Core\Language\Language;
const LANGUAGE_NONE = Language::LANGCODE_NOT_SPECIFIED;

/**
 * Implements Search by Page hook_search_by_page_paths().
 *
 * Reads path information stored in the database, and returns it.
 */
function search_by_page_paths_search_by_page_paths($environment) {
  $min_time = \Drupal::service('search_by_page.settings')
    ->getSetting('search_by_page_paths_min_time', $environment, 1);
  $max_time = \Drupal::service('search_by_page.settings')
    ->getSetting('search_by_page_paths_max_time', $environment, 0);
  $res = \Drupal::database()
    ->query('SELECT * FROM {sbpp_path} p WHERE p.environment=:env', array(
    ':env' => $environment,
  ))
    ->fetchAll();
  $ret = array();
  foreach ($res as $item) {

    // This comes from checkboxes, so it looks like 'en' => 'en', 'es' => 0,
    // etc.
    $languages = @unserialize($item->languages);
    $langs = array();
    if (is_array($languages)) {
      foreach ($languages as $lang => $val) {
        if ($val) {
          $langs[] = $lang;
        }
      }
    }
    $ret[$item->page_path] = array(
      'id' => $item->pid,
      'role' => $item->role,
      'languages' => $langs,
      'min_time' => $min_time,
      'max_time' => $max_time,
    );
  }
  return $ret;
}

/**
 * Implements Search by Page hook_search_by_page_query_modify().
 *
 * Adds an access permission check to the search query.
 */
function search_by_page_paths_search_by_page_query_modify($environment, $query) {

  // Unfortunately, we don't have a wholesale way to check access permissions.
  // So we have to go through all the paths in our table, and check whether the
  // user has permission to see each one, by querying the menu system.
  // Hopefully, there aren't too many! We store them in a temporary table.
  // Create temporary table
  $table = \Drupal::database()
    ->queryTemporary('SELECT p.pid, 1 as perm FROM {sbpp_path} p WHERE p.environment=:env', array(
    ':env' => $environment,
  ));

  // Check permissions on each path, store in temporary table
  $res = \Drupal::database()
    ->query('SELECT p.pid, p.page_path FROM {sbpp_path} p WHERE p.environment=:env', array(
    ':env' => $environment,
  ))
    ->fetchAll();
  foreach ($res as $item) {
    $parts = search_by_page_path_parts($item->page_path);
    $mitem = menu_get_item($parts[0]);
    if (!$mitem['access']) {
      \Drupal::database()
        ->delete($table)
        ->condition('pid', $item->pid)
        ->execute();
    }
  }

  // Join to our temporary table
  $query
    ->leftJoin($table, 'sbpp_p', 'sbpp_p.pid = sp.modid');
  $cond = new \Drupal\Core\Database\Query\Condition('AND');
  $cond
    ->condition('sbpp_p.perm', 1);
  return $cond;
}

/**
 * Implements Search by Page hook_search_by_page_details().
 *
 * Reads details information stored in the database, extracts snippet
 * if necessary, and returns it.
 */
function search_by_page_paths_search_by_page_details($id, $environment, $keys = NULL) {

  // Get info from DB
  $item = \Drupal::database()
    ->query('SELECT p.pid, p.title, p.page_path, p.page_type, p.snippet FROM {sbpp_path} p WHERE p.pid = :pid', array(
    ':pid' => $id,
  ))
    ->fetchObject();
  if (!$item->pid) {
    return NULL;
  }

  // Translate if necessary
  if (\Drupal::moduleHandler()
    ->moduleExists('i18n_string')) {
    $code = 'search_by_page_paths:path:' . $item->pid . ':';
    $item->title = i18n_string_translate($code . 'title', $item->title);
    $item->page_type = i18n_string_translate($code . 'type', $item->page_type);
    $snip = drupal_strtolower($item->snippet);
    if ($snip != 'yes' && $snip != 'no' && $keys) {
      $item->snippet = i18n_string_translate($code . 'snippet', $item->snippet);
    }
  }

  // Basic page info.
  $ret = array(
    'type' => $item->page_type,
    'title' => search_by_page_strip_tags($item->title, $environment),
  );

  // Snippet -- DB either stores "yes", "no", or a custom snippet to use.
  $code = mb_strtolower($item->snippet);
  if ($code == 'yes' && $keys) {
    $content = search_by_page_stored_page_content('search_by_page_paths', $id, $environment);
    if ($content) {
      $ret['snippet'] = \Drupal::service('search_by_page.settings')
        ->excerpt($keys, $content);
    }
  }
  elseif ($code != 'no' && $keys) {

    // User-supplied custom snippet.
    $ret['snippet'] = search_by_page_strip_tags($item->snippet, $environment);
  }
  return $ret;
}

/**
 * Implements Search by Page hook_search_by_page_settings().
 *
 * Adds a few settings to the Search by Page settings page.
 */
function search_by_page_paths_search_by_page_settings($environment) {
  $form = array();
  $form['search_by_page_paths'] = array(
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#weight' => -80,
    '#title' => t('Paths'),
  );
  $times = array(
    '1' => t('1 second'),
    '3600' => t('1 hour'),
    '86400' => t('1 day'),
    '604800' => t('1 week'),
    '31449600' => t('1 year'),
    '0' => t('Never'),
  );
  $form['search_by_page_paths']['search_by_page_paths_min_time'] = array(
    '#type' => 'select',
    '#title' => t('Minimum reindexing time'),
    '#options' => $times,
    '#default_value' => \Drupal::service('search_by_page.settings')
      ->getSetting('search_by_page_paths_min_time', $environment, 1),
    '#weight' => 5,
    '#description' => t("After indexing any new pages added to the environment, Search by Page also cycles through previously-indexed pages, in case the rendered view of the page has changed. On some sites, you may want to limit the amount of reindexing, by setting a minimum time -- the page will not be reindexed until this time has passed."),
  );
  $form['search_by_page_paths']['search_by_page_paths_max_time'] = array(
    '#type' => 'select',
    '#title' => t('Maximum reindexing time'),
    '#options' => $times,
    '#default_value' => \Drupal::service('search_by_page.settings')
      ->getSetting('search_by_page_paths_max_time', $environment, 0),
    '#weight' => 6,
    '#description' => t("Conversely to the minimum reindexing time (see above), Search by Page can be set to prioritize reindexing each page (by marking it as needing immediate reindexing) after this amount of time has passed. This has higher priority than the cycle-through reindexing of the setting above.") . ' ' . t('But be careful with this setting! If you set it too small, it can interfere with new content being indexed, because the reindexed content will have equal priority to content that has never been indexed. So make sure your settings allow for enough time for new content to be indexed before forcing reindexing.'),
  );
  return $form;
}

/**
 * Implements hook_menu().
 *
 * Defines a tab on Search by Page environment edit that lists the paths
 * to be indexed, with ability to add, edit, and delete them.
 */
function search_by_page_paths_menu() {
  $items = array();
  $items['admin/config/search/search_by_page/edit/%/paths'] = array(
    'title' => 'Paths',
    'type' => MENU_LOCAL_TASK,
    'weight' => 0,
    'page callback' => '_search_by_page_paths_list_page',
    'page arguments' => array(
      5,
    ),
    'access arguments' => array(
      'administer search by page',
    ),
  );
  $items['admin/config/search/search_by_page/edit/%/paths/edit'] = array(
    'title' => 'Edit path',
    'type' => MENU_CALLBACK,
    'page callback' => '_search_by_page_paths_edit',
    'page arguments' => array(
      5,
    ),
    'access arguments' => array(
      'administer search by page',
    ),
  );
  $items['admin/config/search/search_by_page/edit/%/paths/add'] = array(
    'title' => 'Add path',
    'type' => MENU_CALLBACK,
    'page callback' => '_search_by_page_paths_edit',
    'page arguments' => array(
      5,
    ),
    'access arguments' => array(
      'administer search by page',
    ),
  );
  $items['admin/config/search/search_by_page/edit/%/paths/delete'] = array(
    'title' => 'Delete path',
    'type' => MENU_CALLBACK,
    //'page callback' => 'drupal_get_form',
    'page callback' => '$form = \\Drupal::formBuilder()->getForm();',
    'page arguments' => array(
      'search_by_page_paths_delete_confirm',
      5,
    ),
    'access arguments' => array(
      'administer search by page',
    ),
  );
  return $items;
}

/**
 * Implements hook_i18n_string_info().
 */
function search_by_page_paths_i18n_string_info() {
  $groups = array();
  $groups['search_by_page_paths'] = array(
    'title' => t('Search by Page Paths'),
    'description' => t('Path setting text for Search by Page Paths'),
    'format' => FALSE,
    'list' => FALSE,
    'refresh callback' => '_search_by_page_paths_refresh_all_translations',
  );
  return $groups;
}

/**
 * Refreshes all path info translations.
 */
function _search_by_page_paths_refresh_all_translations() {
  if (\Drupal::moduleHandler()
    ->moduleExists('i18n_string')) {
    $res = \Drupal::database()
      ->query('SELECT * FROM {sbpp_path} p')
      ->fetchAll();
    foreach ($res as $vals) {
      $arr = array(
        'pid' => $vals->pid,
        'title' => $vals->title,
        'type' => $vals->page_type,
        'snippet' => $vals->snippet,
      );
      _search_by_page_paths_update_translation($arr);
    }
  }
}

/**
 * Page callback for path listings page.
 *
 * Builds a page that lists existing paths to index, with links to edit,
 * delete, and add new.
 */
function _search_by_page_paths_list_page($environment) {
  $output = '';
  $environment = intval($environment);

  // Overview
  $output .= '<p>' . t('This page allows you to define pages on your site to be indexed by the Search by Page module in this environment.') . '</p>' . "\n";

  // Link to add a new path
  $basepath = 'admin/config/search/search_by_page/edit/' . $environment . '/paths/';
  $output .= '<p>' . l(t('Add new path'), $basepath . 'add') . '</p>' . "\n";

  // List existing paths in a table, with edit/delete links
  // and use a pager
  $query = \Drupal::database()
    ->select('sbpp_path', 'p')
    ->extend('PagerDefault');
  $query
    ->addField('p', 'pid');
  $query
    ->addField('p', 'page_path');
  $query
    ->addField('p', 'title');
  $query
    ->condition('environment', $environment)
    ->limit(50)
    ->orderBy('page_path');
  $result = $query
    ->execute()
    ->fetchAll();
  $headers = array(
    t('Path'),
    t('Title'),
    array(
      'data' => t('Operations'),
      'colspan' => 2,
    ),
  );
  $rows = array();
  foreach ($result as $data) {
    $rows[] = array(
      Html::escape($data->page_path),
      Html::escape($data->title),
      l(t('edit'), $basepath . 'edit/' . $data->pid),
      l(t('delete'), $basepath . 'delete/' . $data->pid),
    );
  }
  $output .= theme('table', array(
    'header' => $headers,
    'rows' => $rows,
  ));
  $output .= theme('pager', array(
    'tags' => NULL,
  ));
  return $output;
}

/**
 * Edits or adds new path.
 */
function _search_by_page_paths_edit($environment, $pid = 0) {
  if ($pid) {
    $values = \Drupal::database()
      ->query('SELECT * FROM {sbpp_path} p WHERE p.pid = :pid', array(
      ':pid' => $pid,
    ))
      ->fetchAssoc();
    $values['languages'] = @unserialize($values['languages']);
    if (!$values['languages']) {
      $values['languages'] = array();
    }

    //return drupal_get_form('search_by_page_paths_edit_form', $values);
    return $form = \Drupal::formBuilder()
      ->getForm('search_by_page_paths_edit_form', $values);
  }
  else {

    //return drupal_get_form('search_by_page_paths_edit_form', array('environment' => $environment));
    return \Drupal::formBuilder()
      ->getForm('search_by_page_paths_edit_form', array(
      'environment' => $environment,
    ));
  }
}

/**
 * Returns a form for editing a path item.
 *
 * @ingroup forms
 * @see search_by_page_paths_edit_form_submit()
 */
function search_by_page_paths_edit_form($form, &$form_state, $edit = array()) {
  global $language;
  $edit = array_merge(array(
    'pid' => 0,
    'page_path' => '',
    'title' => '',
    'page_type' => 'Page',
    'snippet' => 'yes',
    'environment' => 0,
    'role' => AccountInterface::ANONYMOUS_ROLE,
    'languages' => array(),
  ), $edit);
  $edit['environment'] = intval($edit['environment']);
  $form['path'] = array(
    '#type' => 'textfield',
    '#title' => t('Page to index'),
    '#default_value' => $edit['page_path'],
    '#size' => 45,
    '#description' => t('Specify a page on your site that you want to index. Do not include the language prefix, if any. To index the home page, enter its native path, as it is entered on the Site Information settings page.'),
    '#field_prefix' => search_by_page_path_field_prefix(),
    '#required' => TRUE,
  );
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title of page'),
    '#default_value' => $edit['title'],
    '#size' => 45,
    '#description' => t('Title to display in search results for this page'),
    '#required' => TRUE,
  );
  $form['type'] = array(
    '#type' => 'textfield',
    '#title' => t('Type of page'),
    '#default_value' => $edit['page_type'],
    '#size' => 45,
    '#description' => t('Type to display in search results for this page, if the theme displays item types'),
    '#required' => TRUE,
  );
  $form['snippet'] = array(
    '#type' => 'textarea',
    '#title' => t('Snippet'),
    '#default_value' => $edit['snippet'],
    '#cols' => 45,
    '#rows' => 5,
    '#description' => t('Enter "yes" (without the quotes, in English) to show a "snippet" of this page in search results, extracted from the page (it should show a portion of the page that contains the search results). Enter "no" (without the quotes, in English) to omit the snippet entirely. Or, enter any other text to use that custom text as the snippet for search results display.'),
    '#required' => TRUE,
  );
  if (\Drupal::moduleHandler()
    ->moduleExists('locale')) {
    $form['texthelp'] = array(
      '#type' => 'markup',
      '#markup' => '<p>' . t("Enter the title, type, and snippet above in your site's default language. If you have a multi-lingual site with the Internationalization project's String Translation module installed and enabled, you can use Drupal's translation interface to translate them.") . '</p>',
    );
    $form['languages'] = array(
      '#type' => 'checkboxes',
      '#title' => t('Languages'),
      '#description' => t('Indicate which languages this page can be viewed in. It will be indexed in each language for searching.'),
      '#options' => locale_language_list('name'),
      '#default_value' => $edit['languages'],
    );
  }
  else {
    $form['languages'] = array(
      '#type' => 'value',
      '#value' => array(
        $language->language => TRUE,
      ),
    );
  }
  $form['role'] = array(
    '#type' => 'radios',
    '#title' => t('Role for indexing'),
    '#options' => user_roles(),
    '#default_value' => $edit['role'],
    '#weight' => 4,
    '#description' => t("When Search by Page indexes this page for searching, the page will be viewed from the perspective and permissions of a user with this role."),
  );
  if ($edit['pid']) {
    $form['pid'] = array(
      '#type' => 'value',
      '#value' => $edit['pid'],
    );
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Update indexed page'),
    );
  }
  else {
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Create new indexed page'),
    );
  }
  $form['submit']['#weight'] = 5;
  $form['environment'] = array(
    '#type' => 'value',
    '#value' => $edit['environment'],
  );
  $form['cancel'] = array(
    '#type' => 'markup',
    '#markup' => "<p>" . l(t('Cancel'), 'admin/config/search/search_by_page/edit/' . $edit['environment'] . '/paths') . "</p>",
    '#weight' => 6,
  );
  return $form;
}

/**
 * Submit callback for search_by_page_paths_edit_form().
 *
 * Adds/edits the path to be indexed, and updates database of paths.
 */
function search_by_page_paths_edit_form_submit($form, &$form_state) {
  $vals = $form_state['values'];
  $langs = serialize($vals['languages']);
  $envid = intval($vals['environment']);

  // Save the new information
  if (isset($vals['pid']) && $vals['pid']) {
    \Drupal::database()
      ->update('sbpp_path')
      ->fields(array(
      'environment' => $envid,
      'page_path' => $vals['path'],
      'title' => $vals['title'],
      'page_type' => $vals['type'],
      'snippet' => $vals['snippet'],
      'role' => $vals['role'],
      'languages' => $langs,
    ))
      ->condition('pid', $vals['pid'])
      ->execute();

    // Force this to be reindexed
    search_by_page_force_reindex('search_by_page_paths', $vals['pid'], $envid);
  }
  else {
    $vals['pid'] = \Drupal::database()
      ->insert('sbpp_path')
      ->fields(array(
      'environment' => $envid,
      'page_path' => $vals['path'],
      'title' => $vals['title'],
      'page_type' => $vals['type'],
      'snippet' => $vals['snippet'],
      'role' => $vals['role'],
      'languages' => $langs,
    ))
      ->execute();
  }

  // Update the translation strings
  _search_by_page_paths_update_translation($vals);

  // Go back to the paths list
  \Drupal::messenger()
    ->addMessage(t('The page to index has been saved'));
  $form_state['redirect'] = 'admin/config/search/search_by_page/edit/' . $envid . '/paths';
}

/**
 * Returns a form confirming deletion a path to be indexed.
 *
 * @ingroup forms
 * @see search_by_page_paths_delete_confirm_submit()
 */
function search_by_page_paths_delete_confirm($form, &$form_state, $environment, $pid) {
  $item = \Drupal::database()
    ->query('SELECT p.page_path FROM {search_by_page_path} p WHERE p.pid = :pid', array(
    ':pid' => $pid,
  ))
    ->fetchObject();
  $environment = intval($environment);
  $form['pid'] = array(
    '#type' => 'value',
    '#value' => $pid,
  );
  $form['environment'] = array(
    '#type' => 'value',
    '#value' => $environment,
  );
  return confirm_form($form, t('Are you sure you want to stop indexing %path in this environment?', array(
    '%path' => $item->page_path,
  )), 'admin/config/search/search_by_page/edit/' . $environment . '/paths');
}

/**
 * Submit callback for search_by_page_paths_delete_confirm().
 *
 * Actually deletes the path.
 */
function search_by_page_paths_delete_confirm_submit($form, &$form_state) {
  if (!$form_state['values']['confirm']) {
    return;
  }
  $pid = $form_state['values']['pid'];
  if (!$pid) {
    return;
  }

  // Remove it from our database, and also from current search index
  \Drupal::database()
    ->delete('sbpp_path')
    ->condition('pid', $pid)
    ->execute();
  $environment = intval($form_state['values']['environment']);
  search_by_page_force_remove('search_by_page_paths', $pid, $environment);

  // Go back to paths page
  $form_state['redirect'] = 'admin/config/search/search_by_page/edit/' . $environment . '/paths';
}

/**
 * Implements hook_search_by_page_delete_environment().
 *
 * Removes the environment from paths database table.
 */
function search_by_page_paths_search_by_page_delete_environment($environment) {
  $envid = intval($environment);
  \Drupal::database()
    ->delete('sbpp_path')
    ->condition('environment', $envid)
    ->execute();
}

/**
 * Updates the strings to be translated for one path record.
 *
 * @param $vals
 *    Associative array of information from sbpp_path table.
 */
function _search_by_page_paths_update_translation($vals) {
  if (\Drupal::moduleHandler()
    ->moduleExists('i18n_string') && $vals['pid']) {
    $code = 'search_by_page_paths:path:' . $vals['pid'] . ':';
    i18n_string_update($code . 'title', $vals['title']);
    i18n_string_update($code . 'type', $vals['type']);
    $snip = mb_strtolower($vals['snippet']);
    if ($snip != 'yes' && $snip != 'no') {
      i18n_string_update($code . 'snippet', $vals['snippet']);
    }
  }
}

Functions

Constants

Namesort descending Description
LANGUAGE_NONE