mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2010-04-30 06:36 am

[dw-free] anniversary promotion

[commit: http://hg.dwscoalition.org/dw-free/rev/cf64a41b1154]

http://bugs.dwscoalition.org/show_bug.cgi?id=2572

Anniversary Promotion code.

Patch by [staff profile] mark.

Files modified:
  • cgi-bin/DW/Controller/Shop.pm
  • cgi-bin/DW/Hooks/AnniversaryPromotion.pm
  • cgi-bin/DW/Hooks/HolidayPromotion.pm
  • cgi-bin/DW/Shop/Cart.pm
  • cgi-bin/DW/Shop/Item/Points.pm
  • cgi-bin/LJ/Widget/ShopCart.pm
--------------------------------------------------------------------------------
diff -r 075727259777 -r cf64a41b1154 cgi-bin/DW/Controller/Shop.pm
--- a/cgi-bin/DW/Controller/Shop.pm	Thu Apr 29 05:07:18 2010 -0500
+++ b/cgi-bin/DW/Controller/Shop.pm	Fri Apr 30 06:36:45 2010 +0000
@@ -59,6 +59,10 @@ sub _shop_controller {
 
     # populate vars with cart display template
     $rv->{cart_display} = DW::Template->template_string( 'shop/cartdisplay.tt', $rv );
+
+    # call any hooks to do things before we return success
+    LJ::Hooks::run_hooks( 'shop_controller', $rv );
+
     return ( 1, $rv );
 }
 
diff -r 075727259777 -r cf64a41b1154 cgi-bin/DW/Hooks/AnniversaryPromotion.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Hooks/AnniversaryPromotion.pm	Fri Apr 30 06:36:45 2010 +0000
@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+#
+# DW::Hooks::AnniversaryPromotion
+#
+# This file explains Dreamwidth's plans for world domination. Be sure to keep it updated!
+#
+# Authors:
+#      Mark Smith <mark@dreamwidth.org>
+#
+# This program is NOT free software or open-source; you can use it as an
+# example of how to implement your own site-specific extensions to the
+# Dreamwidth Studios open-source code, but you cannot use it on your site
+# or redistribute it, with or without modifications.
+#
+
+package DW::Hooks::AnniversasryPromotion;
+
+use strict;
+use LJ::Hooks;
+
+
+# returns if the promotion is valid right now
+sub promo_valid {
+    # valid from 2010-05-01 01:00:00 UTC to 2010-05-08 01:00:00 UTC
+    # this corresponds to 2010-04-30 21:00:00 EDT and a week later...
+    return 0 if time < 1272675600 || time > 1273280400;
+    return 1;
+}
+
+
+# returns how many points this cart is eligible for
+sub cart_bonus_points {
+    return int( $_[0]->total_cash );
+}
+
+
+# promotion HTML
+LJ::Hooks::register_hook( 'shop_controller', sub {
+    my ( $rv ) = @_;
+
+    # ensure we're a valid promotional period and not anon
+    return unless promo_valid();
+    return if $rv->{shop}->anonymous;
+
+    # put the note up top so people know
+    $rv->{cart_display} .=
+        "<div class='shop-error'><strong>" .
+        LJ::Lang::ml( 'shop.anniversarypromoblurb' ) .
+        "</strong></div>\n";
+} );
+
+
+# put information after the cart is rendered
+LJ::Hooks::register_hook( 'shop_cart_render', sub {
+    my ( $retref, %opts ) = @_;
+    return if $opts{admin};
+
+    # promo period and not anonymous
+    return unless promo_valid();
+    return unless $opts{cart}->userid;
+
+    # determine how many points they get ... basically, 1 point per $1 USD
+    # spent..  does not get points for spending points!
+    my $points = cart_bonus_points( $opts{cart} );
+
+    # text depends on how many points they get
+    $$retref .= '<p class="shop-account-status">';
+    if ( $points > 0 ) {
+        $$retref .= LJ::Lang::ml( 'shop.annivpromo.points', { points => $points } );
+    } else {
+        $$retref .= LJ::Lang::ml( 'shop.annivpromo.nopoints' );
+    }
+    $$retref .= '</p>';
+} );
+
+
+# this is where the magic happens.  when a cart enters or leaves the
+# paid state, then we have to apply or unapply their bonus points.
+LJ::Hooks::register_hook( 'shop_cart_state_change', sub {
+    my ( $cart, $newstate ) = @_;
+
+    # if the cart is going INTO the paid state, then we apply the bonus points
+    # to the user who bought the items
+    if ( $newstate == $DW::Shop::STATE_PAID ) {
+        my $points = cart_bonus_points( $cart );
+        my $u = LJ::load_userid( $cart->userid );
+        return unless $points && $u;
+
+        # now give them the points for their bonus
+        $u->give_shop_points( amount => $points, reason => sprintf( 'order %d bonus points', $cart->id ) );
+        return;
+    }
+
+    # however, if the OLD state was PROCESSED (means we're being refunded or
+    # something is happening) then we need to email admins.  the logic to
+    # determine if we ever gave the user bonus points is too fickle, for now
+    # we can just handle point reversals manually.
+    if ( $cart->state == $DW::Shop::STATE_PROCESSED ) {
+        LJ::send_mail( {
+            to => $LJ::ADMIN_EMAIL,
+            from => $LJ::BOGUS_EMAIL,
+            fromname => $LJ::SITENAME,
+            subject => 'Attention: Order Investigation Needed',
+            body => <<EOF,
+Dear admins,
+
+Order #@{[$cart->id]} has left the PROCESSED state during an active promotion
+period and needs to be investigated.  The user may need to have bonus points
+unapplied from their account.
+
+
+Best regards,
+The $LJ::SITENAMESHORT Payment System
+EOF
+        } );
+    }
+
+} );
+
+
+1;
diff -r 075727259777 -r cf64a41b1154 cgi-bin/DW/Hooks/HolidayPromotion.pm
--- a/cgi-bin/DW/Hooks/HolidayPromotion.pm	Thu Apr 29 05:07:18 2010 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-#!/usr/bin/perl
-#
-# DW::Hooks::HolidayPromotion
-#
-# This file explains Dreamwidth's plans for world domination. Be sure to keep it updated!
-#
-# Authors:
-#      Mark Smith <mark@dreamwidth.org>
-#
-# This program is NOT free software or open-source; you can use it as an
-# example of how to implement your own site-specific extensions to the
-# Dreamwidth Studios open-source code, but you cannot use it on your site
-# or redistribute it, with or without modifications.
-#
-
-package DW::Hooks::HolidayPromotion;
-
-use strict;
-use LJ::Hooks;
-
-# promotion HTML
-LJ::Hooks::register_hook( 'shop_cart_status_bar', sub {
-    my ( $shop, $cart, $retref ) = @_;
-
-    # anonymous sessions can't benefit from the promotion
-    return if $shop->anonymous;
-
-    # bail out if it's expired (2010-01-01 00:00:00)
-    return if time > 1262304000;
-
-    # put the note up top so people know
-    $$retref = "<div class='shop-error'><strong>" . BML::ml( 'shop.holidaypromoblurb' ) .
-               "</strong></div>\n" . $$retref;
-} );
-
-# hook to add a new item when they purchase somethign eligibile
-LJ::Hooks::register_hook( 'shop_cart_added_item', sub {
-    my ( $cart, $item ) = @_;
-
-    # bail out if it's expired (2010-01-01 00:00:00)
-    return if time > 1262304000;
-
-    # ignore promo linked items so we don't loop forever
-    return if $item->{_holiday_promo_2009};
-
-    # validation checks
-    return unless $cart->userid;
-    return if $item->t_userid && $item->t_userid == $cart->userid;
-    return if $item->permanent || $item->months < 6;
-
-    # determine what kind of time to give the user.  rules are simple, if
-    # the user has premium, give them premium.  else, they get paid.
-    my $type = DW::Pay::get_account_type( $cart->userid );
-    $type = 'paid' if $type ne 'premium';
-
-    # looks good, build a new object and stick it on the cart
-    my $new = bless {
-        cost_cash => 0.00,
-        cost_points => 0,
-        months => int( $item->months / 6 ) * 2,
-        class  => $type,
-        target_userid => $cart->userid,
-        cannot_conflict => 1,
-        noremove => 1,
-        from_name => $LJ::SITENAME,
-
-        _holiday_promo_2009  => $item->id,
-    }, 'DW::Shop::Item::Account';
-
-    my ( $rv, $msg ) = $cart->add_item( $new );
-    warn "Failed to add holiday promotion time: $msg\n"
-        unless $rv;
-} );
-
-# when they remove an item ...
-LJ::Hooks::register_hook( 'shop_cart_removed_item', sub {
-    my ( $cart, $item ) = @_;
-
-    # don't do anything if we're removing a promo item
-    return if $item->{_holiday_promo_2009};
-
-    # iterate over the cart to see if any items link to this one
-    foreach my $it ( @{$cart->items} ) {
-        if ( $it->{_holiday_promo_2009} == $item->id ) {
-            # they're linked, remove it forcefully (mental image: large hammer)
-            $cart->remove_item( $it->id, force => 1 );
-        }
-    }
-
-} );
-
-
-1;
diff -r 075727259777 -r cf64a41b1154 cgi-bin/DW/Shop/Cart.pm
--- a/cgi-bin/DW/Shop/Cart.pm	Thu Apr 29 05:07:18 2010 -0500
+++ b/cgi-bin/DW/Shop/Cart.pm	Fri Apr 30 06:36:45 2010 +0000
@@ -436,6 +436,8 @@ sub state {
     return $self->{state}
         unless defined $newstate;
 
+    LJ::Hooks::run_hooks( 'shop_cart_state_change', $self, $newstate );
+
     $self->{state} = $newstate;
     $self->save;
 
diff -r 075727259777 -r cf64a41b1154 cgi-bin/DW/Shop/Item/Points.pm
--- a/cgi-bin/DW/Shop/Item/Points.pm	Thu Apr 29 05:07:18 2010 -0500
+++ b/cgi-bin/DW/Shop/Item/Points.pm	Fri Apr 30 06:36:45 2010 +0000
@@ -154,8 +154,9 @@ sub can_be_added {
         return 0;
     }
 
-    # sanity check that the points are in-range and less than 5000?
-    if ( $self->points < 30 || $self->points > 5000 ) {
+    # sanity check that the points are in-range and less than 5000, but only if they're being
+    # purchased.  otherwise, we allow anything if it's a 0.00 cost item.
+    if ( $self->cost_cash > 0.00 && ( $self->points < 30 || $self->points > 5000 ) ) {
         $$errref = LJ::Lang::ml( 'shop.item.points.canbeadded.outofrange' );
         return 0;
     }
diff -r 075727259777 -r cf64a41b1154 cgi-bin/LJ/Widget/ShopCart.pm
--- a/cgi-bin/LJ/Widget/ShopCart.pm	Thu Apr 29 05:07:18 2010 -0500
+++ b/cgi-bin/LJ/Widget/ShopCart.pm	Fri Apr 30 06:36:45 2010 +0000
@@ -29,7 +29,7 @@ sub render_body {
 
     my $ret;
 
-    my $cart = $opts{cart} || DW::Shop->get->cart
+    my $cart = $opts{cart} ||= DW::Shop->get->cart
         or return $class->ml( 'widget.shopcart.error.nocart' );
 
     return $class->ml( 'widget.shopcart.error.noitems' )
@@ -146,6 +146,9 @@ sub render_body {
         $ret .= $class->end_form;
     }
 
+    # allow hooks to alter the cart or append to it
+    LJ::Hooks::run_hooks( 'shop_cart_render', \$ret, %opts );
+
     return $ret;
 }
 
--------------------------------------------------------------------------------
yvi: Teal'c smiling with a heart-shape next to him (Stargate - Teal'c Love)

[personal profile] yvi 2010-04-30 06:42 am (UTC)(link)
+# This file explains Dreamwidth's plans for world domination. Be sure to keep it updated!

:D
kareila: Taking refuge from falling debris under a computer desk. (computercrash)

[personal profile] kareila 2010-04-30 11:17 pm (UTC)(link)
+package DW::Hooks::AnniversasryPromotion;


That, uh, might need to be untypoed?
denise: Image: Me, facing away from camera, on top of the Castel Sant'Angelo in Rome (Default)

[staff profile] denise 2010-04-30 11:27 pm (UTC)(link)
I texted Mark to let him know to fix this, since he's out.
niqaeli: cat with arizona flag in the background (Default)

[personal profile] niqaeli 2010-04-30 11:25 pm (UTC)(link)
I was just skimming through this and I really kind of heart the fact that you have meaningful comments on your code! <3.
denise: Image: Me, facing away from camera, on top of the Castel Sant'Angelo in Rome (Default)

[staff profile] denise 2010-04-30 11:28 pm (UTC)(link)
Mark's coding style is brilliant. Not only does he comment everything, his code itself is awesomely easy to read.