mourningdove/cgi-bin/DW/Controller/Admin/UserViews.pm

204 lines
6.3 KiB
Perl
Raw Normal View History

2026-05-24 01:03:05 +00:00
#!/usr/bin/perl
#
# DW::Controller::Admin::UserViews
#
# Miscellaneous admin pages for viewing user data on the site.
#
# Authors:
# Jen Griffin <kareila@livejournal.com>
#
# Copyright (c) 2020 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::Admin::UserViews;
use strict;
use DW::Controller;
use DW::Controller::Admin;
use DW::Routing;
use DW::Template;
use DW::FormErrors;
use LJ::S2;
use LJ::Support;
use LJ::Talk;
use DW::Auth::Password;
my $styleinfo_privs = [
sub {
my $remote = LJ::isu( $_[0] ) ? $_[0] : $_[0]->{remote};
return (
LJ::Support::has_any_support_priv($remote),
LJ::Lang::ml("/admin/index.tt.anysupportpriv")
);
},
sub {
return ( $LJ::IS_DEV_SERVER, LJ::Lang::ml("/admin/index.tt.devserver") );
}
];
DW::Routing->register_string( "/admin/styleinfo", \&styleinfo_controller, app => 1 );
DW::Controller::Admin->register_admin_page(
'/',
path => 'styleinfo',
ml_scope => '/admin/styleinfo.tt',
privs => $styleinfo_privs
);
DW::Routing->register_string( "/admin/recent_comments", \&recent_comments_controller, app => 1 );
DW::Controller::Admin->register_admin_page(
'/',
path => 'recent_comments',
ml_scope => '/admin/recent_comments.tt',
privs => [ 'siteadmin:commentview', 'siteadmin:*' ]
);
DW::Routing->register_string( "/admin/impersonate", \&impersonate_controller, app => 1 );
# The impersonate page isn't listed in the index because the index is public
# and seeing it listed there makes people nervous that we use it all the time,
# which we don't. It's not a secret but we don't advertise it.
sub impersonate_controller {
my ( $ok, $rv ) = controller( form_auth => 1, privcheck => ['canview:*'] );
return $rv unless $ok;
my $r = DW::Request->get;
my $form_args = $r->post_args;
my $vars = {};
my $errors = DW::FormErrors->new;
if ( $r->did_post && LJ::check_referer('/admin/impersonate') ) {
my $remote = $rv->{remote};
my $u = LJ::load_user( $form_args->{username} );
$errors->add( 'username', '.error.invaliduser',
{ user => LJ::ehtml( $form_args->{username} ) } )
unless $u;
my $password = $form_args->{password};
$errors->add( 'password', '.error.invalidpassword' )
unless $password && DW::Auth::Password->check( $remote, $password );
my $reason = LJ::ehtml( LJ::trim( $form_args->{reason} ) );
$errors->add( 'reason', '.error.emptyreason' ) unless $reason;
unless ( $errors->exist ) {
$remote->logout;
if ( $u->make_fake_login_session ) {
# log for auditing
$remote->log_event( 'impersonator',
{ actiontarget => $u->id, remote => $remote, reason => $reason } );
$u->log_event( 'impersonated',
{ actiontarget => $u->id, remote => $remote, reason => $reason } );
LJ::statushistory_add( $u->id, $remote->id, 'impersonate', $reason );
return $r->redirect($LJ::SITEROOT);
}
else {
$errors->add( '', '.error.failedlogin' );
}
}
}
$vars->{errors} = $errors;
$vars->{formdata} = $form_args;
return DW::Template->render_template( 'admin/impersonate.tt', $vars );
}
sub recent_comments_controller {
my ( $ok, $rv ) = controller( privcheck => [ 'siteadmin:commentview', 'siteadmin:*' ] );
return $rv unless $ok;
my $scope = '/admin/recent_comments.tt';
my $r = DW::Request->get;
my $form_args = $r->get_args;
my $vars = {};
if ( my $user = $form_args->{user} ) {
$vars->{u} = ( $user =~ /^\#(\d+)/ ) ? LJ::load_userid($1) : LJ::load_user($user);
return error_ml("$scope.error.invaliduser") unless $vars->{u};
}
else {
return DW::Template->render_template( 'admin/recent_comments.tt', $vars );
}
my $now = time();
$vars->{num_hours} = sub { sprintf( "%.1f", ( $now - $_[0] ) / 3600 ) };
$vars->{get_journal} = sub { LJ::load_userid( $_[0]->{journalid} ) };
$vars->{get_comment} = sub { LJ::get_log2_row( $_[1], $_[0]->{nodeid} ) };
$vars->{talklink} = sub {
my ( $lrow, $r, $ju ) = @_;
return unless $lrow;
my $talkid = ( $r->{jtalkid} << 8 ) + $lrow->{anum};
my $talkurl = $ju->journal_base . "/$lrow->{ditemid}.html";
return LJ::Talk::talkargs( $talkurl, "thread=$talkid" ) . LJ::Talk::comment_anchor($talkid);
};
my $dbcr = LJ::get_cluster_reader( $vars->{u} );
return error_ml("$scope.error.nodb") unless $dbcr;
$vars->{rows} = $dbcr->selectall_arrayref(
"SELECT posttime, journalid, nodetype, nodeid, jtalkid, publicitem "
. "FROM talkleft WHERE userid=? ORDER BY posttime DESC LIMIT 250",
{ Slice => {} },
$vars->{u}->id
);
return DW::Template->render_template( 'admin/recent_comments.tt', $vars );
}
sub styleinfo_controller {
my ( $ok, $rv ) = controller( privcheck => $styleinfo_privs );
return $rv unless $ok;
my $scope = '/admin/styleinfo.tt';
my $r = DW::Request->get;
my $form_args = $r->did_post ? $r->post_args : $r->get_args;
my $vars = {};
$vars->{formdata} = $form_args;
return DW::Template->render_template( 'admin/styleinfo.tt', $vars )
unless $form_args->{user};
$vars->{u} = LJ::load_user( $form_args->{user} );
return error_ml( "$scope.error.nouser", { user => $form_args->{user} } )
unless $vars->{u};
return error_ml( "$scope.error.purged", { user => $form_args->{user} } )
if $vars->{u}->is_expunged;
return error_ml("$scope.error.s1") unless $vars->{u}->prop("stylesys") == 2;
if ( my $u_style = $vars->{u}->prop("s2_style") ) {
$vars->{s2style} = LJ::S2::load_style($u_style);
$vars->{public} = LJ::S2::get_public_layers();
}
$vars->{mysql_time} = sub { $_[0] ? LJ::mysql_time( $_[0] ) : "" };
$vars->{sort_keys} = sub {
[ sort { $a cmp $b } keys %{ $_[0] } ]
};
return DW::Template->render_template( 'admin/styleinfo.tt', $vars );
}
1;