-VERSION=0.14_25
+VERSION=0.14_26
DISTNAME=bse-$(VERSION)
DISTBUILD=$(DISTNAME)
DISTTAR=../$(DISTNAME).tar
customStr2 varchar(255),
customStr3 varchar(255),
+ affiliate_name varchar(40) not null default '',
+
primary key (id),
- unique (userId)
+ unique (userId),
+ index (affiliate_name)
);
-- this is used to track email addresses that we've sent subscription
-- primary key (subscription_id, siteuser_id)
-- );
+drop table if exists bse_siteuser_images;
+create table bse_siteuser_images (
+ siteuser_id integer not null,
+ image_id varchar(20) not null,
+ filename varchar(80) not null,
+ width integer not null,
+ height integer not null,
+ bytes integer not null,
+ content_type varchar(80) not null,
+ alt varchar(255) not null,
+
+ primary key(siteuser_id, image_id)
+);
+
add=>1,
);
-my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation flags); # flags is saved separately
+my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation flags affiliate_name); # flags is saved separately
my %donttouch = map { $_, $_ } @donttouch;
sub dispatch {
}
}
+ my $aff_name = $cgi->param('affiliate_name');
+ if (defined $aff_name && length $aff_name) {
+ if ($aff_name =~ /^\s*\w+\s*$/) {
+ $aff_name =~ s/^\s+|\s+$//g;
+ my $other = SiteUsers->getBy(affiliate_name => $aff_name);
+ if ($other) {
+ $errors{affiliate_name} = "affiliate name $aff_name is already in use";
+ }
+ }
+ else {
+ $errors{affiliate_name} = "invalid affiliate name, no spaces or special characters are allowed";
+ }
+ }
+ else {
+ undef $aff_name;
+ }
+
keys %errors
and return $class->req_edit($req, undef, \%errors);
}
$user->{password} = $newpass if !$nopassword && $newpass;
+ $user->{affiliate_name} = $aff_name if defined $aff_name;
+
for my $col (@cols) {
my $value = $cgi->param($col);
if (defined $value) {
'select * from site_users where userId = ?',
getSiteUserByPkey =>
'select * from site_users where id = ?',
- addSiteUser => 'insert site_users values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
- replaceSiteUser => 'replace site_users values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ getSiteUserByAffiliate_name =>
+ 'select * from site_users where affiliate_name = ?',
+ addSiteUser => 'insert site_users values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ replaceSiteUser => 'replace site_users values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
'SiteUsers.removeSubscriptions'=>
'delete from subscribed_users where userId = ?',
'SiteUsers.removeSub'=>
where confirmed <> 0 and disabled = 0 and si.id = su.userId and su.subId = ?
EOS
SiteUsers => 'select * from site_users',
+ getBSESiteuserImage => <<SQL,
+select * from bse_siteuser_images
+ where siteuser_id = ? and image_id = ?
+SQL
+ getBSESiteuserImages => <<SQL,
+select * from bse_siteuser_images where siteuser_id = ?
+SQL
+ addBSESiteuserImage => <<SQL,
+insert bse_siteuser_images values(?,?,?,?,?,?,?,?)
+SQL
+ replaceBSESiteuserImage => <<SQL,
+replace bse_siteuser_images values(?,?,?,?,?,?,?,?)
+SQL
+ deleteBSESiteuserImage=> <<SQL,
+delete from bse_siteuser_images where member_id = ? and image_id = ?
+SQL
SubscriptionTypes =>
'select * from subscription_types',
http://your.site.com/cgi-bin/affiliate.pl?id=I<number>
+http://your.site.com/cgi-bin/affiliate.pl?lo=I<logon>
+
+http://your.site.com/cgi-bin/affiliate.pl?co=I<affiliate name>
+
# set the stored affiliate code and refresh to the top of the site
http://your.site.com/cgi-bin/affiliate.pl?a_set=1&id=I<code>
my @allowed = split /;/, $allowed_referer;
my $referer = $ENV{HTTP_REFERER};
if ($referer) {
- my ($domain) = ($referer =~ m!^\w+://([\w/]+)!);
+ my ($domain) = ($referer =~ m!^\w+://([\w.]+)!);
$domain = lc $domain;
my $found = 0;
for my $entry (@allowed) {
=item a_show
-Display the affiliate page based on a user id number.
+Display the affiliate page based on a either a user id number supplied
+in the C<id> paramater, a logon name supplied in the C<lo> parameter
+or an affiliate name supplied in the C<co> parameter.
This is the default target, so you do not need to supply a target
parameter.
my $cgi = $req->cgi;
my $cfg = $req->cfg;
- my $id = $cgi->param('id');
- defined $id
- or return $class->req_none($req, "No identifier supplied");
require SiteUsers;
- my $user = SiteUsers->getByPkey($id);
+ my $user;
+ my $id = $cgi->param('id');
+ my $lo = $cgi->param('lo');
+ my $co = $cgi->param('co');
+ if (defined $id && length $id && $id =~ /^\d+$/) {
+ $user = SiteUsers->getByPkey($id);
+ }
+ elsif (defined $lo && length $lo && $lo =~ /^\w+$/) {
+ $user = SiteUsers->getBy(userId => $lo);
+ }
+ elsif (defined $co && length $co && $co =~ /^\w+$/) {
+ $user = SiteUsers->getBy(affiliate_name => $co);
+ }
+ else {
+ return $class->req_none($req, "No identifier supplied");
+ }
$user
or return $class->req_none($req, "Unknown user");
# require BSE::TB::Subscriptions;
use constant MAX_UNACKED_CONF_MSGS => 3;
use constant MIN_UNACKED_CONF_GAP => 2 * 24 * 60 * 60;
-my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation disabled flags);
+my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation disabled flags affiliate_name);
my %donttouch = map { $_, $_ } @donttouch;
sub user_tags {
}
}
}
+
+ my $aff_name = $cgi->param('affiliate_name');
+ if (defined $aff_name && length $aff_name) {
+ if ($aff_name =~ /^\s*\w+\s*$/) {
+ $aff_name =~ s/^\s+|\s+$//g;
+ my $other = SiteUsers->getBy(affiliate_name => $aff_name);
+ if ($other) {
+ $errors{affiliate_name} = $msgs->(dupaffiliatename =>
+ "affiliate name $aff_name is already in use", $aff_name);
+ }
+ }
+ else {
+ $errors{affiliate_name} = $msgs->(badaffiliatename =>
+ "invalid affiliate name, no spaces or special characters are allowed");
+ }
+ }
+ else {
+ undef $aff_name;
+ }
+
keys %errors
and return $self->show_opts($session, $cgi, $cfg, undef, \%errors);
my $newemail;
++$newemail;
}
$user->{password} = $newpass if !$nopassword && $newpass;
+
+ $user->{affiliate_name} = $aff_name if defined $aff_name;
for my $col (@cols) {
my $value = $cgi->param($col);
billPostCode billCountry instructions billTelephone billFacsimile
billEmail adminNotes disabled flags
customText1 customText2 customText3
- customStr1 customStr2 customStr3/;
+ customStr1 customStr2 customStr3
+ affiliate_name/;
}
sub removeSubscriptions {
return; # PH for now, not subscribed
}
+my @image_cols =
+ qw(siteuser_id image_id filename width height bytes content_type alt);
+
+sub images_cfg {
+ my ($self, $cfg) = @_;
+
+ my @images;
+ my %ids = $cfg->entries('BSE Siteuser Images');
+ for my $id (keys %ids) {
+ my %image = ( id => $id );
+
+ my $sect = "BSE Siteuser Image $id";
+ for my $key (qw(description help minwidth minheight maxwidth maxheight
+ minratio maxratio properror
+ widthsmallerror heightsmallerror smallerror
+ widthlargeerror heightlargeerror largeerror
+ maxspace spaceerror)) {
+ my $value = $cfg->entry($sect, $key);
+ if (defined $value) {
+ $image{$key} = $value;
+ }
+ }
+ push @images, \%image;
+ }
+
+ @images;
+}
+
+sub images {
+ my ($self) = @_;
+
+ BSE::DB->query(getBSESiteuserImages => $self->{id});
+}
+
+sub get_image {
+ my ($self, $id) = @_;
+
+ my ($image) = BSE::DB->query(getBSESiteuserImage => $self->{id}, $id)
+ or return;
+
+ $image;
+}
+
+sub set_image {
+ my ($self, $cfg, $id, $image) = @_;
+
+ my %image = %$image;
+ $image{siteuser_id} = $self->{id};
+ my $old = $self->get_image($id);
+
+ if ($old) {
+ # replace it
+ BSE::DB->run(replaceBSESiteuserImage => @image{@image_cols});
+
+ # lose the old file
+ my $image_dir = $cfg->entryVar('paths', 'siteuser_images');
+ unlink "$image_dir/$old->{filename}";
+ }
+ else {
+ # add it
+ # replace it
+ BSE::DB->run(addBSESiteuserImage => @image{@image_cols});
+ }
+}
+
+sub remove_image {
+ my ($self, $cfg, $id) = @_;
+
+ if (my $old = $self->get_image($id)) {
+ # remove the entry
+ BSE::DB->run(deleteBSESiteuserImage => $self->{id}, $id);
+
+ # lose the old file
+ my $image_dir = $cfg->entryVar('paths', 'siteuser_images');
+ unlink "$image_dir/$old->{filename}";
+ }
+}
+
+
1;
use BSE::Session;
use BSE::Cfg;
use BSE::Util::Tags qw(tag_hash);
+use DevHelp::HTML;
my $cfg = BSE::Cfg->new();
$olddata and $payment = param('paymentType');
defined $payment or $payment = $payment_types[0];
+ my $affiliate_code = $session{affiliate_code};
+ defined $affiliate_code or $affiliate_code = '';
+
my $item_index = -1;
my @options;
my $option_index;
user => $user ? [ \&tag_hash, $user ] : '',
checkedPayment => [ \&tag_checkedPayment, $payment, \%types_by_name ],
ifPayments => [ \&tag_ifPayments, \@payment_types, \%types_by_name ],
+ affiliate_code => escape_html($affiliate_code),
);
for my $type (@pay_types) {
my $id = $type->{id};
page('checkoutconfirm.tmpl', \%acts);
}
-# this can be used instead of the purchase page to work in 2 steps:
-# - collect shipping details
-# - collect CC details
-# the collection of the CC details should go to another script that
-# processes the CC information and then displays the order complete
-# information
-# BUG!!: this duplicates the code in purchase() a great deal
-# sub prePurchase {
-
-# my $cust_class = custom_class($cfg);
-# my @required = $cust_class->required_fields($CGI::Q, $session{custom}, $cfg);
-# for my $field (@required) {
-# defined(param($field)) && length(param($field))
-# or return checkout("Field $field is required", 1);
-# }
-# if (grep /email/, @required) {
-# defined(param('email')) && param('email') =~ /.\@./
-# or return checkout("Please enter a valid email address", 1);
-# }
-
-# use BSE::TB::Orders;
-# use BSE::TB::OrderItems;
-
-# # map some form fields to order field names
-# my %field_map =
-# (
-# name1 => 'delivFirstName',
-# name2 => 'delivLastName',
-# address => 'delivStreet',
-# city => 'delivSuburb',
-# postcode => 'delivPostCode',
-# state => 'delivState',
-# country => 'delivCountry',
-# email => 'emailAddress',
-# cardHolder => 'ccName',
-# cardType => 'ccType',
-# );
-# # paranoia, don't store these
-# my %nostore =
-# (
-# cardNumber => 1,
-# cardExpiry => 1,
-# );
-# my %order;
-# my @cart = @{$session{cart}};
-# @cart or return show_cart('You have no items in your shopping cart');
-
-# # so we can quickly check for columns
-# my @columns = BSE::TB::Order->columns;
-# my %columns;
-# @columns{@columns} = @columns;
-
-# for my $field (param()) {
-# $order{$field_map{$field} || $field} = param($field)
-# unless $nostore{$field};
-# }
-
-# my $ccNumber = param('cardNumber');
-# my $ccExpiry = param('cardExpiry');
-
-# use Digest::MD5 'md5_hex';
-# $ccNumber =~ tr/0-9//cd;
-# $order{ccNumberHash} = md5_hex($ccNumber);
-# $order{ccExpiryHash} = md5_hex($ccExpiry);
-
-# # work out totals
-# $order{total} = 0;
-# $order{gst} = 0;
-# $order{wholesale} = 0;
-# my @products;
-# my $today = epoch_to_sql(time);
-# for my $item (@cart) {
-# my $product = Products->getByPkey($item->{productId});
-# # double check that it's still a valid product
-# if (!$product) {
-# return show_cart("Product $item->{productId} not found");
-# }
-# else {
-# (my $comp_release = $product->{release}) =~ s/ .*//;
-# (my $comp_expire = $product->{expire}) =~ s/ .*//;
-# $comp_release le $today
-# or return show_cart("'$product->{title}' has not been released yet");
-# $today le $comp_expire
-# or return show_cart("'$product->{title}' has expired");
-# $product->{listed}
-# or return show_cart("'$product->{title}' not available");
-# }
-# push(@products, $product); # used in page rendering
-# @$item{qw/price wholesalePrice gst/} =
-# @$product{qw/retailPrice wholesalePrice gst/};
-# $order{total} += $item->{price} * $item->{units};
-# $order{wholesale} += $item->{wholesalePrice} * $item->{units};
-# $order{gst} += $item->{gst} * $item->{units};
-# }
-# use BSE::Util::SQL qw(now_sqldatetime);
-# $order{orderDate} = now_sqldatetime();
-
-# if (my ($msg, $id) = need_logon($cfg, \@cart, \@products, \%session, $CGI::Q)) {
-# refresh_logon($msg, $id);
-# return;
-# }
-
-# $order{total} += $cust_class->total_extras(\@cart, \@products,
-# $session{custom}, $cfg, 'final');
-# ++$session{changed};
-# # blank anything else
-# for my $column (@columns) {
-# defined $order{$column} or $order{$column} = '';
-# }
-# # make sure the user can't set these behind our backs
-# $order{filled} = 0;
-# $order{paidFor} = 0;
-
-# # this should be hard to guess
-# $order{randomId} = md5_hex(time().rand().{}.$$);
-
-# # check if a customizer has anything to do
-# eval {
-# $cust_class->order_save($CGI::Q, \%order, \@cart, \@products,
-# $session{custom}, $cfg);
-# ++$session{changed};
-# };
-# if ($@) {
-# return checkout($@, 1);
-# }
-
-# # load up the database
-# my @data = @order{@columns};
-# shift @data; # lose the dummy id
-# my $order = BSE::TB::Orders->add(@data)
-# or die "Cannot add order";
-# my @items;
-# my @item_cols = BSE::TB::OrderItem->columns;
-# for my $row (@cart) {
-# $row->{orderId} = $order->{id};
-# my @data = @$row{@item_cols};
-# shift @data;
-# push(@items, BSE::TB::OrderItems->add(@data));
-# }
-
-# my $item_index = -1;
-# my @options;
-# my $option_index;
-# my %acts;
-# %acts =
-# (
-# iterate_items_reset => sub { $item_index = -1; },
-# iterate_items =>
-# sub {
-# if (++$item_index < @items) {
-# $option_index = -1;
-# @options = cart_item_opts($items[$item_index],
-# $products[$item_index]);
-# return 1;
-# }
-# return 0;
-# },
-# item=> sub { CGI::escapeHTML($items[$item_index]{$_[0]}); },
-# product =>
-# sub {
-# my $value = $products[$item_index]{$_[0]};
-# defined($value) or $value = '';
-# CGI::escapeHTML($value);
-# },
-# extended =>
-# sub {
-# my $what = $_[0] || 'retailPrice';
-# $items[$item_index]{units} * $items[$item_index]{$what};
-# },
-# order => sub { CGI::escapeHTML($order->{$_[0]}) },
-# money =>
-# sub {
-# my ($func, $args) = split ' ', $_[0], 2;
-# $acts{$func} || return "<: money $_[0] :>";
-# return sprintf("%.02f", $acts{$func}->($args)/100);
-# },
-# old => sub { '' },
-# _format =>
-# sub {
-# my ($value, $fmt) = @_;
-# if ($fmt =~ /^m(\d+)/) {
-# return sprintf("%$1s", sprintf("%.2f", $value/100));
-# }
-# elsif ($fmt =~ /%/) {
-# return sprintf($fmt, $value);
-# }
-# },
-# iterate_options_reset => sub { $option_index = -1 },
-# iterate_options => sub { ++$option_index < @options },
-# option => sub { CGI::escapeHTML($options[$option_index]{$_[0]}) },
-# ifOptions => sub { @options },
-# options => sub { nice_options(@options) },
-# );
-# # this should be reset once the order has been paid
-# $session{orderPayment} = $order->{id};
-
-# page('checkoutcard.tmpl', \%acts);
-# }
-
sub tag_ifPayment {
my ($payment, $types_by_name, $args) = @_;
defined $ccNumber or $ccNumber = '';
my $ccExpiry = param('cardExpiry');
defined $ccExpiry or $ccExpiry = '';
- $order{affiliate_code} = defined $session{affiliate_code} ?
- $session{affiliate_code} : '';
+ my $affiliate_code = $session{affiliate_code};
+ defined $affiliate_code && length $affiliate_code
+ or $affiliate_code = param('affiliate_code');
+ defined $affiliate_code or $affiliate_code = '';
+ $order{affiliate_code} = $affiliate_code;
use Digest::MD5 'md5_hex';
$ccNumber =~ tr/0-9//cd;
my $user = get_siteuser(\%session, $cfg, $CGI::Q);
if ($user) {
$order{userId} = $user->{userId};
+ $order{siteuser_id} = $user->{id};
}
else {
$order{userId} = '';
+ $order{siteuser_id} = -1;
}
# this should be hard to guess
=head1 CHANGES
+=head2 0.14_26
+
+=over
+
+=item *
+
+the referer checks for affiliate.pl extracted the Referer header
+domain incorrectly.
+
+=item *
+
+the order affiliate_code is now accepted as a CGI parameter during
+purchase if it isn't set in the session.
+
+=item *
+
+the order siteuser_id field wasn't being set
+
+=item *
+
+added an affiliate_name column to the siteusers table. This value is
+required to be unique if non-empty and may only contain alphanumeric
+characters. You will need to add this to any custom templates.
+
+=item *
+
+affiliate.pl now accepts alternatives to the id parameter:
+
+=over
+
+=item *
+
+lo - the logon name of the user
+
+=item *
+
+co - the affiliate name of the user
+
+=back
+
+=item *
+
+some prep work for site user images
+
+=back
+
=head2 0.14_25
=over
</td>
<td bgcolor="#FFFFFF"><:help editsiteuser billEmail:> <:error_img billEmail:></td>
</tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Affiliate Name: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="affiliate_name" value="<:old affiliate_name siteuser affiliate_name:>" />
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser affiliate_name:> <:error_img affiliate_name:></td>
+ </tr>
<tr>
<th bgcolor="#FFFFFF" align="left" valign="top">Admin Notes: </th>
<td bgcolor="#FFFFFF">
<input type="text" name="facsimile" value="<:last facsimile:>" size="32" maxlength="80" /><:error_img facsimile:>
</td>
</tr>
+<:ifCfg affiliate prompt_name 0:>
+ <tr>
+ <th nowrap="nowrap" align="left"><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2">Affiliate Name:</font></b></th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="affiliate_name" value="<:last affiliate_name:>" size="40" maxlength="40" /><:error_img affiliate_name:>
+ </td>
+ </tr>
+<:or:><:eif:>
<:if Cfg "site users" billing_on_main_opts 1:>
<tr>
<td colspan="2" height="20"><br>
payment type names.12=EmailProForma
payment type required.11=facsimile
+affiliate.prompt_name=1
+
dealer.bsb=999999
dealer.accountno=77777777
shop.display_facsimile=Fax Number