Make DW::Shop::Item actually a base class, with DW::Shop::Item::Account
a class that inherits from it.
Patch by afuna.
diffs (702 lines):
diff -r f1f20ae2fb51 -r c8f2f6f4000c cgi-bin/DW/Shop/Item.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Shop/Item.pm Mon Feb 22 07:48:49 2010 +0000
@@ -0,0 +1,401 @@
+#!/usr/bin/perl
+#
+# DW::Shop::Item
+#
+# Base class containing basic behavior for items to be sold in the shop
+#
+# Authors:
+# Mark Smith <[email protected]>
+# Janine Smith <[email protected]>
+# Afuna <[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'.
+#
+
+package DW::Shop::Item;
+
+use strict;
+use Carp;
+use DW::InviteCodes;
+use DW::Pay;
+
+=head1 NAME
+
+DW::Shop::Item - base class containing basic behavior for items to be sold in
the shop
+
+=head1 SYNOPSIS
+
+=head1 API
+
+=head2 C<< $class->new( [ $opts ] ) >>
+
+Instantiates an item to be purchased in the shop. The item must be defined in
the
+%LJ::SHOP hash in your config file.
+
+Arguments:
+=item type => item type, passed in by a subclass. Must be configured in
%LJ::SHOP
+=item target_userid => userid,
+=item target_email => email,
+=item from_userid => userid,
+=item deliverydate => "yyyy-mm-dd",
+=item anonymous => 1,
+=item anonymous_target => 1,
+=item cannot_conflict => 1,
+=item noremove => 1,
+=item from_name => sender name,
+
+The type is required. Also, one target_* argument is required; it may be either
+a target_userid or a target_email. All other arguments are optional.
+
+Subclasses must override this function to set the type. Subclasses may also do
any
+other modifications necessary when instantiating itself. See
DW::Shop::Item::Account
+for an example.
+
+=cut
+
+sub new {
+ my ( $class, %args ) = @_;
+
+ my $type = delete $args{type};
+ return undef unless exists $LJ::SHOP{$type};
+
+ # from_userid will be 0 if the sender isn't logged in
+ return undef unless $args{from_userid} == 0 || LJ::load_userid(
$args{from_userid} );
+
+ # now do validation. since new is only called when the item is being added
+ # to the shopping cart, then we are comfortable doing all of these checks
+ # on things at the time this item is put together
+ if ( my $uid = $args{target_userid} ) {
+ # userid needs to exist
+ return undef unless LJ::load_userid( $uid );
+ } elsif ( my $email = $args{target_email} ) {
+ # email address must be valid
+ my @email_errors;
+ LJ::check_email( $email, \...@email_errors );
+ return undef if @email_errors;
+ } else {
+ return undef;
+ }
+
+ if ( $args{deliverydate} ) {
+ return undef unless $args{deliverydate} =~ /^\d\d\d\d-\d\d-\d\d$/;
+ }
+
+ if ( $args{anonymous} ) {
+ return undef unless $args{anonymous} == 1;
+ }
+
+ if ( $args{cannot_conflict} ) {
+ return undef unless $args{cannot_conflict} == 1;
+ }
+
+ if ( $args{noremove} ) {
+ return undef unless $args{noremove} == 1;
+ }
+
+ # looks good
+ return bless {
+ # user supplied arguments (close enough)
+ cost => $LJ::SHOP{$type}->[0] + 0.00,
+ %args,
+
+ # internal things we use to track the state of this item,
+ applied => 0,
+ cartid => 0,
+ }, $class;
+}
+
+
+=head2 C<< $self->apply >>
+
+Called when we are told we need to apply this item, i.e., turn it on. Note
that we
+update ourselves, but it's up to the cart to make sure that it saves.
+
+Subclasses may override this method, but a better approach would be to
override the
+internal $self->_apply method.
+
+=cut
+
+sub apply {
+ my $self = shift;
+ return 1 if $self->applied;
+
+ # 1) deliverydate must be present/past
+ if ( my $ddate = $self->deliverydate ) {
+ my $cur = LJ::mysql_time();
+ $cur =~ s/^(\d\d\d\d-\d\d-\d\d).+$/$1/;
+
+ return 0
+ unless $ddate le $cur;
+ }
+
+ return $self->_apply( @_ );
+}
+
+
+=head2 C<< $self->_apply >>
+
+Internal application sub.
+
+Subclasses must override this for item-specific behavior.
+
+=cut
+
+sub _apply {
+ croak "Cannot apply shop item; this method must be override by a
subclass.";
+}
+
+
+=head2 C<< $self->unapply >>
+
+Called when we need to turn this item off.
+
+Subclasses may override this to add additional behavior or warnings.
+
+=cut
+
+sub unapply {
+ my $self = $_[0];
+ return unless $self->applied;
+
+ # do the application process now, and if it succeeds...
+ $self->{applied} = 0;
+
+ return 1;
+}
+
+=head2 C<< $self->cart_state_changed( $cart, $newstate ) >>
+
+Hook in the cart for custom behavior once the cart has been changed.
+
+Subclasses may override this for custom behavior: for example, creating a
token or certificate once the cart has been paid for.
+
+=cut
+
+sub cart_state_changed {
+ my ( $cart, $newstate ) = @_;
+}
+
+=head2 C<< $self->can_be_added( [ %opts ] ) >>
+
+Returns 1 if this item is allowed to be added to the shopping cart.
+
+Subclasses must override this.
+
+=cut
+
+sub can_be_added {
+ my ( $self, %opts ) = @_;
+
+ return 1;
+}
+
+
+=head2 C<< $self->conflicts( $item ) >>
+
+Given another item, see if that item conflicts with this item (i.e.,
+if you can't have both in your shopping cart at the same time).
+
+Returns undef on "no conflict" else an error message.
+
+Subclasses may override.
+
+=cut
+
+sub conflicts {
+ my ( $self, $item ) = @_;
+
+ # if either item are set as "does not conflict" then never say yes
+ return if
+ $self->cannot_conflict || $item->cannot_conflict;
+
+ # subclasses can add additional logic here
+
+ # guess we allow it
+ return undef;
+}
+
+
+=head2 C<< $self->t_html( [ %opts ] ) >>
+
+Render our target as a string.
+
+Subclasses may override.
+
+=cut
+
+sub t_html {
+ my ( $self, %opts ) = @_;
+
+ if ( my $uid = $self->t_userid ) {
+ my $u = LJ::load_userid( $uid );
+ return $u->ljuser_display
+ if $u;
+ return "<strong>invalid userid $uid</strong>";
+
+ } elsif ( my $email = $self->t_email ) {
+ return "<strong>$email</strong>";
+
+ }
+
+ return "<strong>invalid/unknown target</strong>";
+}
+
+
+=head2 C<< $self->name_html >>
+
+Render the item name as a string, for display.
+
+Subclasses must override to provide a more specific and user-friendly display
name.
+
+=cut
+
+sub name_html {
+ return ref $_[0];
+}
+
+
+=head2 C<< $self->short_desc >>
+
+Returns a short string talking about what this is.
+
+Subclasses may override to provide further description.
+
+=cut
+
+sub short_desc {
+ my $self = $_[0];
+
+ # does not contain HTML, I hope
+ my $desc = $self->name_html;
+
+ my $for = $self->t_email;
+ unless ( $for ) {
+ my $u = LJ::load_userid( $self->t_userid );
+ $for = $u->user
+ if $u;
+ }
+
+ # FIXME: english strip
+ return "$desc for $for";
+}
+
+
+=head2 C<< $self->id( $id ) >>
+
+This is a getter/setter so it is pulled out.
+
+=cut
+
+sub id {
+ return $_[0]->{id} unless defined $_[1];
+ return $_[0]->{id} = $_[1];
+}
+
+
+=head2 C<< $self->cartid( $cartid ) >>
+
+Gets/sets.
+
+=cut
+
+sub cartid {
+ return $_[0]->{cartid} unless defined $_[1];
+ return $_[0]->{cartid} = $_[1];
+}
+
+
+=head2 C<< $self->t_userid( $target_userid ) >>
+
+Gets/sets.
+
+=cut
+
+sub t_userid {
+ return $_[0]->{target_userid} unless defined $_[1];
+ return $_[0]->{target_userid} = $_[1];
+}
+
+
+=head2 C<< $self->from_html >>
+
+Display who this is from.
+
+=cut
+
+sub from_html {
+ my $self = $_[0];
+
+ return $self->{from_name} if $self->{from_name};
+
+ my $from_u = LJ::load_userid( $self->from_userid );
+ return LJ::Lang::ml( 'widget.shopcart.anonymous' )
+ if $self->anonymous || ! LJ::isu( $from_u );
+
+ return $from_u->ljuser_display;
+}
+
+# simple accessors
+
+=head2 C<< $self->applied >>
+
+Returns whether the item which was bought has been already applied
+
+=head2 C<< $self->cost >>
+
+Returns the cost of the item, as configured for this site.
+
+=head2 C<< $self->t_email >>
+
+Returns the target email this item was sent to.
+
+=head2 C<< $self->from_userid >>
+
+Returns the userid of the person who bought this item. May be 0.
+
+=head2 C<< $self->deliverydate >>
+
+Returns the date this item should be delivered, in "yyyy-mm-dd" format
+
+=head2 C<< $self->anonymous >>
+
+Returns whether this item should be gifted anonymously, or credited to the
sender
+
+=head2 C<< $self->noremove >>
+
+Returns whether this item may or may not be removed from the cart. May be used
by
+promotions which automatically add a promo item to a user's cart, to prevent
the
+promo item from being removed
+
+=head2 C<< $self->from_name >>
+
+Name of the sender in special cases. For example, can be the site name for
+promotions. Not exposed/settable via the shop.
+
+=cut
+sub applied { return $_[0]->{applied}; }
+sub cost { return $_[0]->{cost}; }
+sub t_email { return $_[0]->{target_email}; }
+sub from_userid { return $_[0]->{from_userid}; }
+sub deliverydate { return $_[0]->{deliverydate}; }
+sub anonymous { return $_[0]->{anonymous}; }
+sub noremove { return $_[0]->{noremove}; }
+sub from_name { return $_[0]->{from_name}; }
+
+
+=head2 C<< $self->cannot_conflict >>
+
+Returns whether this item may never conflict with any other item. If true, skip
+checks for conflict.
+
+Subclasses may override.
+
+=cut
+
+sub cannot_conflict { return $_[0]->{cannot_conflict}; }
+
+1;
diff -r f1f20ae2fb51 -r c8f2f6f4000c cgi-bin/DW/Shop/Item/Account.pm
--- a/cgi-bin/DW/Shop/Item/Account.pm Mon Feb 22 07:44:11 2010 +0000
+++ b/cgi-bin/DW/Shop/Item/Account.pm Mon Feb 22 07:48:49 2010 +0000
@@ -17,92 +17,60 @@
package DW::Shop::Item::Account;
+use base 'DW::Shop::Item';
+
use strict;
use DW::InviteCodes;
use DW::Pay;
+=head1 NAME
-# instantiates an account to be purchased of some sort
+DW::Shop::Item::Account - Represents a paid account that someone is
purchasing. See
+the documentation for DW::Shop::Item for usage examples and description of
methods
+inherited from that base class.
+
+=head1 API
+
+=head2 C<< $class->new( [ %args ] ) >>
+
+Instantiates an account of some sort to be purchased.
+
+Arguments:
+=item ( see DW::Shop::Item ),
+=item months => number of months of paid time,
+=item class => type of paid account,
+=item random => 1 (if gifting paid time to a random user),
+=item anonymous_target => 1 (if random user should be anonymous, not
identified)
+
+=cut
+
+# override
sub new {
my ( $class, %args ) = @_;
- my $type = delete $args{type};
- return undef unless exists $LJ::SHOP{$type};
-
- # from_userid will be 0 if the sender isn't logged in
- return undef unless $args{from_userid} == 0 || LJ::load_userid(
$args{from_userid} );
-
- # now do validation. since new is only called when the item is being added
- # to the shopping cart, then we are comfortable doing all of these checks
- # on things at the time this item is put together
- if ( my $uid = $args{target_userid} ) {
- # userid needs to exist
- return undef unless LJ::load_userid( $uid );
- } elsif ( my $email = $args{target_email} ) {
- # email address must be valid
- my @email_errors;
- LJ::check_email( $email, \...@email_errors );
- return undef if @email_errors;
- } else {
- return undef;
- }
-
- if ( $args{deliverydate} ) {
- return undef unless $args{deliverydate} =~ /^\d\d\d\d-\d\d-\d\d$/;
- }
-
- if ( $args{anonymous} ) {
- return undef unless $args{anonymous} == 1;
+ if ( $args{anonymous_target} ) {
+ return undef unless $args{anonymous_target} == 1;
}
if ( $args{random} ) {
return undef unless $args{random} == 1;
}
- if ( $args{anonymous_target} ) {
- return undef unless $args{anonymous_target} == 1;
+ my $self = $class->SUPER::new( %args );
+
+ if ( $self ) {
+ $self->{months} = $LJ::SHOP{$self->{type}}->[1];
+ $self->{class} = $LJ::SHOP{$self->{type}}->[2];
}
- if ( $args{cannot_conflict} ) {
- return undef unless $args{cannot_conflict} == 1;
- }
-
- if ( $args{noremove} ) {
- return undef unless $args{noremove} == 1;
- }
-
- # looks good
- return bless {
- # user supplied arguments (close enough)
- cost => $LJ::SHOP{$type}->[0] + 0.00,
- months => $LJ::SHOP{$type}->[1],
- class => $LJ::SHOP{$type}->[2],
- %args,
-
- # internal things we use to track the state of this item
- type => 'account',
- applied => 0,
- cartid => 0,
- }, $class;
+ return $self;
}
-# called when we are told we need to apply this item, i.e., turn it on. note
that we
-# update ourselves, but it's up to the cart to make sure that it saves.
-sub apply {
+# override
+sub _apply {
my $self = $_[0];
- return 1 if $self->applied;
- # 1) deliverydate must be present/past
- if ( my $ddate = $self->deliverydate ) {
- my $cur = LJ::mysql_time();
- $cur =~ s/^(\d\d\d\d-\d\d-\d\d).+$/$1/;
-
- return 0
- unless $ddate le $cur;
- }
-
- # application variability
return $self->_apply_email if $self->t_email;
return $self->_apply_userid if $self->t_userid;
@@ -269,7 +237,7 @@ sub _apply_email {
}
-# called when we need to turn this item off
+# override
sub unapply {
my $self = $_[0];
return unless $self->applied;
@@ -282,7 +250,7 @@ sub unapply {
}
-# returns 1 if this item is allowed to be added to the shopping cart
+# override
sub can_be_added {
my ( $self, %opts ) = @_;
@@ -319,10 +287,7 @@ sub can_be_added {
}
-# given another item, see if that item conflicts with this item (i.e.,
-# if you can't have both in your shopping cart at the same time).
-#
-# returns undef on "no conflict" else an error message.
+# override
sub conflicts {
my ( $self, $item ) = @_;
@@ -352,7 +317,7 @@ sub conflicts {
}
-# render our target as a string
+# override
sub t_html {
my ( $self, %opts ) = @_;
@@ -366,22 +331,14 @@ sub t_html {
} else {
return "<strong>$random_user_string</strong>";
}
- } elsif ( my $uid = $self->t_userid ) {
- my $u = LJ::load_userid( $uid );
- return $u->ljuser_display
- if $u;
- return "<strong>invalid userid $uid</strong>";
+ }
- } elsif ( my $email = $self->t_email ) {
- return "<strong>$email</strong>";
-
- }
-
- return "<strong>invalid/unknown target</strong>";
+ # otherwise, fall back upon default display
+ return $self->SUPER::t_html( %opts );
}
-# render the item name as a string
+# override
sub name_html {
my $self = $_[0];
@@ -390,6 +347,12 @@ sub name_html {
return LJ::Lang::ml( 'shop.item.account.name', { name => $name, num =>
$self->months } );
}
+
+=head2 C<< $self->class_name >>
+
+Return the display name of this account class.
+
+=cut
sub class_name {
my $self = $_[0];
@@ -403,75 +366,33 @@ sub class_name {
}
-# returns a short string talking about what this is
-sub short_desc {
- my $self = $_[0];
+# simple accessors
+=head2 C<< $self->months >>
- # does not contain HTML, I hope
- my $desc = $self->name_html;
+Number of months of paid time to be applied.
- my $for = $self->t_email;
- unless ( $for ) {
- my $u = LJ::load_userid( $self->t_userid );
- $for = $u->user
- if $u;
- }
+=head2 C<< $self->class >>
- # FIXME: english strip
- return "$desc for $for";
-}
+Account class identifier; not for display.
+=head2 C<< $self->permanent >>
-# this is a getter/setter so it is pulled out
-sub id {
- return $_[0]->{id} unless defined $_[1];
- return $_[0]->{id} = $_[1];
-}
+Returns whether this item is for a permanent account, or just a normal paid.
+=head2 C<< $self->random >>
-# gets/sets
-sub cartid {
- return $_[0]->{cartid} unless defined $_[1];
- return $_[0]->{cartid} = $_[1];
-}
+Returns whether this item is for a random user.
+=head2 C<< $self->anonymous_target >>
-# gets/sets
-sub t_userid {
- return $_[0]->{target_userid} unless defined $_[1];
- return $_[0]->{target_userid} = $_[1];
-}
+Returns whether this item for a random user should go to an anonymous user
(true)
+or to an identified user (false)
-
-# display who this is from
-sub from_html {
- my $self = $_[0];
-
- return $self->{from_name} if $self->{from_name};
-
- my $from_u = LJ::load_userid( $self->from_userid );
- return LJ::Lang::ml( 'widget.shopcart.anonymous' )
- if $self->anonymous || ! LJ::isu( $from_u );
-
- return $from_u->ljuser_display;
-}
-
-
-# simple accessors
-sub applied { return $_[0]->{applied}; }
-sub cost { return $_[0]->{cost}; }
+=cut
sub months { return $_[0]->{months}; }
sub class { return $_[0]->{class}; }
-sub t_email { return $_[0]->{target_email}; }
sub permanent { return $_[0]->months == 99; }
-sub from_userid { return $_[0]->{from_userid}; }
-sub deliverydate { return $_[0]->{deliverydate}; }
-sub anonymous { return $_[0]->{anonymous}; }
sub random { return $_[0]->{random}; }
-sub noremove { return $_[0]->{noremove}; }
-sub from_name { return $_[0]->{from_name}; }
sub anonymous_target { return $_[0]->{anonymous_target}; }
-sub cannot_conflict { return $_[0]->{cannot_conflict}; }
-
1;
_______________________________________________
dw-changelog mailing list
[email protected]
http://lists.dwscoalition.org/mailman/listinfo/dw-changelog