You are here

function scanner_execute in Search and Replace Scanner 5.2

Same name and namespace in other branches
  1. 6 scanner.module \scanner_execute()
  2. 7 scanner.module \scanner_execute()

Handles the actual search and replace.

Parameters

str $searchtype - either 'search', or 'replace':

Return value

The themed results.

1 call to scanner_execute()
scanner_view in ./scanner.module
Menu callback; presents the scan form and results.

File

./scanner.module, line 642
Search and Replace Scanner - works on all nodes text content.

Code

function scanner_execute($searchtype = 'search') {
  global $user;

  // variables to monitor possible timeout
  $max_execution_time = ini_get('max_execution_time');
  $start_time = time();
  $expanded = FALSE;

  // get process and undo data if saved from timeout
  $processed = variable_get('scanner_partially_processed', array());
  $undo_data = variable_get('scanner_partial_undo', array());
  unset($_SESSION['scanner_status']);
  $search = $_SESSION['scanner_search'];
  $replace = $_SESSION['scanner_replace'];
  $preceded = $_SESSION['scanner_preceded'];

  //$notpreceded = $_SESSION['scanner_notpreceded'];
  $followed = $_SESSION['scanner_followed'];

  //$notfollowed = $_SESSION['scanner_notfollowed'];
  $mode = $_SESSION['scanner_mode'];
  $wholeword = $_SESSION['scanner_wholeword'];
  $published = $_SESSION['scanner_published'];
  $regex = $_SESSION['scanner_regex'];
  $terms = $_SESSION['scanner_terms'];
  if ($searchtype == 'search') {
    drupal_set_message(t('Scanning for: [%search] ...', array(
      '%search' => $search,
    )));
  }
  else {

    //searchtype == 'replace'
    drupal_set_message(t('Replacing [%search] with [%replace] ...', array(
      '%search' => $search,
      '%replace' => $replace,
    )));
  }
  if ($mode) {

    // Case Sensitive
    $where = "CAST(t.%s AS BINARY) ";

    // BINARY to force case sensative.
    $flag = NULL;
  }
  else {

    // Case Insensitive
    $where = "t.%s ";
    $flag = 'i';

    //ci flag for use in php preg_search and preg_replace
  }
  $preceded_php = '';
  if (!empty($preceded)) {
    if (!$regex) {
      $preceded = addcslashes($preceded, SCANNER_REGEX_CHARS);
    }
    $preceded_php = '(?<=' . $preceded . ')';
  }
  $followed_php = '';
  if (!empty($followed)) {
    if (!$regex) {
      $followed = addcslashes($followed, SCANNER_REGEX_CHARS);
    }
    $followed_php = '(?=' . $followed . ')';
  }

  //Case 1:
  if ($wholeword && $regex) {
    $where .= "REGEXP '[[:<:]]%s[[:>:]]'";
    $search_db = $preceded . $search . $followed;
    $search_php = '\\b' . $preceded_php . $search . $followed_php . '\\b';
  }
  else {
    if ($wholeword && !$regex) {
      $where .= "REGEXP '[[:<:]]%s[[:>:]]'";
      $search_db = $preceded . addcslashes($search, SCANNER_REGEX_CHARS) . $followed;
      $search_php = '\\b' . $preceded_php . addcslashes($search, SCANNER_REGEX_CHARS) . $followed_php . '\\b';
    }
    else {
      if (!$wholeword && $regex) {
        $where .= "REGEXP '%s'";
        $search_db = $preceded . $search . $followed;
        $search_php = $preceded_php . $search . $followed_php;
      }
      else {

        //!wholeword and !regex:
        $where .= "REGEXP '%s'";
        $search_db = $preceded . addcslashes($search, SCANNER_REGEX_CHARS) . $followed;
        $search_php = $preceded_php . addcslashes($search, SCANNER_REGEX_CHARS) . $followed_php;
      }
    }
  }

  //if terms selected, then put together extra join and where clause:
  $join = '';
  if (is_array($terms) && count($terms)) {
    $terms_where = array();
    $terms_params = array();
    foreach ($terms as $term) {
      $terms_where[] = 'tn.tid = %d';
      $terms_params[] = $term;
    }
    $join = 'INNER JOIN {term_node} tn ON t.nid = tn.nid';
    $where .= ' AND (' . implode(' OR ', $terms_where) . ')';
  }
  if ($published) {
    $where .= ' AND n.status = 1 ';
  }
  $tables_map = _scanner_get_selected_tables_map();
  foreach ($tables_map as $map) {
    $table = $map['table'];
    $field = $map['field'];
    $type = $map['type'];
    $on = $map['on'] ? $map['on'] : 'vid';
    $query_params = array(
      $field,
      $table,
      $on,
      $on,
      $type,
      $field,
      $search_db,
    );
    if (!empty($join)) {
      $query_params = array_merge($query_params, $terms_params);
    }
    $result = db_query("\n      SELECT t.%s as content, t.nid, n.title\n      FROM {%s} t\n      INNER JOIN {node} n ON t.%s = n.%s\n      {$join}\n      WHERE n.type = '%s' AND {$where}\n    ", $query_params);
    while ($row = db_fetch_object($result)) {
      $content = $row->content;
      $matches = array();
      $text = '';

      // checking for possible timeout
      // if within 5 seconds of timeout - attempt to expand environment
      if (time() >= $start_time + $max_execution_time - 5) {
        if (!$expanded) {
          if ($user->uid > 0) {
            $verbose = TRUE;
          }
          else {
            $verbose = FALSE;
          }
          if (_scanner_change_env('max_execution_time', '600', $verbose)) {
            drupal_set_message(t('Default max_execution_time too small and changed to 10 minutes.'), 'error');
            $max_execution_time = 600;
          }
          $expanded = TRUE;
        }
        else {
          $shutting_down = TRUE;
          variable_set('scanner_partially_processed', $processed);
          variable_set('scanner_partial_undo', $undo_data);
          if ($searchtype == 'search') {
            drupal_set_message(t('Did not have enough time to complete search.'), 'error');
          }
          else {
            drupal_set_message(t('Did not have enough time to complete. Please re-submit replace'), 'error');
          }
          break 2;
        }
      }

      /*
       * SEARCH
       */
      if ($searchtype == 'search') {

        //pull out the terms and highlight them for display in search results:
        $regexstr = "/(.{0,130}?)({$search_php})(.{0,130})/{$flag}";
        $hits = preg_match_all($regexstr, $content, $matches, PREG_SET_ORDER);
        if ($hits > 0) {
          foreach ($matches as $match) {
            if ($match[1]) {
              $text .= '...' . htmlentities($match[1], ENT_COMPAT, 'UTF-8');
            }
            $text .= '<strong>' . htmlentities($match[2], ENT_COMPAT, 'UTF-8') . '</strong>';
            if ($match[3]) {
              $text .= htmlentities($match[3], ENT_COMPAT, 'UTF-8') . '...';
            }
          }
        }
        else {
          $text = "<div class='warning'>" . t("Can't display search result due to conflict between search term and internal preg_match_all function.") . '</div>';
        }
        $results[] = array(
          'title' => $row->title,
          'type' => $type,
          'count' => $hits,
          'field' => $field,
          'nid' => $row->nid,
          'text' => $text,
        );
      }
      else {
        if (!isset($processed[$field][$row->nid])) {
          $hits = 0;
          $newcontent = preg_replace("/{$search_php}/{$flag}", $replace, $content, -1, $hits);
          $thenode = node_load(array(
            'nid' => $row->nid,
          ));

          //see if we're dealing with a CCK text field and therefore need to strip the

          // "_value" off the end:
          preg_match('/(.+)_value$/', $field, $matches);
          if (empty($matches[0])) {

            //if not CCK text field:
            $thenode->{$field} = $newcontent;
          }
          else {

            //Is this the best way to copy the new content back into the node's CCK field???
            $tmpstr = '$thenode->' . $matches[1] . '[0]["value"] = $newcontent;';
            eval($tmpstr);
          }

          // NOTE: a revision only created for the first change of the node.
          // subsequent changes of the same node do not generate additional revisions:
          if (!isset($undo_data[$thenode->nid]['new_vid'])) {
            $thenode->revision = TRUE;
            $thenode->log = t('@name replaced %search with %replace via Scanner Search and Replace module.', array(
              '@name' => $user->name,
              '%search' => $search,
              '%replace' => $replace,
            ));
            $undo_data[$thenode->nid]['old_vid'] = $thenode->vid;
          }
          if (variable_get('scanner_rebuild_teasers', 1)) {
            $thenode->teaser = node_teaser($thenode->body, $thenode->format);
          }
          node_save($thenode);

          // array to log completed fields in case of shutdown
          $processed[$field][$row->nid] = TRUE;

          // undo data construction
          $undo_data[$thenode->nid]['new_vid'] = $thenode->vid;

          //now set to updated vid after node_save()
          $results[] = array(
            'title' => $thenode->title,
            'type' => $thenode->type,
            'count' => $hits,
            'field' => $field,
            'nid' => $thenode->nid,
          );
        }
      }
    }

    //end while
  }

  //end foreach

  // if completed
  if (!$shutting_down) {
    variable_del('scanner_partially_processed');
    variable_del('scanner_partial_undo');
  }
  if ($searchtype == 'search') {
    return theme('scanner_results', $results);
  }
  else {

    //searchtype == 'replace'
    if (count($undo_data) && !$shutting_down) {
      $undo_id = db_next_id('{scanner}_undo_id');
      db_query('INSERT INTO {scanner} (undo_id, undo_data, undone, searched, replaced, count, time) VALUES (%d, "%s", %d, "%s", "%s", %d, %d)', $undo_id, serialize($undo_data), 0, $search, $replace, count($undo_data), time());
    }
    return theme('scanner_replace_results', $results);
  }
}