Changeset 1947

Show
Ignore:
Timestamp:
04/17/08 07:14:54 (15 months ago)
Author:
fumiakiy
Message:

Implemented comments retrieval via Atom PP. BugId:79334

Location:
branches/release-35
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • branches/release-35/lib/MT/Atom.pm

    r1823 r1947  
    7272    my ($host) = $blog->site_url =~ m!^https?://([^/:]+)(:\d+)?/!; 
    7373 
     74    unless ( $entry->atom_id ) { 
     75        # atom_id is not there - probably because 
     76        # the entry is in HOLD state 
     77        $entry->atom_id($entry->make_atom_id()); 
     78        # call update directly because MT::Entry::save 
     79        # is overkill for the purpose. 
     80        $entry->update if $entry->atom_id(); 
     81    } 
     82 
    7483    $atom->id($entry->atom_id); 
    7584    #$atom->draft('true') if $entry->status != MT::Entry::RELEASE(); 
     
    94103}  
    95104 
     105sub new_with_comment { 
     106    my $class = shift; 
     107    my ( $comment, %param ) = @_; 
     108    my $rfc_compat = $param{Version} && $param{Version} eq '1'; 
     109 
     110    my $entry = $comment->entry; 
     111    return unless $entry; 
     112    my $blog = $comment->blog; 
     113    return unless $blog; 
     114 
     115    my $atom = $class->new(%param); 
     116    $atom->title(encode_text($entry->title, undef, 'utf-8')); 
     117    $atom->content(encode_text($comment->text, undef, 'utf-8')); 
     118    # Old Atom API gets application/xhtml+xml for compatibility -- but why 
     119    # do we say it's that when all we're guaranteed is it's an opaque blob 
     120    # of text? So use 'html' for new RFC compatible output. 
     121    # XML::Atom::Content intelligently determines content-type for rfc compat. 
     122    unless ( $rfc_compat ) { 
     123        $atom->content->type('application/xhtml+xml'); 
     124    } 
     125 
     126    my $atom_author = new XML::Atom::Person(%param); 
     127    $atom_author->name(encode_text($comment->author, undef, 'utf-8')); 
     128    $atom_author->email($comment->email) if $comment->email; 
     129    my $author_url_field = $rfc_compat ? 'uri' : 'url'; 
     130    $atom_author->$author_url_field($comment->url) if $comment->url; 
     131    $atom->author($atom_author); 
     132 
     133    my $co = _create_issued($comment->created_on, $blog); 
     134    $atom->issued($co); 
     135    my $upd = $comment->modified_on; 
     136    if ( $upd ) { 
     137        $atom->updated( _create_issued( $upd, $blog ) ); 
     138    } 
     139    else { 
     140        $atom->updated( $co ); 
     141    } 
     142    $atom->add_link({ rel => 'alternate', type => 'text/html', 
     143                      href => $entry->archive_url . '#comment-' . $comment->id }); 
     144    my ($host) = $blog->site_url =~ m!^https?://([^/:]+)(:\d+)?/!; 
     145 
     146    $atom->id($entry->atom_id . '/' . $comment->id); 
     147    $atom; 
     148} 
     149 
    961501; 
    97151__END__ 
  • branches/release-35/lib/MT/AtomServer.pm

    r1915 r1947  
    466466 
    467467        my $e_title = XML::XPath::Node::Element->new('atom:title', 'atom'); 
    468         $e_title->appendChild(XML::XPath::Node::Text->new(MT->translate('Entries'))); 
     468        $e_title->appendChild(XML::XPath::Node::Text->new(MT->translate('[_1]: Entries', $blog->name))); 
    469469        $entries->appendChild($e_title); 
    470470 
     
    563563        my $pub_ts = MT::Util::iso2ts($blog, $iso); 
    564564        $entry->authored_on($pub_ts); 
     565        require MT::DateTime; 
    565566        if ( 0 < MT::DateTime->compare( blog => $blog, 
    566567                a => $pub_ts, 
     
    654655        my $pub_ts = MT::Util::iso2ts($blog, $iso); 
    655656        $entry->authored_on($pub_ts); 
     657        require MT::DateTime; 
    656658        if ( 0 < MT::DateTime->compare( blog => $blog, 
    657659                a => $pub_ts, 
     
    727729        $e->add_link({ rel => $app->edit_link_rel, type => $app->atom_x_content_type, 
    728730                       href => ($uri . $entry->id), title => encode_text($entry->title, undef,'utf-8') }); 
     731        $e->add_link({ rel => 'replies', type => $app->atom_x_content_type, 
     732                href => $app->base . $app->app_path . $app->config->AtomScript . '/comments/blog_id=' . $blog->id . '/entry_id=' . $entry->id }); 
     733 
    729734        # feed/updated should be added before entries 
    730735        # so we postpone adding them until later 
     
    738743    $feed->add_entry($_) foreach @entries; 
    739744    ## xxx add next/prev links 
     745    $app->run_callbacks( 'get_posts', $feed, $blog ); 
    740746    $app->response_content_type($app->atom_content_type); 
    741747    $feed->as_xml; 
     
    756762    $uri .= '/entry_id='; 
    757763    $atom->add_link({ rel => $app->edit_link_rel, type => $app->atom_x_content_type, 
    758                       href => ($uri . $entry->id), title => encode_text($entry->title, undef,'utf-8') }); 
     764        href => ($uri . $entry->id), title => encode_text($entry->title, undef,'utf-8') }); 
     765    $atom->add_link({ rel => 'replies', type => $app->atom_x_content_type, 
     766        href => $app->base 
     767            . $app->app_path 
     768            . $app->config->AtomScript 
     769            . '/comments/blog_id=' . $blog->id 
     770            . '/entry_id=' . $entry->id 
     771    }); 
     772    $app->run_callbacks( 'get_post', $atom, $entry ); 
     773    $app->response_content_type($app->atom_content_type); 
    759774    $atom->as_xml; 
    760775} 
     
    903918use XML::Atom;  # for LIBXML 
    904919use XML::Atom::Feed; 
    905 use base qw( MT::AtomServer ); 
    906920use MT::Blog; 
    907921use MT::Permission; 
     
    10111025} 
    10121026 
     1027package MT::AtomServer::Comments; 
     1028use strict; 
     1029 
     1030use base qw( MT::AtomServer::Weblog ); 
     1031use MT::I18N qw( encode_text ); 
     1032 
     1033sub script { $_[0]->{cfg}->AtomScript . '/comments' } 
     1034 
     1035sub handle_request { 
     1036    my $app = shift; 
     1037    $app->authenticate || return; 
     1038    if (my $svc = $app->{param}{svc}) { 
     1039        if ($svc eq 'upload') { 
     1040            return $app->handle_upload; 
     1041        } elsif ($svc eq 'categories') { 
     1042            return $app->get_categories; 
     1043        } 
     1044    } 
     1045    my $method = $app->request_method; 
     1046    if ($method eq 'POST') { 
     1047#        return $app->new_comment; 
     1048    } elsif ($method eq 'PUT') { 
     1049#        return $app->edit_comment; 
     1050    } elsif ($method eq 'DELETE') { 
     1051#        return $app->delete_comment; 
     1052    } elsif ($method eq 'GET') { 
     1053        if ($app->{param}{comment_id}) { 
     1054            return $app->get_comment; 
     1055        } elsif ($app->{param}{entry_id}) { 
     1056            return $app->get_comments; 
     1057        } else { 
     1058            return $app->get_blog_comments; 
     1059        } 
     1060    } 
     1061} 
     1062 
     1063sub new_with_comment { 
     1064    my $app = shift; 
     1065    my ($comment) = @_; 
     1066    my $atom = MT::Atom::Entry->new_with_comment( $comment, Version => 1.0 ); 
     1067 
     1068    my $mo = MT::Atom::Entry::_create_issued( 
     1069        $comment->modified_on || $comment->created_on, $comment->blog); 
     1070    $atom->set(MT::AtomServer::Weblog::NS_APP(), 'edited', $mo); 
     1071 
     1072    $atom; 
     1073} 
     1074 
     1075sub get_comment { 
     1076    my $app = shift; 
     1077    my $blog = $app->{blog}; 
     1078    my $comment_id = $app->{param}{comment_id} 
     1079        or return $app->error(400, "No comment_id"); 
     1080    my $comment = MT::Comment->load($comment_id) 
     1081        or return $app->error(400, "Invalid comment_id"); 
     1082    my $entry = $comment->entry; 
     1083    my $uri = $app->base . $app->uri . '/blog_id=' . $blog->id; 
     1084    my $c = $app->new_with_comment($comment); 
     1085    $c->add_link({ rel => 'self', type => $app->atom_x_content_type, 
     1086                   href => $uri . '/comment_id=' . $comment->id }); 
     1087    # feed/updated should be added before entries 
     1088    # so we postpone adding them until later 
     1089    $c->set('http://purl.org/syndication/thread/1.0', 'in-reply-to', 
     1090        undef, 
     1091        { ref => $entry->atom_id, 
     1092            type => 'text/html', 
     1093            href => $entry->permalink } ); 
     1094    $app->run_callbacks( 'get_comment', $c, $comment ); 
     1095    $app->response_content_type($app->atom_content_type); 
     1096    $c->as_xml; 
     1097} 
     1098 
     1099sub get_blog_comments { 
     1100    my $app = shift; 
     1101    my $blog = $app->{blog}; 
     1102    my %terms = (blog_id => $blog->id, visible => 1); 
     1103    my %arg = (sort => $app->get_posts_order_field, direction => 'descend'); 
     1104    my $Limit = 20; 
     1105    $arg{limit} = $Limit + 1; 
     1106    $arg{offset} = $app->{param}{offset} || 0; 
     1107 
     1108    my $feed = $app->new_feed(); 
     1109    my $uri = $app->base . $app->uri . '/blog_id=' . $blog->id; 
     1110    my $blogname = encode_text($blog->name, undef, 'utf-8'); 
     1111    $feed->add_link({ rel => 'alternate', type => 'text/html', 
     1112                      href => $blog->site_url }); 
     1113    $feed->add_link({ rel => 'self', type => $app->atom_x_content_type, 
     1114                      href => $uri }); 
     1115    $feed->title($blogname); 
     1116 
     1117    require URI; 
     1118    my $site_uri = URI->new($blog->site_url); 
     1119    if ( $site_uri ) { 
     1120        my $blog_created = MT::Util::format_ts('%Y-%m-%d', $blog->created_on, $blog, 'en', 0); 
     1121        my $id = 'tag:'.$site_uri->host.','.$blog_created.':'.$site_uri->path.'/'.$blog->id; 
     1122        $feed->id($id); 
     1123    } 
     1124    $app->_comments_in_atom($feed, \%terms, \%arg); 
     1125    $app->run_callbacks( 'get_blog_comments', $feed, $blog ); 
     1126    ## xxx add next/prev links 
     1127    $app->response_content_type($app->atom_content_type); 
     1128    $feed->as_xml; 
     1129} 
     1130 
     1131sub get_comments { 
     1132    my $app = shift; 
     1133    my $blog = $app->{blog}; 
     1134    my $entry_id = $app->{param}{entry_id} 
     1135        or return $app->error(400, "No entry_id"); 
     1136    my $entry = MT::Entry->load($entry_id) 
     1137        or return $app->error(400, "Invalid entry_id"); 
     1138    my %terms = (blog_id => $blog->id, entry_id => $entry->id, visible => 1); 
     1139    my %arg = (sort => $app->get_posts_order_field, direction => 'descend'); 
     1140    my $Limit = 20; 
     1141    $arg{limit} = $Limit + 1; 
     1142    $arg{offset} = $app->{param}{offset} || 0; 
     1143 
     1144    my $feed = $app->new_feed(); 
     1145    my $uri = $app->base . $app->uri . '/blog_id=' . $blog->id; 
     1146    my $blogname = encode_text($blog->name, undef, 'utf-8'); 
     1147    $feed->add_link({ rel => 'alternate', type => 'text/html', 
     1148                      href => $entry->permalink }); 
     1149    $feed->add_link({ rel => 'self', type => $app->atom_x_content_type, 
     1150                      href => $uri . '/entry_id=' . $entry->id }); 
     1151    $feed->title($entry->title); 
     1152    $feed->id($entry->atom_id . '/comments'); 
     1153    $app->_comments_in_atom($feed, \%terms, \%arg); 
     1154    $app->run_callbacks( 'get_comments', $feed, $entry ); 
     1155    ## xxx add next/prev links 
     1156    $app->response_content_type($app->atom_content_type); 
     1157    $feed->as_xml; 
     1158} 
     1159 
     1160sub _comments_in_atom { 
     1161    my $app = shift; 
     1162    my ( $feed, $terms, $args ) = @_; 
     1163    require MT::Comment; 
     1164    my $iter = MT::Comment->load_iter($terms, $args); 
     1165    my $latest_date = 0; 
     1166    my @comments; 
     1167    while (my $comment = $iter->()) { 
     1168        my $c = $app->new_with_comment($comment); 
     1169        # feed/updated should be added before entries 
     1170        # so we postpone adding them until later 
     1171        my $entry = $comment->entry; 
     1172        $c->set('http://purl.org/syndication/thread/1.0', 'in-reply-to', 
     1173            undef, 
     1174            { ref => $entry->atom_id, 
     1175              type => 'text/html', 
     1176              href => $entry->permalink } ); 
     1177        push @comments, $c; 
     1178        my $date = $comment->modified_on || $comment->created_on; 
     1179        if ( $latest_date < $date ) { 
     1180            $latest_date = $date; 
     1181            $feed->updated( $c->updated ); 
     1182        } 
     1183    } 
     1184    $feed->add_entry($_) foreach @comments; 
     1185    $feed; 
     1186} 
     1187 
     1188 
    101311891; 
    10141190__END__ 
     
    11101286$original_entry will have an unassigned 'id'. 
    11111287 
     1288=item get_posts 
     1289 
     1290    callback($eh, $app, $feed, $blog) 
     1291 
     1292Called right before get_posts method returns atom feed response. 
     1293I<$feed> is a reference to XML::Atom::Feed object. 
     1294I<$blog> is a reference to the requested MT::Blog object. 
     1295 
     1296=item get_post 
     1297 
     1298    callback($eh, $app, $atom_entry, $entry) 
     1299 
     1300Called right before get_post method returns atom entry response. 
     1301I<$atom_entry> is a reference to XML::Atom::Entry object. 
     1302I<$entry> is a reference to the requested MT::Entry object. 
     1303 
     1304=item get_blog_comments 
     1305 
     1306    callback($eh, $app, $feed, $blog) 
     1307 
     1308Called right before get_blog_comments method returns atom feed response. 
     1309I<$feed> is a reference to XML::Atom::Feed object. 
     1310I<$blog> is a reference to the requested MT::Blog object. 
     1311 
     1312=item get_comments 
     1313 
     1314    callback($eh, $app, $feed, $entry) 
     1315 
     1316Called right before get_comments method returns atom feed response.  
     1317I<$feed> is a reference to XML::Atom::Feed object. 
     1318I<$entry> is a reference to the requested MT::Entry object. 
     1319 
     1320=item get_comment 
     1321 
     1322    callback($eh, $app, $atom_entry, $comment) 
     1323 
     1324Called right before get_comment method returns atom entry response. 
     1325I<$atom_entry> is a reference to XML::Atom::Entry object. 
     1326I<$comment> is a reference to the requested MT::Comment object. 
     1327 
    11121328=back 
    11131329 
  • branches/release-35/lib/MT/Core.pm

    r1883 r1947  
    235235                    weblog => 'MT::AtomServer::Weblog::Legacy', 
    236236                    '1.0'  => 'MT::AtomServer::Weblog', 
     237                    comments => 'MT::AtomServer::Comments', 
    237238                }, 
    238239            }, 
  • branches/release-35/t/41-atom.t

    r1946 r1947  
    1111use XML::Atom::Entry; 
    1212 
    13 use Test::More tests => 37; 
     13use Test::More tests => 97; 
    1414 
    1515# To keep away from being under FastCGI 
     
    118118    if (ok($resp->is_success)) { 
    119119        my $blog_feed_url = $feed_link{$base_uri}->($resp); 
    120         my $uri = new URI($blog_feed_url); 
    121         is($uri->path, $base_uri . '/blog_id=1', 'blog feed url is correct'); 
     120        my $blog_feed_uri = new URI($blog_feed_url); 
     121        is($blog_feed_uri->path, $base_uri . '/blog_id=1', 'blog feed url is correct'); 
    122122    } 
    123123    else { 
     
    152152        my @entries = $feed->entries; 
    153153        is($entry_count, scalar(@entries), 'number of entries is correct'); 
     154 
     155        # check if entries have replies link relation 
     156        my $failed = 0; 
     157        foreach my $entry (@entries) { 
     158            next if !$entry->id && $entry->title =~ /^I just finished installing Movable Type/; 
     159            my $mt_entry = MT::Entry->load({ 
     160                atom_id => $entry->id, 
     161                blog_id => 1, 
     162            }); 
     163            $failed = 1, last unless $mt_entry; 
     164            my ($replies) = grep { 
     165                $_->rel eq 'replies' 
     166            } $entry->links; 
     167            $failed = 1, last unless $replies; 
     168            my $replies_uri = new URI($replies->href); 
     169            $failed = 1, last unless $replies_uri->path eq '/mt-atom.cgi/comments/blog_id=1/entry_id='.$mt_entry->id; 
     170        } 
     171        is($failed, 0, 'all the entries have replies link rel'); 
    154172    } 
    155173    else { 
     
    342360} #end foreach 
    343361 
     362COMMENT: 
     363# comments retrieval 
     364{ 
     365    my $wsse_header = make_wsse($chuck_token); 
     366    my $uri = new URI; 
     367    $uri->path('/mt-atom.cgi/comments/blog_id=1'); 
     368    my $req = new HTTP::Request(GET => $uri); 
     369    $req->header('Authorization' => 'Atom'); 
     370    $req->header('X-WSSE' => $wsse_header); 
     371 
     372    my $resp = $ua->request($req); 
     373    if (ok($resp->is_success)) { 
     374        my $thr_ns = XML::Atom::Namespace->new(prefix => undef, uri => 'http://purl.org/syndication/thread/1.0'); 
     375        my $comments = XML::Atom::Feed->new(\$resp->content()); 
     376        my $count = MT::Comment->count({ 
     377            blog_id => 1, visible => 1 
     378        }); 
     379        is( $count, scalar($comments->entries), 'comment count' ); 
     380        foreach my $c ( $comments->entries ) { 
     381            my $id = $c->id; 
     382            my ( $cmt_id ) = $id =~ m{/([0-9]+)$}; 
     383            die unless $cmt_id; 
     384            my $mt_comment = MT::Comment->load($cmt_id); 
     385            die unless $mt_comment; 
     386            my $mt_entry = $mt_comment->entry; 
     387            is($c->title, $mt_entry->title, 'comment title == entry title'); 
     388            is( $c->author->name, $mt_comment->author, 'comment author' ); 
     389            is( $c->author->email || '', $mt_comment->email || '', 'commenter email' ); 
     390            is( $c->author->uri || '', $mt_comment->url || '', 'commenter url'  ); 
     391            if ( $XML::Atom::LIBXML ) { 
     392                my $nodelist = $c->elem->getElementsByTagNameNS('http://purl.org/syndication/thread/1.0', 'in-reply-to');     
     393                my $irt = $nodelist->shift; 
     394                ok($irt, 'in-reply-to'); 
     395                is( $irt->ref, $mt_entry->atom_id, 'in-reply-to/ref' ); 
     396                is( $irt->href, $mt_entry->permalink, 'in-reply-to/href' ); 
     397            } 
     398        } 
     399    } 
     400    else { 
     401        die $resp->content(); 
     402    } 
     403} 
     404 
     405{ 
     406    my $iter = MT::Comment->count_group_by( 
     407        { blog_id => 1, visible => 1 }, 
     408        { group => ['entry_id'], sort => [ { desc => 'DESC', column => '1' } ] 
     409        } 
     410    ); 
     411    #$Data::ObjectDriver::PROFILE = 1; 
     412    #my $p = Data::ObjectDriver->profiler; 
     413    #$p->reset; 
     414    #print "$_\n" foreach @{$p->query_log}; 
     415    my ( $count, $eid ) = $iter->(); 
     416    $iter->('finish'); 
     417    my $entry = MT::Entry->load($eid); 
     418 
     419    my $wsse_header = make_wsse($chuck_token); 
     420    my $uri = new URI; 
     421    $uri->path('/mt-atom.cgi/1.0/blog_id=1/entry_id=' . $entry->id); 
     422    my $req = new HTTP::Request(GET => $uri); 
     423    $req->header('Authorization' => 'Atom'); 
     424    $req->header('X-WSSE' => $wsse_header); 
     425 
     426    my $resp = $ua->request($req); 
     427    if (ok($resp->is_success)) { 
     428        my $feed = XML::Atom::Entry->new(\$resp->content()); 
     429        my ($replies) = grep { 
     430            $_->rel eq 'replies' 
     431        } $feed->links; 
     432 
     433        # retrieve comments from replies url 
     434        my $replies_uri = new URI($replies->href); 
     435        my $wsse_header = make_wsse($chuck_token); 
     436        my $uri = new URI; 
     437        $uri->path($replies_uri->path); 
     438        my $req = new HTTP::Request(GET => $uri); 
     439        $req->header('Authorization' => 'Atom'); 
     440        $req->header('X-WSSE' => $wsse_header); 
     441 
     442        my $resp = $ua->request($req); 
     443        my $thr_ns = XML::Atom::Namespace->new(prefix => undef, uri => 'http://purl.org/syndication/thread/1.0'); 
     444        if (ok($resp->is_success)) { 
     445            my $comments = XML::Atom::Feed->new(\$resp->content()); 
     446            is( $count, scalar($comments->entries), 'comment count' ); 
     447            foreach my $e ( $comments->entries ) { 
     448                is($e->title, $entry->title, 'comment title == entry title'); 
     449                my $id = $e->id; 
     450                my ( $cmt_id ) = $id =~ m{/([0-9]+)$}; 
     451                die unless $cmt_id; 
     452                my $mt_comment = MT::Comment->load($cmt_id); 
     453                die unless $mt_comment; 
     454                is( $e->author->name, $mt_comment->author, 'comment author' ); 
     455                is( $e->author->email || '', $mt_comment->email || '', 'commenter email' ); 
     456                is( $e->author->uri || '', $mt_comment->url || '', 'commenter url'  ); 
     457                if ( $XML::Atom::LIBXML ) { 
     458                    my $nodelist = $e->elem->getElementsByTagNameNS('http://purl.org/syndication/thread/1.0', 'in-reply-to');     
     459                    my $irt = $nodelist->shift; 
     460                    ok($irt, 'in-reply-to'); 
     461                    is( $irt->ref, $entry->atom_id, 'in-reply-to/ref' ); 
     462                    is( $irt->href, $entry->permalink, 'in-reply-to/href' ); 
     463                } 
     464            } 
     465        } 
     466        else { 
     467            die $resp->content(); 
     468        } 
     469    } 
     470} 
     471 
     472{ 
     473    my $thr_ns = XML::Atom::Namespace->new(prefix => undef, uri => 'http://purl.org/syndication/thread/1.0'); 
     474    my $wsse_header = make_wsse($chuck_token); 
     475    my $uri = new URI; 
     476    $uri->path('/mt-atom.cgi/comments/blog_id=1/comment_id=1'); 
     477    my $req = new HTTP::Request(GET => $uri); 
     478    $req->header('Authorization' => 'Atom'); 
     479    $req->header('X-WSSE' => $wsse_header); 
     480 
     481    my $resp = $ua->request($req); 
     482    if (ok($resp->is_success)) { 
     483        my $c = XML::Atom::Entry->new(\$resp->content()); 
     484        my $mt_comment = MT::Comment->load(1); 
     485        die unless $mt_comment; 
     486        my $entry = $mt_comment->entry; 
     487        is( $c->title, $entry->title, 'comment title == entry title' ); 
     488        is( $c->author->name, $mt_comment->author, 'comment author' ); 
     489        is( $c->author->email || '', $mt_comment->email || '', 'commenter email' ); 
     490        is( $c->author->uri || '', $mt_comment->url || '', 'commenter url'  ); 
     491        if ( $XML::Atom::LIBXML ) { 
     492            my $nodelist = $c->elem->getElementsByTagNameNS('http://purl.org/syndication/thread/1.0', 'in-reply-to');     
     493            my $irt = $nodelist->shift; 
     494            ok($irt, 'in-reply-to'); 
     495            is( $irt->ref, $entry->atom_id, 'in-reply-to/ref' ); 
     496            is( $irt->href, $entry->permalink, 'in-reply-to/href' ); 
     497        } 
     498    } 
     499    else { 
     500        die $resp->content(); 
     501    } 
     502} 
    344503 
    345504END {