user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: Leah Neukirchen <leah@vuxu.org>
Cc: meta@public-inbox.org
Subject: [RFC] www: support listing of inboxes
Date: Thu, 18 Apr 2019 08:25:56 +0000	[thread overview]
Message-ID: <20190418082556.GA15466@dcvr> (raw)
In-Reply-To: <20180612100915.shfo3ltn6aj55mrf@dcvr>

Eric Wong <e@80x24.org> wrote:
> Leah Neukirchen <leah@vuxu.org> wrote:
> > 5) Is there a way for the HTML view to list all served lists?
> 
> Not currently...  I'm not sure how the UI or configuration
> should be or how to avoid clutter/scalability problems with many
> inboxes.  NNTP has standardized commands and clients can decide
> how to show them, at least.

Better late than never :x

I don't like adding more options, but having thought about this
for a while... the below seems like the best choice here taking
backwards compatibility, clutter, and virtual-hosting into
account.

--------8<---------
Subject: [PATCH] www: support listing of inboxes

We will still return a 404 by default to '/' for compatibility
with users of Plack::App::Cascade or similar.  Inboxes are
sorted by modification times to help users detect activity
(similar to the /$INBOX/ topic view).

New configuration options:

* publicinbox.wwwlisting - configure the listing type
* publicinbox.<name>.hide - hide a particular inbox from the listing

See changes to public-inbox-config.pod for full descriptions
of the new options.

Requested-by: Leah Neukirchen <leah@vuxu.org>
  https://public-inbox.org/meta/871sdfzy80.fsf@gmail.com/
---
 Documentation/public-inbox-config.pod |  37 +++++++++
 MANIFEST                              |   1 +
 lib/PublicInbox/Config.pm             |   2 +-
 lib/PublicInbox/Inbox.pm              |   9 +++
 lib/PublicInbox/WWW.pm                |  14 +++-
 lib/PublicInbox/WwwListing.pm         | 104 ++++++++++++++++++++++++++
 lib/PublicInbox/WwwStream.pm          |  10 ++-
 7 files changed, 171 insertions(+), 6 deletions(-)
 create mode 100644 lib/PublicInbox/WwwListing.pm

diff --git a/Documentation/public-inbox-config.pod b/Documentation/public-inbox-config.pod
index e7dcd8a..1477027 100644
--- a/Documentation/public-inbox-config.pod
+++ b/Documentation/public-inbox-config.pod
@@ -188,6 +188,14 @@ be treated as the default value.
 
 Default: 25
 
+=item publicinbox.<name>.hide
+
+A comma-delimited list of listings to hide the inbox from.
+
+Valid values are currently "www".
+
+Default: none
+
 =item coderepo.<nick>.dir
 
 The path to a git repository for "publicinbox.<name>.coderepo"
@@ -217,6 +225,35 @@ directive is configured.
 
 Default: /var/www/htdocs/cgit/cgit.gti or /usr/lib/cgit/cgit.cgi
 
+=item publicinbox.wwwlisting
+
+Enable a HTML listing style when the root path of the URL '/' is accessed.
+Valid values are:
+
+=over 8
+
+=item all
+
+Show all inboxes
+
+=item 404
+
+Return a 404 page.  This is useful to allow customization with
+L<Plack::App::Cascade(3pm)>
+
+=item match=domain
+
+Only show inboxes with URLs which belong to the domain of the HTTP
+request
+
+=for TODO comment
+
+support showing cgit listing
+
+=back
+
+Default: 404
+
 =back
 
 =head2 NAMED LIMITER (PSGI)
diff --git a/MANIFEST b/MANIFEST
index 150e337..881d2f0 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -127,6 +127,7 @@ lib/PublicInbox/WatchMaildir.pm
 lib/PublicInbox/WwwAtomStream.pm
 lib/PublicInbox/WwwAttach.pm
 lib/PublicInbox/WwwHighlight.pm
+lib/PublicInbox/WwwListing.pm
 lib/PublicInbox/WwwStream.pm
 lib/PublicInbox/WwwText.pm
 sa_config/Makefile
diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm
index 631c788..09f9179 100644
--- a/lib/PublicInbox/Config.pm
+++ b/lib/PublicInbox/Config.pm
@@ -389,7 +389,7 @@ sub _fill {
 	}
 	# TODO: more arrays, we should support multi-value for
 	# more things to encourage decentralization
-	foreach my $k (qw(address altid nntpmirror coderepo)) {
+	foreach my $k (qw(address altid nntpmirror coderepo hide)) {
 		if (defined(my $v = $self->{"$pfx.$k"})) {
 			$ibx->{$k} = _array($v);
 		}
diff --git a/lib/PublicInbox/Inbox.pm b/lib/PublicInbox/Inbox.pm
index 0d28dd0..286555f 100644
--- a/lib/PublicInbox/Inbox.pm
+++ b/lib/PublicInbox/Inbox.pm
@@ -95,6 +95,15 @@ sub new {
 	if (defined $dir && -f "$dir/inbox.lock") {
 		$opts->{version} = 2;
 	}
+
+	# allow any combination of multi-line or comma-delimited hide entries
+	my $hide = {};
+	if (defined(my $h = $opts->{hide})) {
+		foreach my $v (@$h) {
+			$hide->{$_} = 1 foreach (split(/\s*,\s*/, $v));
+		}
+		$opts->{-hide} = $hide;
+	}
 	bless $opts, $class;
 }
 
diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm
index 6e69001..793466e 100644
--- a/lib/PublicInbox/WWW.pm
+++ b/lib/PublicInbox/WWW.pm
@@ -68,8 +68,9 @@ sub call {
 	} split(/[&;]+/, $env->{QUERY_STRING});
 	$ctx->{qp} = \%qp;
 
-	# not using $env->{PATH_INFO} here since that's already decoded
+	# avoiding $env->{PATH_INFO} here since that's already decoded
 	my ($path_info) = ($env->{REQUEST_URI} =~ path_re($env));
+	$path_info //= $env->{PATH_INFO};
 	my $method = $env->{REQUEST_METHOD};
 
 	if ($method eq 'POST') {
@@ -87,7 +88,7 @@ sub call {
 
 	# top-level indices and feeds
 	if ($path_info eq '/') {
-		r404();
+		www_listing($self)->call($env);
 	} elsif ($path_info =~ m!$INBOX_RE\z!o) {
 		invalid_inbox($ctx, $1) || r301($ctx, $1);
 	} elsif ($path_info =~ m!$INBOX_RE(?:/|/index\.html)?\z!o) {
@@ -157,6 +158,7 @@ sub preload {
 	if (ref($self)) {
 		$self->cgit;
 		$self->stylesheets_prepare($_) for ('', '../', '../../');
+		$self->www_listing;
 	}
 }
 
@@ -489,6 +491,14 @@ sub cgit {
 	}
 }
 
+sub www_listing {
+	my ($self) = @_;
+	$self->{www_listing} ||= do {
+		require PublicInbox::WwwListing;
+		PublicInbox::WwwListing->new($self);
+	}
+}
+
 sub get_attach {
 	my ($ctx, $idx, $fn) = @_;
 	require PublicInbox::WwwAttach;
diff --git a/lib/PublicInbox/WwwListing.pm b/lib/PublicInbox/WwwListing.pm
new file mode 100644
index 0000000..e8dad4b
--- /dev/null
+++ b/lib/PublicInbox/WwwListing.pm
@@ -0,0 +1,104 @@
+# Copyright (C) 2019 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Provide an HTTP-accessible listing of inboxes.
+# Used by PublicInbox::WWW
+package PublicInbox::WwwListing;
+use strict;
+use warnings;
+use PublicInbox::Hval qw(ascii_html);
+use PublicInbox::Linkify;
+use PublicInbox::View;
+
+sub list_all ($$) {
+	my ($self, undef) = @_;
+	my @list;
+	$self->{pi_config}->each_inbox(sub {
+		my ($ibx) = @_;
+		push @list, $ibx unless $ibx->{-hide}->{www};
+	});
+	\@list;
+}
+
+sub list_match_domain ($$) {
+	my ($self, $env) = @_;
+	my @list;
+	my $host = $env->{HTTP_HOST} // $env->{SERVER_NAME};
+	$host =~ s/:\d+\z//;
+	my $re = qr!\A(?:https?:)?//\Q$host\E(?::\d+)?/!i;
+	$self->{pi_config}->each_inbox(sub {
+		my ($ibx) = @_;
+		push @list, $ibx if !$ibx->{-hide}->{www} && $ibx->{url} =~ $re;
+	});
+	\@list;
+}
+
+sub list_404 ($$) { [] }
+
+# TODO: +cgit
+my %VALID = (
+	all => *list_all,
+	'match=domain' => *list_match_domain,
+	404 => *list_404,
+);
+
+sub new {
+	my ($class, $www) = @_;
+	my $k = 'publicinbox.wwwListing';
+	my $pi_config = $www->{pi_config};
+	my $v = $pi_config->{lc($k)} // 404;
+	bless {
+		pi_config => $pi_config,
+		style => $www->style("\0"),
+		list_cb => $VALID{$v} || do {
+			warn <<"";
+`$v' is not a valid value for `$k'
+$k be one of `all', `match=domain', or `404'
+
+			*list_404;
+		},
+	}, $class;
+}
+
+sub ibx_entry {
+	my ($mtime, $ibx, $env) = @_;
+	my $ts = PublicInbox::View::fmt_ts($mtime);
+	my $url = PublicInbox::Hval::prurl($env, $ibx->{url});
+	my $tmp = <<"";
+* $ts - $url
+  ${\$ibx->description}
+
+	if (defined(my $info_url = $ibx->{info_url})) {
+		$tmp .= "\n$info_url";
+	}
+	$tmp;
+}
+
+# not really a stand-alone PSGI app, but maybe it could be...
+sub call {
+	my ($self, $env) = @_;
+	my $h = [ 'Content-Type', 'text/html; charset=UTF-8' ];
+	my $list = $self->{list_cb}->($self, $env);
+	my $code = 404;
+	my $title = 'public-inbox';
+	my $out = '';
+	if (@$list) {
+		# Swartzian transform since ->modified is expensive
+		@$list = sort {
+			$b->[0] <=> $a->[0]
+		} map { [ $_->modified, $_ ] } @$list;
+
+		$code = 200;
+		$title .= ' - listing';
+		my $tmp = join("\n", map { ibx_entry(@$_, $env) } @$list);
+		my $l = PublicInbox::Linkify->new;
+		$l->linkify_1($tmp);
+		$out = '<pre>'.$l->linkify_2(ascii_html($tmp)).'</pre><hr>';
+	}
+	$out = "<html><head><title>$title</title></head><body>" . $out;
+	$out .= '<pre>'. PublicInbox::WwwStream::code_footer($env) .
+		'</pre></body></html>';
+	[ $code, $h, [ $out ] ]
+}
+
+1;
diff --git a/lib/PublicInbox/WwwStream.pm b/lib/PublicInbox/WwwStream.pm
index 8b79923..c708c21 100644
--- a/lib/PublicInbox/WwwStream.pm
+++ b/lib/PublicInbox/WwwStream.pm
@@ -71,6 +71,12 @@ sub _html_top ($) {
 		"</head><body>". $top . $tip;
 }
 
+sub code_footer ($) {
+	my ($env) = @_;
+	my $u = PublicInbox::Hval::prurl($env, $CODE_URL);
+	qq(AGPL code for this site: git clone <a\nhref="$u">$u</a> $PROJECT)
+}
+
 sub _html_end {
 	my ($self) = @_;
 	my $urls = 'Archives are clonable:';
@@ -139,12 +145,10 @@ EOF
 			$urls .= qq[<a\nhref="$TOR2WEB_URL">$TOR2WEB_URL</a>];
 		}
 	}
-	my $url = PublicInbox::Hval::prurl($ctx->{env}, $CODE_URL);
 	'<hr><pre>'.join("\n\n",
 		$desc,
 		$urls,
-		'AGPL code for this site: '.
-		qq(git clone <a\nhref="$url">$url</a> $PROJECT)
+		code_footer($ctx->{env})
 	).'</pre></body></html>';
 }
 
-- 
EW

  parent reply	other threads:[~2019-04-18  8:25 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-09 17:06 Some points on public-inbox Leah Neukirchen
2018-06-12 10:09 ` Eric Wong
2018-06-12 11:31   ` Leah Neukirchen
2018-06-13  2:07     ` [PATCH] Makefile.PL: do not depend on git Eric Wong
2018-06-13 14:26       ` Leah Neukirchen
2018-06-13 21:04         ` Eric Wong
2018-06-13 21:20           ` Leah Neukirchen
2018-06-13 21:40     ` Some points on public-inbox Eric Wong
2018-06-13 22:43       ` [PATCH] www: use undecoded paths for Message-ID extraction Eric Wong
2018-06-26  7:46         ` [PATCH] additional tests for bad Message-IDs in URLs Eric Wong
2018-06-12 13:19   ` Some points on public-inbox Leah Neukirchen
2019-01-05  8:39     ` Eric Wong
2018-06-12 17:05   ` Konstantin Ryabitsev
2018-06-13  1:57     ` Eric Wong
2019-04-18  8:25   ` Eric Wong [this message]
2019-05-05 23:36     ` [RFC] www: support listing of inboxes Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://public-inbox.org/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190418082556.GA15466@dcvr \
    --to=e@80x24.org \
    --cc=leah@vuxu.org \
    --cc=meta@public-inbox.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).