Admin frontend for maintaining promotional codes.
Patch by exor674.
diffs (355 lines):
diff -r 17f387d8f318 -r ac1192735bd3 cgi-bin/DW/InviteCodes.pm
--- a/cgi-bin/DW/InviteCodes.pm Wed Jan 06 01:13:10 2010 +0000
+++ b/cgi-bin/DW/InviteCodes.pm Wed Jan 06 01:46:12 2010 +0000
@@ -156,6 +156,42 @@ sub get_promo_code_info {
return undef unless $code && $code =~ /^[a-z0-9]+$/i; # make sure the code
is valid first
return $dbh->selectrow_hashref( "SELECT * FROM acctcode_promo WHERE code =
?", undef, $code );
+}
+
+=head2 C<< $class->get_promo_codes( state => $state ) >>
+
+Return the list of promo codes, optionally filtering by state.
+State can be:
+ * active ( active promo codes )
+ * inactive ( inactive promo codes )
+ * unused ( unused promo codes )
+ * noneleft ( no uses left )
+
+=cut
+
+sub get_promo_codes {
+ my ( $class, %opts ) = @_;
+ my $dbh = LJ::get_db_writer();
+ my $state = $opts{state} || '';
+
+ my $sql = "SELECT * FROM acctcode_promo";
+ if ( $state eq 'active' ) {
+ $sql .= " WHERE active = '1' AND current_count < max_count";
+ } elsif ( $state eq 'inactive' ) {
+ $sql .= " WHERE active = '0' OR current_count >= max_count";
+ } elsif ( $state eq 'unused' ) {
+ $sql .= " WHERE current_count = 0"
+ } elsif ( $state eq 'noneleft' ) {
+ $sql .= " WHERE current_count >= max_count";
+ }
+ my $sth = $dbh->prepare( $sql ) or die $dbh->errstr;
+ $sth->execute() or die $dbh->errstr;
+
+ my @out;
+ while ( my $row = $sth->fetchrow_hashref ) {
+ push @out, $row;
+ }
+ return \...@out;
}
=head2 C<< $class->check_code( code => $invite [, userid => $recipient] ) >>
diff -r 17f387d8f318 -r ac1192735bd3 htdocs/admin/index.bml
--- a/htdocs/admin/index.bml Wed Jan 06 01:13:10 2010 +0000
+++ b/htdocs/admin/index.bml Wed Jan 06 01:46:12 2010 +0000
@@ -53,6 +53,8 @@ body<=
'<?_ml .admin.faq.link _ml?>', '<?_ml .admin.faq.text _ml?>', [
'faqadd', 'faqedit', 'faqcat' ] ],
[ 'fileedit/',
'<?_ml .admin.file_edit.link _ml?>', 'Allows you to edit various
include files.', [ 'fileedit' ] ],
+ [ 'invites/promo',
+ '<?_ml .admin.invite_promo.link _ml?>', '<?_ml
.admin.invite_promo.text _ml?>', [ 'payments' ] ],
[ 'invites/requests',
'<?_ml .admin.invite_requests.link _ml?>', '<?_ml
.admin.invite_requests.text _ml?>', [ 'payments' ] ],
[ 'invites/review',
diff -r 17f387d8f318 -r ac1192735bd3 htdocs/admin/index.bml.text
--- a/htdocs/admin/index.bml.text Wed Jan 06 01:13:10 2010 +0000
+++ b/htdocs/admin/index.bml.text Wed Jan 06 01:46:12 2010 +0000
@@ -28,6 +28,9 @@
.admin.invite_codes.link=Invite Codes
.admin.invite_codes.text=Shows the provenance of an invite code.
+
+.admin.invite_promo.link=Promotional Invite Codes
+.admin.invite_promo.text=Allows you to manage promotional invite codes.
.admin.invite_requests.link=Invite Code Requests
.admin.invite_requests.text=Allows you to review requests for invite codes.
diff -r 17f387d8f318 -r ac1192735bd3 htdocs/admin/invites/promo.bml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/admin/invites/promo.bml Wed Jan 06 01:46:12 2010 +0000
@@ -0,0 +1,213 @@
+<?_c
+# promo.bml - manage promo codes
+#
+# Author: Andrea Nall <[email protected]>
+#
+# 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'.
+_c?>
+<?page
+body<=
+<?_code
+{
+ use strict;
+ use vars qw( %GET %POST $title );
+ use DW::InviteCodes;
+
+ $title = $ML{'.title'};
+
+ my $remote = LJ::get_remote;
+ return "<?needlogin?>" unless $remote;
+
+ return "You are not authorized to view this page."
+ unless $remote->has_priv( "payments" );
+
+ my $ret = "";
+
+ my $code = $GET{code};
+ my $state = lc( $GET{state} || "" );
+
+ my $create_form = sub {
+ my $data = $_[0] || {};
+ my $errors = $_[1] || {};
+ my $suggest_u = ( $data && $data->{suggest_journalid} ) ?
LJ::load_userid($data->{suggest_journalid}) : undef;
+
+ if ( $state eq 'create' ) {
+ $title = $ML{'.title.create'};
+ $ret .= '<a href="/admin/invites/promo">' . $ML{'.return'} .
'</a>';
+ } else {
+ $title = BML::ml('.title.edit', { code => $code } );
+ $ret .= '<a href="/admin/invites/promo?state=' . $state . '">' .
$ML{'.return'} . '</a>';
+ }
+
+ $ret .= "<form method='POST' action='/admin/invites/promo'>";
+ $ret .= LJ::form_auth();
+ $ret .= LJ::html_hidden( { name => 'state', value => $state });
+ $ret .= LJ::html_check( { id => 'active', name => 'active', value =>
1, selected => $data->{active}, label => $ML{'.field.active.label'} } );
+ $ret .= " <strong>[$ML{'.error.label'} " . join(', ',
@{$errors->{active}}) . "]</strong>" if $errors->{active};
+ $ret .= '<br />';
+ if ( $state eq 'create' ) {
+ $ret .= LJ::labelfy( 'code', "$ML{'.field.code.label'} " );
+ $ret .= LJ::html_text( { id => 'code', name => 'code', value => (
$data->{code} || "" ), size => 23, maxlength => 20 } );
+ $ret .= " <strong>[$ML{'.error.label'} " . join(', ',
@{$errors->{code}}) . "]</strong>" if $errors->{code};
+ $ret .= '<br />';
+ } else {
+ $ret .= LJ::html_hidden( { name => 'code', value => $code });
+ $ret .= "$ML{'.field.code.label'} <strong>". ( $data->{code} || ""
) . '</strong>';
+ $ret .= " <strong>[$ML{'.error.label'} " . join(', ',
@{$errors->{code}}) . "]</strong>" if $errors->{code};
+ $ret .= '<br />';
+ }
+ $ret .= LJ::labelfy( 'max_count', "$ML{'.field.count.label'} " ) . (
$data->{current_count} || 0 ) . $ML{'.count.outof'};
+ $ret .= LJ::html_text( { id => 'max_count', name => 'max_count', value
=> ( $data->{max_count} || 0 ), size => 7 } );
+ $ret .= " <strong>[$ML{'.error.label'} " . join(', ',
@{$errors->{max_count}}) . "]</strong>" if $errors->{max_count};
+ $ret .= '<br />';
+ $ret .= LJ::labelfy( 'suggest_journal',
"$ML{'.field.suggest_journal.label'} " );
+ $ret .= LJ::html_text( { id => 'suggest_journal', name =>
'suggest_journal', value => ( $suggest_u ? $suggest_u->username : (
$data->{suggest_journal} || "" ) ), size => 28, maxlength => 25 } );
+ $ret .= " <strong>[$ML{'.error.label'} " . join(', ',
@{$errors->{suggest_journal}}) . "]</strong>" if $errors->{suggest_journal};
+ $ret .= '<br />';
+ $ret .= LJ::html_submit( value => $ML{ ( $data ? '.btn.save' :
'.btn.create' ) } );
+ $ret .= "</form>";
+ };
+
+ if ( LJ::did_post ) {
+ return LJ::error_list( $ML{'error.invalidform'} )
+ unless LJ::check_form_auth();
+ $code = $POST{code} || "";
+ $state = lc( $POST{state} || "" );
+ if ( $state eq 'create' ) {
+ my $data = {
+ active => $POST{active} ? 1 : 0,
+ code => $code,
+ current_count => 0,
+ max_count => $POST{max_count} || 0,
+ suggest_journal => $POST{suggest_journal},
+ };
+ my $valid = 1;
+ my $errors = {};
+ if ( ! $code ) {
+ push @{$errors->{code}}, $ML{'.error.code.missing'};
+ $valid = 0;
+ } elsif ( ! ( $code =~ /^[a-z0-9]+$/i ) ) {
+ push @{$errors->{code}}, $ML{'.error.code.invalid_character'};
+ $valid = 0;
+ } elsif ( DW::InviteCodes->is_promo_code( code => $code ) ) {
+ push @{$errors->{code}}, $ML{'.error.code.exists'};
+ $valid = 0;
+ }
+ if ( $POST{max_count} < 0 ) {
+ push @{$errors->{max_count}}, $ML{'.error.count.negative'};
+ }
+ if ( $POST{suggest_journal} ) {
+ my $user = LJ::load_user($POST{suggest_journal});
+ if ($user) {
+ $data->{suggest_journalid} = $user->userid;
+ } else {
+ push @{$errors->{suggest_journal}},
$ML{'.error.suggest_journal.invalid'};
+ $valid = 0;
+ }
+ } else {
+ $data->{suggest_journal} = undef;
+ }
+ if ( $valid ) {
+ my $dbh = LJ::get_db_writer();
+ $dbh->do( "INSERT INTO acctcode_promo (code, max_count,
active, suggest_journalid) VALUES (?, ?, ?, ?)", undef,
+ $data->{code}, $data->{max_count},
$data->{active}, $data->{suggest_journalid} ) or die $dbh->errstr;
+ } else {
+ $create_form->( $data, $errors );
+ return $ret;
+ }
+ $state = "";
+ $code = "";
+ } else {
+ my $data = {
+ active => $POST{active} ? 1 : 0,
+ code => $code,
+ current_count => 0,
+ max_count => $POST{max_count} || 0,
+ suggest_journal => $POST{suggest_journal},
+ };
+ my $valid = 1;
+ my $errors = {};
+ my $info;
+ if ( ! $code ) {
+ push @{$errors->{code}}, $ML{'.error.code.missing'};
+ $valid = 0;
+ } elsif ( ! ref ( $info = DW::InviteCodes->get_promo_code_info(
code => $code ) ) ) {
+ push @{$errors->{code}}, $ML{'.error.code.invalid'};
+ $valid = 0;
+ } else {
+ $data->{current_count} = $info->{current_count};
+ }
+ if ( $POST{max_count} < 0 ) {
+ push @{$errors->{max_count}}, $ML{'.error.count.negative'};
+ }
+ if ( $POST{suggest_journal} ) {
+ my $user = LJ::load_user($POST{suggest_journal});
+ if ($user) {
+ $data->{suggest_journalid} = $user->userid;
+ } else {
+ push @{$errors->{suggest_journal}},
$ML{'.error.suggest_journal.invalid'};
+ $valid = 0;
+ }
+ } else {
+ $data->{suggest_journal} = undef;
+ }
+ if ( $valid ) {
+ my $dbh = LJ::get_db_writer();
+ $dbh->do( "UPDATE acctcode_promo SET max_count = ?, active =
?, suggest_journalid = ? WHERE code = ?", undef,
+ $data->{max_count}, $data->{active},
$data->{suggest_journalid}, $data->{code} ) or die $dbh->errstr;
+ } else {
+ $create_form->( $data, $errors );
+ return $ret;
+ }
+ $code = "";
+ }
+ }
+
+ if ( $state eq 'create' ) {
+ $create_form->( );
+ } elsif ( DW::InviteCodes->is_promo_code( code => $code ) ) {
+ $create_form->( DW::InviteCodes->get_promo_code_info( code => $code )
);
+ } else {
+ my $codes = DW::InviteCodes->get_promo_codes( state => $state );
+
+ $ret .= '<a href="/admin/invites/promo?state=create">' .
$ML{'.state.new'} . '</a> | ';
+ $ret .= '<a href="/admin/invites/promo">' . $ML{'.state.unfiltered'} .
'</a> | ';
+ $ret .= '<a href="/admin/invites/promo?state=active">' .
$ML{'.state.active'} . '</a> | ';
+ $ret .= '<a href="/admin/invites/promo?state=inactive">' .
$ML{'.state.inactive'} . '</a> | ';
+ $ret .= '<a href="/admin/invites/promo?state=unused">' .
$ML{'.state.unused'} . '</a> | ';
+ $ret .= '<a href="/admin/invites/promo?state=noneleft">' .
$ML{'.state.noneleft'} . '</a>';
+
+ $ret .= "<table>";
+ $ret .=
"<tr><th>$ML{'.heading.code'}</th><th>$ML{'.heading.active'}</th><th>$ML{'.heading.count'}</th><th>$ML{'.heading.suggest'}</th></tr>";
+
+ if ( scalar( @$codes ) ) {
+ foreach my $code (@$codes) {
+ my $suggest_u = $code->{suggest_journalid} ?
LJ::load_userid($code->{suggest_journalid}) : undef;
+ $ret .= "<tr>";
+ $ret .= "<td><a href=\"/admin/invites/promo?code=" .
LJ::eurl($code->{code}) . "&state=$state\">" . LJ::ehtml($code->{code}) .
"</a></td>";
+ $ret .= "<td>" . ( $code->{active} ? $ML{'.active.active'} :
$ML{'.active.inactive'} ) . "</td>";
+ $ret .= "<td>" . $code->{current_count} . $ML{'.count.outof'}
. $code->{max_count} . "</td>";
+ $ret .= "<td>" . ( $suggest_u ? $suggest_u->ljuser_display :
$ML{'.suggest.done'} ) . "</td>";
+ $ret .= "</tr>";
+ }
+ } else {
+ $ret .= '<tr><th colspan="4">' . $ML{'.nomatch'} . '</th></tr>';
+ }
+ $ret .= "</table>";
+ }
+
+ return $ret;
+}
+_code?>
+<=body
+title=><?_code return $title; _code?>
+head<=
+<style type="text/css">
+td, th { padding: 4px; }
+</style>
+<=head
+page?>
diff -r 17f387d8f318 -r ac1192735bd3 htdocs/admin/invites/promo.bml.text
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/admin/invites/promo.bml.text Wed Jan 06 01:46:12 2010 +0000
@@ -0,0 +1,63 @@
+.active.active=active
+
+.active.inactive=inactive
+
+.btn.create=Create
+
+.btn.save=Save
+
+.count.outof= out of
+
+.error.label=Error:
+
+.error.code.exists=Promo code already exists
+
+.error.code.invalid=Code invalid
+
+.error.code.invalid_character=Code must be [a-z0-9] only
+
+.error.code.missing=Code must be specified
+
+.error.count.negative=Count must be positive
+
+.error.suggest_journal.invalid=Cannot find that user
+
+.field.active.label=Active
+
+.field.code.label=Code:
+
+.field.count.label=Count:
+
+.field.suggest_journal.label=Suggest Journal:
+
+.heading.active=Active
+
+.heading.code=Code
+
+.heading.count=Count
+
+.heading.suggest=Suggest Journal
+
+.nomatch=No promo codes match your criteria
+
+.return=Return to list
+
+.suggest.none=<em>(none)</em>
+
+.state.active=Active
+
+.state.new=Create new
+
+.state.noneleft=All creations used
+
+.state.unfiltered=Unfiltered
+
+.state.unused=Unused
+
+.state.inactive=Inactive
+
+.title=Manage Promo Codes
+
+.title.create=Create Promo Code
+
+.title.edit=Edit Promo Code: [[code]]
_______________________________________________
dw-changelog mailing list
[email protected]
http://lists.dwscoalition.org/mailman/listinfo/dw-changelog