Committer: ailyin
LJINT-362 (Comments for side projects)U trunk/cgi-bin/LJ/PartnerSite.pm U trunk/htdocs/identity/facebook-interstitial.bml U trunk/htdocs/identity/twitter-interstitial.bml
Modified: trunk/cgi-bin/LJ/PartnerSite.pm =================================================================== --- trunk/cgi-bin/LJ/PartnerSite.pm 2011-01-18 10:07:48 UTC (rev 9919) +++ trunk/cgi-bin/LJ/PartnerSite.pm 2011-01-18 10:45:24 UTC (rev 9920) @@ -1,3 +1,103 @@ +=head1 NAME + +LJ::PartnerSite - core module for manipulating data in the journals +bound to partner sites like kommersant.ru + +=head1 INTRODUCTION + +This module is the core module of the subsystem that allows external +sites to use LJ to enable commenting on their articles. We consider that +each site has a set of items ('articles') that can be commented on, and +each of these articles has a unique identifier ('docid'). + +There is a database table in the global cluster that stores the mapping +from docids to the entries in the partner's journal on LJ. These entries +are created automatically once a user tries to comment on an article. + +The system is exposed to the outside world by the two pages as follows: + +=over 2 + +=item * + +comments.bml (htdocs/tools/endpoints/comments.bml): returns a JSON or a JSONP +string with information about comments to the selected article. + +=item * + +logcom.bml (htdocs/gadgets/logcom.bml, "log in and comment"): displays a +form that allows a user to log in and comment on the selected article. + +=back + +The system also includes several hacks inside LJ::Comment (for formatting +comment notifications), htdocs/talkpost_do.bml (for handling errors +differently), and interstitials in htdocs/identity. + +The DB table is called 'external_sites_articles_map', and it has the following +fields: + +=over 2 + +=item * + +partnerid + +=item * + +docid + +=item * + +docid_hash + +=item * + +jitemid + +=back + +The memcache keys of 'extarticlesmap_jitemid:$partnerid:$docid' and +'extarticlesmap_docid:$partnerid:$jitemid' are also used to cache the +information from this table. + +=head1 SYNOPSIS + + my $partner = LJ::PartnerSite->find_by_api_key('api key') + || LJ::PartnerSite->find_by_journal_username('kommersant_test'); + + my $entry = $partner->find_entry_by_docid( 1, { 'nocreate' => 1 } ); + $entry = $partner->find_entry_by_docid(1); + # $entry isa 'LJ::Entry' + + # first parameter in the following call is docid: + my $link = $partner->article_link( 1, { 'replyto' => 12345, + 'view' => 12345, } ); + + # or you can pass an entry instead: + $link = $partner->article_link_from_entry($entry, { 'replyto' => 12345, + 'view' => 12345, } ); + + my $docid = $partner->docid_from_entry($entry); + + my $html = $partner->domain_check_js({ 'mode' => 'logcom' }); + + my $js = $partner->domain_check_js({ + 'mode' => 'jsonp', + 'code' => 'alert("everything's ok");', + }); + + $partner->request_logcom_resources; + + my $info = $partner->get_comments_info($docid, { + 'thread' => 12345 || undef, + 'view' => 12345 || undef, + 'page' => 12345 || undef, + 'remote' => LJ::get_remote(), + }); + +=cut + package LJ::PartnerSite; use strict; use warnings; @@ -9,12 +109,115 @@ use base qw( Class::Accessor ); +=head1 METHODS + +=head2 Fields and Accessorts + +=over 2 + +=item * + +id: unique numeric identifier for the partner + +=item * + +name: the user-facing name of the partner + +=item * + +journal_username: the journal that is bound to the partner, username + +=item * + +journalid: the userid of the same journal + +=item * + +journal: the LJ::User object of the same journal + +=item * + +api_key: the unique API key identifying the partner + +=item * + +domain: the domain of the partner's site + +=item * + +link_pattern: a format to use when construcing article URLs from docids; +[[docid]] gets replaced with the actual docid. + +=item * + +custom_css_url: the URL of the partner-provided CSS file to load in the iframe +on the partner's site, if necessary + +=item * + +xdreceiver_url: the URL of a page on the partner's site, to provide a +cross-domain communication channel + +=item * + +rate_limits: the rate limit for the calls to this partner's callbacks; the +value is passed to LJ::RateLimit::check unchanged. + +=item * + +encoding: the encoding to reencode the JSON returned to this partner to. + +=item * + +mapping_locked: do not add any new entries in case we get to work with +a docid we've never seen before. this is added as a migration option. + +=item * + +disabled: disable this partner; when this is true, all calls to +find_entry_by_docid return undef + +=back + +=cut + __PACKAGE__->mk_accessors qw( id name journal_username journalid journal api_key domain link_pattern custom_css_url - xdreceiver_url rate_limits encoding ); + xdreceiver_url rate_limits encoding + mapping_locked disabled ); ### CLASS METHODS ### +=head2 Constructors and searching + +=over 2 + +=item * + +LJ::PartnerSite->new(%params): bless %params into an object; this function +actually computes journal and journalid based on journal_username, so +you don't need to pass these two. + +=item * + +LJ::PartnerSite->find_by_id(1): return an instance with the specified +identifier, or undef. + +=item * + +LJ::PartnerSite->find_by_api_key('api key'); + +=item * + +LJ::PartnerSite->find_by_journal_username('kommersant_test'); + +=back + +Note that the find_* methods actually caches the entire set of instances +in the process memory for performance. + +=cut + my %by_id_map; sub new { @@ -76,12 +279,30 @@ return Digest::MD5::md5_base64($value); } +=head2 Various instance methods + +=over 2 + +=item * + +$partner->find_entry_by_docid( $docid, { %opts } ): return an entry +corresponding to this docid. + +Supported options: nocreate (boolean) restricts this function from +creating a new entry. + +=cut + sub find_entry_by_docid { my ( $self, $docid, $opts ) = @_; + return if $self->disabled; + $docid ||= ''; $opts ||= {}; + $opts->{'nocreate'} = 1 if $self->mapping_locked; + my $memc_key = 'extarticlesmap_jitemid:' . $self->id . ':' . $docid; my $jitemid = LJ::MemCache::get($memc_key); @@ -164,6 +385,15 @@ return LJ::Entry->new( $self->journal, 'jitemid' => $jitemid ); } +=item * + +$partner->article_link( $docid, { %extend } ): format the link +to the article corresponding to the given docid. The second +parameter is an optional hashref containing variables that are +added to the query string of the resulting address. + +=cut + sub article_link { my ( $self, $docid, $extend ) = @_; @@ -179,6 +409,13 @@ return $uri->as_string; } +=item * + +$partner->docid_from_entry($entry): return the docid of the article +corresponding to the given entry. + +=cut + sub docid_from_entry { my ( $self, $entry ) = @_; @@ -206,6 +443,13 @@ return $docid; } +=item * + +$partner->article_link_from_entry( $entry, $extend ): equivalent to +calling $partner->docid_from entry and then $partner->article_link. + +=cut + sub article_link_from_entry { my ( $self, $entry, $extend ) = @_; @@ -213,6 +457,22 @@ return $self->article_link( $docid, $extend ); } +=item * + +$partner->domain_check_js({ %opts }): return a blob of code that +checks the domain it's executed on, as a security measure. + +Supported options: + +mode ('logcom' or 'jsonp'): 'logcom' returns HTML containing JS, and 'jsonp' +returns JS that calls the code passed as a different parameter if the +check succeeds. + +code: only if mode eq 'jsonp', the code that should be ran after the check +succeeds. + +=cut + sub domain_check_js { my ($self, $opts) = @_; @@ -271,6 +531,13 @@ } } +=item * + +$partner->request_logcom_resources: call need_res and include_raw necessary +for the page of logcom.bml + +=cut + sub request_logcom_resources { my ($self) = @_; @@ -383,6 +650,141 @@ return @ret; } +=item * + +$partner->get_comments_info( $docid, { %opts } ): return information about +comments to the selected article. + +Supported options: thread, view, page, remote. + +Return value is a hashref with the fields as follows: + +=over 4 + +=item * + +comments + +=item * + +pages + +=item * + +page + +=item * + +journal + +=item * + +auth_token + +=back + +comments is a list, and every item of this list is a hashref with the fields +as follows: + +=over 4 + +=item * + +comment_id + +=item * + +parent_id + +=item * + +state + +=item * + +is_open + +=item * + +timestamp + +=item * + +subject + +=item * + +text + +=item * + +username + +=item * + +display_name (missing if the poster is anonymous) + +=item * + +journal_url (missing if the poster is anonymous) + +=item * + +profile_url (missing if the poster is anonymous) + +=item * + +posters_jtype (missing if the poster is anonymous) + +=item * + +usericon (missing if the poster is anonymous) + +=item * + +can_delete + +=item * + +identity (missing if the poster is not an identity account) + +=back + +The comment identifiers there correspond to what is called 'dtalkid' +in the other parts of LiveJournal code. + +The 'identity' field, if present, is also a hashref with the fields as +follows: + +=over 4 + +=item * + +typeid + +=item * + +shortcode + +=item * + +name + +=item * + +url + +=item * + +ljuser_display_params + +=back + +'ljuser_display_params' is whatever LJ::Identity::ljuser_display_params +returned for this user; please refer to its documentation for details. + +=cut + # opts: thread, view, page, remote sub get_comments_info { my ( $self, $docid, $opts ) = @_; @@ -441,4 +843,8 @@ }; } +=back + +=cut + 1; Modified: trunk/htdocs/identity/facebook-interstitial.bml =================================================================== --- trunk/htdocs/identity/facebook-interstitial.bml 2011-01-18 10:07:48 UTC (rev 9919) +++ trunk/htdocs/identity/facebook-interstitial.bml 2011-01-18 10:45:24 UTC (rev 9920) @@ -21,7 +21,7 @@ my $returl = LJ::Request->get_param('ret') || $LJ::SITEROOT; my $forwhat = LJ::Request->get_param('forwhat') || 'login'; - my $external_site_case = ( $forwhat =~ /^external-/ ); + my $external_site_case = ( $forwhat eq 'external' ); my @errors; Modified: trunk/htdocs/identity/twitter-interstitial.bml =================================================================== --- trunk/htdocs/identity/twitter-interstitial.bml 2011-01-18 10:07:48 UTC (rev 9919) +++ trunk/htdocs/identity/twitter-interstitial.bml 2011-01-18 10:45:24 UTC (rev 9920) @@ -19,7 +19,7 @@ my $returl = LJ::Request->get_param('ret') || $LJ::SITEROOT; my $forwhat = LJ::Request->get_param('forwhat') || 'login'; - my $external_site_case = ( $forwhat =~ /^external-/ ); + my $external_site_case = ( $forwhat eq 'external' ); my @errors; my $step2 = 0; @@ -156,26 +156,6 @@ $step2 = 1; }; - ## LJINT-362 (Comments for side projects): - ## for external sites, we're actually redirecting to - ## the iframe page, not to $returl - if ( $external_site_case ) { - my (undef, $journalid, $ditemid, $dtalkid) = split /-/, $forwhat; - my $journal = LJ::load_userid($journalid); - my $entry = LJ::Entry->new($journal, 'ditemid' => $ditemid); - - my $uri = URI->new( "$LJ::SITEROOT/gadgets/logcom.bml" ); - $uri->query_form( $uri->query_form, - 'url' => $entry->prop('external_url'), - 'rsk' => LJ::Request->get_param('rsk') ); - - if ($dtalkid) { - $uri->query_form( $uri->query_form, 'replyto' => $dtalkid ); - } - - $returl = $uri->as_string; - } - if (LJ::Request->did_post) { $handle_post->(); return if LJ::Request->redirected; @@ -189,25 +169,8 @@ } my $mode = LJ::Request->get_param('mode'); - $mode = 'full' unless defined $mode; - - ## LJINT-362 (Comments for side projects): - ## in case an account is created on an external site, redirect them - ## back to a page that shows the same interstitial in an iframe - if ( $external_site_case && $mode ne 'compact' ) { - my (undef, $journalid, $ditemid, $dtalkid) = split /-/, $forwhat; - my $journal = LJ::load_userid($journalid); - my $entry = LJ::Entry->new($journal, 'ditemid' => $ditemid); - - my $uri = URI->new( $entry->prop('external_url') ); - if ($dtalkid) { - $uri->query_form( $uri->query_form, 'replyto' => $dtalkid ); - $uri->fragment( 't' . $dtalkid ); - } - $uri->query_form( $uri->query_form, - 'show' => 'twitter-interstitial/' . $forwhat ); - - return LJ::Request->redirect( $uri->as_string ); + unless ( defined $mode ) { + $mode = $external_site_case ? 'compact' : 'full'; } my $template_file = $mode eq 'compact' ? 'TwitterInterstitialCompact.tmpl'