site/cgi-bin/admin/reorder.pl
site/cgi-bin/admin/report.pl
site/cgi-bin/admin/shopadmin.pl
+site/cgi-bin/admin/siteusers.pl
site/cgi-bin/admin/subs.pl
site/cgi-bin/admin/userlist.pl
site/cgi-bin/bse.cfg
site/cgi-bin/modules/BSE/AdminLogon.pm
site/cgi-bin/modules/BSE/AdminMenu.pm
site/cgi-bin/modules/BSE/AdminUsers.pm
+site/cgi-bin/modules/BSE/AdminSiteUsers.pm
site/cgi-bin/modules/BSE/Arrows.pm
site/cgi-bin/modules/BSE/Cfg.pm
site/cgi-bin/modules/BSE/ChangePW.pm
site/cgi-bin/modules/BSE/TB/AdminUsers.pm
site/cgi-bin/modules/BSE/Template.pm
site/cgi-bin/modules/BSE/UserReg.pm
+site/cgi-bin/modules/BSE/Util/Iterate.pm
site/cgi-bin/modules/BSE/Util/SQL.pm
site/cgi-bin/modules/BSE/Util/Secure.pm
site/cgi-bin/modules/BSE/Util/Tags.pm
site/cgi-bin/modules/BSE/Util/Valid.pm
site/cgi-bin/modules/BSE/Version.pm
site/cgi-bin/modules/Constants.pm
+site/cgi-bin/modules/DevHelp/DynSort.pm
site/cgi-bin/modules/DevHelp/Formatter.pm
site/cgi-bin/modules/DevHelp/HTML.pm
site/cgi-bin/modules/DevHelp/Report.pm
site/cgi-bin/modules/DevHelp/Tags.pm
+site/cgi-bin/modules/DevHelp/Tags/Iterate.pm
site/cgi-bin/modules/Generate.pm
site/cgi-bin/modules/Generate/Article.pm
site/cgi-bin/modules/Generate/Catalog.pm
site/templates/admin/subs/send_form.tmpl
site/templates/admin/subs/start_send.tmpl
site/templates/admin/userlist.tmpl
+site/templates/admin/users/add.tmpl
+site/templates/admin/users/edit.tmpl
+site/templates/admin/users/list.tmpl
site/templates/admin/xbase.tmpl
site/templates/base.tmpl
site/templates/cart_base.tmpl
site/templates/user/lostpwdemail_base.tmpl
site/templates/user/nopassword_base.tmpl
site/templates/user/options_base.tmpl
+site/templates/user/options_billing_base.tmpl
site/templates/user/options_saved_base.tmpl
site/templates/user/register_base.tmpl
site/templates/user/subdetail.tmpl
-VERSION=0.14_02
+VERSION=0.14_03
DISTNAME=bse-$(VERSION)
DISTBUILD=$(DISTNAME)
DISTTAR=../$(DISTNAME).tar
customStr1 varchar(255) null,
customStr2 varchar(255) null,
+ customInt1 integer null,
+ customInt2 integer null,
+ customInt3 integer null,
+ customInt4 integer null,
+
PRIMARY KEY (id),
-- if we keep id in the indexes MySQL will sometimes be able to
customStr4 varchar(255) null,
customStr5 varchar(255) null,
+ instructions text not null default '',
+ billTelephone varchar(80) not null default '',
+ billFacsimile varchar(80) not null default '',
+ billEmail varchar(255) not null default '',
+
primary key (id),
index order_cchash(ccNumberHash),
index order_userId(userId, orderDate)
previousLogon datetime not null,
+ -- used for billing information on the checkout form
+ billFirstName varchar(127) not null default '',
+ billLastName varchar(127) not null default '',
+ billStreet varchar(127) not null default '',
+ billSuburb varchar(127) not null default '',
+ billState varchar(40) not null default '',
+ billPostCode varchar(40) not null default '',
+ billCountry varchar(127) not null default '',
+
+ instructions text not null default '',
+ billTelephone varchar(80) not null default '',
+ billFacsimile varchar(80) not null default '',
+ billEmail varchar(255) not null default '',
+
+ adminNotes text not null default '',
+
+ disabled integer not null default 0,
+
primary key (id),
unique (userId)
);
--- /dev/null
+#!/usr/bin/perl -w
+# -d:ptkdb
+BEGIN { $ENV{DISPLAY} = '192.168.32.15:0.0' }
+use strict;
+use FindBin;
+use lib "$FindBin::Bin/../modules";
+use BSE::DB;
+use BSE::Request;
+use BSE::Template;
+use Carp 'confess';
+use BSE::AdminSiteUsers;
+
+$SIG{__DIE__} = sub { confess $@ };
+
+my $req = BSE::Request->new;
+
+my $result = BSE::AdminSiteUsers->dispatch($req);
+BSE::Template->output_result($req, $result);
user/nopassword.tmpl = user,user/nopassword_base.tmpl
user/options.tmpl = user,user/options_base.tmpl
user/options_saved.tmpl = user,user/options_saved_base.tmpl
+user/options_billing.tmpl = user,user/options_billing_base.tmpl
user/register.tmpl = user,user/register_base.tmpl
user/toomany.tmpl = user,user/toomany_base.tmpl
user/toosoon.tmpl = user,user/toosoon_base.tmpl
yearly_sales=Yearly Sales
total_sales=Total Sales
users_vs_orders=Users vs Orders
+user_products=Products bought by User
[report sales_summ_by_product]
sql1=select pr.articleId as "Id", ar.title as "Product", count(*) as "Units" from article ar, product pr, order_item oi where ar.id = pr.articleId and pr.articleId = oi.productId group by pr.articleId, ar.title
-sql1link1=<a href="/cgi-bin/admin/report.pl?s_show=1&r=product_sales_over_time&p1=${articleId}">Sales Over Time</a>
-sql1link2=<a href="/cgi-bin/admin/add.pl?id=${articleId}">Edit</a>
+sql1link1=<a href="/cgi-bin/admin/report.pl?s_show=1&r=product_sales_over_time&p1=${Id}">Sales Over Time</a>
+sql1link2=<a href="/cgi-bin/admin/add.pl?id=${Id}">Edit</a>
[report product_sales_over_time]
sql1=select year(od.orderDate) as "Year", month(od.orderDate) as "Month", count(*) as 'Units' from order_item oi, orders od where oi.orderId = od.id and productId = ? group by year(od.orderDate), month(od.orderDate) order by 1 DESC, 2 DESC
[report user_products]
sql1=select pr.title, count(*) as 'Count' from orders od, order_item oi, article pr, site_users su where oi.orderId = od.id and oi.productId = pr.id and su.userId = od.userId and su.id = ? group by pr.id
+sql1params=1
+param1=site_user,User
[report users_vs_orders]
sql1=select delivFirstName as "First Name", delivLastName as "Last Name", userId as "Logon", format(sum(total)/100.0,2) as '$ Total', count(*) as "Orders" from orders group by delivFirstName, delivLastName order by 5 DESC, 2
thumbImage thumbWidth thumbHeight imagePos
release expire keyword template link admin threshold
summaryLength generator level listed lastModified flags
- customDate1 customDate2 customStr1 customStr2/;
+ customDate1 customDate2 customStr1 customStr2
+ customInt1 customInt2 customInt3 customInt4/;
}
sub step_parents {
--- /dev/null
+package BSE::AdminSiteUsers;
+use strict;
+use BSE::Util::Tags qw(tag_error_img tag_hash);
+use DevHelp::HTML;
+use SiteUsers;
+use BSE::Util::Iterate;
+use DevHelp::DynSort qw(sorter tag_sorthelp);
+use BSE::Util::SQL qw/now_datetime/;
+
+my %actions =
+ (
+ list=>1,
+ edit=>1,
+ save=>1,
+ addform=>1,
+ add=>1,
+ );
+
+my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation);
+my %donttouch = map { $_, $_ } @donttouch;
+
+sub dispatch {
+ my ($class, $req) = @_;
+
+ $req->check_admin_logon()
+ or return BSE::Template->get_refresh($req->url('logon'), $req->cfg);
+
+ my $cgi = $req->cgi;
+ my $action;
+ for my $check (keys %actions) {
+ if ($cgi->param("a_$check")) {
+ $action = $check;
+ last;
+ }
+ }
+ $action ||= 'list';
+ my $method = "req_$action";
+ $class->$method($req);
+}
+
+sub req_list {
+ my ($class, $req, $msg) = @_;
+
+ my $cgi = $req->cgi;
+ if ($msg) {
+ $msg = escape_html($msg);
+ }
+ else {
+ $msg = join("<br />", map escape_html($_), $cgi->param('m'));
+ }
+ my @users = SiteUsers->all;
+ my ($sortby, $reverse) =
+ sorter(data=>\@users, cgi=>$cgi, sortby=>'userId');
+ my $it = BSE::Util::Iterate->new;
+
+ my %acts;
+ %acts =
+ (
+ BSE::Util::Tags->admin(undef, $req->cfg),
+ BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+ BSE::Util::Tags->secure($req),
+ message => $msg,
+ $it->make_paged_iterator('siteuser', 'siteusers', \@users, undef,
+ $cgi, undef, 'pp=20'),
+ sortby=>$sortby,
+ reverse=>$reverse,
+ sorthelp => [ \&tag_sorthelp, $sortby, $reverse ],
+ );
+
+ my $template = 'admin/users/list';
+ my $t = $req->cgi->param('_t');
+ $template .= "_$t" if defined($t) && $t =~ /^\w+$/;
+
+ return BSE::Template->get_response($template, $req->cfg, \%acts);
+}
+
+sub tag_if_required {
+ my ($cfg, $args) = @_;
+
+ return $cfg->entryBool('site users', "require_$args", 0);
+}
+
+sub req_edit {
+ my ($class, $req, $msg, $errors) = @_;
+
+ my $cgi = $req->cgi;
+ my $id = $cgi->param('id');
+ defined $id
+ or return $class->req_list($req, "No site user id supplied");
+ my $siteuser = SiteUsers->getByPkey($id)
+ or return $class->req_list($req, "No such site user found");
+
+ $errors ||= {};
+ if ($msg) {
+ $msg = escape_html($msg);
+ }
+ else {
+ if (keys %$errors) {
+ my %work = %$errors;
+ my @msgs = delete @work{$cgi->param()};
+ push @msgs, values %work;
+ $msg = join "<br />", map escape_html($_), @msgs;
+ }
+ else {
+ $msg = '';
+ }
+ }
+
+ my %acts;
+ %acts =
+ (
+ BSE::Util::Tags->admin(undef, $req->cfg),
+ BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+ BSE::Util::Tags->secure($req),
+ message => $msg,
+ siteuser => [ \&tag_hash, $siteuser ],
+ error_img => [ \&tag_error_img, $req->cfg, $errors ],
+ ifRequired => [ \&tag_if_required, $req->cfg ],
+ );
+
+ my $template = 'admin/users/edit';
+ my $t = $req->cgi->param('_t');
+ $template .= "_$t" if defined($t) && $t =~ /^\w+$/;
+
+ return BSE::Template->get_response($template, $req->cfg, \%acts);
+}
+
+sub req_save {
+ my ($class, $req) = @_;
+
+ my $cgi = $req->cgi;
+ my $cfg = $req->cfg;
+
+ my $id = $cgi->param('id');
+ $id && $id =~ /^\d+$/
+ or return $class->req_list($req, "No user id supplied");
+
+ my $user = SiteUsers->getByPkey($id)
+ or return $class->req_list($req, "No user $id found");
+
+ my %errors;
+ my $nopassword = $req->cfg->entry('site users', 'nopassword', 0);
+ my @cols = grep !$donttouch{$_}, SiteUser->columns;
+ for my $col (@cols) {
+ my $value = $cgi->param($col);
+ if ($cfg->entryBool('site users', "require_$col")) {
+ if (defined $value && $value eq '') {
+ my $disp = $cfg->entry('site users', "display_$col", "\u$col");
+ $errors{$col} = "$disp is a required field";
+ }
+ }
+ }
+
+ my $saveemail;
+ my $email = $cgi->param('email');
+ if (!$email) {
+ $errors{email} = "Email is a required field";
+ }
+ elsif ($email !~ /.\@./) {
+ $errors{email} = "Email is invalid";
+ }
+ unless ($errors{email}) {
+ if ($nopassword) {
+ my $conf_email = $cgi->param('confirmemail');
+ if ($conf_email) {
+ if ($conf_email eq $email) {
+ my $other = SiteUsers->getBy(userId=>$email);
+ if ($other) {
+ $errors{email} = "That email address is already in use";
+ }
+ else {
+ ++$saveemail;
+ }
+ }
+ else {
+ $errors{confirmemail} =
+ "Confirmation email address doesn't match email address";
+ }
+ }
+ else {
+ $errors{confirmemail} = "Please enter a confirmation email address";
+ }
+ }
+ else {
+ ++$saveemail;
+ }
+ }
+ unless ($errors{email}) {
+ my $checkemail = SiteUser->generic_email($email);
+ require BSE::EmailBlacklist;
+ my $blackentry = BSE::EmailBlacklist->getEntry($checkemail);
+ if ($blackentry) {
+ $errors{email} = "Email $email is blacklisted: $blackentry->{why}";
+ }
+ }
+
+ my $newpass;
+ unless ($nopassword) {
+ $newpass = $cgi->param('password');
+ my $confirm = $cgi->param('confirm_password');
+
+ if (defined $newpass && length $newpass) {
+ my $min_pass_length = $cfg->entry('basic', 'minpassword') || 4;
+ my $error;
+ if (length $newpass < $min_pass_length) {
+ $errors{password} = "The password must be at least $min_pass_length characters";
+ }
+ elsif (!defined $confirm || length $confirm == 0) {
+ $errors{confirm_password} = "Please enter a confirmation password";
+ }
+ elsif ($newpass ne $confirm) {
+ $errors{confirm_password} = "The confirmation password is different from the password";
+ }
+ }
+ }
+
+ keys %errors
+ and return $class->req_edit($req, undef, \%errors);
+
+ my $newemail;
+ if ($saveemail && $email ne $user->{email}) {
+ $user->{confirmed} = 0;
+ $user->{confirmSecret} = '';
+ $user->{email} = $email;
+ $user->{userId} = $email if $nopassword;
+ ++$newemail;
+ }
+ $user->{password} = $newpass if !$nopassword && $newpass;
+
+ for my $col (@cols) {
+ my $value = $cgi->param($col);
+ if (defined $value) {
+ $user->{$col} = $value;
+ }
+ }
+
+ $user->{textOnlyMail} = 0
+ if $cgi->param('saveTextOnlyMail') && !defined $cgi->param('textOnlyMail');
+ $user->{keepAddress} = 0
+ if $cgi->param('saveKeepAddress') && !defined $cgi->param('keepAddress');
+ $user->{disabled} = 0
+ if $cgi->param('saveDisabled') && !defined $cgi->param('disabled');
+ $user->save;
+
+ my @msgs = ( "User saved" );
+
+ my $sent_ok = 1; # no error handling if true
+ my $code;
+ my $msg;
+ if ($nopassword) {
+ $sent_ok = $user->send_conf_request($req->cgi, $req->cfg, \$code, \$msg)
+ if $newemail;
+ }
+ else {
+ my @subs = $user->subscriptions;
+ if (@subs && $newemail) {
+ $sent_ok = $user->send_conf_request($req->cgi, $req->cfg, \$code, \$msg);
+ }
+ }
+
+ unless ($sent_ok) {
+ if ($code eq 'blacklist') {
+ push @msgs, "Could not send confirmation: Email address blacklisted: $msg";
+ }
+ elsif ($code eq 'mail') {
+ push @msgs, "Could not send confirmation: Error sending email: $msg";
+ }
+ else {
+ push @msgs, "Could not send confirmation: $msg";
+ }
+ }
+
+ my $r = $cgi->param('r');
+ unless ($r) {
+ $r = $req->url('siteusers', { list => 1 });
+ }
+ $r .= "&m=".escape_uri($_) for @msgs;
+
+ return BSE::Template->get_refresh($r, $req->cfg);
+}
+
+sub req_addform {
+ my ($class, $req, $msg, $errors) = @_;
+
+ my $cgi = $req->cgi;
+
+ $errors ||= {};
+ if ($msg) {
+ $msg = escape_html($msg);
+ }
+ else {
+ if (keys %$errors) {
+ my %work = %$errors;
+ my @msgs = delete @work{$cgi->param()};
+ push @msgs, values %work;
+ $msg = join "<br />", map escape_html($_), grep $_, @msgs;
+ }
+ else {
+ $msg = '';
+ }
+ }
+
+ my %acts;
+ %acts =
+ (
+ BSE::Util::Tags->admin(undef, $req->cfg),
+ BSE::Util::Tags->basic(undef, $req->cgi, $req->cfg),
+ BSE::Util::Tags->secure($req),
+ message => $msg,
+ error_img => [ \&tag_error_img, $req->cfg, $errors ],
+ ifRequired => [ \&tag_if_required, $req->cfg ],
+ );
+
+ my $template = 'admin/users/add';
+ my $t = $req->cgi->param('_t');
+ $template .= "_$t" if defined($t) && $t =~ /^\w+$/;
+
+ return BSE::Template->get_response($template, $req->cfg, \%acts);
+}
+
+sub req_add {
+ my ($class, $req) = @_;
+
+ my $cgi = $req->cgi;
+ my $cfg = $req->cfg;
+
+ my %user;
+ my @cols = SiteUser->columns;
+ shift @cols;
+ for my $field (@cols) {
+ $user{$field} = '';
+ }
+
+ my $nopassword = $cfg->entryBool('site users', 'nopassword', 0);
+ my %errors;
+ my $email = $cgi->param('email');
+ if (!defined $email or !length $email) {
+ $errors{email} = "Please enter an email address";
+ $email = ''; # prevent undefined value warnings later
+ }
+ elsif ($email !~ /.\@./) {
+ $errors{email} = "Please enter a valid email address";
+ }
+ if ($nopassword) {
+ my $confemail = $cgi->param('confirmemail');
+ if (!defined $confemail or !length $confemail) {
+ $errors{confirmemail} = "Please enter a confirmation email address";
+ }
+ elsif ($email ne $confemail) {
+ $errors{confirmemail} = "Confirmation email should match the Email Address";
+ }
+ my $user = SiteUsers->getBy(userId=>$email);
+ if ($user) {
+ $errors{email} = "Sorry, email $email already exists as a user";
+ }
+ $user{userId} = $email;
+ $user{password} = '';
+ }
+ else {
+ my $min_pass_length = $cfg->entry('basic', 'minpassword') || 4;
+ my $userid = $cgi->param('userId');
+ if (!defined $userid || length $userid == 0) {
+ $errors{userId} = "Please enter a userid";
+ }
+ my $pass = $cgi->param('password');
+ my $pass2 = $cgi->param('confirm_password');
+ if (!defined $pass || length $pass == 0) {
+ $errors{password} = "Please enter a password";
+ }
+ elsif (length $pass < $min_pass_length) {
+ $errors{password} = "The password must be at least $min_pass_length characters";
+ }
+ elsif (!defined $pass2 || length $pass2 == 0) {
+ $errors{confirm_password} = "Please enter a confirmation password";
+ }
+ elsif ($pass ne $pass2) {
+ $errors{confirm_password} =
+ "The confirmation password is different from the password";
+ }
+ my $user = SiteUsers->getBy(userId=>$userid);
+ if ($user) {
+ # give the user a suggestion
+ my $workuser = $userid;
+ $workuser =~ s/\d+$//;
+ my $suffix = 1;
+ for my $suffix (1..100) {
+ unless (SiteUsers->getBy(userId=>"$workuser$suffix")) {
+ $cgi->param(userid=>"$workuser$suffix");
+ last;
+ }
+ }
+ $errors{userId} = "Sorry, user $userid already exists";
+ }
+ $user{userId} = $userid;
+ $user{password} = $pass;
+ }
+
+ unless ($errors{email}) {
+ my $checkemail = SiteUser->generic_email($email);
+ require 'BSE/EmailBlacklist.pm';
+ my $blackentry = BSE::EmailBlacklist->getEntry($checkemail);
+ if ($blackentry) {
+ $errors{email} = "Email $email is blacklisted: $blackentry->{why}";
+ }
+ }
+
+ my @mod_cols = grep !$donttouch{$_}, @cols;
+ for my $col (@mod_cols) {
+ my $value = $cgi->param($col);
+ if ($cfg->entryBool('site users', "require_$col")) {
+ unless (defined $value && $value ne '') {
+ my $disp = $cfg->entry('site users', "display_$col", "\u$col");
+
+ $errors{$col} = "$disp is a required field";
+ }
+ }
+ if (defined $value) {
+ $user{$col} = $value;
+ }
+ }
+ if (keys %errors) {
+ return $class->req_addform($req, undef, \%errors);
+ }
+
+ $user{email} = $email;
+ $user{lastLogon} = $user{whenRegistered} =
+ $user{previousLogon} = now_datetime;
+ $user{keepAddress} = 0;
+ $user{wantLetter} = 0;
+ if ($nopassword) {
+ use BSE::Util::Secure qw/make_secret/;
+ $user{password} = make_secret($cfg);
+ }
+
+ my $user;
+ eval {
+ $user = SiteUsers->add(@user{@cols});
+ };
+ if ($user) {
+ # my $subs = $self->_save_subs($user, $session, $cfg, $cgi);
+ my $msg;
+ if ($nopassword) {
+ my $code;
+ my $sent_ok = $user->send_conf_request($cgi, $cfg, \$code, \$msg);
+ }
+ my $r = $cgi->param('r');
+ unless ($r) {
+ $r = $req->url('siteusers', { list => 1,
+ 'm' => "User $user->{userId} added" });
+ }
+ $r .= "&m=".escape_url($msg) if $msg;
+ return BSE::Template->get_refresh($r, $cfg);
+ }
+ else {
+ $class->req_add($req, "Database error $@");
+ }
+}
+
+1;
(
Articles => 'select * from article',
replaceArticle =>
- 'replace article values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ 'replace article values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
addArticle =>
- 'insert article values (null, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ 'insert article values (null, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
deleteArticle => 'delete from article where id = ?',
getArticleByPkey => 'select * from article where id = ?',
Orders => 'select * from orders',
getOrderByPkey => 'select * from orders where id = ?',
getOrderItemByOrderId => 'select * from order_item where orderId = ?',
- addOrder => 'insert orders values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
- replaceOrder => 'replace orders values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ addOrder => 'insert orders values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ replaceOrder => 'replace orders values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
addOrderItem => 'insert order_item values(null,?,?,?,?,?,?,?)',
getOrderByUserId => 'select * from orders where userId = ?',
'select * from site_users where userId = ?',
getSiteUserByPkey =>
'select * from site_users where id = ?',
- addSiteUser => 'insert site_users values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
- replaceSiteUser => 'replace site_users values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ addSiteUser => 'insert site_users values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ replaceSiteUser => 'replace site_users values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
'SiteUsers.removeSubscriptions'=>
'delete from subscribed_users where userId = ?',
'SiteUsers.removeSub'=>
select si.* from site_users si, subscribed_users su
where confirmed <> 0 and si.id = su.userId and su.subId = ?
EOS
+ SiteUsers => 'select * from site_users',
SubscriptionTypes =>
'select * from subscription_types',
select count(*) as "count" from site_users si, subscribed_users su
where confirmed <> 0 and si.id = su.userId and su.subId = ?
EOS
+ 'SubscriptionTypes.userSubscribedTo' => <<'EOS',
+select su.* from subscription_types su, subscribed_users us
+ where us.userId = ? and us.subId = su.id
+EOS
addSubscribedUser=>
'insert subscribed_users values(null,?,?)',
or return '** movefiles can only be used in the files iterator **';
my $urlbase = $self->{cfg}->entryVar('site', 'url');
- my $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}&filelist=1$urladd";
+ my $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}$urladd";
+ my $t = $req->cgi->param('_t');
+ if ($t && $t =~ /^\w+$/) {
+ $url .= "&_t=$t";
+ }
my $down_url = "";
if ($$rindex < $#$files) {
use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
- $self->_refresh_filelist($req, $article, 'File moved');
+ $self->refresh($article, $req->cgi, undef, 'File moved');
}
sub filedel {
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);
+my %donttouch = map { $_, $_ } @donttouch;
+
sub user_tags {
my ($self, $acts, $session, $user) = @_;
or return $self->show_logon($session, $cgi, $cfg,
$msgs->(needpass=>"Please enter your password"));
my $user = SiteUsers->getBy(userId => $userid);
- if ($user->{password} eq '') {
- return $self->show_logon($session, $cgi, $cfg,
- $msgs->(secretpass=>"Please use the URL from your confirmation message to change your details"));
- }
unless ($user && $user->{password} eq $password) {
return $self->show_logon($session, $cgi, $cfg,
$msgs->(baduserpass=>"Invalid user or password"));
}
+ if ($user->{disabled}) {
+ return $self->show_logon($session, $cgi, $cfg,
+ $msgs->(disableduser=>"Account $userid has been disabled"));
+ }
$session->{userid} = $user->{userId};
$user->{previousLogon} = $user->{lastLogon};
$user->{lastLogon} = now_datetime;
sub show_register {
my ($self, $session, $cgi, $cfg, $message, $errors) = @_;
+ my $user_register = $cfg->entryBool('site users', 'user_register', 1);
+ my $nopassword = $cfg->entryBool('site users', 'nopassword', 0);
+ unless ($user_register) {
+ if ($nopassword) {
+ return $self->show_lost_password($session, $cgi, $cfg,
+ "Registration disabled");
+ }
+ else {
+ return $self->show_logon($session, $cgi, $cfg,
+ "Registration disabled");
+ }
+ }
$errors ||= {};
$message ||= $cgi->param('message');
if (defined $message) {
BSE::Template->show_page($template, $cfg, \%acts, $base);
}
+sub _checkemail {
+ my ($user, $errors, $email, $cgi, $msgs, $nopassword) = @_;
+
+ if (!$email) {
+ $errors->{email} = $msgs->(optsnoemail => "Please enter an email address");
+ }
+ elsif ($email !~ /.@./) {
+ $errors->{email} = $msgs->(optsbademail=>
+ "Please enter a valid email address");
+ }
+ else {
+ if ($nopassword && $email ne $user->{email}) {
+ my $conf_email = $cgi->param('confirmemail');
+ if ($conf_email) {
+ if ($conf_email eq $email) {
+ my $other = SiteUsers->getBy(userId=>$email);
+ if ($other) {
+ $errors->{email} =
+ $msgs->(optsdupemail =>
+ "That email address is already in use");
+ }
+ }
+ else {
+ $errors->{confirmemail} =
+ $msgs->(optsconfemailnw=>
+ "Confirmation email address doesn't match email address");
+ }
+ }
+ else {
+ $errors->{confirmemail} =
+ $msgs->(optsnoconfemail=> "Please enter a confirmation email address");
+ }
+
+ }
+ }
+ if (!$errors->{email}) {
+ my $checkemail = _generic_email($email);
+ require 'BSE/EmailBlacklist.pm';
+ my $blackentry = BSE::EmailBlacklist->getEntry($checkemail);
+ if ($blackentry) {
+ $errors->{email} =
+ $msgs->(optsblackemail =>
+ "Email $email is blacklisted: $blackentry->{why}",
+ $email, $blackentry->{why});
+ }
+ }
+}
+
sub saveopts {
my ($self, $session, $cgi, $cfg) = @_;
}
}
my $email = $cgi->param('email');
- if (!$email) {
- $errors{email} = $msgs->(optsnoemail => "Please enter an email address");
- }
- elsif ($email !~ /.@./) {
- $errors{email} = $msgs->(optsbademail=>
- "Please enter a valid email address");
- }
- else {
- if ($nopassword && $email ne $user->{email}) {
- my $conf_email = $cgi->param('confirmemail');
- if ($conf_email) {
- if ($conf_email eq $email) {
- my $other = SiteUsers->getBy(userId=>$email);
- if ($other) {
- $errors{email} =
- $msgs->(optsdupemail =>
- "That email address is already in use");
- }
- }
- else {
- $errors{confirmemail} =
- $msgs->(optsconfemailnw=>
- "Confirmation email address doesn't match email address");
- }
- }
- else {
- $errors{confirmemail} = $msgs->(optsnoconfemail=>
- "Please enter a confirmation email address");
- }
-
- }
+ my $saveemail;
+ if (defined $email) {
+ ++$saveemail;
+ _checkemail($user, \%errors, $email, $cgi, $msgs, $nopassword);
}
- if (!$errors{email}) {
- my $checkemail = _generic_email($email);
- require 'BSE/EmailBlacklist.pm';
- my $blackentry = BSE::EmailBlacklist->getEntry($checkemail);
- if ($blackentry) {
- $errors{email} = $msgs->(optsblackemail =>
- "Email $email is blacklisted: $blackentry->{why}",
- $email, $blackentry->{why});
- }
- }
- my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation);
- my %donttouch = map { $_, $_ } @donttouch;
my @cols = grep !$donttouch{$_}, SiteUser->columns;
for my $col (@cols) {
my $value = $cgi->param($col);
if ($cfg->entryBool('site users', "require_$col")) {
- unless (defined $value && $value ne '') {
+ if (defined $value && $value eq '') {
my $disp = $cfg->entry('site users', "display_$col", "\u$col");
$errors{$col} = $msgs->("optsrequired" =>
"$disp is a required field", $col, $disp);
keys %errors
and return $self->show_opts($session, $cgi, $cfg, undef, \%errors);
my $newemail;
- if ($email ne $user->{email}) {
+ if ($saveemail && $email ne $user->{email}) {
$user->{confirmed} = 0;
$user->{confirmSecret} = '';
$user->{email} = $email;
}
}
- $user->{textOnlyMail} = 0 unless defined $cgi->param('textOnlyMail');
- $user->{keepAddress} = 0 unless defined $cgi->param('keepAddress');
+ $user->{textOnlyMail} = 0
+ if $cgi->param('saveTextOnlyMail') && !defined $cgi->param('textOnlyMail');
+ $user->{keepAddress} = 0
+ if $cgi->param('saveKeepAddress') && !defined $cgi->param('keepAddress');
$user->save;
# subscriptions
- return if $self->_save_subs($user, $session, $cfg, $cgi, 1);
-
- if ($newemail && $nopassword) {
- return $self->send_conf_request($session, $cgi, $cfg, $user);
+ if ($cgi->param('saveSubscriptions')) {
+ my $subs = $self->_save_subs($user, $session, $cfg, $cgi);
+ if ($nopassword) {
+ return $self->send_conf_request($session, $cgi, $cfg, $user)
+ if $newemail;
+ }
+ else {
+ return $self->send_conf_request($session, $cgi, $cfg, $user)
+ if $subs && !$user->{confirmed};
+ }
}
my $url = $cgi->param('r');
# returns true if the caller needs to send output
sub _save_subs {
- my ($self, $user, $session, $cfg, $cgi, $sendconf, $suppress_success) = @_;
+ my ($self, $user, $session, $cfg, $cgi) = @_;
my @subids = $cgi->param('subscription');
$user->removeSubscriptions;
my @subs;
my @cols = BSE::SubscribedUser->columns;
shift @cols; # don't set id
+ my $found = 0;
for my $subid (@subids) {
$subid =~ /^\d+$/ or next;
my $sub = BSE::SubscriptionTypes->getByPkey($subid)
or next;
+ ++$found;
my %usersub;
$usersub{subId} = $subid;
$usersub{userId} = $user->{id};
push(@usersubs, BSE::SubscribedUsers->add(@usersub{@cols}));
push(@subs, $sub);
}
- if ($sendconf && !$user->{confirmed}) {
- return $self->send_conf_request($session, $cgi, $cfg, $user, $suppress_success);
- }
+ return $found;
}
- 1;
+ return 0;
}
sub register {
my $msgs = BSE::Message->new(cfg=>$cfg, section=>'user');
+ my $user_register = $cfg->entryBool('site users', 'user_register', 1);
+ my $nopassword = $cfg->entryBool('site users', 'nopassword', 0);
+ unless ($user_register) {
+ my $msg = $msgs->(regdisabled => "Registration disabled");
+ if ($nopassword) {
+ return $self->show_lost_password($session, $cgi, $cfg, $msg);
+ }
+ else {
+ return $self->show_logon($session, $cgi, $cfg, $msg);
+ }
+ }
+
my %user;
my @cols = SiteUser->columns;
shift @cols;
$user{$field} = '';
}
- my $nopassword = $cfg->entryBool('site users', 'nopassword', 0);
my %errors;
my $email = $cgi->param('email');
if (!defined $email or !length $email) {
}
}
- my @donttouch = qw(id userId password email confirmed confirmSecret waitingForConfirmation);
- my %donttouch = map { $_, $_ } @donttouch;
my @mod_cols = grep !$donttouch{$_}, @cols;
for my $col (@mod_cols) {
my $value = $cgi->param($col);
-value=>$user->{userId},
-path=>"/"),"\n";
$session->{userid} = $user->{userId} unless $nopassword;
+ my $subs = $self->_save_subs($user, $session, $cfg, $cgi);
if ($nopassword) {
- $self->_save_subs($user, $session, $cfg, $cgi, 0);
- # send the confirmation immediately
return $self->send_conf_request($session, $cgi, $cfg, $user);
}
- else {
- return unless $self->_save_subs($user, $session, $cfg, $cgi, 1, 1);
+ elsif ($subs) {
+ return if $self->send_conf_request($session, $cgi, $cfg, $user, 1);
}
use Util qw/refresh_to/;
}
sub _generic_email {
+# SiteUser->generic_email(shift);
my ($checkemail) = @_;
# Build a generic form for the email - since an attacker could
$checkemail;
}
+# returns non-zero iff a page was generated
sub send_conf_request {
my ($self, $session, $cgi, $cfg, $user, $suppress_success) = @_;
unless ($from) {
$acts{mailerror} = sub { escape_html("Configuration Error: The confirmations from address has not been configured") };
BSE::Template->show_page('user/email_conferror', $cfg, \%acts);
- return;
+ return 1;
}
my $blackentry = BSE::EmailBlacklist->getEntry($checkemail);
if ($blackentry) {
$acts{black} = sub { CGI::escapeHTML($blackentry->{$_[0]}) },
BSE::Template->show_page('user/blacklisted', $cfg, \%acts);
- return;
+ return 1;
}
unless ($user->{confirmSecret}) {
# check how many
if ($too_many) {
BSE::Template->show_page('user/toomany', $cfg, \%acts);
- return;
+ return 1;
}
if ($too_soon) {
BSE::Template->show_page('user/toosoon', $cfg, \%acts);
- return;
+ return 1;
}
}
else {
++$confirm->{unackedConfMsgs};
$confirm->{lastConfSent} = now_datetime;
$confirm->save;
- return 1 if $suppress_success;
+ return 0 if $suppress_success;
BSE::Template->show_page($nopassword ? 'user/confsent_nop' : 'user/confsent', $cfg, \%acts);
return 1;
--- /dev/null
+package BSE::Util::Iterate;
+use strict;
+use base 'DevHelp::Tags::Iterate';
+use DevHelp::HTML;
+
+sub escape {
+ escape_html($_[1]);
+}
+
+1;
use DevHelp::Tags;
use DevHelp::HTML;
use vars qw(@EXPORT_OK @ISA);
-@EXPORT_OK = qw(tag_error_img);
+@EXPORT_OK = qw(tag_error_img tag_hash);
@ISA = qw(Exporter);
require Exporter;
sub basic {
my ($class, $acts, $cgi, $cfg) = @_;
+ require BSE::Util::Iterate;
+ my $it = BSE::Util::Iterate->new;
return
(
$class->static($acts, $cfg),
escape_html("@value");
},
old => [ \&tag_old, $cgi ],
+ $it->make_iterator(\&DevHelp::Tags::iter_get_repeat, 'repeat', 'repeats'),
);
}
$str;
}
+sub tag_hash {
+ my ($hash, $args) = @_;
+
+ my $value = $hash->{$args};
+ defined $value or $value = '';
+
+ escape_html($value);
+}
+
1;
--- /dev/null
+package DevHelp::DynSort;
+use strict;
+use vars qw(@EXPORT_OK);
+use base 'Exporter';
+@EXPORT_OK = qw(sorter tag_sorthelp);
+use Carp 'confess';
+
+sub sorter {
+ my (%opts) = @_;
+
+ my $cgi = $opts{cgi}
+ or confess "No cgi object supplied.";
+ my $data = $opts{data}
+ or confess "No data supplied";
+ my $fields = $opts{fields} || {};
+ my $def_sortby = $opts{sortby};
+ my $def_reverse = $opts{reverse};
+ my $sortby_param = $opts{sortparam} || 's';
+ my $tie_field = $opts{tiefield};
+ my $reverse_param = $opts{reverseparam} || 'r';
+
+ my $sortby = $cgi->param($sortby_param);
+ defined $sortby && $sortby =~ /^\w+$/ or $sortby = $def_sortby;
+ my $reverse = $cgi->param($reverse_param);
+ defined $reverse or $reverse = $def_reverse;
+ $reverse = $reverse ? 1 : 0; # make sure it's a number
+
+ if (@$data && $sortby) {
+ exists $data->[0]{$sortby} or $sortby = $def_sortby;
+ exists $data->[0]{$sortby} or $sortby = '';
+ if (defined $sortby && $sortby ne '') {
+ my $tie_numeric;
+ if (defined $tie_field) {
+ exists $data->[0]{$tie_field}
+ or confess "No tie breaker field $tie_field in data";
+ $tie_numeric =
+ $fields && exists $fields->{$tie_field} && $fields->{$tie_field}{numeric};
+ }
+
+ my $numeric =
+ $fields && exists $fields->{$sortby} && $fields->{$sortby}{numeric};
+ @$data = sort
+ {
+ my $left = $a->{$sortby};
+ defined $left or $left = '';
+ my $right = $b->{$sortby};
+ defined $right or $right = '';
+ my $cmp = $numeric ? $left <=> $right : lc $left cmp lc $right;
+ if (!$cmp && $tie_field) {
+ $cmp = $tie_numeric
+ ? $a->{$tie_field} <=> $b->{$tie_field}
+ : lc $a->{$tie_field} cmp lc $b->{$tie_field};
+ }
+ $cmp = -$cmp if $reverse;
+ $cmp;
+ } @$data;
+ }
+ }
+
+ return ($sortby, $reverse);
+}
+
+sub tag_sorthelp {
+ my ($sortby, $reverse, $args) = @_;
+
+ if ($args eq $sortby) {
+ my $rev = $reverse ? 0 : 1;
+ return "s=$args&r=$rev";
+ }
+ else {
+ return "s=$args&r=0";
+ }
+}
+
+1;
+
+=head1 NAME
+
+ DevHelp::DynSort - sort listings at run-time.
+
+=head1 SYNOPSIS
+
+ my @data = ....; # array of hashes
+ my ($sortby, $reverse) =
+ sorter(data=>\@data, cgi=>$cgi, ...),
+
+ my %acts;
+ %acts =
+ (
+ ...
+ sortby => $sortby,
+ reverse => $reverse,
+ );
+
+=head1 DESCRIPTION
+
+Intended for use in sorting supplied data based on user criteria.
+
+=head2 Required Parameters
+
+=over
+
+=item data
+
+An array ref of hashes. This is sorted in place.
+
+=item cgi
+
+A CGI.pm compatible CGI object. Only the param() method is called.
+
+=back
+
+=head2 Optional Parameters
+
+=over
+
+=item fields
+
+Hash of hashes, where the key at the top level is the field name, and
+the value configuration information for that field.
+
+The only field in the configuration information is 'numeric' which
+marks the field for numeric sorting if non-zero.
+
+=item sortby
+
+Default field to sort by. No default (if no sort order is provided by
+the user and this value isn't set, then the data isn't sorted.)
+
+=item reverse
+
+Default reversal. Default: zero (ascending order).
+
+=item sortparam
+
+The name of the CGI parameter to get the sort order field from.
+Default 's'.
+
+=item reverseparam
+
+The name of the CGI parameter to get the reversal from. Default 'r'.
+
+=item tiefield
+
+Name of the field to use for breaking ties in the sort order.
+Default: ties aren't broken. If this is set to a field name then that
+field will be used to break ties in sorting by the primary sort field.
+
+=back
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=cut
sub iter_get_repeat {
my ($args, $acts, $name, $templater) = @_;
+ print STDERR "iter_get_repeat $args\n";
+
my @args = __PACKAGE__->get_parms($args, $acts, $templater);
my @values;
--- /dev/null
+package DevHelp::Tags::Iterate;
+use strict;
+use Carp qw(confess);
+
+sub new {
+ my ($class, %opts) = @_;
+
+ return bless \%opts, $class;
+}
+
+sub escape {
+ return $_[1];
+}
+
+sub _iter_reset_paged {
+ my ($self, $rdata, $rindex) = @_;
+
+ $$rindex = -1;
+
+ 1;
+}
+
+sub _iter_iterate {
+ my ($self, $rdata, $rindex) = @_;
+
+ return ++$$rindex < @$rdata;
+}
+
+sub _iter_item {
+ my ($self, $rdata, $rindex, $single, $plural, $args) = @_;
+
+ $$rindex >= 0 && $$rindex < @$rdata
+ or return "** $single should only be used inside iterator $plural **";
+ return $self->escape($rdata->[$$rindex]{$args});
+}
+
+sub _iter_number_paged {
+ my ($self, $rindex, $baseindex) = @_;
+
+ $$rindex + $baseindex + 1;
+}
+
+sub _iter_reset_page_counter {
+ my ($self, $rpage) = @_;
+
+ $$rpage = 1;
+}
+
+sub _iter_iterate_page_counter {
+ my ($self, $rpage, $page_count) = @_;
+
+ ++$$rpage <= $page_count;
+}
+
+sub _iter_page_counter {
+ my ($self, $rpage) = @_;
+
+ $$rpage;
+}
+
+sub _iter_index {
+ my ($self, $rindex) = @_;
+
+ $$rindex;
+}
+
+sub _iter_if_first {
+ my ($self, $rindex) = @_;
+
+ $$rindex == 0;
+}
+
+sub _iter_if_last {
+ my ($self, $rdata, $rindex) = @_;
+
+ $$rindex == $#$rdata;
+}
+
+sub make_paged_iterator {
+ my ($self, $single, $plural, $rdata, $rindex, $cgi, $pagename,
+ $perpage) = @_;
+
+ my $index;
+ defined $rindex or $rindex = \$index;
+ $$rindex = -1;
+ $rdata or die;
+ my $loaded = 0;
+ my $max;
+ unless ($perpage =~ /^\d+$/) {
+ my ($name, $count) = $perpage =~ /^(\w+)=(\d+)$/
+ or confess "Invalid perpage '$perpage'";
+ $name ||= 'pp';
+ $count ||= 10;
+ my $work = $cgi->param($name);
+ if (defined $work && $work =~ /^\d+$/ && $work >= 1 && $work <= 1000) {
+ $perpage = $work;
+ }
+ else {
+ $perpage = $count;
+ }
+ }
+ my $page_count = int((@$rdata + $perpage - 1) / $perpage);
+ $page_count = 1 unless $page_count;
+ $pagename ||= 'p';
+ my $page_num = $cgi->param($pagename);
+ unless (defined($page_num) && $page_num =~ /^\d+$/
+ && $page_num >= 1 && $page_num <= $page_count) {
+ $page_num = 1;
+ }
+ my $base_index = $perpage * ($page_num - 1);
+ my $end_index = $base_index + $perpage - 1;
+ $end_index <= $#$rdata or $end_index = $#$rdata;
+ my @data;
+ @data = @$rdata[$base_index .. $end_index] if @$rdata;
+
+ my $page_counter;
+
+ return
+ (
+ "iterate_${plural}_reset" =>
+ [ _iter_reset_paged=>$self, \@data, $rindex ],
+ "iterate_${plural}" =>
+ [ _iter_iterate=>$self, \@data, $rindex, $single ],
+ $single => [ _iter_item => $self, \@data, $rindex, $single, $plural ],
+ "if\u$plural" => scalar(@data),
+ "${single}_index" => [ _iter_index=>$self, $rindex ],
+ "${single}_number" => [ _iter_number_paged=>$self, $rindex, $base_index ],
+ "ifLast\u$single" => [ _iter_if_last=>$self, \@data, $rindex ],
+ "ifFirst\u$single" => [ _iter_if_first=>$self, $rindex ],
+ "ifNext\u${plural}\EPage" => $page_num < $page_count,
+ "ifPrev\u${plural}\EPage" => $page_num > 1,
+ "ifFirst\u${plural}\EPage" => $page_num == 1,
+ "ifLast\u${plural}\EPage" => $page_num == $page_count,
+ "next\u${plural}\EPage" =>
+ ( $page_num < $page_count ? $page_num + 1 : $page_num ),
+ "prev\u${plural}\EPage" =>
+ ( $page_num > 1 ? $page_num - 1 : 1 ),
+ "${single}_count" => scalar(@data),
+ "${plural}_pagenum" => $page_num,
+ "${plural}_pagecount" => $page_count,
+ "${single}_totalcount" => scalar(@$rdata),
+ "${plural}_firstnumber" => $base_index + 1,
+ "${plural}_lastnumber" => $end_index + 1,
+ "iterate_${single}_pages_reset" =>
+ [ _iter_reset_page_counter=>$self, \$page_counter ],
+ "iterate_${single}_pages" =>
+ [ _iter_iterate_page_counter=>$self, \$page_counter, $page_count ],
+ "${single}_pagecounter" =>
+ [ _iter_page_counter=>$self, \$page_counter ],
+ "${plural}_perpage" => $perpage,
+ );
+}
+
+sub _iter_reset {
+ my ($self, $rdata, $rindex, $code, $loaded, $nocache,
+ $args, $acts, $name, $templater) = @_;
+
+ if (!$$loaded && !@$rdata && $code || $args || $nocache) {
+ my ($sub, @args) = $code;
+
+ if (ref $code eq 'ARRAY') {
+ ($sub, @args) = @$code;
+ }
+ @$rdata = $sub->(@args, $args, $acts, $name, $templater);
+ ++$$loaded unless $args;
+ }
+
+ $$rindex = -1;
+
+ 1;
+}
+
+sub _iter_number {
+ my ($self, $rindex) = @_;
+
+ 1+$$rindex;
+}
+
+sub make_iterator {
+ my ($self, $code, $single, $plural, $rdata, $rindex, $nocache) = @_;
+
+ my $index;
+ defined $rindex or $rindex = \$index;
+ $$rindex = -1;
+ $rdata ||= [];
+ my $loaded = 0;
+ return
+ (
+ "iterate_${plural}_reset" =>
+ [ _iter_reset=>$self, $rdata, $rindex, $code, \$loaded, $nocache ],
+ "iterate_${plural}" =>
+ [ _iter_iterate=>$self, $rdata, $rindex, $nocache ],
+ $single =>
+ [ _iter_item=>$self, $rdata, $rindex, $single, $plural ],
+ "${single}_index" => [ _iter_index=>$self, $rindex ],
+ "${single}_number" => [ _iter_number =>$self, $rindex ],
+ "if\u$plural" =>
+ [ _iter_count=>$self, $rdata, $code, \$loaded, $nocache ],
+ "${single}_count" =>
+ [ _iter_count=>$self, $rdata, $code, \$loaded, $nocache ],
+ "ifLast\u$single" => [ _iter_if_last=>$self, $rdata, $rindex ],
+ "ifFirst\u$single" => [ _iter_if_first=>$self, $rindex ],
+ );
+}
+
+1;
filled whenFilled whoFilled paidFor paymentReceipt
randomId cancelled userId paymentType
customInt1 customInt2 customInt3 customInt4 customInt5
- customStr1 customStr2 customStr3 customStr4 customStr5/;
+ customStr1 customStr2 customStr3 customStr4 customStr5
+ instructions billTelephone billFacsimile billEmail/;
}
1;
use Squirrel::Row;
use vars qw/@ISA/;
@ISA = qw/Squirrel::Row/;
+use Constants qw($SHOP_FROM);
+use BSE::Util::SQL qw/now_datetime/;
+
+use constant MAX_UNACKED_CONF_MSGS => 3;
+use constant MIN_UNACKED_CONF_GAP => 2 * 24 * 60 * 60;
sub columns {
return qw/id userId password email keepAddress whenRegistered lastLogon
name1 name2 address city state postcode telephone facsimile
country wantLetter confirmed confirmSecret waitingForConfirmation
textOnlyMail title organization referral otherReferral
- prompt otherPrompt profession otherProfession previousLogon/;
+ prompt otherPrompt profession otherProfession previousLogon
+ billFirstName billLastName billStreet billSuburb billState
+ billPostCode billCountry instructions billTelephone billFacsimile
+ billEmail adminNotes disabled/;
}
sub removeSubscriptions {
SiteUsers->doSpecial('removeSub', $self->{id}, $subid);
}
+sub generic_email {
+ my ($class, $checkemail) = @_;
+
+ # Build a generic form for the email - since an attacker could
+ # include comments or extra spaces or a bunch of other stuff.
+ # this isn't strictly correct, but it's good enough
+ 1 while $checkemail =~ s/\([^)]\)//g;
+ if ($checkemail =~ /<([^>]+)>/) {
+ $checkemail = $1;
+ }
+ $checkemail = lc $checkemail;
+ $checkemail =~ s/\s+//g;
+
+ $checkemail;
+}
+
+sub subscriptions {
+ my ($self) = @_;
+
+ require BSE::SubscriptionTypes;
+ return BSE::SubscriptionTypes->getSpecial(userSubscribedTo => $self->{id});
+}
+
+sub send_conf_request {
+ my ($user, $cgi, $cfg, $rcode, $rmsg) = @_;
+
+ my $nopassword = $cfg->entryBool('site users', 'nopassword', 0);
+
+ # check for existing in-progress confirmations
+ my $checkemail = $user->generic_email($user->{email});
+
+ # check the blacklist
+ require BSE::EmailBlacklist;
+
+ # check that the from address has been configured
+ my $from = $cfg->entry('confirmations', 'from') ||
+ $cfg->entry('basic', 'emailfrom')|| $SHOP_FROM;
+ unless ($from) {
+ $$rcode = 'config';
+ $$rmsg = "Configuration Error: The confirmations from address has not been configured";
+ return;
+ }
+
+ my $blackentry = BSE::EmailBlacklist->getEntry($checkemail);
+
+ if ($blackentry) {
+ $$rcode = "blacklist";
+ $$rmsg = $blackentry->{why};
+ return;
+ }
+
+ unless ($user->{confirmSecret}) {
+ use BSE::Util::Secure qw/make_secret/;
+ # print STDERR "Generating secret\n";
+ $user->{confirmSecret} = make_secret($cfg);
+ $user->save;
+ }
+
+ # check for existing confirmations
+ my $confirm = BSE::EmailRequests->getBy(genEmail=>$checkemail);
+ if ($confirm) {
+ if ($confirm->{unackedConfMsgs} >= MAX_UNACKED_CONF_MSGS) {
+ $$rcode = 'toomany';
+ $$rmsg = "Too many confirmations have been sent to this email address";
+ return;
+ }
+ use BSE::Util::SQL qw/sql_datetime_to_epoch/;
+ my $lastSentEpoch = sql_datetime_to_epoch($confirm->{lastConfSent});
+ if ($lastSentEpoch + MIN_UNACKED_CONF_GAP > time) {
+ $$rcode = 'toosoon';
+ $$rmsg = "The last confirmation was sent too recently, please wait before trying again";
+ return;
+ }
+ }
+ else {
+ my %confirm;
+ my @cols = BSE::EmailRequest->columns;
+ shift @cols;
+ $confirm{email} = $user->{email};
+ $confirm{genEmail} = $checkemail;
+ # prevents silliness on error
+ use BSE::Util::SQL qw(sql_datetime);
+ $confirm{lastConfSent} = sql_datetime(time - MIN_UNACKED_CONF_GAP);
+ $confirm{unackedConfMsgs} = 0;
+ $confirm = BSE::EmailRequests->add(@confirm{@cols});
+ }
+
+ # ok, now we can send the confirmation request
+ my %confacts;
+ %confacts =
+ (
+ BSE::Util::Tags->basic(\%confacts, $cgi, $cfg),
+ user => sub { $user->{$_[0]} },
+ confirm => sub { $confirm->{$_[0]} },
+ remote_addr => sub { $ENV{REMOTE_ADDR} },
+ );
+ my $email_template =
+ $nopassword ? 'user/email_confirm_nop' : 'user/email_confirm';
+ my $body = BSE::Template->get_page($email_template, $cfg, \%confacts);
+
+ my $mail = BSE::Mail->new(cfg=>$cfg);
+ my $subject = $cfg->entry('confirmations', 'subject')
+ || 'Subscription Confirmation';
+ unless ($mail->send(from=>$from, to=>$user->{email}, subject=>$subject,
+ body=>$body)) {
+ # a problem sending the mail
+ $$rcode = "mail";
+ $$rmsg = $mail->errstr;
+ return;
+ }
+ ++$confirm->{unackedConfMsgs};
+ $confirm->{lastConfSent} = now_datetime;
+ $confirm->save;
+
+ return 1;
+}
+
1;
}
elsif (ref $action eq 'ARRAY') {
my ($code, @params) = @$action;
- $value = $code->(@params, $args, $acts, $func, $self);
+ if (ref $code) {
+ $value = $code->(@params, $args, $acts, $func, $self);
+ }
+ else {
+ # assume it's a method name, first param is the object/class
+ my $obj = shift @params;
+ $value = $obj->$code(@params, $args, $acts, $func, $self);
+ }
}
elsif (ref $action eq 'SCALAR') {
$value = $$action;
$entryf = $entry;
}
- $resetf->(@rargs, $args, $acts, $name, $self) if $resetf;
+ if ($resetf) {
+ if (ref $resetf) {
+ $resetf->(@rargs, $args, $acts, $name, $self);
+ }
+ else {
+ my $obj = shift @rargs;
+ $obj->$resetf(@rargs, $args, $acts, $name, $self);
+ }
+ }
+ my $eobj;
+ ref $entryf or $eobj = shift @eargs;
my $result = '';
- while ($entryf->(@eargs, $name, $args)) {
+ while ($eobj ? $eobj->$entryf(@eargs, $name, $args)
+ : $entryf->(@eargs, $name, $args)) {
$result .= $self->replace_template($sep, $acts) if length $result;
$result .= $self->replace_template($input, $acts);
}
=head1 CHANGES
+This is a development release and is not suitable for production use.
+
+=head2 0.14_03
+
+=over
+
+=item *
+
+in some cases registration or saving user options wasn't outputting a
+page, producing a 500 error
+
+=item *
+
+user options templating changes:
+
+=over
+
+=item *
+
+for the textOnlyEmail option to be saved, a saveTextOnlyMail hidden
+field should be present with a non-zero value
+
+=item *
+
+for the keepAddress option to be saved, a saveKeepAddress hidden field
+should be present with a non-zero value
+
+=item *
+
+for the subscriptions to be saves, a saveSubscriptions hidden field
+should be present
+
+=back
+
+=item *
+
+added site user manager, this still needs some template work
+
+=item *
+
+user registration can be disabled with the C<user_register> option in
+the C<[site users]> section of the config file (you can still create
+users using the site user manager.)
+
+=item *
+
+billing information fields have been added to the site_users table.
+
+=item *
+
+adminNotes field has been added to the site users table (editable only
+by admins.)
+
+=item *
+
+instructions field has been added to the orders table, for shipping
+instructions (or any). This still needs some support on the admin
+templates, and the email templates.
+
+=item *
+
+added config option C<billing_on_main_opts> to control whether billing
+options are on the main user options page. This is managed by the
+templates, not the code.
+
+=back
+
=head2 0.14_02
=over
You can get the I<id> of a subcription by looking at the Edit link on the
subscriptions management page, the number after "id=" is the id.
+=item billing_on_main_opts
+
+If set to zero then user billing options will be managed on a separate
+page. This is controlled by the user/options_base.tmpl template.
+
+=item user_register
+
+If set to zero then users cannot register themselves. Default: true,
+allowing users to register themselves.
+
=back
=head1 AUTHOR
textarea { font-family: "MS Sans Serif", Verdana, sans-serif; font-size: 12px}
.mini-admin { font-family: "MS Sans Serif", Verdana, sans-serif; font-size: 10px}
.user-buttons { font-family: "MS Sans Serif", Verdana, sans-serif; font-size: 12px ; border: 2px #FF7F00 groove; background-color: #FFFFFF}
+.useroptions th { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: x-small; font-weight: bold; white-space: nowrap; text-align: left }
+.useroptions td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: x-small }
+.useroptions .thead { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: large; font-weight: bold; white-space: nowrap; text-align: center }
--- /dev/null
+<:wrap admin/xbase.tmpl title=>"Add Site User":>
+<h1>Add Site User</h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<a href="/cgi-bin/admin/siteusers.pl">Site users</a> |
+<a href="mailto:<:siteuser email:>">Email</a> |</p>
+
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:>
+
+<form method="POST" action="<:script:>">
+<input type="hidden" name="id" value="<:siteuser id:>" />
+ <table border="0" cellspacing="0" cellpadding="0" bgcolor="#000000" class="table">
+ <tr>
+ <td>
+ <table cellpadding="6" border="0" cellspacing="1">
+<:ifCfg "site users" nopassword:><:or:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Logon: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="userId" value="<:old userId:>" />*
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser userId:> <:error_img userId:></td>
+ </tr>
+<:eif:>
+<:if Cfg "site users" nopassword:><:or Cfg:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left"> Password: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="password" name="password" />*
+ </td>
+ <td bgcolor="#FFFFFF"> <:help editsiteuser password:> <:error_img password:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left"> Confirm: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="password" name="confirm_password" />*
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser confirm_password:> <:error_img confirm_password:> </td>
+ </tr>
+<:eif Cfg:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Email: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="email" value="<:old email :>" size="60" />*
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser email:> <:error_img email:></td>
+ </tr>
+<:ifCfg "site users" nopassword:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Confirm Email: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="confirmemail" value="<:old confirmemail:>" size="60" />*
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser confirmemail:> <:error_img confirmemail:></td>
+ </tr>
+<:or:><:eif:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">First Name: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="name1" value="<:old name1 :>" size="60" /><:ifRequired name1:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser name1:> <:error_img name1:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Last Name: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="name2" value="<:old name2 :>" size="60" /><:ifRequired name2:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser name2:> <:error_img name2:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">address: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="address" value="<:old address:>" size="60" /><:ifRequired address:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser address:> <:error_img address:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">city: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="city" value="<:old city :>" /><:ifRequired city:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser city:> <:error_img city:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">state: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="state" value="<:old state:>" /><:ifRequired state:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser state:> <:error_img state:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">postcode: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="postcode" value="<:old postcode:>" /><:ifRequired postcode:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser postcode:> <:error_img postcode:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">telephone: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="telephone" value="<:old telephone:>" /><:ifRequired telephone:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser telephone:> <:error_img telephone:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">facsimile: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="facsimile" value="<:old facsimile:>" /><:ifRequired facsimile:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser facsimile:> <:error_img facsimile:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">country: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="country" value="<:old country :>" size="60" /><:ifRequired country:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser country:> <:error_img country:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">title: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="title" value="<:old title :>" /><:ifRequired title:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser title:> <:error_img title:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">organization: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="organization" value="<:old organization:>" size="60" /><:ifRequired organization:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser organization:> <:error_img organization:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billFirstName: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billFirstName" value="<:old billFirstName:>" size="60" /><:ifRequired billFirstName:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billFirstName:> <:error_img billFirstName:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billLastName: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billLastName" value="<:old billLastName :>" size="60" /><:ifRequired billLastName:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billLastName:> <:error_img billLastName:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billStreet: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billStreet" value="<:old billStreet :>" size="60" /><:ifRequired billStreet:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billStreet:> <:error_img billStreet:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billSuburb: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billSuburb" value="<:old billSuburb :>" /><:ifRequired billSuburb:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billSuburb:> <:error_img billSuburb:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billState: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billState" value="<:old billState :>" /><:ifRequired billState:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billState:> <:error_img billState:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billPostCode: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billPostCode" value="<:old billPostCode:>" /><:ifRequired billPostCode:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billPostCode:> <:error_img billPostCode:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billCountry: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billCountry" value="<:old billCountry :>" /><:ifRequired billCountry:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billCountry:> <:error_img billCountry:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">instructions: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="instructions" value="<:old instructions:>" /><:ifRequired instructions:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser instructions:> <:error_img instructions:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billTelephone: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billTelephone" value="<:old billTelephone:>" /><:ifRequired billTelephone:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billTelephone:> <:error_img billTelephone:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billFacsimile: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billFacsimile" value="<:old billFacsimile :>" /><:ifRequired billFacsimile:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billFacsimile:> <:error_img billFacsimile:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billEmail: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billEmail" value="<:old billEmail :>" /><:ifRequired billEmail:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billEmail:> <:error_img billEmail:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left" valign="top">Admin Notes: </th>
+ <td bgcolor="#FFFFFF">
+ <textarea name="adminNotes" wrap="soft" rows="5" cols="60"><:old adminNotes :></textarea>
+ </td>
+ <td bgcolor="#FFFFFF" valign="top"><:help editsiteuser adminNotes:> <:error_img adminNotes:></td>
+ </tr>
+ <tr>
+ <td bgcolor="#FFFFFF" colspan="3" align="right">
+ <input type="submit" name="a_add" value=" Add User " />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</form>
--- /dev/null
+<:wrap admin/xbase.tmpl title=>"Edit Site User":>
+<h1>Edit Site User</h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<a href="/cgi-bin/admin/siteusers.pl">Site users</a> |
+<a href="mailto:<:siteuser email:>">Email</a> |</p>
+
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:>
+
+<form method="POST" action="<:script:>">
+<input type="hidden" name="id" value="<:siteuser id:>" />
+ <table border="0" cellspacing="0" cellpadding="0" bgcolor="#000000" class="table">
+ <tr>
+ <td>
+ <table cellpadding="6" border="0" cellspacing="1">
+<:ifCfg "site users" nopassword:><:or:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Logon: </th>
+ <td bgcolor="#FFFFFF">
+ <:siteuser userId:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser userId:> <:error_img userId:></td>
+ </tr>
+<:eif:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Email: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="email" value="<:old email siteuser email:>" size="60" />
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser email:> <:error_img email:></td>
+ </tr>
+<:ifCfg "site users" nopassword:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Confirm Email: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="confirmemail" value="<:old confirmemail:>" size="60" />
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser confirmemail:> <:error_img confirmemail:></td>
+ </tr>
+<:or:><:eif:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Disabled: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="checkbox" name="disabled" value="1" <:ifSiteuser disabled:>checked<:or:><:eif:> /><input type="hidden" name="saveDisabled" value="1" />
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser disabled:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">First Name: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="name1" value="<:old name1 siteuser name1:>" size="60" /><:ifRequired name1:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser name1:> <:error_img name1:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Last Name: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="name2" value="<:old name2 siteuser name2:>" size="60" /><:ifRequired name2:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser name2:> <:error_img name2:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">address: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="address" value="<:old address siteuser address:>" size="60" /><:ifRequired address:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser address:> <:error_img address:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">city: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="city" value="<:old city siteuser city:>" /><:ifRequired city:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser city:> <:error_img city:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">state: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="state" value="<:old state siteuser state:>" /><:ifRequired state:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser state:> <:error_img state:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">postcode: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="postcode" value="<:old postcode siteuser postcode:>" /><:ifRequired postcode:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser postcode:> <:error_img postcode:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">telephone: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="telephone" value="<:old telephone siteuser telephone:>" /><:ifRequired telephone:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser telephone:> <:error_img telephone:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">facsimile: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="facsimile" value="<:old facsimile siteuser facsimile:>" /><:ifRequired facsimile:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser facsimile:> <:error_img facsimile:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">country: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="country" value="<:old country siteuser country:>" size="60" /><:ifRequired country:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser country:> <:error_img country:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">title: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="title" value="<:old title siteuser title:>" /><:ifRequired title:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser title:> <:error_img title:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">organization: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="organization" value="<:old organization siteuser organization:>" size="60" /><:ifRequired organization:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser organization:> <:error_img organization:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billFirstName: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billFirstName" value="<:old billFirstName siteuser billFirstName:>" size="60" /><:ifRequired billFirstName:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billFirstName:> <:error_img billFirstName:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billLastName: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billLastName" value="<:old billLastName siteuser billLastName:>" size="60" /><:ifRequired billLastName:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billLastName:> <:error_img billLastName:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billStreet: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billStreet" value="<:old billStreet siteuser billStreet:>" size="60" /><:ifRequired billStreet:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billStreet:> <:error_img billStreet:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billSuburb: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billSuburb" value="<:old billSuburb siteuser billSuburb:>" /><:ifRequired billSuburb:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billSuburb:> <:error_img billSuburb:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billState: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billState" value="<:old billState siteuser billState:>" /><:ifRequired billState:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billState:> <:error_img billState:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billPostCode: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billPostCode" value="<:old billPostCode siteuser billPostCode:>" /><:ifRequired billPostCode:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billPostCode:> <:error_img billPostCode:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billCountry: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billCountry" value="<:old billCountry siteuser billCountry:>" /><:ifRequired billCountry:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billCountry:> <:error_img billCountry:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">instructions: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="instructions" value="<:old instructions siteuser instructions:>" /><:ifRequired instructions:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser instructions:> <:error_img instructions:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billTelephone: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billTelephone" value="<:old billTelephone siteuser billTelephone:>" /><:ifRequired billTelephone:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billTelephone:> <:error_img billTelephone:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billFacsimile: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billFacsimile" value="<:old billFacsimile siteuser billFacsimile:>" /><:ifRequired billFacsimile:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billFacsimile:> <:error_img billFacsimile:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">billEmail: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="billEmail" value="<:old billEmail siteuser billEmail:>" /><:ifRequired billEmail:>*<:or:><:eif:>
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser billEmail:> <:error_img billEmail:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left" valign="top">Admin Notes: </th>
+ <td bgcolor="#FFFFFF">
+ <textarea name="adminNotes" wrap="soft" rows="5" cols="60"><:old adminNotes siteuser adminNotes:></textarea>
+ </td>
+ <td bgcolor="#FFFFFF" valign="top"><:help editsiteuser adminNotes:> <:error_img adminNotes:></td>
+ </tr>
+<:if UserCan siteuser_changepw:>
+<:if Cfg "site users" nopassword:><:or Cfg:>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left"> Password: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="password" name="password" />
+ </td>
+ <td bgcolor="#FFFFFF"> <:help editsiteuser password:> <:error_img password:></td>
+ </tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left"> Confirm: </th>
+ <td bgcolor="#FFFFFF">
+ <input type="password" name="confirm_password" />
+ </td>
+ <td bgcolor="#FFFFFF"><:help editsiteuser confirm_password:> <:error_img confirm_password:> </td>
+ </tr>
+<:eif Cfg:>
+<:or UserCan:><:eif UserCan:>
+ <tr>
+ <td bgcolor="#FFFFFF" colspan="3" align="right">
+ <input type="submit" name="a_save" value=" Save User " />
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</form>
--- /dev/null
+<:wrap admin/xbase.tmpl title=>"Admin Site Users":>
+<h1>Admin Site Users</h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<:if UserCan siteuser_add :>
+<a href="<:script:>?a_addform=1">Add User</a> |<:or UserCan:><:eif UserCan:>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:>
+
+<p>Page <:siteusers_pagenum:> of <:siteusers_pagecount:>
+<:ifFirstSiteusersPage:><<<< <<<:or:><a href="<:script:>?s=<:sortby:>&r=<:reverse:>&p=1&pp=<:siteusers_perpage:>"><<<<</a> <a href="<:script:>?s=<:sortby:>&r=<:reverse:>&p=<:prevSiteusersPage:>&pp=<:siteusers_perpage:>"><<</a><:eif:>
+<:iterator begin repeats [siteusers_pagecount]:>
+<:if Eq [repeat value] [siteusers_pagenum]:><:repeat value:><:or Eq:><a href="<:script:>?s=<:sortby:>&r=<:reverse:>&p=<:repeat value:>&pp=<:siteusers_perpage:>"><:repeat value:></a><:eif Eq:>
+<:iterator end repeats:>
+<:ifLastSiteusersPage:>>> >>>><:or:><a href="<:script:>?s=<:sortby:>&r=<:reverse:>&p=<:nextSiteusersPage:>&pp=<:siteusers_perpage:>">>></a> <a href="<:script:>?s=<:sortby:>&r=<:reverse:>&p=<:siteusers_pagecount:>&pp=<:siteusers_perpage:>">>>>></a><:eif:>
+</p>
+<form method="post" action="<:script:>">
+<table border="0" cellspacing="0" cellpadding="0" bgcolor="#000000" class="table">
+ <tr>
+ <td>
+ <table cellpadding="6" border="0" cellspacing="1">
+ <tr>
+ <th bgcolor="#FFFFFF" nowrap> <a href="<:script:>?<:sorthelp userId:>&p=<:siteusers_pagenum:>&pp=<:siteusers_perpage:>">Logon</a></th>
+ <th bgcolor="#FFFFFF" nowrap> <a href="<:script:>?<:sorthelp name1:>&p=<:siteusers_pagenum:>&pp=<:siteusers_perpage:>">First Name</a></th>
+ <th bgcolor="#FFFFFF" nowrap> <a href="<:script:>?<:sorthelp name2:>&p=<:siteusers_pagenum:>&pp=<:siteusers_perpage:>">Last Name</a></th>
+ </tr>
+ <:if Siteusers:> <: iterator begin siteusers :>
+ <tr bgcolor="#FFFFFF">
+ <td nowrap> <a href="<:script:>?a_edit=1&id=<:siteuser id:>"><:siteuser userId:></a></td>
+ <td valign="top"><:siteuser name1:></td>
+ <td valign="top"><:siteuser name2:></td>
+ </tr>
+ <: iterator end siteusers :>
+ <:or Siteusers:>
+ <tr bgcolor="#FFFFFF">
+ <td colspan="4" align="center">Your system has no users.</td>
+ </tr>
+ <:eif Siteusers:>
+ </table>
+</td>
+</tr>
+</table>
+</form>
+
<input type="Text" name="email" size=34 value="<:old email:>">
*</font></td>
</tr>
+ <tr>
+ <td valign="top"><font face="Verdana, Arial, Helvetica, sans-serif" size="2">Special<br />Instructions:</font></td>
+ <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
+ <textarea name="instructions" rows="5" cols="40" wrap="virtual"><:old instructions:></textarea></font></td>
+ </tr>
<tr>
<td colspan="2"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
* Required information for order to be shipped</font></td>
<:wrap base.tmpl:>
-<div align=center>
+<div align="center" class="useroptions">
<:if Cfg "site users" nopassword:><:or Cfg:>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<:eif Cfg:>
<table width="250">
<tr>
- <th colspan="2" align="center" height="20">
- <p><font face="Verdana, Arial, Helvetica, sans-serif" size="3"><b>User
- Options</b></font></p>
+ <th colspan="2" height="20" class="thead">
+ <p>User
+ Options</p>
</th>
</tr>
<tr>
</tr>
<:or Message:><:eif Message:>
<form action="/cgi-bin/user.pl" method="post">
+ <input type="hidden" name="saveSubscriptions" value="1" />
<:if Cfg "site users" nopassword:>
<input type="hidden" name="u" value="<:user id:>" />
<input type="hidden" name="p" value="<:user password:>" />
<!--//<tr>
<th nowrap="nowrap" align="left"><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2">Keep
Details:</font></b></th>
- <td width="100%"><input type="checkbox" name="keepAddress" value="1"<:ifLast keepAddress:> checked<:or:><:eif:> /></td>
+ <td width="100%"><input type="hidden" name="saveKeepAddress" value="1" /><input type="checkbox" name="keepAddress" value="1"<:ifLast keepAddress:> checked<:or:><:eif:> /></td>
</tr> //-->
<:if Subscriptions:>
<tr>
your subscription. If you change your email address you will need
to reconfirm your subscription.</font></div>
<:eif User:> <:eif AnySubs:>
- <p> <input type="checkbox" name="textOnlyMail" value="1"<:ifLast textOnlyMail:>
+ <p> <input type="hidden" name="saveTextOnlyMail" value="1" /><input type="checkbox" name="textOnlyMail" value="1"<:ifLast textOnlyMail:>
checked<:or:><:eif:> /> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">Text
only E-mail messages</font></p>
</td>
<input type="text" name="facsimile" value="<:last facsimile:>" size="32" maxlength="80" /><:error_img facsimile:>
</td>
</tr>
+<:if Cfg "site users" billing_on_main_opts 1:>
+ <tr>
+ <td colspan="2" height="20"><br>
+ <br>
+ Billing contact information:</td>
+ </tr>
+ <tr>
+ <th>First Name:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billFirstName" value="<:last billFirstName:>" size="40" maxlength="127" /><:error_img billFirstName:>
+ </td>
+ </tr>
+ <tr>
+ <th>Last Name:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billLastName" value="<:last billLastName:>" size="40" maxlength="127" /><:error_img billLastName:>
+ </td>
+ </tr>
+ <tr>
+ <th>Street:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billStreet" value="<:last billStreet:>" size="40" maxlength="127" /><:error_img billStreet:>
+ </td>
+ </tr>
+ <tr>
+ <th>Suburb:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billSuburb" value="<:last billSuburb:>" size="40" maxlength="127" /><:error_img billSuburb:>
+ </td>
+ </tr>
+ <tr>
+ <th>State:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billState" value="<:last billState:>" size="40" maxlength="127" /><:error_img billState:>
+ </td>
+ </tr>
+ <tr>
+ <th>Post Code:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billPostCode" value="<:last billPostCode:>" size="40" maxlength="127" /><:error_img billPostCode:>
+ </td>
+ </tr>
+ <tr>
+ <th>Country:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billCountry" value="<:last billCountry:>" size="40" maxlength="127" /><:error_img billCountry:>
+ </td>
+ </tr>
+ <tr>
+ <th>Telephone:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billTelephone" value="<:last billTelephone:>" size="40" maxlength="127" /><:error_img billTelephone:>
+ </td>
+ </tr>
+ <tr>
+ <th>Facsimile:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billFacsimile" value="<:last billFacsimile:>" size="40" maxlength="127" /><:error_img billFacsimile:>
+ </td>
+ </tr>
+ <tr>
+ <th>Email:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billEmail" value="<:last billEmail:>" size="40" maxlength="127" /><:error_img billEmail:>
+ </td>
+ </tr>
+<:or Cfg:>
+ <tr>
+ <td colspan="2" height="20" align="center"><br>
+ <br>
+ <a href="<:script:>?show_opts=1&_t=billing">Manage Billing Contact Information</a></td>
+ </tr>
+<:eif Cfg:>
<:if Cfg "site users" nopassword:><:or Cfg:>
<tr>
<td colspan="2" align="left" height="20"><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"><br>
--- /dev/null
+<:wrap base.tmpl:>
+<div align=center class="useroptions">
+<:if Cfg "site users" nopassword:><:or Cfg:>
+ <table border="0" cellspacing="0" cellpadding="0">
+ <tr>
+ <td>
+ <form name="yourorders" method="post" action="<:script:>">
+ <input type="submit" name="Submit" value="View your account" class="user-buttons" />
+ <input type="hidden" name="userpage" value="1" />
+ </form>
+ </td>
+ <:ifCfg shop enabled:><td>
+ <form name="ff" method="post" action="/cgi-bin/shop.pl">
+ <input type="submit" name="cart" value="View shopping cart" class="user-buttons" />
+ </form>
+ </td><:or:><:eif:>
+ </tr>
+ </table>
+ <br />
+<:eif Cfg:>
+ <table width="250">
+ <tr>
+ <th colspan="2" height="20" class="thead">
+ <p>User Options - Billing</p>
+ </th>
+ <tr>
+ <td colspan="2" align="center">
+ <p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2">Hello <:ifUser name1:><:user name1:> <:user name2:><:or:><:user
+ userId:><:eif:></font></b></p>
+ <p><font face="Verdana, Arial, Helvetica, sans-serif" size="2" color="#999999">Last logged in: <:date user previousLogon:><br>
+ Registered since: <:date user whenRegistered:></font><br>
+ <br>
+ </p>
+ </td>
+ </tr>
+ <:if Message:>
+ <tr>
+ <td colspan="2" align="center">
+ <p><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>Error:
+ <:message:></b></font></p>
+ </td>
+ </tr>
+ <:or Message:><:eif Message:>
+ </tr>
+ <tr>
+ <td colspan="2" height="20" align="center"><br>
+ <br>
+ <a href="<:script:>?show_opts=1">Return to Main Options</a></td>
+ </tr>
+ <form action="/cgi-bin/user.pl" method="post">
+ <input type="hidden" name="_t" value="billing" />
+ <:if Cfg "site users" nopassword:>
+ <input type="hidden" name="u" value="<:user id:>" />
+ <input type="hidden" name="p" value="<:user password:>" />
+ <input type="hidden" name="r" value="<:script:>?show_opts=<:user password:>&u=<:user id:>&_t=saved" />
+ <:or Cfg:><:eif Cfg:>
+<:if Cfg "site users" nopassword:><:or Cfg:>
+ <tr>
+ <th>Logon Name:</th>
+ <td width="100%"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><:user
+ userId:></font></td>
+ </tr>
+<:eif Cfg:>
+ <tr>
+ <th>First Name:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billFirstName" value="<:last billFirstName:>" size="40" maxlength="127" /><:error_img billFirstName:>
+ </td>
+ </tr>
+ <tr>
+ <th>Last Name:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billLastName" value="<:last billLastName:>" size="40" maxlength="127" /><:error_img billLastName:>
+ </td>
+ </tr>
+ <tr>
+ <th>Street:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billStreet" value="<:last billStreet:>" size="40" maxlength="127" /><:error_img billStreet:>
+ </td>
+ </tr>
+ <tr>
+ <th>Suburb:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billSuburb" value="<:last billSuburb:>" size="40" maxlength="127" /><:error_img billSuburb:>
+ </td>
+ </tr>
+ <tr>
+ <th>State:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billState" value="<:last billState:>" size="40" maxlength="127" /><:error_img billState:>
+ </td>
+ </tr>
+ <tr>
+ <th>Post Code:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billPostCode" value="<:last billPostCode:>" size="40" maxlength="127" /><:error_img billPostCode:>
+ </td>
+ </tr>
+ <tr>
+ <th>Country:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billCountry" value="<:last billCountry:>" size="40" maxlength="127" /><:error_img billCountry:>
+ </td>
+ </tr>
+ <tr>
+ <th>Telephone:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billTelephone" value="<:last billTelephone:>" size="40" maxlength="127" /><:error_img billTelephone:>
+ </td>
+ </tr>
+ <tr>
+ <th>Facsimile:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billFacsimile" value="<:last billFacsimile:>" size="40" maxlength="127" /><:error_img billFacsimile:>
+ </td>
+ </tr>
+ <tr>
+ <th>Email:</th>
+ <td width="100%" nowrap="nowrap">
+ <input type="text" name="billEmail" value="<:last billEmail:>" size="40" maxlength="127" /><:error_img billEmail:>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right">
+ <input type="submit" name="saveopts" value="Save Options" class="user-buttons" />
+ </td>
+ </tr>
+ </form>
+ </table>
+</div>
#site users.subscribe_all=1
site users.subscribe_1=1
#debug.cookies=1
+site users.billing_on_main_opts=0
+site users.user_register=0