#!/usr/bin/perl # # DW::Controller::RPC::Misc # # The AJAX endpoint for general calls. # # Authors: # Mark Smith # # Copyright (c) 2009-2013 by Dreamwidth Studios, LLC. # # This program is free software; you may redistribute it and/or modify it under # the same terms as Perl itself. For a copy of the license, please reference # 'perldoc perlartistic' or 'perldoc perlgpl'. # package DW::Controller::RPC::Misc; use strict; use LJ::JSON; use DW::Routing; use DW::Controller; DW::Routing->register_rpc( "contentfilters", \&contentfilters_handler, format => 'json' ); DW::Routing->register_rpc( "extacct_auth", \&extacct_auth_handler, format => 'json' ); DW::Routing->register_rpc( "general", \&general_handler, format => 'json' ); DW::Routing->register_rpc( "addcomment", \&addcomment_handler, format => 'json', methods => { POST => 1 } ); sub contentfilters_handler { my $r = DW::Request->get; my $get = $r->get_args; my $post = $r->post_args; # make sure we have a user of some sort my $remote = LJ::get_remote(); my $remote_user = $remote ? $remote->user : undef; my $u = LJ::get_authas_user( $get->{user} || $remote_user ); return DW::RPC->alert('Unable to load user for call.') unless $u; # in theory, they're passing a mode in the GET arguments my $mode = $get->{mode} or return DW::RPC->alert('No mode passed.'); my %ret; # list_filters mode is very simple: it returns an array of the filters with the # pertinent information about those filters if ( $mode eq 'list_filters' ) { $ret{filters} = {}; my @filters = $u->content_filters; foreach my $filt (@filters) { $ret{filters}->{ $filt->id } = { id => $filt->id, name => $filt->name, sortorder => $filt->sortorder, public => $filt->public, }; } # list the names of the people who are on a filter } elsif ( $mode eq 'list_members' ) { $ret{members} = {}; my $filter = $u->content_filters( id => $get->{filterid} ) or return DW::RPC->alert('No such filter.'); my $data = $filter->data; foreach my $uid ( keys %$data ) { my $member = $data->{$uid}; # FIXME: use load_userids_multiple to get the user objects... $ret{members}->{$uid} = { user => LJ::load_userid($uid)->user, adultcontent => $member->{adultcontent} || 'any', postertype => $member->{postertype} || 'any', tagmode => $member->{tagmode} || 'any_of', tags => { map { $_ => 1 } @{ $member->{tags} || [] } }, }; } # called to make a brand new filter } elsif ( $mode eq 'create_filter' ) { return DW::RPC->alert('Can only create filters for people.') unless $u->is_individual; return DW::RPC->alert('No name provided.') unless $get->{name} =~ /\S/; my $fid = $u->create_content_filter( name => $get->{name} ); return DW::RPC->alert('Failed to create content filter.') unless $fid; my $cf = $u->content_filters( id => $fid ); return DW::RPC->alert('Failed to retrieve content filter.') unless $cf; %ret = ( id => $cf->id, name => $cf->name, public => $cf->public, sortorder => $cf->sortorder, ); # delete a content filter } elsif ( $mode eq 'delete_filter' ) { return DW::RPC->alert('Can only create filters for people.') unless $u->is_individual; return DW::RPC->alert('No/invalid id provided.') unless $get->{id} =~ /^\d+$/; my $id = $u->delete_content_filter( id => $get->{id} ); return DW::RPC->alert('Failed to delete the content filter.') unless $id == $get->{id}; $ret{ok} = 1; # save incoming changes } elsif ( $mode eq 'save_filters' ) { my $obj = from_json( $post->{json} ); foreach my $fid ( keys %$obj ) { my $filt = $obj->{$fid}; # load this filter my $cf = $u->content_filters( id => $fid ); return DW::RPC->alert("Filter id $fid does not exist.") unless $cf; # update the name if necessary, this has to be before the members check # because they might not have loaded members (or it might have none) $cf->name( $filt->{name} ) if $filt->{name} && $filt->{name} ne $cf->name; # skip the filter if it hasn't actually been loaded next unless defined $filt->{members}; # get data object for use later my $data = $cf->data; # fix up the member list foreach my $uid ( keys %{ $filt->{members} } ) { my $member = $filt->{members}->{$uid}; # don't need this, we can look it up delete $member->{user}; # tags are given to us as a hashref, we need to flatten to an array $member->{tags} = [ keys %{ $member->{tags} || {} } ]; # these may or may not be present, nuke them if they're default delete $member->{postertype} if $member->{postertype} && $member->{postertype} eq 'any'; delete $member->{adultcontent} if $member->{adultcontent} && $member->{adultcontent} eq 'any'; delete $member->{tagmode} if $member->{tagmode} && $member->{tagmode} eq 'any_of'; # now save this in the actual filter $data->{$uid} = $member; } # see whether we deleted any members from the filter foreach my $uid ( keys %{$data} ) { # delete userid from $data if it is not also in $filt->{members} delete $data->{$uid} unless exists $filt->{members}->{$uid}; } # save public and sortorder preferences $cf->_getset( 'sortorder', $filt->{sortorder} + 0 ) unless $filt->{sortorder} == $cf->{sortorder}; $cf->_getset( 'public', $filt->{public} ? 1 : 0 ) unless $filt->{public} == $cf->{public}; # save the filter, very important... $cf->_save; } $ret{ok} = 1; } elsif ( $mode eq "view_filter" ) { # called to get reading page url return DW::RPC->alert('No name provided.') unless $get->{name} =~ /\S/; $ret{url} = $u->journal_base . "/read/" . $get->{name}; } return DW::RPC->out(%ret); } sub extacct_auth_handler { my $r = DW::Request->get; my $get = $r->get_args; my $u = LJ::get_remote(); return DW::RPC->err( LJ::Lang::ml('error.extacct_auth.nouser') ) unless $u; # get the account my $acctid = LJ::ehtml( $get->{acctid} ); my $account = DW::External::Account->get_external_account( $u, $acctid ); return DW::RPC->err( LJ::Lang::ml( 'error.extacct_auth.nosuchaccount', { acctid => $acctid, username => $u->username } ) ) unless $account; # make sure this account supports challenge/response authentication return DW::RPC->err( LJ::Lang::ml( 'error.extacct_auth.nochallenge', { account => LJ::ehtml( $account->displayname ) } ) ) unless $account->supports_challenge; # get the auth challenge my $challenge = $account->challenge; return DW::RPC->err( LJ::Lang::ml( 'error.extacct_auth.authfailed', { account => LJ::ehtml( $account->displayname ) } ) ) unless $challenge; return DW::RPC->out( challenge => $challenge, success => 1 ); } sub general_handler { my $r = DW::Request->get; my $args = $r->get_args; # make sure we have a user of some sort my $remote = LJ::get_remote(); my $u = LJ::load_user( $args->{user} ) || $remote or return DW::RPC->alert('Unable to load user for call.'); # in theory, they're passing a mode in the args-> arguments my $mode = $args->{mode} or return DW::RPC->alert('No mode passed.'); my %ret; # gets the list of people that this account subscribes to if ( $mode eq 'list_subscriptions' ) { $ret{subs} = $u->watch_list; my $uobjs = LJ::load_userids( keys %{ $ret{subs} } ); foreach my $userid ( keys %$uobjs ) { $ret{subs}->{$userid}->{username} = $uobjs->{$userid}->user; $ret{subs}->{$userid}->{journaltype} = $uobjs->{$userid}->journaltype; } # get the list of someone's tags } elsif ( $mode eq 'list_tags' ) { $ret{tags} = LJ::Tags::get_usertags( $u, { remote => $remote } ); foreach my $val ( values %{ $ret{tags} } ) { delete $val->{security_level}; delete $val->{security}; delete $val->{display}; } # get the list of members of an access filter } elsif ( $mode eq 'list_filter_members' ) { my $filterid = $args->{filterid} + 0; $ret{filter_members}->{filterusers} = $u->trust_group_members( id => $filterid ); $ret{filter_members}->{filtername} = $u->trust_groups( id => $filterid ); my $uobjs = LJ::load_userids( keys %{ $ret{filter_members}->{filterusers} } ); foreach my $userid ( keys %$uobjs ) { next unless $uobjs->{$userid}; $ret{filter_members}->{filterusers}->{$userid}->{fancy_username} = $uobjs->{$userid}->ljuser_display; } # problems } else { return DW::RPC->alert('Unknown mode.'); } return DW::RPC->out(%ret); } sub addcomment_handler { my $remote = LJ::get_remote(); my $r = DW::Request->get; my $post = $r->post_args; return DW::RPC->err( LJ::Lang::ml('error.invalidform.quickerreply') ) if $r->did_post && !LJ::check_form_auth( $post->{lj_form_auth} ); # build the comment my $req = { ver => 1, username => $remote->username, journal => $post->{journal}, ditemid => $post->{itemid}, parent => $post->{parenttalkid}, body => $post->{body}, subject => $post->{subject}, prop_picture_keyword => $post->{prop_picture_keyword}, prop_editor => $post->{prop_editor}, useragent => "rpc-addcomment", }; # post! my $post_error; my $result = LJ::Protocol::do_request( "addcomment", $req, \$post_error, { noauth => 1, nocheckcap => 1 } ); return DW::RPC->err( LJ::Protocol::error_message($post_error) ) if $post_error; # figure out if they posted with a non-default editor my $editor = DW::Formats::validate( $post->{prop_editor} ); my %format_opts = (); if ( DW::Formats::is_active($editor) && $editor ne $remote->comment_editor ) { %format_opts = ( extra => DW::Template->template_string( 'default_editor_form.tt', { type => 'comment', format => $DW::Formats::formats{$editor}, exit_text => "Return to comment", exit_url => $result->{commentlink}, } ) ); } # now get the comment count my $entry; my $uid = LJ::get_userid( $post->{journal} ); $entry = LJ::Entry->new( $uid, ditemid => $post->{itemid} ) if $uid; $entry = undef unless $entry && $entry->valid; my $count; $count = $entry->reply_count( force_lookup => 1 ) if $entry; return DW::RPC->out( message => LJ::Lang::ml('comment.rpc.posted'), count => $count, %format_opts ); } 1;