diff options
Diffstat (limited to 'lib/PublicInbox/SearchView.pm')
-rw-r--r-- | lib/PublicInbox/SearchView.pm | 122 |
1 files changed, 70 insertions, 52 deletions
diff --git a/lib/PublicInbox/SearchView.pm b/lib/PublicInbox/SearchView.pm index c0c801b3..9919e25c 100644 --- a/lib/PublicInbox/SearchView.pm +++ b/lib/PublicInbox/SearchView.pm @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # # Displays search results for the web interface @@ -30,59 +30,67 @@ sub mbox_results { sub sres_top_html { my ($ctx) = @_; - my $srch = $ctx->{ibx}->isrch or + my $srch = $ctx->{srch} = $ctx->{ibx}->isrch or return PublicInbox::WWW::need($ctx, 'Search'); my $q = PublicInbox::SearchQuery->new($ctx->{qp}); - my $x = $q->{x}; - my $o = $q->{o}; + my $o = $q->{o} // 0; my $asc; if ($o < 0) { $asc = 1; $o = -($o + 1); # so [-1] is the last element, like Perl lists } - my $code = 200; # double the limit for expanded views: - my $opts = { + my $opt = { limit => $q->{l}, offset => $o, relevance => $q->{r}, threads => $q->{t}, asc => $asc, }; - my ($mset, $total, $err, $html); -retry: - eval { - my $query = $q->{'q'}; - $srch->query_approxidate($ctx->{ibx}->git, $query); - $mset = $srch->mset($query, $opts); - $total = $mset->get_matches_estimated; - }; - $err = $@; + my $qs = $q->{'q'}; + $srch->query_approxidate($ctx->{ibx}->git, $qs); + sub { + $ctx->{wcb} = $_[0]; # PSGI server supplied write cb + $srch->async_mset($qs, $opt, \&sres_html_cb, $ctx, $opt, $q); + } +} + +sub sres_html_cb { # async_mset cb + my ($ctx, $opt, $q, $mset, $err) = @_; + my $code = 200; + my $total = $mset ? $mset->get_matches_estimated : undef; ctx_prepare($q, $ctx); + my ($res, $html); if ($err) { $code = 400; $html = '<pre>'.err_txt($ctx, $err).'</pre><hr>'; } elsif ($total == 0) { - if (defined($ctx->{-uxs_retried})) { - # undo retry damage: + if (defined($ctx->{-uxs_retried})) { # undo retry damage: $q->{'q'} = $ctx->{-uxs_retried}; - } elsif (index($q->{'q'}, '%') >= 0) { + } elsif (index($q->{'q'}, '%') >= 0) { # retry unescaped $ctx->{-uxs_retried} = $q->{'q'}; - $q->{'q'} = uri_unescape($q->{'q'}); - goto retry; + my $qs = $q->{'q'} = uri_unescape($q->{'q'}); + $ctx->{srch}->query_approxidate($ctx->{ibx}->git, $qs); + return $ctx->{srch}->async_mset($qs, $opt, + \&sres_html_cb, $ctx, $opt, $q); } $code = 404; $html = "<pre>\n[No results found]</pre><hr>"; + } elsif ($q->{x} eq 'A') { + $res = adump($mset, $q, $ctx); } else { - return adump($_[0], $mset, $q, $ctx) if $x eq 'A'; - $ctx->{-html_tip} = search_nav_top($mset, $q, $ctx); - return mset_thread($ctx, $mset, $q) if $x eq 't'; - mset_summary($ctx, $mset, $q); # appends to {-html_tip} - $html = ''; + if ($q->{x} eq 't') { + $res = mset_thread($ctx, $mset, $q); + } else { + mset_summary($ctx, $mset, $q); # appends to {-html_tip} + $html = ''; + } } - html_oneshot($ctx, $code); + $res //= html_oneshot($ctx, $code, $html); + my $wcb = delete $ctx->{wcb}; + ref($res) eq 'CODE' ? $res->($wcb) : $wcb->($res); } # display non-nested search results similar to what users expect from @@ -102,11 +110,8 @@ sub mset_summary { foreach my $m ($mset->items) { my $num = shift @nums; - my $smsg = delete($num2msg{$num}) or do { - eval { - $m = "$m $num expired\n"; - $ctx->{env}->{'psgi.errors'}->print($m); - }; + my $smsg = delete($num2msg{$num}) // do { + warn "$m $num expired\n"; next; }; my $mid = $smsg->{mid}; @@ -122,7 +127,7 @@ sub mset_summary { $min = $pct; my $s = ascii_html($smsg->{subject}); - my $f = ascii_html($smsg->{from_name}); + my $f = ascii_html(delete $smsg->{from_name}); if ($obfs_ibx) { obfuscate_addrs($obfs_ibx, $s); obfuscate_addrs($obfs_ibx, $f); @@ -137,7 +142,7 @@ sub mset_summary { $q->{-min_pct} = $min; $q->{-max_pct} = $max; } - $$res .= search_nav_bot($mset, $q); + $$res .= search_nav_bot($ctx, $mset, $q); undef; } @@ -170,7 +175,7 @@ sub err_txt { sub search_nav_top { my ($mset, $q, $ctx) = @_; my $m = $q->qs_html(x => 'm', r => undef, t => undef); - my $rv = qq{<form\naction="?$m"\nmethod="post"><pre>}; + my $rv = qq{<form\nid=d\naction="?$m"\nmethod=post><pre>}; my $initial_q = $ctx->{-uxs_retried}; if (defined $initial_q) { my $rewritten = $q->{'q'}; @@ -195,17 +200,25 @@ sub search_nav_top { $rv .= '] view['; my $x = $q->{x}; - if ($x eq '') { - my $t = $q->qs_html(x => 't'); - $rv .= qq{<b>summary</b>|<a\nhref="?$t">nested</a>} - } elsif ($q->{x} eq 't') { + my $pfx = "\t\t\t"; + if ($x eq 't') { my $s = $q->qs_html(x => ''); $rv .= qq{<a\nhref="?$s">summary</a>|<b>nested</b>}; + $pfx = "thread overview <a\nhref=#t>below</a> | "; + } else { + my $t = $q->qs_html(x => 't'); + $rv .= qq{<b>summary</b>|<a\nhref="?$t">nested</a>} } my $A = $q->qs_html(x => 'A', r => undef); - $rv .= qq{|<a\nhref="?$A">Atom feed</a>]}; + $rv .= qq{|<a\nhref="?$A">Atom feed</a>]\n}; + $rv .= <<EOM if $x ne 't' && $q->{t}; +*** "t=1" collapses threads in summary, "full threads" requires mbox.gz *** +EOM + $rv .= <<EOM if $x eq 'm'; +*** "x=m" ignored for GET requests, use download buttons below *** +EOM if ($ctx->{ibx}->isrch->has_threadid) { - $rv .= qq{\n\t\t\tdownload mbox.gz: } . + $rv .= qq{${pfx}download mbox.gz: } . # we set name=z w/o using it since it seems required for # lynx (but works fine for w3m). qq{<input\ntype=submit\nname=z\n} . @@ -213,14 +226,14 @@ sub search_nav_top { qq{|<input\ntype=submit\nname=x\n} . q{value="full threads"/>}; } else { # BOFH needs to --reindex - $rv .= qq{\n\t\t\t\t\t\tdownload: } . + $rv .= qq{${pfx}download: } . qq{<input\ntype=submit\nname=z\nvalue="mbox.gz"/>} } $rv .= qq{</pre></form><pre>}; } sub search_nav_bot { # also used by WwwListing for searching extindex miscidx - my ($mset, $q) = @_; + my ($ctx, $mset, $q) = @_; my $total = $mset->get_matches_estimated; my $l = $q->{l}; my $rv = '</pre><hr><pre id=t>'; @@ -269,14 +282,17 @@ sub search_nav_bot { # also used by WwwListing for searching extindex miscidx $rv .= qq{<a\nhref="?$prev"\nrel=prev>prev $pd</a>} if $prev; my $rev = $q->qs_html(o => $o < 0 ? 0 : -1); - $rv .= qq{ | <a\nhref="?$rev">reverse</a></pre>}; + $rv .= qq{ | <a\nhref="?$rev">reverse</a>}; + exists($ctx->{ibx}) and + $rv .= q{ | options <a href=#d>above</a></pre>}; + $rv; } sub sort_relevance { - [ sort { + @{$_[0]} = sort { (eval { $b->topmost->{pct} } // 0) <=> (eval { $a->topmost->{pct} } // 0) - } @{$_[0]} ] + } @{$_[0]}; } sub mset_thread { @@ -294,7 +310,9 @@ sub mset_thread { my $rootset = PublicInbox::SearchThread::thread($msgs, $r ? \&sort_relevance : \&PublicInbox::View::sort_ds, $ctx); - my $skel = search_nav_bot($mset, $q). "<pre>"; + my $skel = search_nav_bot($ctx, $mset, $q).'<pre>'. <<EOM; +-- pct% links below jump to the message on this page, permalinks otherwise -- +EOM $ctx->{-upfx} = ''; $ctx->{anchor_idx} = 1; $ctx->{cur_level} = 0; @@ -312,20 +330,20 @@ sub mset_thread { # link $INBOX_DIR/description text to "recent" view around # the newest message in this result set: - $ctx->{-t_max} = max(map { delete $_->{ts} } @$msgs); + $ctx->{-t_max} = max(map { $_->{ts} } @$msgs); @$msgs = reverse @$msgs if $r; $ctx->{msgs} = $msgs; - PublicInbox::WwwStream::aresponse($ctx, 200, \&mset_thread_i); + PublicInbox::WwwStream::aresponse($ctx, \&mset_thread_i); } # callback for PublicInbox::WwwStream::getline sub mset_thread_i { my ($ctx, $eml) = @_; - $ctx->zmore($ctx->html_top) if exists $ctx->{-html_tip}; + print { $ctx->zfh } $ctx->html_top if exists $ctx->{-html_tip}; $eml and return PublicInbox::View::eml_entry($ctx, $eml); my $smsg = shift @{$ctx->{msgs}} or - $ctx->zmore(${delete($ctx->{skel})}); + print { $ctx->zfh } ${delete($ctx->{skel})}; $smsg; } @@ -347,10 +365,10 @@ sub ctx_prepare { } sub adump { - my ($cb, $mset, $q, $ctx) = @_; + my ($mset, $q, $ctx) = @_; $ctx->{ids} = $ctx->{ibx}->isrch->mset_to_artnums($mset); $ctx->{search_query} = $q; # used by WwwAtomStream::atom_header - PublicInbox::WwwAtomStream->response($ctx, 200, \&adump_i); + PublicInbox::WwwAtomStream->response($ctx, \&adump_i); } # callback for PublicInbox::WwwAtomStream::getline |