about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2016-06-30 02:35:21 +0000
committerEric Wong <e@80x24.org>2016-06-30 08:57:19 +0000
commitb30bcf12bdc0c3658e5e31c02b05c3609da04e7f (patch)
tree083db6ac69f75214e54e49c4bee9f2683b160d7b /lib
parentdfaff07b1578d5b32aae3225cb09c6a3f2177896 (diff)
downloadpublic-inbox-b30bcf12bdc0c3658e5e31c02b05c3609da04e7f.tar.gz
This hybrid view is better than the old flat, but can
still fall down compared to the old threaded view in
some cases.
Diffstat (limited to 'lib')
-rw-r--r--lib/PublicInbox/View.pm109
-rw-r--r--lib/PublicInbox/WWW.pm13
2 files changed, 108 insertions, 14 deletions
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 0b96bda8..17d6de56 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -142,7 +142,19 @@ sub index_entry {
                 " / <a\nhref=\"${mhref}#R\">reply</a>";
         if (my $pct = $ctx->{pct}) { # used by SearchView.pm
                 $rv .= " [relevance $pct->{$mid_raw}%]";
+        } elsif ($mapping) {
+                my $threaded = 'threaded';
+                my $flat = 'flat';
+                my $end = '';
+                if ($ctx->{flat}) {
+                        $flat = "<b>$flat</b>";
+                } else {
+                        $threaded = "<b>$threaded</b>";
+                }
+                $rv .= " [<a\nhref=\"${mhref}t/#u\">$threaded</a>";
+                $rv .= "|<a\nhref=\"${mhref}T/#u\">$flat</a>]";
         }
+
         $rv .= $more ? "\n\n" : "\n";
 }
 
@@ -150,7 +162,7 @@ sub pad_link ($$;$) {
         my ($mid, $level, $s) = @_;
         $s ||= '...';
         my $id = id_compress($mid, 1);
-        (' 'x19).indent_for($level).th_pfx($level)."<a\nhref=#r$id>($s)<a>\n";
+        (' 'x19).indent_for($level).th_pfx($level)."<a\nhref=#r$id>($s)</a>\n";
 }
 
 sub _th_index_lite {
@@ -186,7 +198,7 @@ sub _th_index_lite {
         my $s_c = nr_to_s($nr_c, 'reply', 'replies');
         my $this = $map->[1];
         $this =~ s!\n\z!</b>\n!s;
-        $this =~ s!<a\nhref.*a> !!s; # no point in duplicating subject
+        $this =~ s!<a\nhref.*</a> !!s; # no point in duplicating subject
         $rv .= "<b>@ $this";
         my $node = $map->[2];
         if (my $child = $node->child) {
@@ -210,7 +222,7 @@ sub _th_index_lite {
                         $rv .= $pad . $mapping->{$nn->messageid}->[1];
                 }
         }
-        $rv .= "<a\nhref=#e$id\nid=m$id>_<a> <a\nhref=#r$id\n>$s_s, $s_c</a>\n";
+        $rv .= "<a\nhref=#e$id\nid=m$id>_</a> <a\nhref=#r$id>$s_s, $s_c</a>\n";
 }
 
 sub walk_thread {
@@ -237,6 +249,51 @@ sub pre_thread  {
         skel_dump($ctx, $level, $node);
 }
 
+sub thread_index_entry {
+        my ($ctx, $level, $mime) = @_;
+        my ($beg, $end) = thread_adj_level($ctx, $level);
+        $beg . '<pre>' . index_entry($mime, $ctx, 0) . '</pre>' . $end;
+}
+
+sub stream_thread ($$) {
+        my ($th, $ctx) = @_;
+        my $inbox = $ctx->{-inbox};
+        my $mime;
+        my @q = map { (0, $_) } $th->rootset;
+        my $level;
+        while (@q) {
+                $level = shift @q;
+                my $node = shift @q or next;
+                unshift @q, $level+1, $node->child, $level, $node->next;
+                $mime = $inbox->msg_by_mid($node->messageid) and last;
+        }
+        return missing_thread($ctx) unless $mime;
+
+        $mime = Email::MIME->new($mime);
+        $ctx->{-title_html} = ascii_html($mime->header('Subject'));
+        $ctx->{-html_tip} = thread_index_entry($ctx, $level, $mime);
+        my $body = PublicInbox::WwwStream->new($ctx, sub {
+                return unless $ctx;
+                while (@q) {
+                        $level = shift @q;
+                        my $node = shift @q or next;
+                        unshift @q, $level+1, $node->child, $level, $node->next;
+                        my $mid = $node->messageid;
+                        if ($mime = $inbox->msg_by_mid($mid)) {
+                                $mime = Email::MIME->new($mime);
+                                return thread_index_entry($ctx, $level, $mime);
+                        } else {
+                                return ghost_index_entry($ctx, $level, $mid);
+                        }
+                }
+                my $ret = join('', thread_adj_level($ctx, 0));
+                $ret .= ${$ctx->{dst}}; # skel
+                $ctx = undef;
+                $ret;
+        });
+        [ 200, ['Content-Type', 'text/html; charset=UTF-8'], $body ];
+}
+
 sub thread_html {
         my ($ctx) = @_;
         my $mid = $ctx->{mid};
@@ -244,30 +301,34 @@ sub thread_html {
         my $msgs = load_results($sres);
         my $nr = $sres->{total};
         return missing_thread($ctx) if $nr == 0;
-        my $skel = '</pre><hr /><pre>';
+        my $skel = '<hr /><pre>';
         $skel .= $nr == 1 ? 'only message in thread' : 'end of thread';
         $skel .= ", back to <a\nhref=\"../../\">index</a>";
         $skel .= "\n<a\nid=t>$nr+ messages in thread:</a> (download: ";
         $skel .= "<a\nhref=\"../t.mbox.gz\">mbox.gz</a>";
         $skel .= " / follow: <a\nhref=\"../t.atom\">Atom feed</a>)\n";
+        $ctx->{-upfx} = '../../';
         $ctx->{cur_level} = 0;
         $ctx->{dst} = \$skel;
-        $ctx->{mapping} = {}; # mid -> [ reply count, from@date, node ];
         $ctx->{prev_attr} = '';
         $ctx->{prev_level} = 0;
         $ctx->{root_anchor} = anchor_for($mid);
         $ctx->{seen} = {};
+        $ctx->{mapping} = {};
 
-        walk_thread(thread_results($msgs), $ctx, *pre_thread);
+        my $th = thread_results($msgs);
+        walk_thread($th, $ctx, *pre_thread);
+        $skel .= '</pre>';
+        return stream_thread($th, $ctx) unless $ctx->{flat};
 
-        # lazy load the full message from mini_mime:
+        # flat display: lazy load the full message from mini_mime:
         my $inbox = $ctx->{-inbox};
         my $mime;
         while ($mime = shift @$msgs) {
                 $mime = $inbox->msg_by_mid(mid_clean(mid_mime($mime))) and last;
         }
+        return missing_thread($ctx) unless $mime;
         $mime = Email::MIME->new($mime);
-        $ctx->{-upfx} = '../../';
         $ctx->{-title_html} = ascii_html($mime->header('Subject'));
         $ctx->{-html_tip} = '<pre>'.index_entry($mime, $ctx, scalar @$msgs);
         $mime = undef;
@@ -282,7 +343,7 @@ sub thread_html {
                         return index_entry($mime, $ctx, scalar @$msgs);
                 }
                 $msgs = undef;
-                $skel .= '</pre>';
+                '</pre>'.$skel;
         });
         [ 200, ['Content-Type', 'text/html; charset=UTF-8'], $body ];
 }
@@ -846,4 +907,34 @@ sub emit_index_topics {
         $opts{offset};
 }
 
+sub thread_adj_level {
+        my ($ctx, $level) = @_;
+
+        my $max = $ctx->{cur_level};
+        if ($level <= 0) {
+                return ('', '') if $max == 0; # flat output
+
+                # reset existing lists
+                my $beg = $max > 1 ? ('</ul></li>' x ($max - 1)) : '';
+                $ctx->{cur_level} = 0;
+                ("$beg</ul>", '');
+        } elsif ($level == $max) { # continue existing list
+                qw(<li> </li>);
+        } elsif ($level < $max) {
+                my $beg = $max > 1 ? ('</ul></li>' x ($max - $level)) : '';
+                $ctx->{cur_level} = $level;
+                ("$beg<li>", '</li>');
+        } else { # ($level > $max) # start a new level
+                $ctx->{cur_level} = $level;
+                my $beg = ($max ? '<li>' : '') . '<ul><li>';
+                ($beg, '</li>');
+        }
+}
+
+sub ghost_index_entry {
+        my ($ctx, $level, $mid) = @_;
+        my ($beg, $end) = thread_adj_level($ctx,  $level);
+        $beg . '<pre>'. ghost_parent($ctx->{-upfx}, $mid) . '</pre>' . $end;
+}
+
 1;
diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm
index da5c1d30..cbd3142d 100644
--- a/lib/PublicInbox/WWW.pm
+++ b/lib/PublicInbox/WWW.pm
@@ -23,7 +23,7 @@ require PublicInbox::Git;
 use PublicInbox::GitHTTPBackend;
 our $INBOX_RE = qr!\A/([\w\.\-]+)!;
 our $MID_RE = qr!([^/]+)!;
-our $END_RE = qr!(t/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
+our $END_RE = qr!(T/|t/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
 our $ATTACH_RE = qr!(\d[\.\d]*)-([[:alnum:]][\w\.-]+[[:alnum:]])!i;
 
 sub new {
@@ -92,9 +92,10 @@ sub call {
                 invalid_inbox_mid($self, $ctx, $1, $2) ||
                         get_attach($ctx, $idx, $fn);
         # in case people leave off the trailing slash:
-        } elsif ($path_info =~ m!$INBOX_RE/$MID_RE/(?:T|T/|t)\z!o) {
-                my ($inbox, $mid) = ($1, $2);
-                r301($ctx, $inbox, $mid, 't/#u');
+        } elsif ($path_info =~ m!$INBOX_RE/$MID_RE/(T|t)\z!o) {
+                my ($inbox, $mid, $suffix) = ($1, $2, $3);
+                $suffix .= $suffix =~ /\A[tT]\z/ ? '/#u' : '/';
+                r301($ctx, $inbox, $mid, $suffix);
 
         } elsif ($path_info =~ m!$INBOX_RE/$MID_RE/R/?\z!o) {
                 my ($inbox, $mid) = ($1, $2);
@@ -241,8 +242,9 @@ sub get_mid_html {
 
 # /$INBOX/$MESSAGE_ID/t/
 sub get_thread {
-        my ($ctx) = @_;
+        my ($ctx, $flat) = @_;
         searcher($ctx) or return need_search($ctx);
+        $ctx->{flat} = $flat;
         require PublicInbox::View;
         PublicInbox::View::thread_html($ctx);
 }
@@ -416,6 +418,7 @@ sub msg_page {
         my $ret;
         $ret = invalid_inbox_mid($self, $ctx, $inbox, $mid) and return $ret;
         '' eq $e and return get_mid_html($ctx);
+        'T/' eq $e and return get_thread($ctx, 1);
         't/' eq $e and return get_thread($ctx);
         't.atom' eq $e and return get_thread_atom($ctx);
         't.mbox' eq $e and return get_thread_mbox($ctx);