Committer: afedorov
LJSUP-13431: Antispam for edited entries in the communitiesU trunk/cgi-bin/LJ/RelationService/MysqlAPI.pm U trunk/cgi-bin/LJ/RelationService.pm U trunk/cgi-bin/LJ/User.pm U trunk/cgi-bin/ljprotocol.pl U trunk/htdocs/community/settings.bml
Modified: trunk/cgi-bin/LJ/RelationService/MysqlAPI.pm =================================================================== --- trunk/cgi-bin/LJ/RelationService/MysqlAPI.pm 2012-09-14 12:13:00 UTC (rev 22897) +++ trunk/cgi-bin/LJ/RelationService/MysqlAPI.pm 2012-09-14 13:06:18 UTC (rev 22898) @@ -768,6 +768,25 @@ return $LJ::REQ_CACHE_REL{$key} = $dbval; } +sub is_relation_type_to { + my $class = shift; + my $u = shift; + my $friend = shift; + my $types = shift; + my %opts = @_; + + return undef unless $types && $u && $friend; + + my $userid = LJ::want_userid($u); + my $friendid = LJ::want_userid($friend); + + my $dbh = LJ::get_db_writer(); + my $relcount = $dbh->selectrow_array("SELECT COUNT(*) FROM reluser ". + "WHERE userid=$userid AND targetid=$friendid ". + "AND type IN (?)", undef, $types); + return $relcount; +} + sub get_groupmask { my $class = shift; my $u = shift; Modified: trunk/cgi-bin/LJ/RelationService.pm =================================================================== --- trunk/cgi-bin/LJ/RelationService.pm 2012-09-14 12:13:00 UTC (rev 22897) +++ trunk/cgi-bin/LJ/RelationService.pm 2012-09-14 13:06:18 UTC (rev 22898) @@ -185,6 +185,30 @@ return $interface->is_relation_to($u, $friend, $type, %opts); } +sub is_relation_type_to { + my $class = shift; + my $u = shift; + my $friend = shift; + my $types = shift; + my %opts = @_; + + $u = LJ::want_user($u); + $friend = LJ::want_user($friend); + + return undef unless $u && $friend && $types; + $types = [ $types ] unless ref $types eq 'ARRAY'; + + if ($class->_load_alt_api('read', $types)) { + my $alt = $class->alt_api($u); + if ($alt) { + $alt->is_relation_type_to($u, $friend, $types, %opts); + } + } + + my $interface = $class->relation_api($u); + return $interface->is_relation_type_to($u, $friend, $types, %opts); +} + sub get_groupmask { my $class = shift; my $u = shift; Modified: trunk/cgi-bin/LJ/User.pm =================================================================== --- trunk/cgi-bin/LJ/User.pm 2012-09-14 12:13:00 UTC (rev 22897) +++ trunk/cgi-bin/LJ/User.pm 2012-09-14 13:06:18 UTC (rev 22898) @@ -6515,6 +6515,14 @@ return 0; } +sub check_non_whitelist_enabled { + my $u = shift; + return 0 if $LJ::DISABLED{'spam_button'}; + my $check_non_whitelist = $u->prop('check_non_whitelist'); + return 1 if (!defined($check_non_whitelist) || $check_non_whitelist eq 'Y'); + return 0; +} + # return sticky entries existing sub has_sticky_entry { my ($self) = @_; Modified: trunk/cgi-bin/ljprotocol.pl =================================================================== --- trunk/cgi-bin/ljprotocol.pl 2012-09-14 12:13:00 UTC (rev 22897) +++ trunk/cgi-bin/ljprotocol.pl 2012-09-14 13:06:18 UTC (rev 22898) @@ -3152,6 +3152,110 @@ return $res if $res->{type}; } + # don't moderate admins, moderators & pre-approved users + unless ( LJ::RelationService->is_relation_type_to( $ownerid, $posterid, [ 'A','M','N' ] ) ) { + + my $need_moderated = 0; + if ( $uowner->check_non_whitelist_enabled() ) { + LJ::run_hook('spam_community_detector', $uowner, $req, \$need_moderated); + } + + if ($uowner->{'journaltype'} eq 'C' && $need_moderated && !$flags->{'nomod'}) { + + $req->{'_moderate'}->{'authcode'} = LJ::make_auth_code(15); + + # create tag <lj-embed> from HTML-tag <embed> + LJ::EmbedModule->parse_module_embed($uowner, \$req->{event}); + + my $fr = $dbcm->quote(Storable::nfreeze($req)); + return fail($err, 409) if length($fr) > 200_000; + + # store + my $modid = LJ::alloc_user_counter($uowner, "M"); + return fail($err, 501) unless $modid; + + $uowner->do("INSERT INTO modlog (journalid, modid, posterid, subject, logtime) ". + "VALUES ($ownerid, $modid, $posterid, ?, NOW())", undef, + LJ::text_trim($req->{'subject'}, 30, 0)); + return fail($err, 501) if $uowner->err; + + $uowner->do("INSERT INTO modblob (journalid, modid, request_stor) ". + "VALUES ($ownerid, $modid, $fr)"); + if ($uowner->err) { + $uowner->do("DELETE FROM modlog WHERE journalid=$ownerid AND modid=$modid"); + return fail($err, 501); + } + + # alert moderator(s) + my $mods = LJ::load_rel_user($dbh, $ownerid, 'M') || []; + if (@$mods) { + # load up all these mods and figure out if they want email or not + my $modlist = LJ::load_userids(@$mods); + + my @emails; + my $ct; + foreach my $mod (values %$modlist) { + last if $ct > 20; # don't send more than 20 emails. + + next unless $mod->is_visible; + + LJ::load_user_props($mod, 'opt_nomodemail'); + next if $mod->{opt_nomodemail}; + next if $mod->{status} ne "A"; + + push @emails, + { + to => $mod->email_raw, + browselang => $mod->prop('browselang'), + charset => $mod->mailencoding || 'utf-8', + }; + + ++$ct; + } + + foreach my $to (@emails) { + # TODO: html/plain text. + my $body = LJ::Lang::get_text( + $to->{'browselang'}, + 'esn.moderated_submission.body', undef, + { + user => $u->{'user'}, + subject => $req->{'subject'}, + community => $uowner->{'user'}, + modid => $modid, + siteroot => $LJ::SITEROOT, + sitename => $LJ::SITENAME, + moderateurl => "$LJ::SITEROOT/community/moderate.bml?authas=$uowner->{'user'}&modid=$modid", + viewurl => "$LJ::SITEROOT/community/moderate.bml?authas=$uowner->{'user'}", + }); + + my $subject = LJ::Lang::get_text($to->{'browselang'},'esn.moderated_submission.subject'); + + LJ::send_mail({ + 'to' => $to->{to}, + 'from' => $LJ::DONOTREPLY_EMAIL, + 'charset' => $to->{charset}, + 'subject' => $subject, + 'body' => $body, + }); + } + } + + my $msg = translate($u, "modpost", undef); + return { + 'message' => $msg, + xc3 => { + u => $u, + post => { + coords => $req->{props}->{current_coords}, + has_images => ($req->{event} =~ /pics\.livejournal\.com/ ? 1 : 0), + from_mobile => ($req->{event} =~ /m\.livejournal\.com/ ? 1 : 0) + } + } + }; + } + } + # fetch the old entry from master database so we know what we # really have to update later. usually people just edit one part, # not every field in every table. reads are quicker than writes, Modified: trunk/htdocs/community/settings.bml =================================================================== --- trunk/htdocs/community/settings.bml 2012-09-14 12:13:00 UTC (rev 22897) +++ trunk/htdocs/community/settings.bml 2012-09-14 13:06:18 UTC (rev 22898) @@ -263,9 +263,12 @@ $cu->set_prop('adult_content', $adult_content); } - if ($moderated =~ /^[AF]$/ && ! LJ::load_rel_user($cu->{'userid'}, 'M')->[0]) { - LJ::set_rel($cu->{'userid'}, $remote->{'userid'}, 'M'); - } + if ($moderated =~ /^[AF]$/) { + unless ( LJ::load_rel_user($cu->{'userid'}, 'M')->[0] ) { + LJ::set_rel($cu->{'userid'}, $remote->{'userid'}, 'M'); + } + $cu->set_prop('check_non_whitelist', $POST{check_non_whitelist} ? 'Y' : 'N'); + } # since journaltype changed $cu->invalidate_directory_record; @@ -302,10 +305,11 @@ $cname = $POST{'cuser'}; # if we're falling through with errors when creating my %info = ( - 'membership'=>$POST{'membership'} || 'open', - 'postlevel'=>$POST{'postlevel'} || 'members', - 'nonmember_posting'=>$POST{'nonmember_posting'} || 0, - 'moderated'=>($POST{'moderated'} =~ /^[NAF]$/) ? $POST{'moderated'} : 'F', + 'membership' => $POST{'membership'} || 'open', + 'postlevel' => $POST{'postlevel'} || 'members', + 'nonmember_posting' => $POST{'nonmember_posting'} || 0, + 'moderated' => ($POST{'moderated'} =~ /^[NAF]$/) ? $POST{'moderated'} : 'F', + 'check_non_whitelist' => $POST{'check_non_whitelist'} || 'N', ); if ($mode eq 'modify') { @@ -330,6 +334,7 @@ LJ::load_user_props($c, "nonmember_posting", "moderated"); $info{'nonmember_posting'} = $c->{'nonmember_posting'} ? 1 : 0; $info{'moderated'} = ($c->{'moderated'} =~ /^[01NAF]$/) ? $c->{'moderated'} : 'N'; + $info{'check_non_whitelist'} = $c->check_non_whitelist_enabled ? 'Y' : 'N'; } $ret .= "<form method='post' action='settings.bml?mode=$mode'>"; @@ -449,6 +454,10 @@ $ret .= "<a href='/community/whitelist.bml?authas=$GET{authas}' id='moderate_filter_link' style='display:none'>$ML{'.label.whitelist'}</a>"; $ret .= "<noscript><a href='/community/whitelist.bml?authas=$GET{authas}'>$ML{'.label.whitelist'}</a></noscript>"; $ret .= "</p>"; + $ret .= "<p class='input-wrapper'>"; + $ret .= LJ::html_check({ type => 'checkbox', id => 'check_non_whitelist', name => 'check_non_whitelist', value => 'Y', selected => ($info{'check_non_whitelist'} eq 'Y') ? 1 : 0 }); + $ret .= " <label for='check_non_whitelist'>$ML{'.label.check_non_whitelist'}</label></p>"; + $ret .= "</p>"; $ret .= "</div></fieldset>"; $ret .= "</div>";