X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FView.pm;h=2b8e220bb2a7f3ca4628a261c49f0efce04b4250;hb=bedc22c355d570ed07ff6600717a97226f791ff5;hp=26094082996678975a5830876f00c0de431d9b91;hpb=c5c508f3094ffe7ef4a52f8c471340fe8992f6bb;p=public-inbox.git diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm index 26094082..2b8e220b 100644 --- a/lib/PublicInbox/View.pm +++ b/lib/PublicInbox/View.pm @@ -7,6 +7,7 @@ package PublicInbox::View; use strict; use v5.10.1; use List::Util qw(max); +use Text::Wrap qw(wrap); # stdlib, we need Perl 5.6+ for $huge use PublicInbox::MsgTime qw(msg_datestamp); use PublicInbox::Hval qw(ascii_html obfuscate_addrs prurl mid_href ts2str fmt_ts); @@ -19,6 +20,7 @@ use PublicInbox::WwwStream qw(html_oneshot); use PublicInbox::Reply; use PublicInbox::ViewDiff qw(flush_diff); use PublicInbox::Eml; +use POSIX qw(strftime); use Time::Local qw(timegm); use PublicInbox::Smsg qw(subject_normalized); use PublicInbox::ContentHash qw(content_hash); @@ -36,7 +38,7 @@ sub msg_page_i { : $ctx->gone('over'); $ctx->{mhref} = ($ctx->{nr} || $ctx->{smsg}) ? "../${\mid_href($smsg->{mid})}/" : ''; - my $obuf = $ctx->{obuf} = _msg_page_prepare_obuf($eml, $ctx); + my $obuf = _msg_page_prepare_obuf($eml, $ctx); if (length($$obuf)) { multipart_text_as_html($eml, $ctx); $$obuf .= '
WARNING: multiple messages have this Message-ID\n"; - $rv .= '
'; +"WARNING: multiple messages have this Message-ID\n"; } else { $ctx->{first_hdr} = $eml->header_obj; $ctx->{chash} = content_hash($eml) if $ctx->{smsg}; # reused MID $rv .= ""; # anchor for body start } - $ctx->{-upfx} = '../' if $over; + $ctx->{-upfx} = '../'; my @title; # (Subject[0], From[0]) for my $v ($eml->header('From')) { my @n = PublicInbox::Address::names($v); @@ -698,7 +703,7 @@ sub _msg_page_prepare_obuf { my $v = ascii_html(shift @subj); obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; $rv .= 'Subject: '; - $rv .= $over ? qq($v\n) : "$v\n"; + $rv .= $have_over ? qq($v\n) : "$v\n"; $title[0] = $v; for $v (@subj) { # multi-Subject message :< $v = ascii_html($v); @@ -706,34 +711,40 @@ sub _msg_page_prepare_obuf { $rv .= "Subject: $v\n"; } } else { # dummy anchor for thread skeleton at bottom of page - $rv .= qq() if $over; + $rv .= qq() if $have_over; $title[0] = '(no subject)'; } for my $v ($eml->header('Date')) { $v = ascii_html($v); obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; # possible :P - $rv .= qq{Date: $v\t[thread overview]\n}; + $rv .= qq{Date: $v\n}; + } + # [thread overview] link is typically added after Date, + # but added after Subject, or even nothing. + if ($have_over) { + chop $rv; # drop "\n", or noop if $rv eq '' + $rv .= qq{\t[thread overview]\n}; } if (!$nr) { # first (and only) message, common case $ctx->{-title_html} = join(' - ', @title); $rv = $ctx->html_top . $rv; } + + $ctx->{-linkify} //= PublicInbox::Linkify->new; if (scalar(@$mids) == 1) { # common case my $mhtml = ascii_html($mids->[0]); - $rv .= "Message-ID: <$mhtml> "; - $rv .= "(raw)\n"; + $rv .= qq[Message-ID: <$mhtml> (raw)\n]; } else { # X-Alt-Message-ID can happen if a message is injected from # public-inbox-nntpd because of multiple Message-ID headers. - my $lnk = PublicInbox::Linkify->new; my $s = ''; for my $h (qw(Message-ID X-Alt-Message-ID)) { $s .= "$h: $_\n" for ($eml->header_raw($h)); } - $lnk->linkify_mids('..', \$s, 1); + $ctx->{-linkify}->linkify_mids('..', \$s, 1); $rv .= $s; } - $rv .= _parent_headers($eml, $over); + _parent_headers($ctx, $eml); $rv .= "\n"; \$rv; } @@ -772,7 +783,6 @@ sub thread_skel ($$$) { # when multiple Subject: headers are present, so we follow suit: my $subj = $hdr->header('Subject') // ''; $subj = '(no subject)' if $subj eq ''; - $ctx->{prev_subj} = [ split(/ /, subject_normalized($subj)) ]; $ctx->{cur} = $mid; $ctx->{prev_attr} = ''; $ctx->{prev_level} = 0; @@ -786,49 +796,73 @@ sub thread_skel ($$$) { } sub _parent_headers { - my ($hdr, $over) = @_; - my $rv = ''; + my ($ctx, $hdr) = @_; my @irt = $hdr->header_raw('In-Reply-To'); my $refs; if (@irt) { - my $lnk = PublicInbox::Linkify->new; - $rv .= "In-Reply-To: $_\n" for @irt; - $lnk->linkify_mids('..', \$rv); + my $s = ''; + $s .= "In-Reply-To: $_\n" for @irt; + $ctx->{-linkify}->linkify_mids('..', \$s); + ${$ctx->{obuf}} .= $s; } else { $refs = references($hdr); my $irt = pop @$refs; if (defined $irt) { my $html = ascii_html($irt); my $href = mid_href($irt); - $rv .= "In-Reply-To: <"; - $rv .= "$html>\n"; + ${$ctx->{obuf}} .= <$html> +EOM } } # do not display References: if search is present, # we show the thread skeleton at the bottom, instead. - return $rv if $over; + return if $ctx->{ibx}->over; $refs //= references($hdr); if (@$refs) { - @$refs = map { linkify_ref_no_over($_) } @$refs; - $rv .= 'References: '. join("\n\t", @$refs) . "\n"; + $_ = linkify_ref_no_over($_) for @$refs; + ${$ctx->{obuf}} .= 'References: '. join("\n\t", @$refs) . "\n"; } - $rv; } # returns a string buffer sub html_footer { my ($ctx, $hdr) = @_; - my $ibx = $ctx->{ibx}; my $upfx = '../'; my $skel; my $rv = ' '; - if ($ibx->over) { + my $related; + my $qry = delete $ctx->{-qry}; + if ($qry && $ctx->{ibx}->isrch) { + my $q = ''; # search for either ancestor or descendent patches + for (@{$qry->{dfpre}}, @{$qry->{dfpost}}) { + chop if length > 7; # include 1 abbrev "older" patches + $q .= "dfblob:$_ "; + } + chop $q; # omit trailing SP + local $Text::Wrap::columns = COLS; + local $Text::Wrap::huge = 'overflow'; + $q = wrap('', '', $q); + my $rows = ($q =~ tr/\n/\n/) + 1; + $q = ascii_html($q); + $related = <'; + $rv .= $related // ''; $rv .= msg_reply($ctx, $hdr); } @@ -1131,9 +1166,10 @@ sub dump_topics { } my @out; - my $ibx = $ctx->{ibx}; - my $obfs_ibx = $ibx->{obfuscate} ? $ibx : undef; - + my $obfs_ibx = $ctx->{ibx}->{obfuscate} ? $ctx->{ibx} : undef; + if (my $note = delete $ctx->{t_note}) { + push @out, $note; # "messages from ... to ..." + } # sort by recency, this allows new posts to "bump" old topics... foreach my $topic (sort { $b->[0] <=> $a->[0] } @$order) { my ($ds, $n, $seen, $top_subj, @extra) = @$topic; @@ -1158,9 +1194,9 @@ sub dump_topics { my $s = "$top_subj\n" . " $ds UTC $n\n"; - for (my $i = 0; $i < scalar(@extra); $i += 2) { - my $level = $extra[$i]; - my $subj = $extra[$i + 1]; # already normalized + while (@extra) { + my $level = shift @extra; + my $subj = shift @extra; # already normalized $mid = delete $seen->{$subj}; my @subj = split(/ /, $subj); my @next_prev = @subj; # full copy @@ -1192,7 +1228,7 @@ sub pagination_footer ($$) { $next = $next ? "$next | " : ' | '; $prev .= qq[ | latest]; } - " find likely ancestor, descendant, or conflicting patches for this message: + +\t(help)+EOM + } + if ($ctx->{ibx}->over) { my $t = ts2str($ctx->{-t_max}); my $t_fmt = fmt_ts($ctx->{-t_max}); - $skel .= <\t"; + $skel = < ~$t_fmt UTC|newest] EOF @@ -869,6 +903,7 @@ EOF $rv .= qq(reply); $rv .= $skel; $rv .= ' page: $next$prev"; + ($next || $prev) ? "page: $next$prev" : ''; } sub paginate_recent ($$) { @@ -1207,23 +1243,30 @@ sub paginate_recent ($$) { $t =~ s/\A([0-9]{8,14})-// and $after = str2ts($1); $t =~ /\A([0-9]{8,14})\z/ and $before = str2ts($1); - my $ibx = $ctx->{ibx}; - my $msgs = $ibx->recent($opts, $after, $before); - my $nr = scalar @$msgs; - if ($nr < $lim && defined($after)) { + my $msgs = $ctx->{ibx}->over->recent($opts, $after, $before); + if (defined($after) && scalar(@$msgs) < $lim) { $after = $before = undef; - $msgs = $ibx->recent($opts); - $nr = scalar @$msgs; + $msgs = $ctx->{ibx}->over->recent($opts); } - my $more = $nr == $lim; + my $more = scalar(@$msgs) == $lim; my ($newest, $oldest); - if ($nr) { + if (@$msgs) { $newest = $msgs->[0]->{ts}; $oldest = $msgs->[-1]->{ts}; # if we only had $after, our SQL query in ->recent ordered if ($newest < $oldest) { ($oldest, $newest) = ($newest, $oldest); - $more = 0 if defined($after) && $after < $oldest; + $more = undef if defined($after) && $after < $oldest; + } + if (defined($after // $before)) { + my $n = strftime('%Y-%m-%d %H:%M:%S', gmtime($newest)); + my $o = strftime('%Y-%m-%d %H:%M:%S', gmtime($oldest)); + $ctx->{t_note} = <more...] +EOM + my $s = ts2str($newest); + $ctx->{prev_page} = qq[] . + 'prev (newer)'; } } if (defined($oldest) && $more) { @@ -1231,11 +1274,6 @@ sub paginate_recent ($$) { $ctx->{next_page} = qq[] . 'next (older)'; } - if (defined($newest) && (defined($before) || defined($after))) { - my $s = ts2str($newest); - $ctx->{prev_page} = qq[] . - 'prev (newer)'; - } $msgs; } @@ -1243,11 +1281,8 @@ sub paginate_recent ($$) { sub index_topics { my ($ctx) = @_; my $msgs = paginate_recent($ctx, 200); # 200 is our window - if (@$msgs) { - walk_thread(thread_results($ctx, $msgs), $ctx, \&acc_topic); - } - html_oneshot($ctx, dump_topics($ctx), \pagination_footer($ctx, '.')); - + walk_thread(thread_results($ctx, $msgs), $ctx, \&acc_topic) if @$msgs; + html_oneshot($ctx, dump_topics($ctx), pagination_footer($ctx, '.')); } sub thread_adj_level {