[dw-free] allow promo codes to have attached paid time
[commit: http://hg.dwscoalition.org/dw-free/rev/53fc4397240f]
http://bugs.dwscoalition.org/show_bug.cgi?id=3128
Allow promo invite codes to come preloaded with paid time.
Patch by
exor674.
Files modified:
http://bugs.dwscoalition.org/show_bug.cgi?id=3128
Allow promo invite codes to come preloaded with paid time.
Patch by
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Files modified:
- bin/upgrading/update-db-general.pl
- cgi-bin/DW/InviteCodes.pm
- cgi-bin/DW/InviteCodes/Promo.pm
- cgi-bin/LJ/User.pm
- cgi-bin/LJ/Widget/CreateAccount.pm
- cgi-bin/LJ/Widget/CreateAccountProgressMeter.pm
- htdocs/admin/invites/promo.bml
- htdocs/admin/invites/promo.bml.text
-------------------------------------------------------------------------------- diff -r 88ea0f26d63f -r 53fc4397240f bin/upgrading/update-db-general.pl --- a/bin/upgrading/update-db-general.pl Thu Oct 14 11:47:13 2010 +0800 +++ b/bin/upgrading/update-db-general.pl Thu Oct 14 13:12:05 2010 +0800 @@ -2908,6 +2908,8 @@ CREATE TABLE acctcode_promo ( current_count int(10) unsigned not null default 0, active enum('1','0') not null default 1, suggest_journalid int unsigned, + paid_class varchar(100), + paid_months tinyint unsigned, PRIMARY KEY ( code ) ) @@ -3854,6 +3856,14 @@ EOF do_alter( 'externalaccount', "ALTER TABLE externalaccount ADD COLUMN options blob"); } + + unless ( column_type( 'acctcode_promo', 'paid_class' ) ) { + do_alter( 'acctcode_promo', "ALTER TABLE acctcode_promo ADD COLUMN paid_class varchar(10000)" ); + } + + unless ( column_type( 'acctcode_promo', 'paid_months' ) ) { + do_alter( 'acctcode_promo', "ALTER TABLE acctcode_promo ADD COLUMN paid_months tinyint unsigned" ); + } }); diff -r 88ea0f26d63f -r 53fc4397240f cgi-bin/DW/InviteCodes.pm --- a/cgi-bin/DW/InviteCodes.pm Thu Oct 14 11:47:13 2010 +0800 +++ b/cgi-bin/DW/InviteCodes.pm Thu Oct 14 13:12:05 2010 +0800 @@ -67,6 +67,8 @@ use constant DIGITS => qw(A B C D E F G use constant DIGITS => qw(A B C D E F G H J K L M N P Q R S T U V W X Y Z 2 3 4 5 6 7 8 9); use constant { CODE_LEN => AUTH_LEN + ACID_LEN, DIGITS_LEN => scalar(DIGITS) }; +use DW::InviteCodes::Promo; + =head1 API =head2 C<< $class->generate( [ count => $howmany, ] owner => $forwho, reason => $why >> @@ -129,75 +131,6 @@ sub could_be_code { return 1; } -=head2 C<< $class->is_promo_code( code => $code ) >> - -Returns if the given code is a promo code or not. - -=cut - -sub is_promo_code { - my ( $class, %opts ) = @_; - - my $promo_code_info = $class->get_promo_code_info( code => $opts{code} ); - - return ref $promo_code_info ? 1 : 0; -} - -=head2 C<< $class->get_promo_code_info( code => $code ) >> - -Return the info for this promo code in a hashref. - -=cut - -sub get_promo_code_info { - my ( $class, %opts ) = @_; - my $dbh = LJ::get_db_writer(); - my $code = $opts{code}; - - 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 ) - * all ( all promo codes ) - -=cut - -sub get_promo_codes { - my ( $class, %opts ) = @_; - my $dbh = LJ::get_db_writer(); - my $state = $opts{state} || 'active'; - - my $sql = "SELECT * FROM acctcode_promo"; - if ( $state eq 'all' ) { - # do nothing - } elsif ( $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] ) >> Checks whether code $invite is valid before trying to create an account. Takes @@ -213,7 +146,7 @@ sub check_code { # check if this code is a promo code first # if it is, make sure it's active and we're not over the creation limit for the code - my $promo_code_info = $class->get_promo_code_info( code => $code ); + my $promo_code_info = DW::InviteCodes::Promo->load( code => $code ); if ( ref $promo_code_info ) { return 0 unless $promo_code_info->{active} && ( $promo_code_info->{current_count} < $promo_code_info->{max_count} ); return 1; @@ -277,24 +210,6 @@ sub use_code { undef, $opts{user}->{userid}, $self->{acid} ); return 1; # 1 means success? Needs error return in that case. -} - -=head2 C<< $class->use_promo_code >> - -Increments the current_count on the given promo code. - -=cut - -sub use_promo_code { - my ( $class, %opts ) = @_; - my $dbh = LJ::get_db_writer(); - my $code = $opts{code}; - - return 0 unless $class->is_promo_code( code => $code ); - - $dbh->do( "UPDATE acctcode_promo SET current_count = current_count + 1 WHERE code = ?", undef, $code ); - - return 1; } =head2 C<< $object->send_code ( [ email => $email ] ) >> diff -r 88ea0f26d63f -r 53fc4397240f cgi-bin/DW/InviteCodes/Promo.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cgi-bin/DW/InviteCodes/Promo.pm Thu Oct 14 13:12:05 2010 +0800 @@ -0,0 +1,187 @@ +#!/usr/bin/perl +# +# DW::InviteCodes::Promo - Represents a promotional invite code +# +# Authors: +# Andrea Nall <anall@andreanall.com> +# +# Copyright (c) 2010 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::InviteCodes::Promo; + +=head1 NAME + +DW::InviteCodes::Promo - Represents a promotional invite code + +=cut + +use strict; + +sub _from_row { + my ( $class, $row ) = @_; + return bless $row, $class; +} + +=head1 CLASS METHODS + +=head2 C<< DW::InviteCodes::Promo->load( code => $code ); >> + +Gets a DW::InviteCode::Promo objct + +=cut +# FIXME: Consider process caching and/or memcache, if this is a busy enough path +sub load { + my ( $class, %opts ) = @_; + my $dbh = LJ::get_db_writer(); + my $code = $opts{code}; + + return undef unless $code && $code =~ /^[a-z0-9]+$/i; # make sure the code is valid first + my $data = $dbh->selectrow_hashref( "SELECT * FROM acctcode_promo WHERE code = ?", undef, $code ); + return undef unless $data; + + return $class->_from_row( $data ); +} + +=head2 C<< DW::InviteCodes::Promo->load_bulk( 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 ) + * all ( all promo codes ) + +=cut +sub load_bulk { + my ( $class, %opts ) = @_; + my $dbh = LJ::get_db_writer(); + my $state = $opts{state} || 'active'; + + my $sql = "SELECT * FROM acctcode_promo"; + if ( $state eq 'all' ) { + # do nothing + } elsif ( $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, $class->_from_row( $row ); + } + return \@out; +} + +=head2 C<< $class->is_promo_code( code => $code ) >> + +Returns if the given code is a promo code or not. + +=cut +sub is_promo_code { + my ( $class, %opts ) = @_; + + my $promo_code_info = $class->load( %opts ); + + return ref $promo_code_info ? 1 : 0; +} + +=head1 INSTANCE METHODS + +=head2 C<< $self->apply_for_user( $u ) >> + +Handle any post-create operations for this user. + +=cut +sub apply_for_user { + my ( $self, $u ) = @_; + + my $code = $self->code; + my $paid_type = $self->paid_class; + my $paid_months = $self->paid_months; + + LJ::statushistory_add( $u, undef, 'create_from_promo', "Created new account from promo code '$code'." ); + + if ( defined $paid_type ) { + if ( DW::Pay::add_paid_time( $u, $paid_type, $paid_months ) ) { + LJ::statushistory_add( $u, undef, 'paid_from_promo', "Created new '$paid_type' account from promo code '$code'." ); + } + } +} + +=head2 C<< $self->code >> + +=cut +sub code { + return $_[0]->{code}; +} + +=head2 C<< $self->paid_class_name >> + +Return the display name of this account class. + +=cut +sub paid_class_name { + my $self = $_[0]; + + foreach my $cap ( keys %LJ::CAP ) { + return $LJ::CAP{$cap}->{_visible_name} + if $LJ::CAP{$cap} && $LJ::CAP{$cap}->{_account_type} eq $self->paid_class; + } + + return 'Invalid Account Class'; +} + +=head2 C<< $self->paid_months >> + +=cut +sub paid_months { + return $_[0]->{paid_class} ? $_[0]->{paid_months} : 0; +} + +=head2 C<< $self->paid_type >> + +=cut +sub paid_class { + return $_[0]->{paid_class}; +} + +=head2 C<< $self->suggest_journal + +Return the LJ::User to suggest + +=cut +sub suggest_journal { + my $id = $_[0]->{suggest_journalid}; + return $id ? LJ::load_userid( $id ) : undef; +} + +=head2 C<< $self->use_code >> + +Increments the current_count on the given promo code. + +=cut +sub use_code { + my ( $self ) = @_; + my $dbh = LJ::get_db_writer(); + + my $code = $self->code; + + $dbh->do( "UPDATE acctcode_promo SET current_count = current_count + 1 WHERE code = ?", undef, $code ); + + return 1; +} + +1; diff -r 88ea0f26d63f -r 53fc4397240f cgi-bin/LJ/User.pm --- a/cgi-bin/LJ/User.pm Thu Oct 14 11:47:13 2010 +0800 +++ b/cgi-bin/LJ/User.pm Thu Oct 14 13:12:05 2010 +0800 @@ -42,6 +42,7 @@ use DW::Pay; use DW::Pay; use DW::User::ContentFilters; use DW::User::Edges; +use DW::InviteCodes::Promo; use LJ::Community; use LJ::Subscription; @@ -250,8 +251,9 @@ sub create_personal { if ( $LJ::USE_ACCT_CODES && $opts{code} ) { my $code = $opts{code}; my $itemidref; - if ( DW::InviteCodes->is_promo_code( code => $code ) ) { - LJ::statushistory_add( $u, undef, 'create_from_promo', "Created new account from promo code '$code'." ); + my $promo_code = DW::InviteCodes::Promo->load( code => $code ); + if ( $promo_code ) { + $promo_code->apply_for_user( $u ); } elsif ( my $cart = DW::Shop::Cart->get_from_invite( $code, itemidref => \$itemidref ) ) { my $item = $cart->get_item( $itemidref ); if ( $item && $item->isa( 'DW::Shop::Item::Account' ) ) { diff -r 88ea0f26d63f -r 53fc4397240f cgi-bin/LJ/Widget/CreateAccount.pm --- a/cgi-bin/LJ/Widget/CreateAccount.pm Thu Oct 14 11:47:13 2010 +0800 +++ b/cgi-bin/LJ/Widget/CreateAccount.pm Thu Oct 14 13:12:05 2010 +0800 @@ -251,16 +251,24 @@ sub render_body { $ret .= "</label>"; $ret .= "</td></tr>\n"; - if ( $LJ::USE_ACCT_CODES && !DW::InviteCodes->is_promo_code( code => $code ) ) { - my $item = DW::InviteCodes->paid_status( code => $code ); - if ( $item ) { - $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>"; - if ( $item->permanent ) { - $ret .= $class->ml( 'widget.createaccount.field.paidaccount.permanent', { type => "<strong>" . $item->class_name . "</strong>" } ); - } else { - $ret .= $class->ml( 'widget.createaccount.field.paidaccount', { type => "<strong>" . $item->class_name . "</strong>", nummonths => $item->months } ); + if ( $LJ::USE_ACCT_CODES ) { + if ( my $pc = DW::InviteCodes::Promo->load( code => $code ) ) { + if ( $pc->paid_class ) { + $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>"; + $ret .= $class->ml( 'widget.createaccount.field.paidaccount', { type => "<strong>" . $pc->paid_class_name . "</strong>", nummonths => $pc->paid_months } ); + $ret .= "</td></tr>"; + } + } else { + my $item = DW::InviteCodes->paid_status( code => $code ); + if ( $item ) { + $ret .= "<tr valign='top'><td class='field-name'> </td>\n<td>"; + if ( $item->permanent ) { + $ret .= $class->ml( 'widget.createaccount.field.paidaccount.permanent', { type => "<strong>" . $item->class_name . "</strong>" } ); + } else { + $ret .= $class->ml( 'widget.createaccount.field.paidaccount', { type => "<strong>" . $item->class_name . "</strong>", nummonths => $item->months } ); + } + $ret .= "</td></tr>"; } - $ret .= "</td></tr>"; } } @@ -319,11 +327,9 @@ sub handle_post { $from_post{code_valid} = 1; # and if this is a community promo code, set the inviter - if ( my $pc = DW::InviteCodes->get_promo_code_info( code => $code ) ) { - if ( $pc->{suggest_journalid} ) { - my $invu = LJ::load_userid( $pc->{suggest_journalid} ); - $post->{from} = $invu->user if $invu; - } + if ( my $pc = DW::InviteCodes::Promo->load( code => $code ) ) { + my $invu = $pc->suggest_journal; + $post->{from} = $invu->user if $invu; } } else { @@ -453,8 +459,8 @@ sub handle_post { # we're all done; mark the invite code as used if ( $LJ::USE_ACCT_CODES && $code ) { - if ( DW::InviteCodes->is_promo_code( code => $code ) ) { - DW::InviteCodes->use_promo_code( code => $code ); + if ( my $pc = DW::InviteCodes::Promo->load( code => $code ) ) { + $pc->use_code; } else { my $invitecode = DW::InviteCodes->new( code => $code ); $invitecode->use_code( user => $nu ); diff -r 88ea0f26d63f -r 53fc4397240f cgi-bin/LJ/Widget/CreateAccountProgressMeter.pm --- a/cgi-bin/LJ/Widget/CreateAccountProgressMeter.pm Thu Oct 14 11:47:13 2010 +0800 +++ b/cgi-bin/LJ/Widget/CreateAccountProgressMeter.pm Thu Oct 14 13:12:05 2010 +0800 @@ -27,7 +27,7 @@ sub render_body { my $given_step = $opts{step} || 1; my @steps_to_show = !LJ::is_enabled( 'payments' ) - || ( $LJ::USE_ACCT_CODES && $given_step == 1 && !DW::InviteCodes->is_promo_code( code => $opts{code} ) && DW::InviteCodes->paid_status( code => $opts{code} ) ) + || ( $LJ::USE_ACCT_CODES && $given_step == 1 && !DW::InviteCodes::Promo->is_promo_code( code => $opts{code} ) && DW::InviteCodes->paid_status( code => $opts{code} ) ) || ( $given_step > 1 && $u && $u->is_paid ) ? ( 1, 2, 4 ) : ( 1..4 ); diff -r 88ea0f26d63f -r 53fc4397240f htdocs/admin/invites/promo.bml --- a/htdocs/admin/invites/promo.bml Thu Oct 14 11:47:13 2010 +0800 +++ b/htdocs/admin/invites/promo.bml Thu Oct 14 13:12:05 2010 +0800 @@ -15,7 +15,7 @@ body<= { use strict; use vars qw( %GET %POST $title ); - use DW::InviteCodes; + use DW::InviteCodes::Promo; $title = $ML{'.title'}; @@ -68,6 +68,24 @@ body<= $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::labelfy( 'paid_class', "$ML{'.field.paid_class.label'} " ); + $ret .= LJ::html_select( { + id => 'paid_class', + name => 'paid_class', + selected => ( $data->{paid_class} || '' ), + }, + { value => '', text => $ML{'.field.paid_class.none'} }, + { value => 'paid', text => $ML{'.field.paid_class.paid'} }, + { value => 'premium', text => $ML{'.field.paid_class.premium'} }, + ); + $ret .= '<br />'; + + $ret .= LJ::labelfy( 'paid_months', "$ML{'.field.paid_months.label'} " ); + $ret .= LJ::html_text( { id => 'paid_months', name => 'paid_months', value => ( $data->{paid_months} || "" ), size => 10, maxlength => 2 } ); + $ret .= '<br />'; + + $ret .= LJ::html_submit( value => $ML{ ( $data ? '.btn.save' : '.btn.create' ) } ); $ret .= "</form>"; }; @@ -84,6 +102,8 @@ body<= current_count => 0, max_count => $POST{max_count} || 0, suggest_journal => $POST{suggest_journal}, + paid_class => $POST{paid_class} || '', + paid_months => $POST{paid_months} || undef, }; my $valid = 1; my $errors = {}; @@ -93,7 +113,7 @@ body<= } elsif ( ! ( $code =~ /^[a-z0-9]+$/i ) ) { push @{$errors->{code}}, $ML{'.error.code.invalid_character'}; $valid = 0; - } elsif ( DW::InviteCodes->is_promo_code( code => $code ) ) { + } elsif ( DW::InviteCodes::Promo->is_promo_code( code => $code ) ) { push @{$errors->{code}}, $ML{'.error.code.exists'}; $valid = 0; } @@ -111,10 +131,14 @@ body<= } else { $data->{suggest_journal} = undef; } + if ( $data->{paid_class} !~ /^(paid|premium)$/ ) { + $data->{paid_class} = undef; + $data->{paid_months} = 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; + $dbh->do( "INSERT INTO acctcode_promo (code, max_count, active, suggest_journalid, paid_class, paid_months) VALUES (?, ?, ?, ?, ?, ?)", undef, + $data->{code}, $data->{max_count}, $data->{active}, $data->{suggest_journalid}, $data->{paid_class}, $data->{paid_months} ) or die $dbh->errstr; } else { $create_form->( $data, $errors ); return $ret; @@ -128,6 +152,8 @@ body<= current_count => 0, max_count => $POST{max_count} || 0, suggest_journal => $POST{suggest_journal}, + paid_class => $POST{paid_class} || '', + paid_months => $POST{paid_months} || undef, }; my $valid = 1; my $errors = {}; @@ -135,7 +161,7 @@ body<= if ( ! $code ) { push @{$errors->{code}}, $ML{'.error.code.missing'}; $valid = 0; - } elsif ( ! ref ( $info = DW::InviteCodes->get_promo_code_info( code => $code ) ) ) { + } elsif ( ! ref ( $info = DW::InviteCodes::Promo->load( code => $code ) ) ) { push @{$errors->{code}}, $ML{'.error.code.invalid'}; $valid = 0; } else { @@ -155,10 +181,14 @@ body<= } else { $data->{suggest_journal} = undef; } + if ( $data->{paid_class} !~ /^(paid|premium)$/ ) { + $data->{paid_class} = undef; + $data->{paid_months} = 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; + $dbh->do( "UPDATE acctcode_promo SET max_count = ?, active = ?, suggest_journalid = ?, paid_class = ?, paid_months = ? WHERE code = ?", undef, + $data->{max_count}, $data->{active}, $data->{suggest_journalid}, $data->{paid_class}, $data->{paid_months}, $data->{code} ) or die $dbh->errstr; } else { $create_form->( $data, $errors ); return $ret; @@ -169,10 +199,11 @@ body<= if ( $state eq 'create' ) { $create_form->( ); - } elsif ( DW::InviteCodes->is_promo_code( code => $code ) ) { - $create_form->( DW::InviteCodes->get_promo_code_info( code => $code ) ); + } elsif ( DW::InviteCodes::Promo->is_promo_code( code => $code ) ) { + $create_form->( DW::InviteCodes::Promo->load( code => $code ) ); } else { - my $codes = DW::InviteCodes->get_promo_codes( state => $state ); + # FIXME: Do not do hash accesses on this. + my $codes = DW::InviteCodes::Promo->load_bulk( state => $state ); $ret .= '<a href="/admin/invites/promo?state=create">' . $ML{'.state.new'} . '</a> | '; $ret .= '<a href="/admin/invites/promo?state=all">' . $ML{'.state.unfiltered'} . '</a> | '; @@ -182,7 +213,7 @@ body<= $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>"; + $ret .= "<tr><th>$ML{'.heading.code'}</th><th>$ML{'.heading.active'}</th><th>$ML{'.heading.count'}</th><th>$ML{'.heading.suggest'}</th><th>$ML{'.heading.paid'}</tr>"; if ( scalar( @$codes ) ) { foreach my $code (@$codes) { @@ -192,6 +223,15 @@ body<= $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.none'} ) . "</td>"; + if ( defined $code->{paid_class} ) { + $ret .= "<td>" . + BML::ml('.paid', { + type => $code->{paid_class}, + months => $code->{paid_months} + } ) . "</td>"; + } else { + $ret .= "<td>$ML{'.paid.no'}</td>"; + } $ret .= "</tr>"; } } else { diff -r 88ea0f26d63f -r 53fc4397240f htdocs/admin/invites/promo.bml.text --- a/htdocs/admin/invites/promo.bml.text Thu Oct 14 11:47:13 2010 +0800 +++ b/htdocs/admin/invites/promo.bml.text Thu Oct 14 13:12:05 2010 +0800 @@ -28,6 +28,16 @@ .field.count.label=Count: +.field.paid_class.label=Paid Account: + +.field.paid_class.none=(No Paid Time) + +.field.paid_class.paid=Paid + +.field.paid_class.premium=Premium Paid + +.field.paid_months.label=Months: + .field.suggest_journal.label=Suggest Journal: .heading.active=Active @@ -36,9 +46,15 @@ .heading.count=Count +.heading.paid=Paid Time + .heading.suggest=Suggest Journal .nomatch=No promo codes match your criteria + +.paid=[[type]] for [[months]] [[?months|month|months]] + +.paid.no=(none) .return=Return to list --------------------------------------------------------------------------------