#!/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;
