about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--lib/PublicInbox/SearchView.pm239
-rw-r--r--lib/PublicInbox/View.pm19
-rw-r--r--lib/PublicInbox/WWW.pm5
3 files changed, 208 insertions, 55 deletions
diff --git a/lib/PublicInbox/SearchView.pm b/lib/PublicInbox/SearchView.pm
index 82b97f32..c15a0463 100644
--- a/lib/PublicInbox/SearchView.pm
+++ b/lib/PublicInbox/SearchView.pm
@@ -6,52 +6,49 @@ use warnings;
 use PublicInbox::SearchMsg;
 use PublicInbox::Hval;
 use PublicInbox::View;
+use PublicInbox::MID qw(mid2path mid_clean);
+use Email::MIME;
 use POSIX qw/strftime/;
-our $LIM = 25;
+our $LIM = 50;
 
 sub sres_top_html {
-        my ($ctx, $q) = @_;
-        my $cgi = $ctx->{cgi};
+        my ($ctx) = @_;
+        my $q = PublicInbox::SearchQuery->new($ctx->{cgi});
         my $code = 200;
-        # $q ||= $cgi->param('q');
-        my $o = int($cgi->param('o') || 0);
-        my $r = $cgi->param('r');
-        $r = (defined $r && $r ne '0');
-        my $opts = { limit => $LIM, offset => $o, mset => 1, relevance => $r };
+
+        # double the limit for expanded views:
+        my $opts = {
+                limit => $LIM,
+                offset => $q->{o},
+                mset => 1,
+                relevance => $q->{r},
+        };
         my ($mset, $total);
+
         eval {
-                $mset = $ctx->{srch}->query($q, $opts);
+                $mset = $ctx->{srch}->query($q->{q}, $opts);
                 $total = $mset->get_matches_estimated;
         };
         my $err = $@;
-        my $query = PublicInbox::Hval->new_oneline($q);
-        my $qh = $query->as_html;
-        my $res = "<html><head><title>$qh - search results</title></head>" .
-                  qq{<body><form\naction="">} .
-                  qq{<input\nname=q\nvalue="$qh"\ntype=text />};
-
-        $res .= qq{<input\ntype=hidden\nname=r />} if $r;
-
-        $res .= qq{<input\ntype=submit\nvalue=search /></form>} .
-                  PublicInbox::View::PRE_WRAP;
-
-        my $foot = $ctx->{footer} || '';
-        $foot = qq{Back to <a\nhref=".">index</a>.};
+        my $res = html_start($q) . PublicInbox::View::PRE_WRAP;
         if ($err) {
                 $code = 400;
-                $res .= err_txt($err) . "</pre><hr /><pre>$foot";
+                $res .= err_txt($err) . "</pre><hr /><pre>" . foot($ctx);
         } elsif ($total == 0) {
                 $code = 404;
-                $res .= "\n\n[No results found]</pre><hr /><pre>$foot";
+                $res .= "\n\n[No results found]</pre><hr /><pre>".foot($ctx);
         } else {
-                $q = $query->as_href;
-                $q =~ s/%20/+/g; # improve URL readability
-                $res .= search_nav_top($q, $o, $r);
-                $res .= "\n\n";
+                my $x = $q->{x};
+                # TODO
+                #return sub { adump($_[0], $mset, $q, $ctx) } if ($x eq 'A');
 
-                dump_mset(\$res, $mset, $o);
-                $res .= search_nav_bot($mset, $q, $o, $r);
-                $res .= "\n\n" . $foot;
+                $res .= search_nav_top($mset, $q);
+                if ($x eq 't') {
+                        return sub { tdump($_[0], $res, $mset, $q, $ctx) };
+                }
+                $res .= "\n\n";
+                dump_mset(\$res, $mset);
+                $res .= search_nav_bot($mset, $q) . "\n\n" . foot($ctx);
         }
 
         $res .= "</pre></body></html>";
@@ -89,44 +86,194 @@ sub err_txt {
 }
 
 sub search_nav_top {
-        my ($q, $o, $r) = @_;
-        my $qs = "q=$q";
-        $qs .= "&amp;o=$o" if $o;
+        my ($mset, $q) = @_;
 
         my $rv = "Search results ordered by [";
-        if ($r) {
-                $rv .= qq{<a\nhref="?$qs">date</a>|<b>relevance</b>};
+        if ($q->{r}) {
+                my $d = $q->qs_html(r => 0);
+                $rv .= qq{<a\nhref="?$d">date</a>|<b>relevance</b>};
         } else {
-                $qs .= '&amp;r';
-                $rv .= qq{<b>date</b>|<a\nhref="?$qs">relevance</a>};
+                my $d = $q->qs_html(r => 1);
+                $rv .= qq{<b>date</b>|<a\nhref="?$d">relevance</a>};
+        }
+
+        $rv .= ']  view[';
+
+        my $x = $q->{x};
+        if ($x eq '') {
+                my $t = $q->qs_html(x => 't');
+                $rv .= qq{<b>summary</b>|};
+                $rv .= qq{<a\nhref="?$t">threaded</a>}
+        } elsif ($q->{x} eq 't') {
+                my $s = $q->qs_html(x => '');
+                $rv .= qq{<a\nhref="?$s">summary</a>|};
+                $rv .= qq{<b>threaded</b>};
         }
+        # my $A = $q->qs_html(x => 'a');
+        # $rv .= qq{|<a\nhref="?$A">Atom</a>}; # TODO
         $rv .= ']';
 }
 
 sub search_nav_bot {
-        my ($mset, $q, $o, $r) = @_;
+        my ($mset, $q) = @_;
         my $total = $mset->get_matches_estimated;
         my $nr = scalar $mset->items;
+        my $o = $q->{o};
         my $end = $o + $nr;
         my $beg = $o + 1;
-
         my $rv = "<hr /><pre>Results $beg-$end of $total";
-
         my $n = $o + $LIM;
+
         if ($n < $total) {
-                my $qs = "q=$q&amp;o=$n";
-                $qs .= "&amp;r" if $r;
+                my $qs = $q->qs_html(o => $n);
                 $rv .= qq{, <a\nhref="?$qs">next</a>}
         }
         if ($o > 0) {
                 $rv .= $n < $total ? '/' : ',      ';
                 my $p = $o - $LIM;
-                my $qs = "q=$q";
-                $qs .= "&amp;o=$p" if $p > 0;
-                $qs .= "&amp;r" if $r;
+                my $qs = $q->qs_html(o => ($p > 0 ? $p : 0));
                 $rv .= qq{<a\nhref="?$qs">prev</a>};
         }
         $rv;
 }
 
+sub tdump {
+        my ($cb, $res, $mset, $q, $ctx) = @_;
+        my $fh = $cb->([200, ['Content-Type'=>'text/html; charset=UTF-8']]);
+        $fh->write($res);
+        my %pct;
+        my @m = map {
+                my $i = $_;
+                my $m = PublicInbox::SearchMsg->load_doc($i->get_document);
+                $pct{$m->mid} = $i->get_percent;
+                $m = $m->mini_mime;
+                $m;
+        } ($mset->items);
+
+        require PublicInbox::Thread;
+        my $th = PublicInbox::Thread->new(@m);
+        {
+                no warnings 'once';
+                $Mail::Thread::nosubject = 0;
+        }
+        $th->thread;
+        if ($q->{r}) {
+                $th->order(sub {
+                        sort { (eval { $pct{$b->topmost->messageid} } || 0)
+                                        <=>
+                                (eval { $pct{$a->topmost->messageid} } || 0)
+                        } @_;
+                });
+        } else {
+                no warnings 'once';
+                $th->order(*PublicInbox::View::rsort_ts);
+        }
+
+        require PublicInbox::GitCatFile;
+        my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+        my $state = { ctx => $ctx, anchor_idx => 0, pct => \%pct };
+        $ctx->{searchview} = 1;
+        tdump_ent($fh, $git, $state, $_, 0) for $th->rootset;
+        $git = undef;
+        Email::Address->purge_cache;
+
+        $fh->write(search_nav_bot($mset, $q). "\n\n" .
+                        foot($ctx). '</pre></body></html>');
+
+        $fh->close;
+}
+
+sub tdump_ent {
+        my ($fh, $git, $state, $node, $level) = @_;
+        return unless $node;
+        my $mime = $node->message;
+
+        if ($mime) {
+                # lazy load the full message from mini_mime:
+                my $mid = $mime->header('Message-ID');
+                $mime = eval {
+                        my $path = mid2path(mid_clean($mid));
+                        Email::MIME->new($git->cat_file('HEAD:'.$path));
+                };
+        }
+        if ($mime) {
+                PublicInbox::View::index_entry($fh, $mime, $level, $state);
+        } else {
+                my $mid = $node->messageid;
+                $fh->write(PublicInbox::View::ghost_table('', $mid, $level));
+        }
+        tdump_ent($fh, $git, $state, $node->child, $level + 1);
+        tdump_ent($fh, $git, $state, $node->next, $level);
+}
+
+sub foot {
+        my ($ctx) = @_;
+        my $foot = $ctx->{footer} || '';
+        qq{Back to <a\nhref=".">index</a>.\n$foot};
+}
+
+sub html_start {
+        my ($q) = @_;
+        my $query = PublicInbox::Hval->new_oneline($q->{q});
+
+        my $qh = $query->as_html;
+        my $res = "<html><head><title>$qh - search results</title></head>" .
+                  qq{<body><form\naction="">} .
+                  qq{<input\nname=q\nvalue="$qh"\ntype=text />};
+
+        $res .= qq{<input\ntype=hidden\nname=r />} if $q->{r};
+        if (my $x = $q->{x}) {
+                my $xh = PublicInbox::Hval->new_oneline($x)->as_html;
+                $res .= qq{<input\ntype=hidden\nname=x\nvalue="$xh" />};
+        }
+
+        $res .= qq{<input\ntype=submit\nvalue=search /></form>};
+}
+
+package PublicInbox::SearchQuery;
+use strict;
+use warnings;
+use fields qw(q o t x r);
+use PublicInbox::Hval;
+
+sub new {
+        my ($class, $cgi) = @_;
+        my $self = fields::new($class);
+        $self->{q} = $cgi->param('q');
+        $self->{x} = $cgi->param('x') || '';
+        $self->{o} = int($cgi->param('o') || 0) || 0;
+        my $r = $cgi->param('r');
+        $self->{r} = (defined $r && $r ne '0');
+
+        $self;
+}
+
+sub qs_html {
+        my ($self, %over) = @_;
+
+        if (keys %over) {
+                my $tmp = fields::new(ref($self));
+                %$tmp = %$self;
+                foreach my $k (keys %over) {
+                        $tmp->{$k} = $over{$k};
+                }
+                $self = $tmp;
+        }
+
+        my $q = PublicInbox::Hval->new($self->{q})->as_href;
+        $q =~ s/%20/+/g; # improve URL readability
+        my $qs = "q=$q";
+
+        if (my $o = $self->{o}) { # ignore o == 0
+                $qs .= "&amp;o=$o";
+        }
+        if (my $r = $self->{r}) {
+                $qs .= "&amp;r";
+        }
+        if (my $x = $self->{x}) {
+                $qs .= "&amp;x=$x" if ($x eq 't' || $x eq 'A');
+        }
+        $qs;
+}
+
 1;
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 7d855550..de2d667c 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -72,7 +72,7 @@ sub index_entry {
         my $subj = $mime->header('Subject');
         my $header_obj = $mime->header_obj;
 
-        my $mid_raw = $header_obj->header('Message-ID');
+        my $mid_raw = mid_clean($header_obj->header('Message-ID'));
         my $id = anchor_for($mid_raw);
         my $seen = $state->{seen};
         $seen->{$id} = "#$id"; # save the anchor for children, later
@@ -139,7 +139,9 @@ sub index_entry {
                 }
                 $rv .= " <a\nhref=\"$parent_anchor\">parent</a>";
         }
-        if ($srch) {
+        if (my $pct = $state->{pct}) {
+                $rv .= " [$pct->{$mid_raw}%]";
+        } elsif ($srch) {
                 if ($ctx->{flat}) {
                         $rv .= " [<a\nhref=\"${path}$href/t/#u\">threaded</a>" .
                                 "|<b>flat</b>]";
@@ -601,6 +603,14 @@ sub ghost_parent {
         qq{[parent not found: &lt;<a\nhref="$upfx$href/">$html</a>&gt;]};
 }
 
+sub ghost_table {
+        my ($upfx, $mid, $level) = @_;
+        "<table\nsummary=ghost><tr><td>" .
+                (INDENT x $level) . "</td><td>" .
+                PRE_WRAP . ghost_parent($upfx, $mid) .
+                '</pre></td></table>';
+}
+
 sub __thread_entry {
         my ($cb, $git, $state, $mime, $level) = @_;
 
@@ -617,10 +627,7 @@ sub __thread_entry {
         if (my $ghost = delete $state->{ghost}) {
                 # n.b. ghost messages may only be parents, not children
                 foreach my $g (@$ghost) {
-                        $$cb->write("<table\nsummary=ghost><tr><td>" .
-                                (INDENT x $g->[1]) . "</td><td>" .
-                                PRE_WRAP . ghost_parent('../../', $g->[0]) .
-                                '</pre></td></table>');
+                        $$cb->write(ghost_table('../../', @$g));
                 }
         }
         index_entry($$cb, $mime, $level, $state);
diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm
index 508abf73..58a4256a 100644
--- a/lib/PublicInbox/WWW.pm
+++ b/lib/PublicInbox/WWW.pm
@@ -130,11 +130,10 @@ sub get_index {
         my ($ctx) = @_;
         require PublicInbox::Feed;
         my $srch = searcher($ctx);
-        my $q = $ctx->{cgi}->param('q');
         footer($ctx);
-        if (defined $q) {
+        if (defined $ctx->{cgi}->param('q')) {
                 require PublicInbox::SearchView;
-                PublicInbox::SearchView::sres_top_html($ctx, $q);
+                PublicInbox::SearchView::sres_top_html($ctx);
         } else {
                 PublicInbox::Feed::generate_html_index($ctx);
         }