# Copyright 2002-2008 Josh Clark and Global Moxie, LLC. This code cannot be
# redistributed without permission from globalmoxie.com.  For more
# information, consult your Big Medium license.
#
# $Id: WebSearch.pm 3236 2008-08-21 14:30:44Z josh $

package BigMed::App::Web::WebSearch;
use strict;
use warnings;
use utf8;
use Carp;
$Carp::Verbose = 1;
use base qw(BigMed::App::Web::Public);
use BigMed::Site;
use BigMed::Search;
use BigMed::DiskUtil qw(bm_file_path);
use BigMed::App::Web::PageNav;
use BigMed::PageUtils;

my $PERL_5_8 = ( $] >= 5.008 ); #determines utf-8 handling

sub setup {
    my $app = shift;
    $app->start_mode('q');
    $app->run_modes(
        'AUTOLOAD' => sub { $_[0]->rm_query() },
        'q'        => 'rm_query',
    );
    return;
}

###########################################################
# RUN MODES
###########################################################

sub rm_query {
    my $app  = shift;
    my $sid  = $app->utf8_param('bms') || q{};
    my $site = $app->_load_site_object($sid) or $app->error_stop;
    $sid = $site->id;    #not necessarily the same as sid above

    BigMed::Search->register_search_prefs;
    my $lang = $site->get_pref_value('html_htmlhead_lang')
      || 'en-us';
    my $search = BigMed::Search->new( locale => $lang ) or $app->error_stop;
    my $st = $app->utf8_param('st') || q{};
    $st =~ s/\D//msg;
    my $query  = $app->utf8_param('bmq');
    my $result = $search->search( $site, $query, { start => $st } );

    my $html_q = $app->escape($query);
    my ( $intro, $results, $navigation );
    my $form_url = $app->build_url(
        script => 'bm-search.cgi',
        rm     => 'q'
    );
    if ( $result && $result->num_results ) {
        my $num = $result->num_results;
        my $total = $result->total_results;
        $results = [$result->result_set];
        if ( $num == 1 && $total == 1 ) {    #go straight there
            my $url = $results->[0]->{url};
            my $hdir = $site->homepage_url;
            my $pdir = $site->html_url;
            
            #only if it's an internal link and not a document
            if (( index( $url, $pdir ) == 0 || index( $url, $hdir ) == 0 )
                && index( $url, '.shtml' ) > 0    #not a .shtml page
              )
            {
                $app->header_type('redirect');
                $app->header_props( -url => $url );
                return "Redirecting to $url";
            }
        }

        my $start = $result->start_position;
        my $end   = $start + $num - 1;

        $intro = $site->get_pref_value('html_search_intro');
        $intro =~ s/&lt;%start%&gt;/$start/msig;
        $intro =~ s/&lt;%end%&gt;/$end/msig;
        $intro =~ s/&lt;%total%&gt;/$total/msig;
        $intro =~ s/&lt;%query%&gt;/$html_q/msig;

        my $esc_q    = CGI::escape($query);
        my $base_url = $form_url . "?bmq=${esc_q}&amp;bms=$sid&amp;st=";
        my $rurl     = sub {
            my $offset = ( shift @_ ) + 1;    #change zero-based to one
            return $base_url . $offset;
        };

        my $previous = $site->get_pref_value('html_search_previous');
        my $next     = $site->get_pref_value('html_search_next');
        ($navigation) = $app->appweb_pagenav_menu(
            offset        => $start - 1,
            this_total    => $num,
            total_records => $total,
            per_page      => 10,
            url_callback  => $rurl,
            previous      => $previous,
            'next'        => $next,
        );
        $navigation =~ s{<span[^>]*>.*?</span>}{}ms;  # get rid of browse text
    }
    elsif ($result) {
        $intro   = $site->get_pref_value('html_search_noresults');
        $results = [];
    }
    else {
        my %message = $app->error_html_hash;
        $intro   = $app->language( $message{text} );
        $results = [];
    }
    my $button = $site->get_pref_value('html_search_button');
    my $tmpl = $app->_load_page_template($site) or $app->error_stop;
    $tmpl->param(
        intro         => $intro,
        results       => $results,
        navigation    => $navigation,
        site_id       => $sid,
        form_url      => $form_url,
        search_text   => $html_q,
        search_button => $button,
        'close'       => ' /',

    );
    my $html = $tmpl->output();
    
    #handle new-window links
    if ( $site->get_pref_value( 'html_links_window' ) ) {
        $html =~ s{(<a([^>]+)href\s*=\s*"([^"]+)"([^>]*)>)}{
            _update_new_win_tag($site, $3,$1,$2,$4);
          }msge;
    }

    return BigMed::PageUtils::replace_all_includes($site, $html);
}

#should probably abstract this new-window code into a separate module;
#using pretty identical code in Format::HTML, Comment, and Web::WebSearch

my $NEW_WIN = ' target="newsite"';

sub _update_new_win_tag {
    my ( $site, $url, $tag, $int1, $int2 ) = @_;
    return $tag if !_is_new_window_url( $site, $url );
    foreach ( $int1, $int2 ) {
        return $tag if /target\s*=\s*"/i;
    }
    $tag =~ s/(href\s*=\s*"\Q$url\E")/$1$NEW_WIN/;
    return $tag;
}
    
sub _is_new_window_url {
    my ( $site, $url ) = @_;
    return 0 if index( $url, 'http' ) != 0;
    foreach my $dom ( $site->get_pref_value('html_links_window_intdomains') ) {
        return 0 if index( $url, $dom ) == 0;
    }
    return 1;
}


sub _load_page_template {
    my ( $app, $site ) = @_;
    my $tmpl_obj;
    my @alt_path;
    foreach my $dir (qw(templates_custom templates)) {
        push @alt_path,
          bm_file_path( $app->env('MOXIEDATA'),
            $dir, 'site_templates', 'HTML' );
    }

    #collect the template to use; load filehandle for flock permission to
    #avoid collisions when building pages (HTML::Template doesn't use
    #flock locking for loading templates)
    my $tfile =
      bm_file_path( $site->homepage_dir,
        'bm' . $app->env('DOT') . 'search.shtml' );
    my $tmpl_arg;
    if ( -e $tfile ) {
        my $TEMPLATE;
        open( $TEMPLATE, '<', $tfile )
          or return $app->set_io_error( $TEMPLATE, 'open', $tfile, $! );
        flock( $TEMPLATE, 1 )    #just a tiny bit faster than LOCK_SH
          or return $app->set_io_error( $TEMPLATE, 'lock_sh', $tfile, $! );
        if ( $PERL_5_8 ) {
            binmode( $TEMPLATE, ":utf8" );
            $tmpl_arg = $TEMPLATE;
        }
        else { #perl 5.6; keep the filehandle open but pass in the filename
            my $content = pack "U0C*", unpack "C*", join(q{}, <$TEMPLATE>);
            $tmpl_arg = \$content;
        }
    }
    else {    #no file to use, just use the raw results template
        $tmpl_arg = 'wi_search_result.tmpl';
    }
    return $app->load_tmpl(
        $tmpl_arg,
        path              => \@alt_path,
        die_on_bad_params => 0,
        loop_context_vars => 1,
    );
}

sub _load_site_object {
    my ( $app, $sid ) = @_;
    $sid =~ s/\D//msg;
    my $site;
    if ( !$sid ) {
        my $sites = BigMed::Site->select() or return;
        if ( $sites->count == 1 ) {    #just one, use this one
            $site = $sites->next;
            return if !defined $site;
            $sid = $site->id if $site;
        }
    }
    elsif ( !$site ) {
        $site = BigMed::Site->fetch($sid);
        return if !defined $site;
    }

    if ( !$site ) {
        return $app->set_error(
            head => 'SEARCH_Unknown site',
            text => 'SEARCH_TEXT_Unknown site',
        );
    }
    return $site;
}
1;
__END__

