diff options
author | Eric Wong <e@80x24.org> | 2021-06-23 07:14:21 -0400 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2021-06-23 19:24:49 +0000 |
commit | 520be116e8a686cb223b48fad1de29201dee45be (patch) | |
tree | a20d7a81be01fd67c6346efe5e06f05be02f58d8 /lib/PublicInbox/WwwListing.pm | |
parent | 4d594e98063aaad1ce9a90709af7edc5c44a0163 (diff) | |
download | public-inbox-520be116e8a686cb223b48fad1de29201dee45be.tar.gz |
When dealing with thousands of inboxes, displaying all of them on a single page isn't going to work. So steal some pagination and search results code from the message search to generate some basic HTML output that looks good in w3m.
Diffstat (limited to 'lib/PublicInbox/WwwListing.pm')
-rw-r--r-- | lib/PublicInbox/WwwListing.pm | 155 |
1 files changed, 131 insertions, 24 deletions
diff --git a/lib/PublicInbox/WwwListing.pm b/lib/PublicInbox/WwwListing.pm index f28eddf1..eb015742 100644 --- a/lib/PublicInbox/WwwListing.pm +++ b/lib/PublicInbox/WwwListing.pm @@ -5,7 +5,7 @@ # Used by PublicInbox::WWW package PublicInbox::WwwListing; use strict; -use PublicInbox::Hval qw(prurl fmt_ts); +use PublicInbox::Hval qw(prurl fmt_ts ascii_html); use PublicInbox::Linkify; use PublicInbox::GzipFilter qw(gzf_maybe); use PublicInbox::ConfigIter; @@ -13,18 +13,19 @@ use PublicInbox::WwwStream; use bytes (); # bytes::length sub ibx_entry { - my ($ctx, $ibx) = @_; - my $mtime = $ibx->modified; - my $ts = fmt_ts($mtime); + my ($ctx, $ibx, $ce) = @_; + $ce->{description} //= $ibx->description; + my $ts = fmt_ts($ce->{-modified} //= $ibx->modified); my $url = prurl($ctx->{env}, $ibx->{url}); my $tmp = <<""; * $ts - $url - ${\$ibx->description} + $ce->{description} if (defined(my $info_url = $ibx->{infourl})) { $tmp .= ' ' . prurl($ctx->{env}, $info_url) . "\n"; } - push @{$ctx->{-list}}, [ $mtime, $tmp ]; + push(@{$ctx->{-list}}, (scalar(@_) == 3 ? # $misc in use, already sorted + $tmp : [ $ce->{-modified}, $tmp ] )); } sub list_match_i { # ConfigIter callback @@ -41,7 +42,7 @@ sub list_match_i { # ConfigIter callback } } -sub url_regexp { +sub url_filter { my ($ctx, $key, $default) = @_; $key //= 'publicInbox.wwwListing'; $default //= '404'; @@ -50,9 +51,9 @@ again: if ($v eq 'match=domain') { my $h = $ctx->{env}->{HTTP_HOST} // $ctx->{env}->{SERVER_NAME}; $h =~ s/:[0-9]+\z//; - qr!\A(?:https?:)?//\Q$h\E(?::[0-9]+)?/!i; + (qr!\A(?:https?:)?//\Q$h\E(?::[0-9]+)?/!i, "url:$h"); } elsif ($v eq 'all') { - qr/./; + (qr/./, undef); } elsif ($v eq '404') { undef; } else { @@ -67,22 +68,122 @@ EOF sub hide_key { 'www' } +sub add_misc_ibx { # MiscSearch->retry_reopen callback + my ($misc, $ctx, $re, $qs) = @_; + require PublicInbox::SearchQuery; + my $q = $ctx->{-sq} = PublicInbox::SearchQuery->new($ctx->{qp}); + my $o = $q->{o}; + my ($asc, $min, $max); + if ($o < 0) { + $asc = 1; + $o = -($o + 1); # so [-1] is the last element, like Perl lists + } + my $r = $q->{r}; + my $opt = { + offset => $o, + asc => $asc, + relevance => $r, + limit => $q->{l} + }; + $qs .= ' type:inbox'; + if (my $user_query = $q->{'q'}) { + $qs = "( $qs ) AND ( $user_query )"; + } + my $mset = $misc->mset($qs, $opt); # sorts by $MODIFIED (mtime) + $ctx->{-list} = []; + my $pi_cfg = $ctx->{www}->{pi_cfg}; + for my $mi ($mset->items) { + my $doc = $mi->get_document; + my ($eidx_key) = PublicInbox::Search::xap_terms('Q', $doc); + $eidx_key // next; + my $ibx = $pi_cfg->lookup_eidx_key($eidx_key) // next; + next if $ibx->{-hide}->{$ctx->hide_key}; + grep(/$re/, @{$ibx->{url}}) or next; + $ctx->ibx_entry($ibx, $misc->doc2ibx_cache_ent($doc)); + if ($r) { # for descriptions in search_nav_bot + my $pct = PublicInbox::Search::get_pct($mi); + # only when sorting by relevance, ->items is always + # ordered descending: + $max //= $pct; + $min = $pct; + } + } + if ($r) { # for descriptions in search_nav_bot + $q->{-min_pct} = $min; + $q->{-max_pct} = $max; + } + $ctx->{-mset} = $mset; + psgi_triple($ctx); +} + sub response { my ($class, $ctx) = @_; bless $ctx, $class; - if (my $ALL = $ctx->{www}->{pi_cfg}->ALL) { - $ALL->misc->reopen; - } - my $re = $ctx->url_regexp or return $ctx->psgi_triple; - my $iter = PublicInbox::ConfigIter->new($ctx->{www}->{pi_cfg}, + my ($re, $qs) = $ctx->url_filter; + $re // return $ctx->psgi_triple; + if (my $ALL = $ctx->{www}->{pi_cfg}->ALL) { # fast path + $ALL->misc->reopen->retry_reopen(\&add_misc_ibx, + $ctx, $re, $qs); + } else { # slow path, no [extindex "all"] configured + my $iter = PublicInbox::ConfigIter->new($ctx->{www}->{pi_cfg}, \&list_match_i, $re, $ctx); - sub { - $ctx->{-wcb} = $_[0]; # HTTP server callback - $ctx->{env}->{'pi-httpd.async'} ? - $iter->event_step : $iter->each_section; + sub { + $ctx->{-wcb} = $_[0]; # HTTP server callback + $ctx->{env}->{'pi-httpd.async'} ? + $iter->event_step : $iter->each_section; + } } } +sub mset_footer ($$) { + my ($ctx, $mset) = @_; + # no footer if too few matches + return '' if $mset->get_matches_estimated == $mset->size; + require PublicInbox::SearchView; + PublicInbox::SearchView::search_nav_bot($mset, $ctx->{-sq}); +} + +sub mset_nav_top { + my ($ctx, $mset) = @_; + my $q = $ctx->{-sq}; + my $qh = $q->{'q'} // ''; + utf8::decode($qh); + $qh = ascii_html($qh); + $qh = qq[\nvalue="$qh"] if $qh ne ''; + my $rv = <<EOM; +<form +action="./"><pre><input +name=q +type=text$qh /><input +type=submit +value="locate inbox" /></pre></form><pre> +EOM + chomp $rv; + if (defined($q->{'q'})) { + my $initial_q = $ctx->{-uxs_retried}; + if (defined $initial_q) { + my $rewritten = $q->{'q'}; + utf8::decode($initial_q); + utf8::decode($rewritten); + $initial_q = ascii_html($initial_q); + $rewritten = ascii_html($rewritten); + $rv .= " Warning: Initial query:\n <b>$initial_q</b>\n"; + $rv .= " returned no results, used:\n"; + $rv .= " <b>$rewritten</b>\n instead\n\n"; + } + $rv .= 'Search results ordered by ['; + if ($q->{r}) { + my $d = $q->qs_html(r => 0); + $rv .= qq{<a\nhref="?$d">updated</a>|<b>relevance</b>}; + } else { + my $d = $q->qs_html(r => 1); + $rv .= qq{<b>updated</b>|<a\nhref="?$d">relevance</a>}; + } + $rv .= ']'; + } + $rv .= qq{</pre>}; +} + sub psgi_triple { my ($ctx) = @_; my $h = [ 'Content-Type', 'text/html; charset=UTF-8', @@ -90,17 +191,23 @@ sub psgi_triple { my $gzf = gzf_maybe($h, $ctx->{env}); $gzf->zmore('<html><head><title>' . 'public-inbox listing</title>' . - '</head><body><pre>'); + '</head><body>'); my $code = 404; - if (my $list = $ctx->{-list}) { + if (my $list = delete $ctx->{-list}) { + my $mset = delete $ctx->{-mset}; $code = 200; - # sort by ->modified - @$list = map { $_->[1] } sort { $b->[0] <=> $a->[0] } @$list; + if ($mset) { # already sorted, so search bar: + $gzf->zmore(mset_nav_top($ctx, $mset)); + } else { # sort config dump by ->modified + @$list = map { $_->[1] } + sort { $b->[0] <=> $a->[0] } @$list; + } $list = join("\n", @$list); my $l = PublicInbox::Linkify->new; - $gzf->zmore($l->to_html($list)); + $gzf->zmore('<pre>'.$l->to_html($list)); + $gzf->zmore(mset_footer($ctx, $mset)) if $mset; } else { - $gzf->zmore('no inboxes, yet'); + $gzf->zmore('<pre>no inboxes, yet'); } my $out = $gzf->zflush('</pre><hr><pre>'. PublicInbox::WwwStream::code_footer($ctx->{env}) . |