#!/usr/bin/perl # # DW::InviteCodeRequests - Invite code request backend for Dreamwidth # # Authors: # Afuna # Pau Amma # # Copyright (c) 2009 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::InviteCodeRequests; =head1 NAME DW::InviteCodeRequests - Invite code request backend for Dreamwidth =head1 SYNOPSIS use DW::InviteCodeRequests; ## aggregate information # list all outstanding requests, by all users my @all_outstanding = DW::InviteCodeRequests->outstanding; ## per-user information, and request creation # list all of the invite code requests a user has made my @user_requests = DW::InviteCodeRequests->by_user( userid => $userid ); # find out how many invite code requests a user has outstanding my $outstanding_count = DW::InviteCodeRequests->outstanding_count( userid => $userid ); # create a new request for this user if they don't have outstanding requests my $new_request = DW::InviteCodeRequests->create( userid => $userid, reason => $reason ) if ! $outstanding_count; ## request processing # load a request object my $request = DW::InviteCodeRequests->new( reqid => $reqid ) # accept or reject the request $request->accept( num_invites => $num_invites ); $request->reject; ## request data my $id = $request->id; my $userid = $request->userid; my $status = $request->status; # accepted, rejected, outstanding my $reason = $request->reason; my $timegenerate = $request->timegenerate; # unix timestamp my $timeprocessed = $request->timeprocessed; # unix timestamp =cut use strict; use warnings; use fields qw( reqid userid status reason timegenerate timeprocessed ); =head1 API =head2 C<< $class->new( reqid => $reqid ) >> Returns object for invite code request, or undef if none exists =cut sub new { my ( $class, %opts ) = @_; my $reqid = $opts{reqid}; my $dbr = LJ::get_db_reader(); my $req = $dbr->selectrow_hashref( "SELECT reqid, userid, status, reason, timegenerate, timeprocessed " . "FROM acctcode_request WHERE reqid=?", undef, $reqid ); return undef unless defined $req; my $ret = fields::new($class); while ( my ( $k, $v ) = each %$req ) { $ret->{$k} = $v; } return $ret; } =head2 C<< $class->by_user( userid => $userid ) >> Returns an array of all the requests the user has made. =cut sub by_user { my ( $class, %opts ) = @_; my $dbr = LJ::get_db_reader(); my $sth = $dbr->prepare( "SELECT reqid, userid, status, reason, timegenerate, timeprocessed " . "FROM acctcode_request WHERE userid = ?" ) or die "Unable to retrieve user's requests: " . $dbr->errstr; $sth->execute( $opts{userid} ) or die "Unable to retrieve user's requests: " . $sth->errstr; my @requests; while ( my $req = $sth->fetchrow_hashref ) { my $ret = fields::new($class); while ( my ( $k, $v ) = each %$req ) { $ret->{$k} = $v; } push @requests, $ret; } return @requests; } =head2 C<< $class->create( userid => $userid, reason => $reason ) >> Create and return a request for additional invite codes, which will be put into a queue for admin review. Returns undef on failure. =cut sub create { my ( $class, %opts ) = @_; my $userid = $opts{userid}; my $reason = $opts{reason}; return undef unless DW::BusinessRules::InviteCodeRequests::can_request( user => LJ::load_userid($userid) ); my $dbh = LJ::get_db_writer(); $dbh->do( "INSERT INTO acctcode_request (userid, status, reason, timegenerate, timeprocessed) VALUES (?, 'outstanding', ?, UNIX_TIMESTAMP(), NULL)", undef, $userid, $reason ); die "Unable to request a new invite code: " . $dbh->errstr if $dbh->err; my $reqid = $dbh->{'mysql_insertid'}; return undef unless $reqid; return $class->new( reqid => $reqid ); } =head2 C<< $class->outstanding_count( userid => $userid ) >> Returns how many outstanding invite code requests a user has. =cut sub outstanding_count { my ( $class, %opts ) = @_; my $userid = $opts{userid}; my $dbr = LJ::get_db_reader(); my $count = $dbr->selectrow_array( "SELECT COUNT(*) FROM acctcode_request " . "WHERE userid = ? AND status='outstanding'", undef, $userid ); return $count; } =head2 C<< $class->outstanding >> Return a list of all outstanding invite code requests. =cut sub outstanding { my ($class) = @_; my $dbr = LJ::get_db_reader(); my $sth = $dbr->prepare( "SELECT reqid, userid, status, reason, timegenerate, timeprocessed " . "FROM acctcode_request WHERE status = 'outstanding'" ) or die "Unable to retrieve outstanding invite requests: " . $dbr->errstr; $sth->execute or die "Unable to retrieve outstanding invite requests: " . $sth->errstr; my @outstanding; while ( my $req = $sth->fetchrow_hashref ) { my $ret = fields::new($class); while ( my ( $k, $v ) = each %$req ) { $ret->{$k} = $v; } push @outstanding, $ret; } return @outstanding; } =head2 C<< $class->invite_sysbanned( user => $u ) >> Return whether this user is sysbanned from the invite codes system. Accepts a user object. =cut sub invite_sysbanned { my ( $class, %opts ) = @_; my $u = $opts{user}; return 1 if LJ::sysban_check( "invite_user", $u->user ); return 1 if LJ::sysban_check( "invite_email", $u->email_raw ); return 0; } =head2 C<< $object->accept( [num_invites => $num_invites ] ) >> Accept this request. =cut sub accept { my ( $self, %opts ) = @_; my $u = LJ::load_userid( $self->userid ); my @invitecodes = DW::InviteCodes->generate( count => $opts{num_invites}, owner => $u, reason => "Accepted: " . $self->reason ); die "Unable to generate invite codes " unless @invitecodes; $self->change_status( status => "accepted", count => $opts{num_invites} ); LJ::send_mail( { to => $u->email_raw, from => $LJ::ACCOUNTS_EMAIL, fromname => $LJ::SITENAME, subject => LJ::Lang::ml('email.invitecoderequest.accept.subject'), body => LJ::Lang::ml( 'email.invitecoderequest.accept.body2', { siteroot => $LJ::SITEROOT, invitesurl => $LJ::SITEROOT . '/invite', sitename => $LJ::SITENAMESHORT, number => $opts{num_invites}, codes => join( "\n", @invitecodes ), } ), } ); } =head2 C<< $object->reject >> Reject this request. =cut sub reject { my ( $self, %opts ) = @_; $self->change_status( status => "rejected" ); my $u = LJ::load_userid( $self->userid ); LJ::send_mail( { to => $u->email_raw, from => $LJ::ACCOUNTS_EMAIL, fromname => $LJ::SITENAME, subject => LJ::Lang::ml('email.invitecoderequest.reject.subject'), body => LJ::Lang::ml('email.invitecoderequest.reject.body'), } ); } =head2 C<< $object->change_status( status => $status ) >> Internal. Accepts or rejects a request. =cut sub change_status { my $dbh = LJ::get_db_writer(); my ( $self, %opts ) = @_; $dbh->do( "UPDATE acctcode_request SET status = ?, timeprocessed = UNIX_TIMESTAMP() WHERE reqid = ?", undef, $opts{status}, $self->id ); die "Unable to change status to $opts{status}: " . $dbh->errstr if $dbh->err; } =head2 C<< $object->id >> Returns the id of this object. =cut sub id { my ($self) = @_; return $self->{reqid}; } =head2 C<< $object->userid >> Returns the userid of the user who made the request. =cut sub userid { my ($self) = @_; return $self->{userid}; } =head2 C<< $object->status >> Returns the status of the request. Values can be one of "accepted", "rejected", "outstanding". =cut sub status { my ($self) = @_; return $self->{status}; } =head2 C<< $object->reason >> Returns the user-provided reason for requesting more invite codes. =cut sub reason { my ($self) = @_; return $self->{reason}; } =head2 C<< $object->timegenerate >> Returns the time the request was made as a unix timestamp. =cut sub timegenerate { my ($self) = @_; return $self->{timegenerate}; } =head2 C<< $object->timeprocessed >> Returns the time the request was accepted or rejected as a unix timestamp. =cut sub timeprocessed { my ($self) = @_; return $self->{timeprocessed}; } =head1 BUGS =head1 AUTHORS Afuna Pau Amma =head1 COPYRIGHT AND LICENSE Copyright (c) 2009 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'. =cut 1;