function scanner_execute in Search and Replace Scanner 6
Same name and namespace in other branches
- 5.2 scanner.module \scanner_execute()
- 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 681 - 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_' . $user->uid, array());
$undo_data = variable_get('scanner_partial_undo_' . $user->uid, 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_' . $user->uid, $processed);
variable_set('scanner_partial_undo_' . $user->uid, $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_' . $user->uid);
variable_del('scanner_partial_undo_' . $user->uid);
}
if ($searchtype == 'search') {
return theme('scanner_results', $results);
}
else {
//searchtype == 'replace'
if (count($undo_data) && !$shutting_down) {
db_query('INSERT INTO {scanner} (undo_data, undone, searched, replaced, count, time) VALUES ("%s", %d, "%s", "%s", %d, %d)', serialize($undo_data), 0, $search, $replace, count($undo_data), time());
}
return theme('scanner_replace_results', $results);
}
}