# This code is a part of Slash, and is released under the GPL.
# Copyright 1997-2001 by Open Source Development Network. See README
# and COPYING for more information, or see http://slashcode.com/.
# $Id: Search.pm,v 1.2.2.44 2001/10/31 00:14:08 brian Exp $

package Slash::Search;

use strict;
use Slash::Utility;
use Slash::DB::Utility;
use vars qw($VERSION);
use base 'Slash::DB::Utility';

($VERSION) = ' $Revision: 1.2.2.44 $ ' =~ /\$Revision:\s+([^\s]+)/;

# FRY: And where would a giant nerd be? THE LIBRARY!

#################################################################
sub new {
	my($class, $user) = @_;
	my $self = {};

	my $slashdb = getCurrentDB();
	my $plugins = $slashdb->getDescriptions('plugins');
	return unless $plugins->{'Search'};

	bless($self, $class);
	$self->{virtual_user} = $user;
	$self->sqlConnect();

	return $self;
}

#################################################################
# Private method used by the search methods
sub _keysearch {
	my($self, $keywords, $columns) = @_;

	my @words = split m/ /, $keywords;
	my $sql;
	my $x = 0;
	my $latch = 0;

	for my $word (@words) {
		next if length $word < 3;
		last if $x++ > 3;
		$sql .= " AND " if $sql;
		$sql .= " ( ";
		$latch = 0;
		for (@$columns) {
			$sql .= " OR " if $latch;
			$sql .= "$_ LIKE " . $self->sqlQuote("%$word%"). " ";
			$latch++;
		}
		$sql .= " ) ";
	}
	# void context, does nothing?
	$sql = "0" unless $sql;

	return qq|($sql)|;
};

####################################################################################
# This has been changed. Since we no longer delete comments
# it is safe to have this run against stories.
sub findComments {
	my($self, $form, $start, $limit, $sort) = @_;
	# select comment ID, comment Title, Author, Email, link to comment
	# and SID, article title, type and a link to the article
	my $query = $self->sqlQuote($form->{query});
	my $columns = "section, discussions.url, discussions.uid, discussions.title, pid, subject, ts, date, comments.uid as uid, comments.cid as cid ";
	$columns .= ", TRUNCATE((MATCH (comments.subject) AGAINST($query)), 1) as score "
		if ($form->{query} && $sort ne 'date');

	my $tables = "comments, discussions";

	my $key = " MATCH (comments.subject) AGAINST ($query) ";

	# Welcome to the join from hell -Brian
	my $where;
	$where .= " comments.sid = discussions.id ";
	$where .= "	  AND $key "
			if $form->{query};

	$where .= "     AND discussions.sid=" . $self->sqlQuote($form->{sid})
			if $form->{sid};
	$where .= "     AND points >= $form->{threshold} "
			if $form->{threshold};
	$where .= "     AND section=" . $self->sqlQuote($form->{section})
			if $form->{section};

	my $other;
	if ($form->{query} && $sort ne 'date') {
		$other = " ORDER BY score DESC ";
	} else {
		$other = " ORDER BY cid DESC ";
	}


	my $sql = "SELECT $columns FROM $tables WHERE $where $other";
	$sql .= " LIMIT $start, $limit" if $limit;

	$self->sqlConnect();
	my $cursor = $self->{_dbh}->prepare($sql);
	$cursor->execute;

	my $search = $cursor->fetchall_arrayref;
	return $search;
}


####################################################################################
# This has been changed. Since we no longer delete comments
# it is safe to have this run against stories.
# Original with comment body
#sub findComments {
#	my($self, $form, $start, $limit) = @_;
#	# select comment ID, comment Title, Author, Email, link to comment
#	# and SID, article title, type and a link to the article
#	my $query = $self->sqlQuote($form->{query});
#	my $columns = "section, stories.sid, stories.uid as author, discussions.title as title, pid, subject, stories.writestatus as writestatus, time, date, comments.uid as uid, comments.cid as cid ";
#	$columns .= ", TRUNCATE((((MATCH (comments.subject) AGAINST($query) + (MATCH (comment_text.comment) AGAINST($query)))) / 2), 1) as score "
#		if $form->{query};
#
#	my $tables = "stories, comments, comment_text, discussions";
#
#	my $key = " (MATCH (comments.subject) AGAINST ($query) or MATCH (comment_text.comment) AGAINST ($query)) ";
#
#
#	$limit = " LIMIT $start, $limit" if $limit;
#
#	# Welcome to the join from hell -Brian
#	my $where;
#	$where .= " comments.cid = comment_text.cid ";
#	$where .= " AND comments.sid = discussions.id ";
#	$where .= " AND discussions.sid = stories.sid ";
#	$where .= "	  AND $key "
#			if $form->{query};
#
#	$where .= "     AND stories.sid=" . $self->sqlQuote($form->{sid})
#			if $form->{sid};
#	$where .= "     AND points >= $form->{threshold} "
#			if $form->{threshold};
#	$where .= "     AND section=" . $self->sqlQuote($form->{section})
#			if $form->{section};
#
#	my $other;
#	if ($form->{query}) {
#		$other = " ORDER BY score DESC, time DESC ";
#	} else {
#		$other = " ORDER BY date DESC, time DESC ";
#	}
#
#
#	my $sql = "SELECT $columns FROM $tables WHERE $where $other $limit";
#
#	my $cursor = $self->{_dbh}->prepare($sql);
#	$cursor->execute;
#
#	my $search = $cursor->fetchall_arrayref;
#	return $search;
#}

####################################################################################
sub findUsers {
	my($self, $form, $start, $limit, $sort, $users_to_ignore) = @_;
	# userSearch REALLY doesn't need to be ordered by keyword since you
	# only care if the substring is found.
	my $query = $self->sqlQuote($form->{query});
	$limit = " LIMIT $start, $limit" if $limit;

	my $columns = 'fakeemail,nickname,users.uid ';
	$columns .= ", TRUNCATE((MATCH (nickname) AGAINST($query)), 1) as score "
		if ($form->{query} && $sort ne 'date');

	my $key = " MATCH (nickname) AGAINST ($query) ";
	my $tables = 'users';
	my $where .= ' seclev > 0 ';
	$where .= " AND $key" if $form->{query};


	my $other;
	if ($form->{query} && $sort ne 'date') {
		$other = " ORDER BY score "
	} else {
		$other = " ORDER BY users.uid "
	}

	my $sql = "SELECT $columns FROM $tables WHERE $where $other $limit";

	$self->sqlConnect();
	my $sth = $self->{_dbh}->prepare($sql);
	$sth->execute;

	my $users = $sth->fetchall_arrayref;

	return $users;
}

####################################################################################
sub findStory {
	my($self, $form, $start, $limit, $sort) = @_;
	$start ||= 0;

	my $query = $self->sqlQuote($form->{query});
	my $columns = "users.nickname, stories.title, stories.sid as sid, time, commentcount, section";
	$columns .= ", TRUNCATE((((MATCH (stories.title) AGAINST($query) + (MATCH (introtext,bodytext) AGAINST($query)))) / 2), 1) as score "
		if ($form->{query} && $sort ne 'date');

	my $tables = "stories,users";
	$tables .= ",story_text" if $form->{query};

	my $other;
	if ($form->{query} && $sort ne 'date') {
		$other = " ORDER BY score DESC";
	} else {
		$other = " ORDER BY time DESC";
	}
	$other .= " LIMIT $start, $limit" if $limit;

	# The big old searching WHERE clause, fear it
	my $key = " (MATCH (stories.title) AGAINST ($query) or MATCH (introtext,bodytext) AGAINST ($query)) ";
	my $where = "stories.uid = users.uid ";
	$where .= " AND stories.sid = story_text.sid AND $key" 
		if $form->{query};

	if ($form->{section}) { 
		$where .= " AND ((displaystatus = 0 and '$form->{section}' = '')";
		$where .= " OR (section = '$form->{section}' AND displaystatus != -1))";
	} else {
		$where .= " AND displaystatus != -1";
	}
	$where .= " AND time < now() AND stories.writestatus != 'delete' ";
	$where .= " AND stories.uid=" . $self->sqlQuote($form->{author})
		if $form->{author};
	$where .= " AND section=" . $self->sqlQuote($form->{section})
		if $form->{section};
	$where .= " AND tid=" . $self->sqlQuote($form->{topic})
		if $form->{topic};
	
	my $sql = "SELECT $columns FROM $tables WHERE $where $other";

	$self->sqlConnect();
	my $cursor = $self->{_dbh}->prepare($sql);
	$cursor->execute;
	my $stories = $cursor->fetchall_arrayref;

	return $stories;
}

################################################################################
sub findRetrieveSite {
	my($self, $query, $start, $limit, $sort) = @_;
	$query = $self->sqlQuote($query);
	$limit = " LIMIT $start, $limit" if $limit;

	# Welcome to the join from hell -Brian
	my $sql = " SELECT bid,title, MATCH (description,title,block) AGAINST($query) as score  FROM blocks WHERE rdf IS NOT NULL AND url IS NOT NULL and retrieve=1 AND MATCH (description,title,block) AGAINST ($query) $limit";


	$self->sqlConnect();
	my $cursor = $self->{_dbh}->prepare($sql);
	$cursor->execute;

	my $search = $cursor->fetchall_arrayref;
	return $search;
}

####################################################################################
sub findJournalEntry {
	my($self, $form, $start, $limit, $sort) = @_;
	$start ||= 0;

	my $query = $self->sqlQuote($form->{query});
	my $columns = "users.nickname, journals.description, journals.id as id, date";
	$columns .= ", TRUNCATE((((MATCH (description) AGAINST($query) + (MATCH (article) AGAINST($query)))) / 2), 1) as score "
		if ($form->{query} && $sort ne 'date');
	my $tables = "journals, journals_text, users";
	my $other;
	if ($form->{query} && $sort ne 'date') {
		$other = " ORDER BY score DESC";
	} else {
		$other = " ORDER BY date DESC";
	}
	$other .= " LIMIT $start, $limit" if $limit;

	# The big old searching WHERE clause, fear it
	my $key = " (MATCH (description) AGAINST ($query) or MATCH (article) AGAINST ($query)) ";
	my $where = "journals.id = journals_text.id AND journals.uid = users.uid ";
	$where .= " AND $key" if $form->{query};
	$where .= " AND time < now() AND writestatus != 'delete' ";
	$where .= " AND users.nickname=" . $self->sqlQuote($form->{nickname})
		if $form->{nickname};
	$where .= " AND users.uid=" . $self->sqlQuote($form->{uid})
		if $form->{uid};
	$where .= " AND tid=" . $self->sqlQuote($form->{topic})
		if $form->{topic};
	
	my $sql = "SELECT $columns FROM $tables WHERE $where $other";

	$self->sqlConnect();
	my $cursor = $self->{_dbh}->prepare($sql);
	$cursor->execute;
	my $stories = $cursor->fetchall_arrayref;

	return $stories;
}

#################################################################
sub DESTROY {
	my($self) = @_;
	$self->{_dbh}->disconnect unless $ENV{GATEWAY_INTERFACE};
}

1;
# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

Slash::Search - Slash Search module

=head1 SYNOPSIS

	use Slash::Search;

=head1 DESCRIPTION

Slash search module.

Blah blah blah.

=head1 SEE ALSO

Slash(3).

=cut
