* [PATCH] www: stop generating /$MESSAGE_ID/f/ links
@ 2016-04-13 3:04 Eric Wong
2016-04-15 23:33 ` [PATCH 0/4] more quote-folding removal Eric Wong
0 siblings, 1 reply; 6+ messages in thread
From: Eric Wong @ 2016-04-13 3:04 UTC (permalink / raw)
To: meta
Quote-folding can be detrimental as it fails to hide the
real problem of over-quoting.
Over-quoting wastes bandwidth and space for all readers, not
just WWW readers of the public-inbox. So hopefully removing
quote-folding support from the WWW interface can shame those
repliers into quoting only relevant portions of what they reply
to.
---
Documentation/design_www.txt | 4 +--
lib/PublicInbox/Feed.pm | 2 +-
lib/PublicInbox/View.pm | 75 ++++++++++----------------------------------
t/feed.t | 4 +--
t/view.t | 12 -------
5 files changed, 21 insertions(+), 76 deletions(-)
diff --git a/Documentation/design_www.txt b/Documentation/design_www.txt
index 1be4d18..980b2ea 100644
--- a/Documentation/design_www.txt
+++ b/Documentation/design_www.txt
@@ -11,13 +11,13 @@ URL naming
/$LISTNAME/$MESSAGE_ID/t.mbox.gz -> gzipped mbox of thread
### Stable endpoints
-/$LISTNAME/$MESSAGE_ID/ -> HTML content (short quotes)
+/$LISTNAME/$MESSAGE_ID/ -> HTML content
/$LISTNAME/$MESSAGE_ID -> 301 to /$LISTNAME/$MESSAGE_ID
/$LISTNAME/$MESSAGE_ID/raw -> raw mbox
-/$LISTNAME/$MESSAGE_ID/f/ -> HTML content (full quotes)
/$LISTNAME/$MESSAGE_ID/R/ -> HTML reply instructions
### Legacy endpoints (may be ambiguous given Message-IDs with similar suffixes)
+/$LISTNAME/$MESSAGE_ID/f/ -> HTML content
/$LISTNAME/m/$MESSAGE_ID/ -> 301 to /$LISTNAME/$MESSAGE_ID/
/$LISTNAME/m/$MESSAGE_ID.html -> 301 to /$LISTNAME/$MESSAGE_ID/
/$LISTNAME/m/$MESSAGE_ID.txt -> 301 to /$LISTNAME/$MESSAGE_ID/raw
diff --git a/lib/PublicInbox/Feed.pm b/lib/PublicInbox/Feed.pm
index 54fa6e5..096bff9 100644
--- a/lib/PublicInbox/Feed.pm
+++ b/lib/PublicInbox/Feed.pm
@@ -316,7 +316,7 @@ sub add_to_feed {
defined $mid or return 0;
$mid = PublicInbox::Hval->new_msgid($mid);
my $href = $mid->as_href;
- my $content = PublicInbox::View->feed_entry($mime, "$midurl$href/f/");
+ my $content = PublicInbox::View->feed_entry($mime);
defined($content) or return 0;
$mime = undef;
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 2f718b7..77b42b2 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -135,17 +135,11 @@ sub index_entry {
my $fh = $state->{fh};
$fh->write($rv .= "- $from @ $ts UTC (<a\nhref=\"$txt\">raw</a>)\n\n");
- my $fhref;
my $mhref = "${path}$href/";
- # show full message if it's our root message
- my $neq = $root_anchor ne $id;
- if ($neq || ($neq && $level != 0 && !$ctx->{flat})) {
- $fhref = "${path}$href/f/";
- }
# scan through all parts, looking for displayable text
$mime->walk_parts(sub {
- index_walk($fh, $_[0], $enc, \$part_nr, $fhref);
+ index_walk($fh, $_[0], $enc, \$part_nr);
});
$mime->body_set('');
$rv = "\n" . html_footer($hdr, 0, undef, $ctx, $mhref);
@@ -231,8 +225,8 @@ sub emit_thread_html {
}
sub index_walk {
- my ($fh, $part, $enc, $part_nr, $fhref) = @_;
- my $s = add_text_body($enc, $part, $part_nr, $fhref, 1);
+ my ($fh, $part, $enc, $part_nr) = @_;
+ my $s = add_text_body($enc, $part, $part_nr, 1);
return if $s eq '';
@@ -264,7 +258,7 @@ sub multipart_text_as_html {
# scan through all parts, looking for displayable text
$mime->walk_parts(sub {
my ($part) = @_;
- $part = add_text_body($enc, $part, \$part_nr, $full_pfx, 1);
+ $part = add_text_body($enc, $part, \$part_nr, 1);
$rv .= $part;
$rv .= "\n" if $part ne '';
});
@@ -283,55 +277,21 @@ sub add_filename_line {
}
sub flush_quote {
- my ($quot, $n, $part_nr, $full_pfx, $final, $do_anchor) = @_;
-
- # n.b.: do not use <blockquote> since it screws up alignment
- # w.r.t. unquoted text. Repliers may rely on pre-formatted
- # alignment to point out a certain word in quoted text.
- if ($full_pfx) {
- if (!$final && scalar(@$quot) <= MAX_INLINE_QUOTED) {
- # show quote inline
- my $l = PublicInbox::Linkify->new;
- my $rv = join('', map { $l->linkify_1($_) } @$quot);
- @$quot = ();
- $rv = ascii_html($rv);
- return $l->linkify_2($rv);
- }
+ my ($quot, $n, $part_nr, $final, $do_anchor) = @_;
- # show a short snippet of quoted text and link to full version:
- @$quot = map { s/^(?:>\s*)+//gm; $_ } @$quot;
- my $cur = join(' ', @$quot);
- @$quot = split(/\s+/, $cur);
- $cur = '';
- do {
- my $tmp = shift(@$quot);
- my $len = length($tmp) + length($cur);
- if ($len > MAX_TRUNC_LEN) {
- @$quot = ();
- } else {
- $cur .= $tmp . ' ';
- }
- } while (@$quot && length($cur) < MAX_TRUNC_LEN);
- @$quot = ();
- $cur =~ s/ \z/ .../s;
- $cur = ascii_html($cur);
- my $nr = ++$$n;
- "> [<a\nhref=\"$full_pfx#q${part_nr}_$nr\">$cur</a>]\n";
- } else {
- # show everything in the full version with anchor from
- # short version (see above)
- my $l = PublicInbox::Linkify->new;
- my $rv .= join('', map { $l->linkify_1($_) } @$quot);
- @$quot = ();
- $rv = ascii_html($rv);
- return $l->linkify_2($rv) unless $do_anchor;
- my $nr = ++$$n;
- "<a\nid=q${part_nr}_$nr></a>" . $l->linkify_2($rv);
- }
+ # show everything in the full version with anchor from
+ # short version (see above)
+ my $l = PublicInbox::Linkify->new;
+ my $rv .= join('', map { $l->linkify_1($_) } @$quot);
+ @$quot = ();
+ $rv = ascii_html($rv);
+ return $l->linkify_2($rv) unless $do_anchor;
+ my $nr = ++$$n;
+ qq(<a\nid="q${part_nr}_$nr"></a>) . $l->linkify_2($rv);
}
sub add_text_body {
- my ($enc_msg, $part, $part_nr, $full_pfx, $do_anchor) = @_;
+ my ($enc_msg, $part, $part_nr, $do_anchor) = @_;
return '' if $part->subparts;
my $ct = $part->content_type;
@@ -361,7 +321,7 @@ sub add_text_body {
# show the previously buffered quote inline
if (scalar @quot) {
$s .= flush_quote(\@quot, \$n, $$part_nr,
- $full_pfx, 0, $do_anchor);
+ 0, $do_anchor);
}
# regular line, OK
@@ -374,8 +334,7 @@ sub add_text_body {
}
}
if (scalar @quot) {
- $s .= flush_quote(\@quot, \$n, $$part_nr, $full_pfx, 1,
- $do_anchor);
+ $s .= flush_quote(\@quot, \$n, $$part_nr, 1, $do_anchor);
}
++$$part_nr;
diff --git a/t/feed.t b/t/feed.t
index 73b7d0b..2096b73 100644
--- a/t/feed.t
+++ b/t/feed.t
@@ -76,9 +76,7 @@ EOF
"id is set to default");
}
- unlike($feed, qr/drop me/, "long quoted text dropped");
- like($feed, qr!/\d%40example\.com/f/#q!,
- "/f/ url generated for long quoted text");
+ like($feed, qr/drop me/, "long quoted text kept");
like($feed, qr/inline me here/, "short quoted text kept");
like($feed, qr/keep me/, "unquoted text saved");
}
diff --git a/t/view.t b/t/view.t
index 568ab30..2da741a 100644
--- a/t/view.t
+++ b/t/view.t
@@ -49,18 +49,6 @@ EOF
like($html, qr/> keep this inline/, "short quoted text is inline");
like($html, qr/<a\nid=[^>]+><\/a>> Long and wordy/,
"long quoted text is anchored");
-
- # short page
- my $pfx = "../hello%40example.com/f/";
- $mime = Email::MIME->new($s);
- my $short = PublicInbox::View::msg_html(undef, $mime, $pfx);
- like($short, qr!<a\nhref="\.\./hello%40example\.com/f/!s,
- "MID link present");
- like($short, qr/\n> keep this inline/,
- "short quoted text is inline");
- like($short, qr/<a\nhref="\Q$pfx\E#[^>]+>Long and wordy/,
- "long quoted text is made into a link");
- ok(length($short) < length($html), "short page is shorter");
}
# multipart crap
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 0/4] more quote-folding removal
2016-04-13 3:04 [PATCH] www: stop generating /$MESSAGE_ID/f/ links Eric Wong
@ 2016-04-15 23:33 ` Eric Wong
2016-04-15 23:33 ` [PATCH 1/4] view: drop vestigial elements of quote folding Eric Wong
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Eric Wong @ 2016-04-15 23:33 UTC (permalink / raw)
To: meta
These are rather significant changes and will hamper readability
of some messages. However, I've come to believe these are for
the better if they can encourage people to avoid over-quoting in
replies.
Eric Wong (4):
view: drop vestigial elements of quote folding
doc: update design notes on WWW development
www: redirect /$MESSAGE_ID/f/ endpoints
view: thread skeleton tweaks
Documentation/design_www.txt | 53 +++++++++++----
lib/PublicInbox/View.pm | 157 +++++++++++++++++++------------------------
lib/PublicInbox/WWW.pm | 38 +++++------
t/cgi.t | 8 ++-
t/plack.t | 32 +++++----
t/view.t | 4 +-
6 files changed, 149 insertions(+), 143 deletions(-)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/4] view: drop vestigial elements of quote folding
2016-04-15 23:33 ` [PATCH 0/4] more quote-folding removal Eric Wong
@ 2016-04-15 23:33 ` Eric Wong
2016-04-15 23:33 ` [PATCH 2/4] doc: update design notes on WWW development Eric Wong
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2016-04-15 23:33 UTC (permalink / raw)
To: meta
...And mark quotes as <span class="q"> since it barely
costs us anything and allows users to choose colors
themselves with custom, user-supplied CSS.
Reduce allocations of the Linkify object, too.
---
lib/PublicInbox/View.pm | 107 +++++++++++++++++++++---------------------------
t/view.t | 2 -
2 files changed, 46 insertions(+), 63 deletions(-)
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 77b42b2..2bf7cd5 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -16,10 +16,6 @@ use PublicInbox::Linkify;
use PublicInbox::MID qw/mid_clean id_compress mid2path mid_mime/;
require POSIX;
-# TODO: make these constants tunable
-use constant MAX_INLINE_QUOTED => 12; # half an 80x24 terminal
-use constant MAX_TRUNC_LEN => 72;
-use constant T_ANCHOR => '#u';
use constant INDENT => ' ';
my $enc_utf8 = find_encoding('UTF-8');
@@ -226,7 +222,7 @@ sub emit_thread_html {
sub index_walk {
my ($fh, $part, $enc, $part_nr) = @_;
- my $s = add_text_body($enc, $part, $part_nr, 1);
+ my $s = add_text_body($enc, $part, $part_nr);
return if $s eq '';
@@ -258,7 +254,7 @@ sub multipart_text_as_html {
# scan through all parts, looking for displayable text
$mime->walk_parts(sub {
my ($part) = @_;
- $part = add_text_body($enc, $part, \$part_nr, 1);
+ $part = add_text_body($enc, $part, \$part_nr);
$rv .= $part;
$rv .= "\n" if $part ne '';
});
@@ -277,21 +273,21 @@ sub add_filename_line {
}
sub flush_quote {
- my ($quot, $n, $part_nr, $final, $do_anchor) = @_;
+ my ($s, $l, $quot, $part_nr) = @_;
# show everything in the full version with anchor from
# short version (see above)
- my $l = PublicInbox::Linkify->new;
- my $rv .= join('', map { $l->linkify_1($_) } @$quot);
+ my $rv = $l->linkify_1(join('', @$quot));
@$quot = ();
- $rv = ascii_html($rv);
- return $l->linkify_2($rv) unless $do_anchor;
- my $nr = ++$$n;
- qq(<a\nid="q${part_nr}_$nr"></a>) . $l->linkify_2($rv);
+
+ # we use a <div> here to allow users to specify their own
+ # color for quoted text
+ $rv = $l->linkify_2(ascii_html($rv));
+ $$s .= qq(<span\nclass="q">) . $rv . '</span>'
}
sub add_text_body {
- my ($enc_msg, $part, $part_nr, $do_anchor) = @_;
+ my ($enc_msg, $part, $part_nr) = @_;
return '' if $part->subparts;
my $ct = $part->content_type;
@@ -301,8 +297,6 @@ sub add_text_body {
return '';
}
my $enc = enc_for($ct, $enc_msg);
- my $n = 0;
- my $nr = 0;
my $s = $part->body;
$part->body_set('');
$s = $enc->decode($s);
@@ -316,16 +310,13 @@ sub add_text_body {
}
my @quot;
+ my $l = PublicInbox::Linkify->new;
while (defined(my $cur = shift @lines)) {
if ($cur !~ /^>/) {
# show the previously buffered quote inline
- if (scalar @quot) {
- $s .= flush_quote(\@quot, \$n, $$part_nr,
- 0, $do_anchor);
- }
+ flush_quote(\$s, $l, \@quot, $$part_nr) if @quot;
# regular line, OK
- my $l = PublicInbox::Linkify->new;
$cur = $l->linkify_1($cur);
$cur = ascii_html($cur);
$s .= $l->linkify_2($cur);
@@ -333,9 +324,8 @@ sub add_text_body {
push @quot, $cur;
}
}
- if (scalar @quot) {
- $s .= flush_quote(\@quot, \$n, $$part_nr, 1, $do_anchor);
- }
+
+ flush_quote(\$s, $l, \@quot, $$part_nr) if @quot;
++$$part_nr;
$s =~ s/[ \t]+$//sgm; # kill per-line trailing whitespace
@@ -347,7 +337,15 @@ sub add_text_body {
sub headers_to_html_header {
my ($hdr, $full_pfx, $ctx) = @_;
my $srch = $ctx->{srch} if $ctx;
- my $rv = "";
+ my $atom = '';
+ my $rv = '';
+ my $upfx = $full_pfx ? '' : '../';
+
+ if ($srch) {
+ $atom = qq{<link\nrel=alternate\ntitle="Atom feed"\n} .
+ qq!href="${upfx}t.atom"\ntype="application/atom+xml"/>!;
+ }
+
my @title;
my $mid = $hdr->header_raw('Message-ID');
$mid = PublicInbox::Hval->new_msgid($mid);
@@ -362,8 +360,8 @@ sub headers_to_html_header {
} elsif ($h eq 'Subject') {
$title[0] = $v->as_html;
if ($srch) {
- $rv .= "$h: <b\nid=t>";
- $rv .= $v->as_html . "</b>\n";
+ $rv .= qq($h: <a\nhref="#r"\nid=t>);
+ $rv .= $v->as_html . "</a>\n";
next;
}
}
@@ -371,25 +369,17 @@ sub headers_to_html_header {
}
$rv .= 'Message-ID: <' . $mid->as_html . '> ';
- my $upfx = $full_pfx ? '' : '../';
$rv .= "(<a\nhref=\"${upfx}raw\">raw</a>)\n";
- my $atom;
- if ($srch) {
- thread_inline(\$rv, $ctx, $hdr, $upfx);
-
- $atom = qq{<link\nrel=alternate\ntitle="Atom feed"\n} .
- qq!href="${upfx}t.atom"\ntype="application/atom+xml"/>!;
- } else {
- $rv .= _parent_headers_nosrch($hdr);
- $atom = '';
- }
+ $rv .= _parent_headers($hdr, $srch);
$rv .= "\n";
("<html><head><title>". join(' - ', @title) . "</title>$atom".
- PublicInbox::Hval::STYLE . "</head><body><pre>" . $rv);
+ PublicInbox::Hval::STYLE .
+ "</head><body><pre\nid=b>" . # anchor for body start
+ $rv);
}
-sub thread_inline {
+sub thread_skel {
my ($dst, $ctx, $hdr, $upfx) = @_;
my $srch = $ctx->{srch};
my $mid = mid_clean($hdr->header_raw('Message-ID'));
@@ -398,7 +388,6 @@ sub thread_inline {
my $expand = "<a\nhref=\"${upfx}t/#u\">expand</a> " .
"/ <a\nhref=\"${upfx}t.mbox.gz\">mbox.gz</a>";
- $$dst .= 'Thread: ';
my $parent = in_reply_to($hdr);
if ($nr <= 1) {
if (defined $parent) {
@@ -412,11 +401,8 @@ sub thread_inline {
return;
}
- $$dst .= "~$nr messages ($expand";
- if ($nr > MAX_INLINE_QUOTED) {
- $$dst .= qq! / <a\nhref="#b">[scroll down]</a>!;
- }
- $$dst .= ")\n";
+ $$dst .= "$nr+ messages in thread ($expand";
+ $$dst .= qq! / <a\nhref="#b">[top]</a>)\n!;
my $subj = $srch->subject_path($hdr->header('Subject'));
my $state = {
@@ -427,15 +413,14 @@ sub thread_inline {
prev_level => 0,
};
for (thread_results(load_results($sres))->rootset) {
- inline_dump($dst, $state, $upfx, $_, 0);
+ skel_dump($dst, $state, $upfx, $_, 0);
}
- $$dst .= "<a\nid=b></a>"; # anchor for body start
$ctx->{next_msg} = $state->{next_msg};
$ctx->{parent_msg} = $parent;
}
-sub _parent_headers_nosrch {
- my ($hdr) = @_;
+sub _parent_headers {
+ my ($hdr, $srch) = @_;
my $rv = '';
my $irt = in_reply_to($hdr);
@@ -447,6 +432,10 @@ sub _parent_headers_nosrch {
$rv .= "<a\nhref=\"../$href/\">$html</a>>\n";
}
+ # do not display References: if search is present,
+ # we show the thread skeleton at the bottom, instead.
+ return $rv if $srch;
+
my $refs = $hdr->header_raw('References');
if ($refs) {
# avoid redundant URLs wasting bandwidth
@@ -505,7 +494,7 @@ sub mailto_arg_link {
}
sub html_footer {
- my ($mime, $standalone, $full_pfx, $ctx, $mhref) = @_;
+ my ($hdr, $standalone, $full_pfx, $ctx, $mhref) = @_;
my $srch = $ctx->{srch} if $ctx;
my $upfx = $full_pfx ? '../' : '../../';
@@ -517,6 +506,7 @@ sub html_footer {
$idx .= qq{ / follow: <a\nhref="${tpfx}t.atom">Atom feed</a>\n};
}
if ($idx && $srch) {
+ thread_skel(\$idx, $ctx, $hdr, $tpfx);
my $p = $ctx->{parent_msg};
my $next = $ctx->{next_msg};
if ($p) {
@@ -531,11 +521,6 @@ sub html_footer {
} else {
$irt .= ' ' x length('next ');
}
- if ($p || $next) {
- $irt .= "<a\nhref=\"${tpfx}t/#u\">thread</a> ";
- } else {
- $irt .= ' ' x length('thread ');
- }
} else {
$irt = '';
}
@@ -717,7 +702,7 @@ sub _msg_date {
sub fmt_ts { POSIX::strftime('%Y-%m-%d %k:%M', gmtime($_[0])) }
-sub _inline_header {
+sub _skel_header {
my ($dst, $state, $upfx, $hdr, $level) = @_;
my $dot = $level == 0 ? '' : '` ';
@@ -769,13 +754,13 @@ sub _inline_header {
}
}
-sub inline_dump {
+sub skel_dump {
my ($dst, $state, $upfx, $node, $level) = @_;
return unless $node;
if (my $mime = $node->message) {
my $hdr = $mime->header_obj;
my $mid = mid_clean($hdr->header_raw('Message-ID'));
- _inline_header($dst, $state, $upfx, $hdr, $level);
+ _skel_header($dst, $state, $upfx, $hdr, $level);
} else {
my $mid = $node->messageid;
if ($mid eq 'subject dummy') {
@@ -790,8 +775,8 @@ sub inline_dump {
$$dst .= qq{<<a\nhref="$href">$html</a>>\n};
}
}
- inline_dump($dst, $state, $upfx, $node->child, $level+1);
- inline_dump($dst, $state, $upfx, $node->next, $level);
+ skel_dump($dst, $state, $upfx, $node->child, $level+1);
+ skel_dump($dst, $state, $upfx, $node->next, $level);
}
sub sort_ts {
diff --git a/t/view.t b/t/view.t
index 2da741a..1f46476 100644
--- a/t/view.t
+++ b/t/view.t
@@ -47,8 +47,6 @@ EOF
like($html, qr!<a\nhref="\.\./raw"!s, "raw link present");
like($html, qr/hello world\b/, "body present");
like($html, qr/> keep this inline/, "short quoted text is inline");
- like($html, qr/<a\nid=[^>]+><\/a>> Long and wordy/,
- "long quoted text is anchored");
}
# multipart crap
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/4] doc: update design notes on WWW development
2016-04-15 23:33 ` [PATCH 0/4] more quote-folding removal Eric Wong
2016-04-15 23:33 ` [PATCH 1/4] view: drop vestigial elements of quote folding Eric Wong
@ 2016-04-15 23:33 ` Eric Wong
2016-04-15 23:33 ` [PATCH 3/4] www: redirect /$MESSAGE_ID/f/ endpoints Eric Wong
2016-04-15 23:33 ` [PATCH 4/4] view: thread skeleton tweaks Eric Wong
3 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2016-04-15 23:33 UTC (permalink / raw)
To: meta
Start documenting our anchors and CSS classes for in case users
want to write their own CSS or even JavaScript for local usage.
---
Documentation/design_www.txt | 47 +++++++++++++++++++++++++++++++++-----------
1 file changed, 36 insertions(+), 11 deletions(-)
diff --git a/Documentation/design_www.txt b/Documentation/design_www.txt
index 980b2ea..18b716c 100644
--- a/Documentation/design_www.txt
+++ b/Documentation/design_www.txt
@@ -1,5 +1,5 @@
-URL naming
-----------
+URL and anchor naming
+---------------------
### Unstable endpoints
/$LISTNAME/?r=$GIT_COMMIT -> HTML only
@@ -7,12 +7,23 @@ URL naming
#### Optional, relies on Search::Xapian
/$LISTNAME/$MESSAGE_ID/t/ -> HTML content of thread
+ anchors:
+ #u location of $MESSAGE_ID in URL
+ #m<SHA-1> per-message links, where <SHA-1> is of the Message-ID
+ of each message (stable)
+ #s<NUM> relative numeric position of message in thread (unstable)
+
/$LISTNAME/$MESSAGE_ID/t.atom -> Atom feed for thread
/$LISTNAME/$MESSAGE_ID/t.mbox.gz -> gzipped mbox of thread
### Stable endpoints
/$LISTNAME/$MESSAGE_ID/ -> HTML content
-/$LISTNAME/$MESSAGE_ID -> 301 to /$LISTNAME/$MESSAGE_ID
+ anchors:
+ #r location of the current message in thread skeleton
+ (requires Xapian search)
+ #b start of the message body (linked from thread skeleton)
+
+/$LISTNAME/$MESSAGE_ID -> 301 to /$LISTNAME/$MESSAGE_ID/
/$LISTNAME/$MESSAGE_ID/raw -> raw mbox
/$LISTNAME/$MESSAGE_ID/R/ -> HTML reply instructions
@@ -26,7 +37,9 @@ URL naming
/$LISTNAME/atom.xml [2] -> identical to /$LISTNAME/new.atom
-Additionally, we support "git clone" pointed to http://$HOST/$LISTNAME
+Additionally, we support git clone/fetch over HTTP (dumb and smart):
+
+ git clone --mirror http://$HOSTNAME/$LISTNAME
FIXME: we must refactor/cleanup/add tests for most of our CGI before
adding more endpoints and features.
@@ -41,7 +54,8 @@ Encoding notes
--------------
Raw HTML and XML should only contain us-ascii characters which render
-to UTF-8.
+to UTF-8. We must not rely on users having the necessary fonts
+installed to render uncommon characters.
Plain text (raw message) endpoints display in the original encoding(s)
of the original email.
@@ -55,17 +69,19 @@ We also set <title> to make window management easier.
We favor <pre>-formatted text since public-inbox is intended as a place
to share and discuss patches and code. Unfortunately, long paragraphs
tends to be less readable with fixed-width serif fonts which GUI
-browsers default to. So perhaps we will add different endpoints for
-variable-width fonts.
+browsers default to.
* No graphics, images, or icons at all. We tolerate, but do not
encourage the use of GUIs.
* No setting colors or font sizes, power to users to decide those.
+ We will include and document <span class=?> to support colors
+ for user-supplied CSS, and may support client-supplied CSS
+ via cookie.
-* Only one font type (fixed or variable) per page. This is for
- accessibility, we must not blow certain elements out-of-proportion
- when a reader increases font size.
+* Only one font type: fixed. This is for accessibility, we must
+ not blow certain elements out-of-proportion with different
+ fonts on the page when a reader increases font size.
* Bold and underline elements are OK since they should render fine
regardless of chosen font and gracefully degrade if a display does
@@ -80,7 +96,16 @@ variable-width fonts.
* We only use CSS for one reason: wrapping pre-formatted text
This is necessary because unfortunate GUI browsers tend to be
- prone to layout widening. w3m is fine here without CSS :)
+ prone to layout widening from unwrapped mailers.
+ w3m is fine here without CSS :)
No other CSS is allowed, especially with scary things like:
http://thejh.net/misc/website-terminal-copy-paste
+
+ However, we will try to make it easy for users to supply their
+ own colors and perhaps offer color options over cookies.
+
+CSS classes (for user-supplied CSS)
+-----------------------------------
+span.q - quoted text in email messages
+...
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/4] www: redirect /$MESSAGE_ID/f/ endpoints
2016-04-15 23:33 ` [PATCH 0/4] more quote-folding removal Eric Wong
2016-04-15 23:33 ` [PATCH 1/4] view: drop vestigial elements of quote folding Eric Wong
2016-04-15 23:33 ` [PATCH 2/4] doc: update design notes on WWW development Eric Wong
@ 2016-04-15 23:33 ` Eric Wong
2016-04-15 23:33 ` [PATCH 4/4] view: thread skeleton tweaks Eric Wong
3 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2016-04-15 23:33 UTC (permalink / raw)
To: meta
Quote-folding was a major design mistake pre-1.0. Since this
project is still in its infancy and unlikely to be in wide
use at the moment, redirect the /f/ endpoints back to the
plain message.
---
Documentation/design_www.txt | 6 ++++--
lib/PublicInbox/View.pm | 27 +++++++++++++--------------
lib/PublicInbox/WWW.pm | 38 ++++++++++++++++----------------------
t/cgi.t | 8 +++++---
t/plack.t | 32 +++++++++++++++++++-------------
t/view.t | 2 +-
6 files changed, 58 insertions(+), 55 deletions(-)
diff --git a/Documentation/design_www.txt b/Documentation/design_www.txt
index 18b716c..3cf6ea8 100644
--- a/Documentation/design_www.txt
+++ b/Documentation/design_www.txt
@@ -27,12 +27,14 @@ URL and anchor naming
/$LISTNAME/$MESSAGE_ID/raw -> raw mbox
/$LISTNAME/$MESSAGE_ID/R/ -> HTML reply instructions
+# Covering up a pre-1.0 design mistake:
+/$LISTNAME/$MESSAGE_ID/f/ -> 301 to /$LISTNAME/$MESSAGE_ID/
+
### Legacy endpoints (may be ambiguous given Message-IDs with similar suffixes)
-/$LISTNAME/$MESSAGE_ID/f/ -> HTML content
/$LISTNAME/m/$MESSAGE_ID/ -> 301 to /$LISTNAME/$MESSAGE_ID/
/$LISTNAME/m/$MESSAGE_ID.html -> 301 to /$LISTNAME/$MESSAGE_ID/
/$LISTNAME/m/$MESSAGE_ID.txt -> 301 to /$LISTNAME/$MESSAGE_ID/raw
-/$LISTNAME/f/$MESSAGE_ID.html -> 301 to /$LISTNAME/$MESSAGE_ID/f/
+/$LISTNAME/f/$MESSAGE_ID.html -> 301 to /$LISTNAME/$MESSAGE_ID/
/$LISTNAME/f/$MESSAGE_ID.txt [1] -> 301 to /$LISTNAME/$MESSAGE_ID/raw
/$LISTNAME/atom.xml [2] -> identical to /$LISTNAME/new.atom
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index 2bf7cd5..ac44d44 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -22,13 +22,13 @@ my $enc_utf8 = find_encoding('UTF-8');
# public functions:
sub msg_html {
- my ($ctx, $mime, $full_pfx, $footer) = @_;
+ my ($ctx, $mime, $footer) = @_;
$footer = defined($footer) ? "\n$footer" : '';
my $hdr = $mime->header_obj;
- headers_to_html_header($hdr, $full_pfx, $ctx) .
- multipart_text_as_html($mime, $full_pfx) .
+ headers_to_html_header($hdr, $ctx) .
+ multipart_text_as_html($mime) .
'</pre><hr /><pre>' .
- html_footer($hdr, 1, $full_pfx, $ctx) .
+ html_footer($hdr, 1, $ctx) .
$footer .
'</pre></body></html>';
}
@@ -72,11 +72,10 @@ sub msg_reply {
}
sub feed_entry {
- my ($class, $mime, $full_pfx) = @_;
+ my ($class, $mime) = @_;
# no <head> here for <style>...
- PublicInbox::Hval::PRE .
- multipart_text_as_html($mime, $full_pfx) . '</pre>';
+ PublicInbox::Hval::PRE . multipart_text_as_html($mime) . '</pre>';
}
sub in_reply_to {
@@ -138,7 +137,7 @@ sub index_entry {
index_walk($fh, $_[0], $enc, \$part_nr);
});
$mime->body_set('');
- $rv = "\n" . html_footer($hdr, 0, undef, $ctx, $mhref);
+ $rv = "\n" . html_footer($hdr, 0, $ctx, $mhref);
if (defined $irt) {
unless (defined $parent_anchor) {
@@ -246,7 +245,7 @@ sub enc_for {
}
sub multipart_text_as_html {
- my ($mime, $full_pfx, $srch) = @_;
+ my ($mime) = @_;
my $rv = "";
my $part_nr = 0;
my $enc = enc_for($mime->header("Content-Type"));
@@ -335,11 +334,11 @@ sub add_text_body {
}
sub headers_to_html_header {
- my ($hdr, $full_pfx, $ctx) = @_;
+ my ($hdr, $ctx) = @_;
my $srch = $ctx->{srch} if $ctx;
my $atom = '';
my $rv = '';
- my $upfx = $full_pfx ? '' : '../';
+ my $upfx = '';
if ($srch) {
$atom = qq{<link\nrel=alternate\ntitle="Atom feed"\n} .
@@ -494,11 +493,11 @@ sub mailto_arg_link {
}
sub html_footer {
- my ($hdr, $standalone, $full_pfx, $ctx, $mhref) = @_;
+ my ($hdr, $standalone, $ctx, $mhref) = @_;
my $srch = $ctx->{srch} if $ctx;
- my $upfx = $full_pfx ? '../' : '../../';
- my $tpfx = $full_pfx ? '' : '../';
+ my $upfx = '../';
+ my $tpfx = '';
my $idx = $standalone ? " <a\nhref=\"$upfx\">index</a>" : '';
my $irt = '';
diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm
index bb54aaa..ce00e34 100644
--- a/lib/PublicInbox/WWW.pm
+++ b/lib/PublicInbox/WWW.pm
@@ -22,7 +22,7 @@ require PublicInbox::Git;
use PublicInbox::GitHTTPBackend;
our $LISTNAME_RE = qr!\A/([\w\.\-]+)!;
our $MID_RE = qr!([^/]+)!;
-our $END_RE = qr!(f/|T/|t/|R/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
+our $END_RE = qr!(T/|t/|R/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
sub new {
my ($class, $pi_config) = @_;
@@ -72,11 +72,14 @@ sub call {
msg_page($self, $ctx, $1, $2, $3);
# in case people leave off the trailing slash:
- } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/(f|T|t|R)\z!o) {
+ } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/(T|t|R)\z!o) {
my ($listname, $mid, $suffix) = ($1, $2, $3);
$suffix .= $suffix =~ /\A[tT]\z/ ? '/#u' : '/';
r301($ctx, $listname, $mid, $suffix);
+ } elsif ($path_info =~ m!$LISTNAME_RE/$MID_RE/f/?\z!o) {
+ r301($ctx, $1, $2);
+
# convenience redirects order matters
} elsif ($path_info =~ m!$LISTNAME_RE/([^/]{2,})\z!o) {
r301($ctx, $1, $2);
@@ -202,21 +205,7 @@ sub get_mid_html {
my $mime = Email::MIME->new($x);
searcher($ctx);
[ 200, [ 'Content-Type' => 'text/html; charset=UTF-8' ],
- [ PublicInbox::View::msg_html($ctx, $mime, 'f/', $foot) ] ];
-}
-
-# /$LISTNAME/$MESSAGE_ID/f/ -> HTML content (fullquotes)
-sub get_full_html {
- my ($ctx) = @_;
- my $x = mid2blob($ctx) or return r404($ctx);
-
- require PublicInbox::View;
- my $foot = footer($ctx);
- require Email::MIME;
- my $mime = Email::MIME->new($x);
- searcher($ctx);
- [ 200, [ 'Content-Type' => 'text/html; charset=UTF-8' ],
- [ PublicInbox::View::msg_html($ctx, $mime, undef, $foot)] ];
+ [ PublicInbox::View::msg_html($ctx, $mime, $foot) ] ];
}
# /$LISTNAME/$MESSAGE_ID/R/ -> HTML content (fullquotes)
@@ -354,7 +343,7 @@ sub legacy_redirects {
r301($ctx, $1, $2, 'raw');
} elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)/\z!o) {
- r301($ctx, $1, $2, 'f/');
+ r301($ctx, $1, $2);
# thread display
} elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)/\z!o) {
@@ -371,7 +360,7 @@ sub legacy_redirects {
r301($ctx, $1, $2, 't/#u');
} elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)\.html\z!o) {
- r301($ctx, $1, $2, 'f/');
+ r301($ctx, $1, $2);
} elsif ($path_info =~ m!$LISTNAME_RE/(?:m|f)/(\S+)\.txt\z!o) {
r301($ctx, $1, $2, 'raw');
@@ -385,7 +374,7 @@ sub legacy_redirects {
} elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)\z!o) {
r301($ctx, $1, $2, 't/#u');
} elsif ($path_info =~ m!$LISTNAME_RE/f/(\S+)\z!o) {
- r301($ctx, $1, $2, 'f/');
+ r301($ctx, $1, $2);
# some Message-IDs have slashes in them and the HTTP server
# may try to be clever and unescape them :<
@@ -393,8 +382,10 @@ sub legacy_redirects {
msg_page($self, $ctx, $1, $2, $3);
# in case people leave off the trailing slash:
- } elsif ($path_info =~ m!$LISTNAME_RE/(\S+/\S+)/(f|T|t)\z!o) {
+ } elsif ($path_info =~ m!$LISTNAME_RE/(\S+/\S+)/(T|t)\z!o) {
r301($ctx, $1, $2, $3 eq 't' ? 't/#u' : $3);
+ } elsif ($path_info =~ m!$LISTNAME_RE/(\S+/\S+)/f\z!o) {
+ r301($ctx, $1, $2);
} else {
$self->news_www->call($ctx->{cgi}->{env});
}
@@ -426,7 +417,10 @@ sub msg_page {
't.mbox.gz' eq $e and return get_thread_mbox($ctx, '.gz');
'T/' eq $e and return get_thread($ctx, 1);
'raw' eq $e and return get_mid_txt($ctx);
- 'f/' eq $e and return get_full_html($ctx);
+
+ # legacy, but no redirect for compatibility:
+ 'f/' eq $e and return get_mid_html($ctx);
+
'R/' eq $e and return get_reply_html($ctx);
r404($ctx);
}
diff --git a/t/cgi.t b/t/cgi.t
index f1a2730..d7e3ac5 100644
--- a/t/cgi.t
+++ b/t/cgi.t
@@ -188,9 +188,11 @@ EOF
like($res->{head}, qr/Status: 300 Multiple Choices/, "mid html miss");
$res = cgi_run("/test/blahblah\@example.com/f/");
- like($res->{body}, qr/\A<html>/, "mid html");
- like($res->{head}, qr/Status: 200 OK/, "200 response");
- $res = cgi_run("/test/blahblah\@example.con/f/");
+ like($res->{head}, qr/Status: 301 Moved/, "301 response");
+ like($res->{head},
+ qr!^Location: http://[^/]+/test/blahblah%40example\.com/\r\n!ms,
+ '301 redirect location');
+ $res = cgi_run("/test/blahblah\@example.con/");
like($res->{head}, qr/Status: 300 Multiple Choices/, "mid html miss");
$res = cgi_run("/test/");
diff --git a/t/plack.t b/t/plack.t
index 568f09f..1ae5873 100644
--- a/t/plack.t
+++ b/t/plack.t
@@ -98,9 +98,9 @@ EOF
my ($cb) = @_;
my $u = $pfx . "/blah%40example.com/$t";
my $res = $cb->(GET($u));
- is(301, $res->code, "redirect for missing /");
+ is(301, $res->code, "redirect for legacy /f");
my $location = $res->header('Location');
- like($location, qr!/\Q$t\E/\z!,
+ like($location, qr!/blah%40example\.com/\z!,
'redirected with missing /');
});
}
@@ -125,16 +125,22 @@ EOF
'atom feed generated correct URL');
});
- foreach my $t (('', 'f/')) {
- test_psgi($app, sub {
- my ($cb) = @_;
- my $path = "/blah%40example.com/$t";
- my $res = $cb->(GET($pfx . $path));
- is(200, $res->code, "success for $path");
- like($res->content, qr!<title>hihi - Me</title>!,
- "HTML returned");
- });
- }
+ test_psgi($app, sub {
+ my ($cb) = @_;
+ my $path = '/blah%40example.com/';
+ my $res = $cb->(GET($pfx . $path));
+ is(200, $res->code, "success for $path");
+ like($res->content, qr!<title>hihi - Me</title>!,
+ "HTML returned");
+
+ $path .= 'f/';
+ $res = $cb->(GET($pfx . $path));
+ is(301, $res->code, "redirect for $path");
+ my $location = $res->header('Location');
+ like($location, qr!/blah%40example\.com/\z!,
+ '/$MESSAGE_ID/f/ redirected to /$MESSAGE_ID/');
+ });
+
test_psgi($app, sub {
my ($cb) = @_;
my $res = $cb->(GET($pfx . '/blah%40example.com/raw'));
@@ -156,7 +162,7 @@ EOF
my %umap = (
'm' => '',
- 'f' => 'f/',
+ 'f' => '',
't' => 't/',
);
while (my ($t, $e) = each %umap) {
diff --git a/t/view.t b/t/view.t
index 1f46476..1a47416 100644
--- a/t/view.t
+++ b/t/view.t
@@ -44,7 +44,7 @@ EOF
my $html = PublicInbox::View::msg_html(undef, $mime);
# ghetto tests
- like($html, qr!<a\nhref="\.\./raw"!s, "raw link present");
+ like($html, qr!<a\nhref="raw"!s, "raw link present");
like($html, qr/hello world\b/, "body present");
like($html, qr/> keep this inline/, "short quoted text is inline");
}
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/4] view: thread skeleton tweaks
2016-04-15 23:33 ` [PATCH 0/4] more quote-folding removal Eric Wong
` (2 preceding siblings ...)
2016-04-15 23:33 ` [PATCH 3/4] www: redirect /$MESSAGE_ID/f/ endpoints Eric Wong
@ 2016-04-15 23:33 ` Eric Wong
3 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2016-04-15 23:33 UTC (permalink / raw)
To: meta
Allow the Subject: <-> skeleton line to point to each other so
the reader can bounce around between them without refocusing
their browser.
---
lib/PublicInbox/View.pm | 29 ++++++++++++-----------------
1 file changed, 12 insertions(+), 17 deletions(-)
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index ac44d44..f07979e 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -379,19 +379,20 @@ sub headers_to_html_header {
}
sub thread_skel {
- my ($dst, $ctx, $hdr, $upfx) = @_;
+ my ($dst, $ctx, $hdr, $tpfx) = @_;
my $srch = $ctx->{srch};
my $mid = mid_clean($hdr->header_raw('Message-ID'));
my $sres = $srch->get_thread($mid);
my $nr = $sres->{total};
- my $expand = "<a\nhref=\"${upfx}t/#u\">expand</a> " .
- "/ <a\nhref=\"${upfx}t.mbox.gz\">mbox.gz</a>";
+ my $expand = qq(<a\nhref="${tpfx}t/#u">expand</a> ) .
+ qq(/ <a\nhref="${tpfx}t.mbox.gz">mbox.gz</a> ) .
+ qq(/ <a\nhref="${tpfx}t.atom">Atom feed</a>);
my $parent = in_reply_to($hdr);
if ($nr <= 1) {
if (defined $parent) {
$$dst .= "($expand)\n ";
- $$dst .= ghost_parent("$upfx../", $parent) . "\n";
+ $$dst .= ghost_parent("$tpfx../", $parent) . "\n";
} else {
$$dst .= "[no followups, yet] ($expand)\n";
}
@@ -412,7 +413,7 @@ sub thread_skel {
prev_level => 0,
};
for (thread_results(load_results($sres))->rootset) {
- skel_dump($dst, $state, $upfx, $_, 0);
+ skel_dump($dst, $state, $tpfx, $_, 0);
}
$ctx->{next_msg} = $state->{next_msg};
$ctx->{parent_msg} = $parent;
@@ -500,11 +501,8 @@ sub html_footer {
my $tpfx = '';
my $idx = $standalone ? " <a\nhref=\"$upfx\">index</a>" : '';
my $irt = '';
-
- if ($srch && $standalone) {
- $idx .= qq{ / follow: <a\nhref="${tpfx}t.atom">Atom feed</a>\n};
- }
if ($idx && $srch) {
+ $idx .= "\n";
thread_skel(\$idx, $ctx, $hdr, $tpfx);
my $p = $ctx->{parent_msg};
my $next = $ctx->{next_msg};
@@ -709,7 +707,7 @@ sub _skel_header {
my $mid = mid_clean($hdr->header_raw('Message-ID'));
my $f = ascii_html($hdr->header('X-PI-From'));
my $d = _msg_date($hdr);
- my $pfx = ' ' . $d . ' ' . indent_for($level);
+ my $pfx = $d . ' ' . indent_for($level);
my $attr = $f;
$state->{first_level} ||= $level;
@@ -723,7 +721,7 @@ sub _skel_header {
if ($cur) {
if ($cur eq $mid) {
delete $state->{cur};
- $$dst .= "$pfx$dot<b><a\nid=r\nhref=\"#b\">".
+ $$dst .= "$pfx$dot<b><a\nid=r\nhref=\"#t\">".
"$attr [this message]</a></b>\n";
return;
@@ -746,11 +744,8 @@ sub _skel_header {
}
my $m = PublicInbox::Hval->new_msgid($mid);
$m = $upfx . '../' . $m->as_href . '/';
- if (defined $s) {
- $$dst .= "$pfx$dot<a\nhref=\"$m\">$s</a> $attr\n";
- } else {
- $$dst .= "$pfx$dot<a\nhref=\"$m\">$f</a>\n";
- }
+ $$dst .= "$pfx$dot<a\nhref=\"$m\">";
+ $$dst .= defined($s) ? "$s</a> $f\n" : "$f</a>\n";
}
sub skel_dump {
@@ -765,7 +760,7 @@ sub skel_dump {
if ($mid eq 'subject dummy') {
$$dst .= "\t[no common parent]\n";
} else {
- $$dst .= ' [not found] ';
+ $$dst .= ' [not found] ';
my $dot = $level == 0 ? '' : '` ';
$$dst .= indent_for($level) . $dot;
$mid = PublicInbox::Hval->new_msgid($mid);
--
EW
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2016-04-15 23:33 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-13 3:04 [PATCH] www: stop generating /$MESSAGE_ID/f/ links Eric Wong
2016-04-15 23:33 ` [PATCH 0/4] more quote-folding removal Eric Wong
2016-04-15 23:33 ` [PATCH 1/4] view: drop vestigial elements of quote folding Eric Wong
2016-04-15 23:33 ` [PATCH 2/4] doc: update design notes on WWW development Eric Wong
2016-04-15 23:33 ` [PATCH 3/4] www: redirect /$MESSAGE_ID/f/ endpoints Eric Wong
2016-04-15 23:33 ` [PATCH 4/4] view: thread skeleton tweaks Eric Wong
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).