mourningdove/cgi-bin/DW/Controller/RPC/Misc.pm

370 lines
12 KiB
Perl
Raw Normal View History

2026-05-24 01:03:05 +00:00
#!/usr/bin/perl
#
# DW::Controller::RPC::Misc
#
# The AJAX endpoint for general calls.
#
# Authors:
# Mark Smith <mark@dreamwidth.org>
#
# 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;