]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/SearchView.pm
searchview: hoist out subroutines for clarity
[public-inbox.git] / lib / PublicInbox / SearchView.pm
1 # Copyright (C) 2015 all contributors <meta@public-inbox.org>
2 # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
3 package PublicInbox::SearchView;
4 use strict;
5 use warnings;
6 use PublicInbox::SearchMsg;
7 use PublicInbox::Hval;
8 use PublicInbox::View;
9 use POSIX qw/strftime/;
10 our $LIM = 25;
11
12 sub sres_top_html {
13         my ($ctx, $q) = @_;
14         my $cgi = $ctx->{cgi};
15         my $code = 200;
16         # $q ||= $cgi->param('q');
17         my $o = int($cgi->param('o') || 0);
18         my $r = $cgi->param('r');
19         $r = (defined $r && $r ne '0');
20         my $opts = { limit => $LIM, offset => $o, mset => 1, relevance => $r };
21         my ($mset, $total);
22         eval {
23                 $mset = $ctx->{srch}->query($q, $opts);
24                 $total = $mset->get_matches_estimated;
25         };
26         my $err = $@;
27         my $query = PublicInbox::Hval->new_oneline($q);
28         my $qh = $query->as_html;
29         my $res = "<html><head><title>$qh - search results</title></head>" .
30                   qq{<body><form\naction="">} .
31                   qq{<input\nname=q\nvalue="$qh"\ntype=text />};
32
33         $res .= qq{<input\ntype=hidden\nname=r />} if $r;
34
35         $res .= qq{<input\ntype=submit\nvalue=search /></form>} .
36                   PublicInbox::View::PRE_WRAP;
37
38         my $foot = $ctx->{footer} || '';
39         $foot = qq{Back to <a\nhref=".">index</a>.};
40         if ($err) {
41                 $code = 400;
42                 $res .= err_txt($err) . "</pre><hr /><pre>$foot";
43         } elsif ($total == 0) {
44                 $code = 404;
45                 $res .= "\n\n[No results found]</pre><hr /><pre>$foot";
46         } else {
47                 $q = $query->as_href;
48                 $q =~ s/%20/+/g; # improve URL readability
49                 $res .= search_nav_top($q, $o, $r);
50                 $res .= "\n\n";
51
52                 dump_mset(\$res, $mset, $o);
53                 $res .= search_nav_bot($mset, $q, $o, $r);
54                 $res .= "\n\n" . $foot;
55         }
56
57         $res .= "</pre></body></html>";
58         [$code, ['Content-Type'=>'text/html; charset=UTF-8'], [$res]];
59 }
60
61 sub dump_mset {
62         my ($res, $mset) = @_;
63
64         my $total = $mset->get_matches_estimated;
65         my $pad = length("$total");
66         my $pfx = ' ' x $pad;
67         foreach my $m ($mset->items) {
68                 my $rank = sprintf("%${pad}d", $m->get_rank + 1);
69                 my $pct = $m->get_percent;
70                 my $smsg = PublicInbox::SearchMsg->load_doc($m->get_document);
71                 my $s = PublicInbox::Hval->new_oneline($smsg->subject);
72                 my $f = $smsg->from_name;
73                 $f = PublicInbox::Hval->new_oneline($f)->as_html;
74                 my $d = strftime('%Y-%m-%d %H:%M', gmtime($smsg->ts));
75                 my $mid = PublicInbox::Hval->new_msgid($smsg->mid)->as_href;
76                 $$res .= qq{$rank. <b><a\nhref="$mid/">}.
77                         $s->as_html . "</a></b>\n";
78                 $$res .= "$pfx  - by $f @ $d UTC [$pct%]\n\n";
79         }
80 }
81
82 sub err_txt {
83         my ($err) = @_;
84         my $u = 'http://xapian.org/docs/queryparser.html';
85         $err =~ s/^\s*Exception:\s*//; # bad word to show users :P
86         $err = PublicInbox::Hval->new_oneline($err)->as_html;
87         "\n\nBad query: <b>$err</b>\n" .
88                 qq{See <a\nhref="$u">$u</a> for Xapian query syntax};
89 }
90
91 sub search_nav_top {
92         my ($q, $o, $r) = @_;
93         my $qs = "q=$q";
94         $qs .= "&amp;o=$o" if $o;
95
96         my $rv = "Search results ordered by [";
97         if ($r) {
98                 $rv .= qq{<a\nhref="?$qs">date</a>|<b>relevance</b>};
99         } else {
100                 $qs .= '&amp;r';
101                 $rv .= qq{<b>date</b>|<a\nhref="?$qs">relevance</a>};
102         }
103         $rv .= ']';
104 }
105
106 sub search_nav_bot {
107         my ($mset, $q, $o, $r) = @_;
108         my $total = $mset->get_matches_estimated;
109         my $nr = scalar $mset->items;
110         my $end = $o + $nr;
111         my $beg = $o + 1;
112
113         my $rv = "<hr /><pre>Results $beg-$end of $total";
114
115         my $n = $o + $LIM;
116         if ($n < $total) {
117                 my $qs = "q=$q&amp;o=$n";
118                 $qs .= "&amp;r" if $r;
119                 $rv .= qq{, <a\nhref="?$qs">next</a>}
120         }
121         if ($o > 0) {
122                 $rv .= $n < $total ? '/' : ',      ';
123                 my $p = $o - $LIM;
124                 my $qs = "q=$q";
125                 $qs .= "&amp;o=$p" if $p > 0;
126                 $qs .= "&amp;r" if $r;
127                 $rv .= qq{<a\nhref="?$qs">prev</a>};
128         }
129         $rv;
130 }
131
132 1;