about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2015-08-16 08:14:40 +0000
committerEric Wong <e@80x24.org>2015-08-16 08:19:53 +0000
commiteb5f82b20944d780ac3b2ff9a926c023da9468fd (patch)
tree993d57db192d18dd46216adc66158135e43860d7 /lib
parent7745ac38a4e2d4ae42e7192183f65c84a8e5662a (diff)
downloadpublic-inbox-eb5f82b20944d780ac3b2ff9a926c023da9468fd.tar.gz
Quick-and-dirty wiring up of to Subject: paths.
This may prove more memorizable and easier-to-share than
/t/$MESSAGE_ID.html links, but less strict.

This changes our schema version to 1, since we now
use lower-case subject paths.
Diffstat (limited to 'lib')
-rw-r--r--lib/PublicInbox/Search.pm15
-rw-r--r--lib/PublicInbox/View.pm72
-rw-r--r--lib/PublicInbox/WWW.pm20
3 files changed, 93 insertions, 14 deletions
diff --git a/lib/PublicInbox/Search.pm b/lib/PublicInbox/Search.pm
index 39b06b0a..f4f00b25 100644
--- a/lib/PublicInbox/Search.pm
+++ b/lib/PublicInbox/Search.pm
@@ -12,7 +12,10 @@ use PublicInbox::MID qw/mid_clean mid_compressed/;
 
 use constant {
         TS => 0,
-        SCHEMA_VERSION => 0,
+        # SCHEMA_VERSION history
+        # 0 - initial
+        # 1 - subject_path is lower-cased
+        SCHEMA_VERSION => 1,
         LANG => 'english',
         QP_FLAGS => FLAG_PHRASE|FLAG_BOOLEAN|FLAG_LOVEHATE|FLAG_WILDCARD,
 };
@@ -209,6 +212,12 @@ sub query {
         $self->do_enquire($query, $opts);
 }
 
+sub get_subject_path {
+        my ($self, $path, $opts) = @_;
+        my $query = $self->qp->parse_query("path:$path", 0);
+        $self->do_enquire($query);
+}
+
 # given a message ID, get replies to a message
 sub get_replies {
         my ($self, $mid, $opts) = @_;
@@ -461,13 +470,13 @@ sub merge_threads {
 
 # normalize subjects so they are suitable as pathnames for URLs
 sub subject_path {
-        my ($subj) = @_;
+        my $subj = pop;
 
         $subj =~ s/\A\s+//;
         $subj =~ s/\s+\z//;
         $subj =~ s/^(?:re|aw):\s*//i; # remove reply prefix (aw: German)
         $subj =~ s![^a-zA-Z0-9_\.~/\-]+!_!g;
-        $subj;
+        lc($subj);
 }
 
 sub do_cat_mail {
diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm
index c40a2a75..696d7d5a 100644
--- a/lib/PublicInbox/View.pm
+++ b/lib/PublicInbox/View.pm
@@ -31,7 +31,7 @@ sub msg_html {
         } else {
                 $footer = '';
         }
-        headers_to_html_header($mime, $full_pfx) .
+        headers_to_html_header($mime, $full_pfx, $srch) .
                 multipart_text_as_html($mime, $full_pfx) .
                 '</pre><hr />' . PRE_WRAP .
                 html_footer($mime, 1, $full_pfx, $srch) . $footer .
@@ -179,6 +179,52 @@ sub thread_html {
         $rv .= "</pre><hr />" . PRE_WRAP . $next . $foot . "</pre>";
 }
 
+sub subject_path_html {
+        my (undef, $ctx, $foot, $srch) = @_;
+        my $path = $ctx->{subject_path};
+        my $res = $srch->get_subject_path($path);
+        my $rv = '';
+        require PublicInbox::GitCatFile;
+        my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+        my $nr = scalar @{$res->{msgs}};
+        return $rv if $nr == 0;
+        my @msgs;
+        while (my $smsg = shift @{$res->{msgs}}) {
+                my $m = $smsg->mid;
+
+                # Duplicated from WWW.pm
+                my ($x2, $x38) = ($m =~ /\A([a-f0-9]{2})([a-f0-9]{38})\z/);
+
+                unless (defined $x38) {
+                        require Digest::SHA;
+                        $m = Digest::SHA::sha1_hex($m);
+                        ($x2, $x38) = ($m =~ /\A([a-f0-9]{2})([a-f0-9]{38})\z/);
+                }
+
+                # FIXME: duplicated code from Feed.pm
+                my $mime = eval {
+                        my $str = $git->cat_file("HEAD:$x2/$x38");
+                        Email::MIME->new($str);
+                };
+                unless ($@) {
+                        my $t = eval { str2time($mime->header('Date')) };
+                        defined($t) or $t = 0;
+                        $mime->header_set('X-PI-TS', $t);
+                        push @msgs, $mime;
+                }
+        }
+        require PublicInbox::Thread;
+        my $th = PublicInbox::Thread->new(@msgs);
+        $th->thread;
+        $th->order(*PublicInbox::Thread::sort_ts);
+        my $state = [ undef, { root_anchor => 'dummy' }, undef, 0 ];
+        thread_entry(\$rv, $state, $_, 0) for $th->rootset;
+        my $final_anchor = $state->[3];
+        my $next = "<a\nid=\"s$final_anchor\">end of thread</a>\n";
+
+        $rv .= "</pre><hr />" . PRE_WRAP . $next . $foot . "</pre>";
+}
+
 # only private functions below.
 
 sub index_walk {
@@ -235,7 +281,7 @@ sub enc_for {
 }
 
 sub multipart_text_as_html {
-        my ($mime, $full_pfx) = @_;
+        my ($mime, $full_pfx, $srch) = @_;
         my $rv = "";
         my $part_nr = 0;
         my $enc_msg = enc_for($mime->header("Content-Type"));
@@ -339,7 +385,7 @@ sub add_text_body_full {
 }
 
 sub headers_to_html_header {
-        my ($mime, $full_pfx) = @_;
+        my ($mime, $full_pfx, $srch) = @_;
 
         my $rv = "";
         my @title;
@@ -347,18 +393,21 @@ sub headers_to_html_header {
                 my $v = $mime->header($h);
                 defined($v) && length($v) or next;
                 $v = PublicInbox::Hval->new_oneline($v);
-                $rv .= "$h: " . $v->as_html . "\n";
 
                 if ($h eq 'From') {
                         my @from = Email::Address->parse($v->raw);
-                        $v = $from[0]->name;
-                        unless (defined($v) && length($v)) {
-                                $v = '<' . $from[0]->address . '>';
-                        }
-                        $title[1] = ascii_html($v);
+                        $title[1] = ascii_html($from[0]->name);
                 } elsif ($h eq 'Subject') {
                         $title[0] = $v->as_html;
+                        if ($srch) {
+                                my $path = $srch->subject_path($v->raw);
+                                $rv .= "$h: <a\nhref=\"../s/$path.html\">";
+                                $rv .= $v->as_html . "</a>\n";
+                                next;
+                        }
                 }
+                $rv .= "$h: " . $v->as_html . "\n";
+
         }
 
         my $header_obj = $mime->header_obj;
@@ -510,6 +559,9 @@ sub hash_subj {
 sub thread_replies {
         my ($dst, $root, $res) = @_;
         my @msgs = map { $_->mini_mime } @{$res->{msgs}};
+        foreach (@{$res->{msgs}}) {
+                print STDERR "smsg->path: <", $_->path, ">\n";
+        }
         require PublicInbox::Thread;
         $root->header_set('X-PI-TS', '0');
         my $th = PublicInbox::Thread->new($root, @msgs);
@@ -532,7 +584,7 @@ sub thread_entry {
         my ($dst, $state, $node, $level) = @_;
         # $state = [ $search_res, $seen, undef, 0 (msg_nr) ];
         # $seen is overloaded with 3 types of fields:
-        #        1) "root" => Message-ID,
+        #        1) "root_anchor" => anchor_for(Message-ID),
         #        2) seen subject hashes: sha1(subject) => 1
         #        3) anchors hashes: "#$sha1_hex" (same as $seen in index_entry)
         if (my $mime = $node->message) {
diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm
index 52e51c43..7fe9b85b 100644
--- a/lib/PublicInbox/WWW.pm
+++ b/lib/PublicInbox/WWW.pm
@@ -53,8 +53,13 @@ sub run {
         } elsif ($path_info =~ m!$LISTNAME_RE/t/(\S+)\.html\z!o) {
                 invalid_list_mid(\%ctx, $1, $2) || get_thread(\%ctx, $cgi);
 
+        # subject_path display
+        } elsif ($path_info =~ m!$LISTNAME_RE/s/(\S+)\.html\z!o) {
+                my $sp = $2;
+                invalid_list(\%ctx, $1) || get_subject_path(\%ctx, $cgi, $sp);
+
         # convenience redirects, order matters
-        } elsif ($path_info =~ m!$LISTNAME_RE/(m|f|t)/(\S+)\z!o) {
+        } elsif ($path_info =~ m!$LISTNAME_RE/(m|f|t|s)/(\S+)\z!o) {
                 my $pfx = $2;
                 invalid_list_mid(\%ctx, $1, $3) ||
                         redirect_mid(\%ctx, $cgi, $2);
@@ -211,6 +216,19 @@ sub get_thread {
           [ $body ] ];
 }
 
+# /$LISTNAME/s/$SUBJECT_PATH.html
+sub get_subject_path {
+        my ($ctx, $cgi, $sp) = @_;
+        $ctx->{subject_path} = $sp;
+        my $srch = searcher($ctx) or return need_search($ctx);
+        require PublicInbox::View;
+        my $foot = footer($ctx);
+        my $body = PublicInbox::View->subject_path_html($ctx, $foot, $srch) or
+                return r404();
+        [ 200, [ 'Content-Type' => 'text/html; charset=UTF-8' ],
+          [ $body ] ];
+}
+
 sub self_url {
         my ($cgi) = @_;
         ref($cgi) eq 'CGI' ? $cgi->self_url : $cgi->uri->as_string;