commit before applying Adrian's patch
authorTony Cook <tony@develop-help.com>
Wed, 15 Jun 2005 11:52:43 +0000 (11:52 +0000)
committertony <tony@45cb6cf1-00bc-42d2-bb5a-07f51df49f94>
Wed, 15 Jun 2005 11:52:43 +0000 (11:52 +0000)
39 files changed:
MANIFEST
schema/bse.sql
site/cgi-bin/admin/admin.pl
site/cgi-bin/admin/admin_seminar.pl [new file with mode: 0755]
site/cgi-bin/modules/BSE/DB/Mysql.pm
site/cgi-bin/modules/BSE/Edit/Article.pm
site/cgi-bin/modules/BSE/Edit/Catalog.pm
site/cgi-bin/modules/BSE/Edit/Seminar.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Generate/Seminar.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/Request.pm
site/cgi-bin/modules/BSE/TB/Location.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/Locations.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/Order.pm
site/cgi-bin/modules/BSE/TB/Seminar.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/TB/Seminars.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/UI/AdminSeminar.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/UI/Shop.pm
site/cgi-bin/modules/BSE/Util/Tags.pm
site/cgi-bin/modules/BSE/Validate.pm [new file with mode: 0644]
site/cgi-bin/modules/DevHelp/Formatter.pm
site/cgi-bin/modules/Generate/Catalog.pm
site/cgi-bin/modules/SiteUser.pm
site/cgi-bin/modules/Squirrel/Row.pm
site/docs/bse.pod
site/templates/admin/edit_catalog.tmpl
site/templates/admin/edit_seminar.tmpl [new file with mode: 0644]
site/templates/admin/locations/add.tmpl [new file with mode: 0644]
site/templates/admin/locations/delete.tmpl [new file with mode: 0644]
site/templates/admin/locations/edit.tmpl [new file with mode: 0644]
site/templates/admin/locations/list.tmpl [new file with mode: 0644]
site/templates/admin/order_detail.tmpl
site/templates/checkoutnew_base.tmpl
site/templates/user/options_base.tmpl
site/templates/user/options_billing_base.tmpl
site/templates/user/register_base.tmpl
t/t050format.t
t/t20gen.t
t/t40images.t
test.cfg

index ba52429..a2bfba0 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -14,6 +14,7 @@ schema/mssql.sql
 schema/mysql_build.pl   # builds site/util/mysql.str
 site/cgi-bin/admin/add.pl
 site/cgi-bin/admin/admin.pl
+site/cgi-bin/admin/admin_seminar.pl
 site/cgi-bin/admin/adminusers.pl
 site/cgi-bin/affiliate.pl
 site/cgi-bin/admin/changepw.pl
@@ -59,12 +60,14 @@ site/cgi-bin/modules/BSE/Edit/Article.pm
 site/cgi-bin/modules/BSE/Edit/Base.pm
 site/cgi-bin/modules/BSE/Edit/Catalog.pm
 site/cgi-bin/modules/BSE/Edit/Product.pm
+site/cgi-bin/modules/BSE/Edit/Seminar.pm
 site/cgi-bin/modules/BSE/Edit/Site.pm
 site/cgi-bin/modules/BSE/EmailBlackEntry.pm
 site/cgi-bin/modules/BSE/EmailBlacklist.pm
 site/cgi-bin/modules/BSE/EmailRequest.pm
 site/cgi-bin/modules/BSE/EmailRequests.pm
 site/cgi-bin/modules/BSE/Formatter.pm
+site/cgi-bin/modules/BSE/Generate/Seminar.pm
 # site/cgi-bin/modules/BSE/FileEditor.pm
 site/cgi-bin/modules/BSE/Mail.pm
 site/cgi-bin/modules/BSE/Mail/SMTP.pm
@@ -89,16 +92,21 @@ site/cgi-bin/modules/BSE/TB/AdminPerm.pm
 site/cgi-bin/modules/BSE/TB/AdminPerms.pm
 site/cgi-bin/modules/BSE/TB/AdminUser.pm
 site/cgi-bin/modules/BSE/TB/AdminUsers.pm
+site/cgi-bin/modules/BSE/TB/Location.pm
+site/cgi-bin/modules/BSE/TB/Locations.pm
 site/cgi-bin/modules/BSE/TB/Order.pm
 site/cgi-bin/modules/BSE/TB/Orders.pm
 site/cgi-bin/modules/BSE/TB/OrderItem.pm
 site/cgi-bin/modules/BSE/TB/OrderItems.pm
+site/cgi-bin/modules/BSE/TB/Seminar.pm
+site/cgi-bin/modules/BSE/TB/Seminars.pm
 site/cgi-bin/modules/BSE/TB/Subscription.pm
 site/cgi-bin/modules/BSE/TB/Subscription/Calc.pm
 site/cgi-bin/modules/BSE/TB/Subscriptions.pm
 site/cgi-bin/modules/BSE/Template.pm
 site/cgi-bin/modules/BSE/Thumb/Imager.pm
 site/cgi-bin/modules/BSE/UI/AdminDispatch.pm
+site/cgi-bin/modules/BSE/UI/AdminSeminar.pm
 site/cgi-bin/modules/BSE/UI/Affiliate.pm
 site/cgi-bin/modules/BSE/UI/Dispatch.pm
 site/cgi-bin/modules/BSE/UI/Formmail.pm
@@ -112,6 +120,7 @@ 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/Validate.pm
 site/cgi-bin/modules/BSE/Version.pm
 site/cgi-bin/modules/BSE/WebUtil.pm
 site/cgi-bin/modules/Constants.pm
@@ -253,6 +262,7 @@ site/templates/admin/edit_1.tmpl
 # site/templates/admin/edit_5.tmpl
 site/templates/admin/edit_catalog.tmpl
 site/templates/admin/edit_product.tmpl
+site/templates/admin/edit_seminar.tmpl
 site/templates/admin/edit_steps.tmpl
 site/templates/admin/filelist.tmpl
 site/templates/admin/grouplist.tmpl
@@ -262,6 +272,10 @@ site/templates/admin/menu.tmpl
 site/templates/admin/menu_adv.tmpl
 # site/templates/admin/menu_custom.tmpl
 site/templates/admin/interestemail.tmpl
+site/templates/admin/locations/add.tmpl
+site/templates/admin/locations/delete.tmpl
+site/templates/admin/locations/edit.tmpl
+site/templates/admin/locations/list.tmpl
 site/templates/admin/order_detail.tmpl
 site/templates/admin/order_list.tmpl
 site/templates/admin/order_list_filled.tmpl
index 51f4787..1cfb97e 100644 (file)
@@ -44,7 +44,7 @@ CREATE TABLE article (
   summaryLength smallint(5) unsigned DEFAULT '200' NOT NULL,
 
   -- the class whose generate() method generates the page
-  generator varchar(20) not null default 'article',
+  generator varchar(40) not null default 'article',
 
   -- the level of the article, 1 for top-level
   level smallint not null,
@@ -264,6 +264,12 @@ create table orders (
   -- order was completed by the customer
   complete integer not null default 1,
 
+  delivOrganization varchar(127) not null default '',
+  billOrganization varchar(127) not null default '',
+
+  delivStreet2 varchar(127) not null default '',
+  billStreet2 varchar(127) not null default '',
+
   primary key (id),
   index order_cchash(ccNumberHash),
   index order_userId(userId, orderDate)
@@ -521,6 +527,11 @@ create table site_users (
   delivMobile varchar(80) not null default '',
   billMobile varchar(80) not null default '',
 
+  delivStreet2 varchar(127) not null default '',
+  billStreet2 varchar(127) not null default '',
+
+  billOrganization varchar(127) not null default '',
+
   primary key (id),
   unique (userId),
   index (affiliate_name)
@@ -663,3 +674,49 @@ create table bse_siteuser_images (
   primary key(siteuser_id, image_id)
 );
 
+drop table if exists bse_locations;
+create table bse_locations (
+  id integer not null auto_increment,
+  description varchar(255) not null,
+  room varchar(40) not null,
+  street1 varchar(255) not null,
+  street2 varchar(255) not null,
+  suburb varchar(255) not null,
+  state varchar(80) not null,
+  country varchar(80) not null,
+  postcode varchar(40) not null,
+  public_notes text not null,
+
+  bookings_name varchar(80) not null,
+  bookings_phone varchar(80) not null,
+  bookings_fax varchar(80) not null,
+  bookings_url varchar(255) not null,
+  facilities_name varchar(255) not null,
+  facilities_phone varchar(80) not null,
+
+  admin_notes text not null,
+
+  disabled integer not null default 0,
+
+  primary key(id)
+);
+
+drop table if exists bse_seminars;
+create table bse_seminars (
+  seminar_id integer not null primary key,
+  duration integer not null
+);
+
+drop table if exists bse_seminar_sessions;
+create table bse_seminar_sessions (
+  id integer not null auto_increment,
+  seminar_id integer not null,
+  location_id integer not null,
+  when_at datetime not null,
+
+  primary key (id),
+  unique (seminar_id, location_id, when_at),
+  index (seminar_id),
+  index (location_id)
+);
+
index d6d2393..3a783f6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -w
 # -d:ptkdb
-#BEGIN { $ENV{DISPLAY} = '192.168.32.97:0.0' }
+#BEGIN { $ENV{DISPLAY} = '192.168.32.15:0.0' }
 use strict;
 use FindBin;
 use CGI::Carp 'fatalsToBrowser';
diff --git a/site/cgi-bin/admin/admin_seminar.pl b/site/cgi-bin/admin/admin_seminar.pl
new file mode 100755 (executable)
index 0000000..954c62b
--- /dev/null
@@ -0,0 +1,18 @@
+#!/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::UI::AdminSeminar;
+
+$SIG{__DIE__} = sub { confess $@ };
+
+my $req = BSE::Request->new;
+
+my $result = BSE::UI::AdminSeminar->dispatch($req);
+BSE::Template->output_result($req, $result);
index a20f569..a6693b7 100644 (file)
@@ -84,8 +84,8 @@ SQL
    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 = ?',
    deleteOrdersItems => 'delete from order_item where orderId = ?',
@@ -129,8 +129,8 @@ SQL
    'select * from site_users where id = ?',
    getSiteUserByAffiliate_name =>
    'select * from site_users where affiliate_name = ?',
-   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'=>
@@ -349,6 +349,29 @@ select su.*, us.started_at, us.ends_at, us.max_lapsed
 where us.siteuser_id = ? and us.subscription_id = su.subscription_id
    and us.ends_at >= curdate()
 SQL
+
+   addLocation => <<SQL,
+insert bse_locations values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
+SQL
+   replaceLocation => <<SQL,
+replace bse_locations values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
+SQL
+   getLocationByPkey => 'select * from bse_locations where id = ?',
+   deleteLocation => 'delete from bse_locations where id = ?',
+   Locations => 'select * from bse_locations order by description',
+
+   Seminars => <<SQL,
+select ar.*, pr.*, se.*
+  from article ar, product pr, bse_seminars se
+  where ar.id = pr.articleId and ar.id = se.articleId
+SQL
+   addSeminar => 'insert bse_seminars values(?,?)',
+   replaceSeminar => 'replace bse_seminars values(?,?)',
+   getSeminarByPkey => <<SQL,
+select ar.*, pr.*, se.*
+  from article ar, product pr, bse_seminars se
+  where id = ? and ar.id = pr.articleId and ar.id = se.seminar_id
+SQL
   );
 
 sub _single
index aa6acce..ef90b32 100644 (file)
@@ -1384,12 +1384,17 @@ sub save_new {
       or $data{$col} = $self->default_value($req, \%data, $col);
   }
 
+  print STDERR "release cgi ", $cgi->param('release'), " data $data{release}\n";
   # these columns are handled a little differently
   for my $col (qw(release expire threshold summaryLength )) {
     $data{$col} 
       or $data{$col} = $self->default_value($req, \%data, $col);
   }
 
+  for my $col (qw(release expire)) {
+    $data{$col} = sql_date($data{$col});
+  }
+
   shift @columns;
   my $article = $table_object->add(@data{@columns});
 
index 4e45dd6..83adb47 100644 (file)
@@ -93,7 +93,7 @@ sub make_link {
 }
 
 sub child_types {
-  return qw(BSE::Edit::Product BSE::Edit::Catalog);
+  return qw(BSE::Edit::Product BSE::Edit::Seminar BSE::Edit::Catalog);
 }
 
 sub default_template {
diff --git a/site/cgi-bin/modules/BSE/Edit/Seminar.pm b/site/cgi-bin/modules/BSE/Edit/Seminar.pm
new file mode 100644 (file)
index 0000000..8fd5adb
--- /dev/null
@@ -0,0 +1,136 @@
+package BSE::Edit::Seminar;
+use strict;
+use base 'BSE::Edit::Product';
+use BSE::TB::Seminars;
+use BSE::Util::Tags qw(tag_hash tag_hash_mbcs);
+
+sub base_template_dirs {
+  return ( "seminar" );
+}
+
+sub edit_template { 
+  my ($self, $article, $cgi) = @_;
+
+  my $base = 'seminar';
+  my $t = $cgi->param('_t');
+  if ($t && $t =~ /^\w+$/) {
+    $base = $t;
+  }
+  return $self->{cfg}->entry('admin templates', $base, 
+                            "admin/edit_$base");
+}
+
+sub generator { "BSE::Generate::Seminar" }
+
+sub default_template {
+  my ($self, $article, $cfg, $templates) = @_;
+
+  my $template = $cfg->entry('seminars', 'template');
+  return $template
+    if $template && grep $_ eq $template, @$templates;
+
+  return $self->SUPER::default_template($article, $cfg, $templates);
+}
+
+sub flag_sections {
+  my ($self) = @_;
+
+  return ( 'seminar flags', $self->SUPER::flag_sections );
+}
+
+sub type_default_value {
+  my ($self, $req, $col) = @_;
+
+  my $value = $req->cfg->entry('seminar defaults', $col);
+  defined $value and return $value;
+
+  return $self->SUPER::type_default_value($req, $col);
+}
+
+sub add_template { 
+  my ($self, $article, $cgi) = @_;
+
+  return $self->{cfg}->entry('admin templates', 'add_seminar', 
+                            'admin/edit_seminar');
+}
+
+sub table_object {
+  my ($self, $articles) = @_;
+
+  'BSE::TB::Seminars';
+}
+
+sub low_edit_tags {
+  my ($self, $acts, $req, $article, $articles, $msg, $errors) = @_;
+
+  my $cfg = $req->cfg;
+  my $mbcs = $cfg->entry('html', 'mbcs', 0);
+  my $tag_hash = $mbcs ? \&tag_hash_mbcs : \&tag_hash;
+  my $it = BSE::Util::Iterate->new;
+  return 
+    (
+     seminar => [ $tag_hash, $article ],
+     $self->SUPER::low_edit_tags($acts, $req, $article, $articles, $msg,
+                               $errors),
+    );
+}
+
+sub get_article {
+  my ($self, $articles, $article) = @_;
+
+  return BSE::TB::Seminars->getByPkey($article->{id});
+}
+
+my %defaults =
+  (
+   duration => 60,
+  );
+
+sub default_value {
+  my ($self, $req, $article, $col) = @_;
+
+  my $value = $self->SUPER::default_value($req, $article, $col);
+  defined $value and return $value;
+
+  exists $defaults{$col} and return $defaults{$col};
+
+  return;
+}
+
+sub _fill_seminar_data {
+  my ($self, $req, $data, $src) = @_;
+
+  if (exists $src->{duration}) {
+    $data->{duration} = $src->{duration};
+  }
+}
+
+sub fill_new_data {
+  my ($self, $req, $data, $articles) = @_;
+
+  $self->_fill_seminar_data($req, $data, $data);
+
+  return $self->SUPER::fill_new_data($req, $data, $articles);
+}
+
+sub fill_old_data {
+  my ($self, $req, $article, $src) = @_;
+
+  $self->_fill_seminar_data($req, $article, $src);
+
+  return $self->SUPER::fill_old_data($req, $article, $src);
+}
+
+sub _validate_common {
+  my ($self, $data, $articles, $errors) = @_;
+
+  my $duration = $data->{duration};
+  if (defined $duration && $duration !~ /^\d+\s*$/) {
+    $errors->{duration} = "Duration invalid";
+  }
+
+  return $self->SUPER::_validate_common($data, $articles, $errors);
+}
+
+1;
+
diff --git a/site/cgi-bin/modules/BSE/Generate/Seminar.pm b/site/cgi-bin/modules/BSE/Generate/Seminar.pm
new file mode 100644 (file)
index 0000000..0aec0cb
--- /dev/null
@@ -0,0 +1,22 @@
+package BSE::Generate::Seminar;
+use strict;
+use base 'Generate::Product';
+use BSE::TB::Seminars;
+use DevHelp::HTML;
+use BSE::Util::Tags qw(tag_hash);
+
+sub baseActs {
+  my ($self, $articles, $acts, $seminar, $embedded) = @_;
+
+  unless ($seminar->isa('BSE::TB::Seminar')) {
+    $seminar = BSE::TB::Seminars->getByPkey($seminar->{id});
+  }
+  return
+    (
+     $self->SUPER::baseActs($articles, $acts, $seminar, $embedded),
+     seminar => [ \&tag_hash, $seminar ],
+     admin => [ tag_admin => $self, $seminar, 'seminar', $embedded ],
+    );
+}
+
+1;
index 1e14835..ad5cb72 100644 (file)
@@ -171,7 +171,7 @@ sub dyn_response {
   }
 
   return BSE::Template->get_response($template, $req->cfg, $acts,
-                                   $base_template);
+                                    $base_template);
 }
 
 sub response {
@@ -204,6 +204,49 @@ sub siteuser {
   }
 }
 
+sub validate {
+  my ($req, %options) = @_;
+
+  $options{rules} ||= {};
+
+  require BSE::Validate;
+  BSE::Validate::bse_validate($req->cgi, $options{errors},
+                             { 
+                              fields => $options{fields},
+                              rules => $options{rules},
+                             },
+                             $req->cfg, $options{section});
+}
+
+sub validate_hash {
+  my ($req, %options) = @_;
+
+  $options{rules} ||= {};
+
+  require BSE::Validate;
+  BSE::Validate::bse_validate_hash($options{data}, $options{errors},
+                                  { 
+                                   fields=>$options{fields},
+                                   rules => $options{rules},
+                                  },
+                                  $req->cfg, $options{section});
+}
+
+sub configure_fields {
+  my ($self, $fields, $section) = @_;
+
+  my $cfg = $self->cfg;
+  require BSE::Validate;
+  BSE::Validate::bse_configure_fields($fields, $cfg, $section);
+
+  for my $name (keys %$fields) {
+    for my $cfg_name (qw/htmltype type width height size maxlength/) {
+      my $value = $cfg->entry($section, "${name}_${cfg_name}");
+      defined $value and $fields->{$name}{$cfg_name} = $value;
+    }
+  }
+}
+
 sub DESTROY {
   my ($self) = @_;
   if ($self->{session}) {
diff --git a/site/cgi-bin/modules/BSE/TB/Location.pm b/site/cgi-bin/modules/BSE/TB/Location.pm
new file mode 100644 (file)
index 0000000..675628a
--- /dev/null
@@ -0,0 +1,66 @@
+package BSE::TB::Location;
+use strict;
+use base qw(Squirrel::Row);
+
+sub columns {
+  return qw/id 
+            description 
+            room street1 street2 suburb state country postcode 
+            public_notes 
+            bookings_name bookings_phone bookings_fax bookings_url 
+            facilities_name facilities_phone 
+            admin_notes disabled/;
+}
+
+sub valid_fields {
+  return
+    (
+     description => { description => "Description",
+                     rules => 'required', maxlength=>255, width=>60 },
+     room => { description => 'Room', maxlength=>40, width=>20 },
+     street1 => { description => 'Street Address 1', required => 1,
+                 maxlength=>255, width=>50 },
+     street2 => { description => 'Street Address 2',
+                 maxlength=>255, width=>50 },
+     suburb => { description => 'Suburb', required => 1,
+                maxlength=>255, width=>50 },
+     state => { description => 'State', required => 1,
+               maxlength=>80, width=>15 },
+     country => { description => 'Country', maxlength=>80 },
+     postcode => { description => 'Post Code', 
+                  rules => 'postcode', 
+                  required => 1, maxlength=>40, width=>5 },
+     public_notes => { description => 'Notes', width=>60, height=>10 },
+     bookings_name => { description => 'Bookings Contact Name',
+                       maxlength=>80, width=>50 },
+     bookings_phone => { description => 'Bookings Contact Phone',
+                        rules => 'phone',
+                      maxlength=>80, width=>30 },
+     bookings_fax => { description => 'Bookings Contact Fax',
+                      rules => 'phone',
+                      maxlength=>80, width=>30 },
+     bookings_url => { description => 'Bookings URL',
+                      rules => 'weburl',
+                    maxlength=>255, width=>60 },
+     facilities_name => { description => 'Facilities Contact Name',
+                         maxlength=>255, width=>60 },
+     facilities_phone => { description => 'Facilities Contact Phone',
+                          rules => 'phone',
+                        maxlength=>80, width=>30 },
+     admin_notes => { description => 'Administration Notes',
+                     width=>60, height=>10},
+    );
+}
+
+sub valid_rules {
+  # no rules yet
+  return;
+}
+
+sub is_removable {
+  my ($self) = @_;
+
+  return 1; # this will change
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/TB/Locations.pm b/site/cgi-bin/modules/BSE/TB/Locations.pm
new file mode 100644 (file)
index 0000000..5126232
--- /dev/null
@@ -0,0 +1,12 @@
+package BSE::TB::Locations;
+use strict;
+use Squirrel::Table;
+use vars qw(@ISA $VERSION);
+@ISA = qw(Squirrel::Table);
+use BSE::TB::Location;
+
+sub rowClass {
+  return 'BSE::TB::Location';
+}
+
+1;
index 8825dc0..539e88e 100644 (file)
@@ -23,7 +23,8 @@ sub columns {
            siteuser_id affiliate_code shipping_cost
            delivMobile billMobile
            ccOnline ccSuccess ccReceipt ccStatus ccStatusText
-           ccStatus2 ccTranId complete/;
+           ccStatus2 ccTranId complete delivOrganization billOrganization
+           delivStreet2 billStreet2/;
 }
 
 =item siteuser
@@ -67,20 +68,42 @@ sub valid_fields {
 
   my %fields =
     (
-     delivFirstName => { description=>'Delivery First Name', },
-     delivLastName => { description => 'Delivery Last Name' },
-     delivStreet => { description => 'Delivery Street' },
-     delivState => { description => 'Delivery State' },
-     delivSuburb => { description => 'Delivery Suburb' },
-     delivPostCode => { description => 'Delivery Post Code' },
-     delivCountry => { description => 'Delivery Country' },
-     billFirstName => { description => 'Billing First Name' },
-     billLastName => { description => 'Billing Last Name' },
-     billStreet => { description => 'Billing First Name' },
-     billSuburb => { description => 'Billing First Name' },
-     billState => { description => 'Billing First Name' },
-     billPostCode => { description => 'Billing First Name' },
-     billCountry => { description => 'Billing First Name' },
+     delivFirstName => { description=>'Delivery First Name', 
+                        rules=>'dh_one_line' },
+     delivLastName => { description => 'Delivery Last Name', 
+                        rules=>'dh_one_line'  },
+     delivOrganization => { description => 'Delivery Organization', 
+                           rules=>'dh_one_line'  },
+     delivStreet => { description => 'Delivery Street', 
+                        rules=>'dh_one_line'  },
+     delivStreet2 => { description => 'Delivery Street 2', 
+                        rules=>'dh_one_line'  },
+     delivState => { description => 'Delivery State', 
+                        rules=>'dh_one_line'  },
+     delivSuburb => { description => 'Delivery Suburb', 
+                        rules=>'dh_one_line'  },
+     delivPostCode => { description => 'Delivery Post Code', 
+                        rules=>'dh_one_line;postcode'  },
+     delivCountry => { description => 'Delivery Country', 
+                        rules=>'dh_one_line'  },
+     billFirstName => { description => 'Billing First Name', 
+                        rules=>'dh_one_line'  },
+     billLastName => { description => 'Billing Last Name', 
+                        rules=>'dh_one_line'  },
+     billOrganization => { description => 'Billing Organization', 
+                          rules=>'dh_one_line'  },
+     billStreet => { description => 'Billing Street', 
+                        rules=>'dh_one_line'  },
+     billStreet2 => { description => 'Billing Street 2', 
+                        rules=>'dh_one_line'  },
+     billSuburb => { description => 'Billing Suburb', 
+                        rules=>'dh_one_line'  },
+     billState => { description => 'Billing State', 
+                        rules=>'dh_one_line'  },
+     billPostCode => { description => 'Billing Post Code', 
+                        rules=>'dh_one_line;postcode'  },
+     billCountry => { description => 'Billing First Name', 
+                        rules=>'dh_one_line'  },
      telephone => { description => 'Telephone Number',
                    rules => "phone" },
      facsimile => { description => 'Facsimile Number',
diff --git a/site/cgi-bin/modules/BSE/TB/Seminar.pm b/site/cgi-bin/modules/BSE/TB/Seminar.pm
new file mode 100644 (file)
index 0000000..fa59024
--- /dev/null
@@ -0,0 +1,22 @@
+package BSE::TB::Seminar;
+use strict;
+# represents a seminar from the database
+use Product;
+use vars qw/@ISA/;
+@ISA = qw/Product/;
+
+# subscription_usage values
+use constant SUBUSAGE_START_ONLY => 1;
+use constant SUBUSAGE_RENEW_ONLY => 2;
+use constant SUBUSAGE_EITHER => 3;
+
+sub columns {
+  return ($_[0]->SUPER::columns(), 
+         qw/seminar_id duration/ );
+}
+
+sub bases {
+  return { seminar_id=>{ class=>'Product'} };
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/TB/Seminars.pm b/site/cgi-bin/modules/BSE/TB/Seminars.pm
new file mode 100644 (file)
index 0000000..de07ae1
--- /dev/null
@@ -0,0 +1,12 @@
+package BSE::TB::Seminars;
+use strict;
+use Squirrel::Table;
+use vars qw(@ISA $VERSION);
+@ISA = qw(Squirrel::Table);
+use BSE::TB::Seminar;
+
+sub rowClass {
+  return 'BSE::TB::Seminar';
+}
+
+1;
diff --git a/site/cgi-bin/modules/BSE/UI/AdminSeminar.pm b/site/cgi-bin/modules/BSE/UI/AdminSeminar.pm
new file mode 100644 (file)
index 0000000..6d59faf
--- /dev/null
@@ -0,0 +1,266 @@
+package BSE::UI::AdminSeminar;
+use strict;
+use base qw(BSE::UI::AdminDispatch);
+use BSE::Util::Tags qw(tag_hash tag_error_img);
+use BSE::Util::DynSort qw(sorter tag_sorthelp);
+use BSE::Template;
+use BSE::Util::Iterate;
+use BSE::TB::Locations;
+use DevHelp::HTML;
+use constant SECT_LOCATION_VALIDATION => "BSE Location Validation";
+
+my %rights =
+  (
+   loclist => 'bse_location_list',
+   locaddform => 'bse_location_add',
+   locadd => 'bse_location_add',
+   locedit => 'bse_location_edit',
+   locsave => 'bse_location_edit',
+   locdelask => 'bse_location_delete',
+   locdelete => 'bse_location_delete',
+#    detail => 'bse_subscr_detail',
+#    update => 'bse_subscr_update',
+  );
+
+sub actions { \%rights }
+
+sub rights { \%rights }
+
+sub default_action { 'loclist' }
+
+sub req_loclist {
+  my ($class, $req, $errors) = @_;
+
+  my $msg = $req->message($errors);
+  my $cgi = $req->cgi;
+  my @locations = BSE::TB::Locations->all;
+  my ($sortby, $reverse) =
+    sorter(data=>\@locations, cgi=>$cgi, sortby=>'description', 
+          session=>$req->session,
+           name=>'locations', fields=> { id => {numeric => 1 } });
+  my $it = BSE::Util::Iterate->new;
+  my $current_loc;
+
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(\%acts, $req->cgi, $req->cfg),
+     BSE::Util::Tags->admin(\%acts, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     msg => $msg,
+     message => $msg,
+     $it->make_paged_iterator('ilocation', 'locations', \@locations, undef,
+                              $cgi, undef, 'pp=20', $req->session, 
+                              'locations', \$current_loc),
+     sorthelp => [ \&tag_sorthelp, $sortby, $reverse ],
+     sortby=>$sortby,
+     reverse=>$reverse,
+     ifRemovable => [ \&tag_ifRemovable, \$current_loc ],
+    );
+
+  return $req->dyn_response('admin/locations/list', \%acts);
+}
+
+sub tag_ifRemovable {
+  my ($rlocation) = @_;
+
+  $$rlocation or return;
+
+  $$rlocation->is_removable;
+}
+
+sub tag_field {
+  my ($fields, $args) = @_;
+
+  my ($name, $parm) = split ' ', $args;
+
+  exists $fields->{$name}
+    or return "** Unknown field $name **";
+  exists $fields->{$name}{$parm}
+    or return '';
+
+  return escape_html($fields->{$name}{$parm});
+}
+
+sub req_locaddform {
+  my ($class, $req, $errors) = @_;
+
+  my $msg = $req->message($errors);
+
+  my %fields = BSE::TB::Location->valid_fields();
+  $req->configure_fields(\%fields, SECT_LOCATION_VALIDATION);
+
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(\%acts, $req->cgi, $req->cfg),
+     BSE::Util::Tags->admin(\%acts, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     msg => $msg,
+     message => $msg,
+     error_img => [ \&tag_error_img, $req->cfg, $errors ],
+     field => [ \&tag_field, \%fields ],
+    );
+
+  return $req->dyn_response('admin/locations/add', \%acts);
+}
+
+sub req_locadd {
+  my ($class, $req) = @_;
+
+  my $cgi = $req->cgi;
+  my $cfg = $req->cfg;
+  my %fields = BSE::TB::Location->valid_fields($cfg);
+  my %rules = BSE::TB::Location->valid_rules($cfg);
+  my %errors;
+  $req->validate(errors=> \%errors, 
+                fields=> \%fields,
+                rules => \%rules,
+                section => SECT_LOCATION_VALIDATION);
+
+  keys %errors
+    and return $class->req_locaddform($req, \%errors);
+
+  my %location;
+  for my $field (keys %fields) {
+    $location{$field} = $cgi->param($field);
+  }
+  $location{disabled} = 0;
+  my @cols = BSE::TB::Location->columns;
+  shift @cols;
+  my $loc = BSE::TB::Locations->add(@location{@cols});
+
+  my $r = $class->_loclist_refresh($req, "Location $location{description} added");
+
+  return BSE::Template->get_refresh($r, $req->cfg);
+}
+
+sub _loc_show_common {
+  my ($class, $req, $errors, $template) = @_;
+
+  my $loc_id = $req->cgi->param('id');
+  $loc_id && $loc_id =~ /^\d+/
+    or return $class->req_loclist
+      ($req, { id=>'Missing or invalid location id' });
+  my $location = BSE::TB::Locations->getByPkey($loc_id);
+  $location
+    or return $class->req_loclist
+      ($req, { id=>'Unknown location id' });
+
+  my $msg = $req->message($errors);
+
+  my %fields = BSE::TB::Location->valid_fields();
+  $req->configure_fields(\%fields, SECT_LOCATION_VALIDATION);
+
+  my %acts;
+  %acts =
+    (
+     BSE::Util::Tags->basic(\%acts, $req->cgi, $req->cfg),
+     BSE::Util::Tags->admin(\%acts, $req->cfg),
+     BSE::Util::Tags->secure($req),
+     msg => $msg,
+     message => $msg,
+     error_img => [ \&tag_error_img, $req->cfg, $errors ],
+     location => [ \&tag_hash, $location ],
+     field => [ \&tag_field, \%fields ],
+    );
+
+  return $req->dyn_response($template, \%acts);
+}
+
+sub req_locedit {
+  my ($class, $req, $errors) = @_;
+
+  return $class->_loc_show_common($req, $errors, 'admin/locations/edit');
+}
+
+sub req_locsave {
+  my ($class, $req) = @_;
+
+  my $loc_id = $req->cgi->param('id');
+  $loc_id && $loc_id =~ /^\d+/
+    or return $class->req_loclist
+      ($req, { id=>'Missing or invalid location id' });
+  my $location = BSE::TB::Locations->getByPkey($loc_id);
+  $location
+    or return $class->req_loclist
+      ($req, { id=>'Unknown location id' });
+
+  my $cgi = $req->cgi;
+  my $cfg = $req->cfg;
+  my %fields = $location->valid_fields($cfg);
+  my %rules = $location->valid_rules($cfg);
+  my %errors;
+  $req->validate(errors=> \%errors, 
+                fields=> \%fields,
+                rules => \%rules,
+                section => SECT_LOCATION_VALIDATION);
+
+  keys %errors
+    and return $class->req_locedit($req, \%errors);
+
+  for my $field (keys %fields) {
+    my $value = $cgi->param($field);
+    $location->{$field} = $value if defined $value;
+  }
+
+  if ($cgi->param('save_disabled')) {
+    $location->{disabled} = $cgi->param('disabled') ? 1 : 0;
+  }
+
+  $location->save;
+
+  my $r = $class->_loclist_refresh($req, 
+                                  "Location $location->{description} saved");
+
+  return BSE::Template->get_refresh($r, $req->cfg);
+}
+
+sub req_locdelask {
+  my ($class, $req, $errors) = @_;
+
+  return $class->_loc_show_common($req, $errors, 'admin/locations/delete');
+}
+
+sub req_locdelete {
+  my ($class, $req) = @_;
+
+  my $loc_id = $req->cgi->param('id');
+  $loc_id && $loc_id =~ /^\d+/
+    or return $class->req_loclist
+      ($req, { id=>'Missing or invalid location id' });
+  my $location = BSE::TB::Locations->getByPkey($loc_id);
+  $location
+    or return $class->req_loclist
+      ($req, { id=>'Unknown location id' });
+
+  $location->is_removable
+    or return $class->req_loclist
+      ($req, { id=>"Location $location->{description} cannot be removed" });
+
+  my $description = $location->{description};
+  $location->remove;
+
+  my $r = $class->_loclist_refresh($req, 
+                                  "Location $description removed");
+
+  return BSE::Template->get_refresh($r, $req->cfg);
+}
+
+sub _loclist_refresh {
+  my ($class, $req, $msg) = @_;
+
+  my $r = $req->cgi->param('r') || $req->cgi->param('refreshto');
+  unless ($r) {
+    $r = "/cgi-bin/admin/admin_seminar.pl";
+  }
+  if ($msg && $r !~ /[&?]m=/) {
+    my $sep = $r =~ /\?/ ? '&' : '?';
+
+    $r .= $sep . "m=" . escape_uri($msg);
+  }
+
+  return $r;
+}
+
+1;
index dadc2c6..9d5aaef 100644 (file)
@@ -40,6 +40,7 @@ my %field_map =
    name1 => 'delivFirstName',
    name2 => 'delivLastName',
    address => 'delivStreet',
+   organization => 'delivOrganization',
    city => 'delivSuburb',
    postcode => 'delivPostCode',
    state => 'delivState',
@@ -154,12 +155,12 @@ sub req_add {
       my $sub = $product->subscription;
       if ($product->is_renew_sub_only) {
        unless ($user->subscribed_to_grace($sub)) {
-         return show_cart("This product can only be used to renew your subscription to $sub->{title} and you are not subscribed nor within the renewal grace period");
+         return $class->req_cart($req, "This product can only be used to renew your subscription to $sub->{title} and you are not subscribed nor within the renewal grace period");
        }
       }
       elsif ($product->is_start_sub_only) {
        if ($user->subscribed_to_grace($sub)) {
-         return show_cart("This product can only be used to start your subscription to $sub->{title} and you are already subscribed or within the grace period");
+         return $class->req_cart($req, "This product can only be used to start your subscription to $sub->{title} and you are already subscribed or within the grace period");
        }
       }
     }
index f6545d3..2ae05d7 100644 (file)
@@ -4,7 +4,7 @@ use HTML::Entities;
 use DevHelp::Tags;
 use DevHelp::HTML qw(:default escape_xml);
 use vars qw(@EXPORT_OK @ISA);
-@EXPORT_OK = qw(tag_error_img tag_hash tag_hash_plain);
+@EXPORT_OK = qw(tag_error_img tag_hash tag_hash_plain tag_hash_mbcs);
 @ISA = qw(Exporter);
 require Exporter;
 
@@ -91,7 +91,14 @@ sub static {
        $hour = $min = $sec = 0 unless defined $sec;
        $year -= 1900;
        --$month;
-       return POSIX::strftime($fmt, $sec, $min, $hour, $day, $month, $year);
+       # passing the isdst as 0 seems to provide a more accurate result than
+       # -1 on glibc.
+       return POSIX::strftime($fmt, $sec, $min, $hour, $day, $month, $year, -1, -1, 0);
+       # the following breaks some of our defaults
+#        # pass the time through mktime() since the perl strftime()
+#        # doesn't actually do it.
+#        my $time = POSIX::mktime($sec, $min, $hour, $day, $month, $year);
+#        return POSIX::strftime($fmt, localtime $time);
      },
      today => \&tag_today,
      money =>
@@ -758,10 +765,24 @@ sub tag_hash {
 
   my $value = $hash->{$args};
   defined $value or $value = '';
+  if ($value =~ /\cJ/ && $value =~ /\cM/) {
+    $value =~ tr/\cM//d;
+  }
 
   escape_html($value);
 }
 
+sub tag_hash_mbcs {
+  my ($object, $args) = @_;
+
+  my $value = $object->{$args};
+  defined $value or $value = '';
+  if ($value =~ /\cJ/ && $value =~ /\cM/) {
+    $value =~ tr/\cM//d;
+  }
+  escape_html($value, '<>&"');
+}
+
 sub tag_hash_plain {
   my ($hash, $args) = @_;
 
diff --git a/site/cgi-bin/modules/BSE/Validate.pm b/site/cgi-bin/modules/BSE/Validate.pm
new file mode 100644 (file)
index 0000000..a074a4c
--- /dev/null
@@ -0,0 +1,38 @@
+package BSE::Validate;
+use strict;
+use base 'DevHelp::Validate';
+use DevHelp::Validate qw(dh_validate dh_validate_hash dh_configure_fields);
+use vars qw(@EXPORT @EXPORT_OK);
+@EXPORT = ();
+@EXPORT_OK = qw(bse_validate bse_validate_hash bse_configure_fields);
+
+sub bse_validate {
+  return dh_validate(@_);
+}
+
+sub bse_validate_hash {
+  return dh_validate_hash(@_);
+}
+
+sub bse_configure_fields {
+  return dh_configure_fields(@_);
+}
+
+1;
+
+=head1 NAME
+
+BSE::Validate - intended for future BSE specific expansion of
+DevHelp::Validate.
+
+=head1 SYNOPSIS
+
+  $req->validate(fields=>$fields, rules=>$rules, errors=>\%errors);
+  $req->validate_hash(fields=>$fields, rules=>$rules, errors=>\%errors, 
+                      data=>\%hash)
+
+=head1 DESCRIPTION
+
+
+
+=cut
index 30b1048..f42cb67 100644 (file)
@@ -409,9 +409,12 @@ sub remove_format {
          and next TRY;
        $part =~ s#style\[([^\]\[\|]+)\|([^\]\[]+)\]#$2#ig
          and next TRY;
+       $part =~ s!(?<=\W)\[([^\]\[]+)\]!\x01$1\x02!g
+          and next TRY;
        
        last TRY;
       }
+      $part =~ tr/\x01\x02/[]/; # put back the bare []
       $out .= $part;
     }
   } 
index b860cf1..cf0f857 100644 (file)
@@ -112,7 +112,12 @@ sub generate_low {
   my @stepprods = $article->visible_stepkids;
   my $stepprod_index;
   my @allkids = $article->all_visible_kids;
-  require 'Generate/Product.pm';
+  # make sure we have all of the inheritance info
+  my %generate = map { $_->{generator} => 1 } @allkids;
+  for my $gen (keys %generate) {
+    (my $file = $gen . ".pm") =~ s!::!/!g;
+    require $file;
+  }
   my @allprods = grep UNIVERSAL::isa($_->{generator}, 'Generate::Product'), 
     @allkids;
   for (@allprods) {
index 04508a6..7e3f905 100644 (file)
@@ -21,7 +21,9 @@ sub columns {
             billEmail adminNotes disabled flags
             customText1 customText2 customText3
             customStr1 customStr2 customStr3
-            affiliate_name delivMobile billMobile/;
+            affiliate_name delivMobile billMobile
+            delivStreet2 billStreet2
+            billOrganization/;
 }
 
 sub valid_fields {
@@ -45,6 +47,8 @@ sub valid_fields {
                       maxlen=>127  },
      delivMobile => { description => "Mobile", rules=>"phone",
                      maxlen => 80 },
+     delivStreet2 => { description => 'Address2', rules => "dh_one_line",
+                      maxlen=> 127 },
      textOnlyEmail => { description => "Text Only Email", type=>"boolean" },
      referral => { description=>'Referral', rules=>"natural"  },
      otherReferral => { description=>'Other Referral', rules=>"dh_one_line",
@@ -60,6 +64,8 @@ sub valid_fields {
      billLastName => { descriptin=>"Billing Last Name", rules=>"dh_one_line" },
      billStreet => { description => "Billing Street Address",
                     rules=>"dh_one_line", maxlen=>127 },
+     billStreet2 => { description => 'Billing Street Address 2', 
+                     rules => "dh_one_line", maxlen=> 127 },
      billSuburb => { description => "Billing Suburb", rules=>"dh_one_line", 
                     maxlen=>127 },
      billState => { description => "Billing State", rules=>"dh_one_line", 
@@ -77,6 +83,8 @@ sub valid_fields {
                    maxlen=>255 },
      billMobile => { description => "Billing Mobile", rules=>"phone",
                     maxlen => 80 },
+     billOrganization => { description => "Billing Organization",
+                          rules=>"dh_one_line", maxlen => 127 },
      customText1 => { description => "Custom Text 1" },
      customText2 => { description => "Custom Text 2" },
      customText3 => { description => "Custom Text 3" },
index 47ba63f..296287f 100644 (file)
@@ -24,32 +24,38 @@ sub new {
   unless (defined $self->{$primary[0]}) {
     my $bases = $class->bases;
     if (keys %$bases) {
-      keys %$bases == 1
-       or confess "I don't know how to handle more than one base for $class";
-      my ($my_col) = keys %$bases;
-      my $base_class = $bases->{$my_col}{class};
+      my @bases = $class->_get_bases;
+      my $base_base = $bases[0];
+      my $base_class = $base_base->[1];
       my $sth = $dh->stmt("add$base_class")
        or confess "No add$base_class member in DatabaseHandle";
-      
+
       # extract the base class columns
       my @base_cols = $base_class->columns;
       my %data;
       @data{$class->columns} = @values;
       $sth->execute(@data{@base_cols[1..$#base_cols]})
        or confess "Could not add $class/$base_class(undef, @data{@base_cols[1..$#base_cols]} )";
-      $self->{$primary[0]} = $self->{$my_col} =
-       $data{$my_col} = $data{$primary[0]} = $dh->insert_id($sth);
-      
-      # now do this class
-      # what do we store
-      my %saved;
-      @saved{@base_cols} = @base_cols;
-      delete $saved{$my_col}; # make sure we save this
-      my @save_cols = grep !$saved{$_}, @columns;
-      $sth = $dh->stmt("add$class")
-       or confess "No add$class member in DatabaseHandle";
-      $sth->execute(@data{@save_cols})
-       or confess "Could not add $class(@data{1..$#save_cols})";
+      my $primary_value = $dh->insert_id($sth);
+
+      $self->{$primary[0]} = $primary_value;
+      my $used_cols = @base_cols;
+
+      # now do the derived classes and ourselves
+      for my $derived (@bases) {
+       my ($my_col, $base_class, $parent_class) = @$derived;
+
+       $self->{$my_col} = $primary_value;
+
+       my @cols = $parent_class->columns;
+       splice(@cols, 0, $used_cols); # strip base column names
+       $used_cols += @cols;
+
+       $sth = $dh->stmt("add$parent_class")
+         or confess "No add$parent_class member in DatabaseHandle";
+       $sth->execute(@$self{@cols})
+         or confess "Could not add $parent_class(@$self{@cols})";
+      }
     }
     else {
       my $sth = $dh->stmt("add$class")
@@ -64,25 +70,6 @@ sub new {
   confess "Undefined primary key fields in ${class}::new"
     if grep !defined, @$self{@primary};
 
-  my $foreign = $self->foreign;
-  for my $key (keys %$foreign) {
-    my $module = $foreign->{$key}{module};
-    my $version = $foreign->{$key}{version};
-
-    next unless defined $module;
-
-    next if !defined($self->{$key}) && exists $foreign->{$key}{null};
-
-    require $module.'.pm';
-
-    $module->VERSION($version) if defined $version;
-
-    my $mod = $module->new;
-
-    confess "Bad FK field $class($key) ($self->{$key})"
-       unless $self->{$key} = $mod->getByPkey($self->{$key});
-  }
-
   $self->{pkey} = join("", @$self{@primary});
   $self->{changed} = 0;
 
@@ -106,34 +93,37 @@ sub save {
   my %saved;
   my $bases = $self->bases;
   if (keys %$bases) {
-    # save to the bases
-    # this should probably recurse at some point
-    for my $base_key (keys %$bases) {
-      # we have bases, update them
-      my $base_class = $bases->{$base_key}{class};
-      my @base_cols = $base_class->columns;
-      my $sth = $dh->stmt('replace'.$base_class)
-       or confess "No replace$base_class member in DatabaseHandle";
-      my @data;
-      for my $col (@base_cols) {
-       push(@data, ref $self->{$col} ? $self->{$col}{pkey} : $self->{$col});
-       ++$saved{$col};
-      }
-      $sth->execute(@data)
-       or confess "Cannot save $base_class part of ",ref $self,":",
-         $sth->errstr;
+    my @bases = $self->_get_bases;
+    my $base_base = $bases[0];
+    my $base_class = $base_base->[1];
+    my $sth = $dh->stmt("replace$base_class")
+      or confess "No replace$base_class member in DatabaseHandle";
+
+    my @base_cols = $base_class->columns;
+    $sth->execute(@$self{@base_cols})
+      or confess "Cannot save $base_class part of ref $self:", $sth->errstr;
+      
+    # save the derived
+    for my $derived (@bases) {
+      my ($key_col, $base_class, $parent_class) = @$derived;
+
+      my $base_cols = () = $base_class->columns;
+      my @parent_cols = $parent_class->columns;
+      splice(@parent_cols, 0, $base_cols);
+
+      my $sth = $dh->stmt('replace'.$parent_class)
+       or confess "No replace$parent_class statement available";
+      $sth->execute(@$self{@parent_cols})
+       or confess "Cannot save $parent_class part of ",ref $self,":",
+         $sth->errstr
     }
   }
-
-  my $sth = $dh->stmt('replace'.ref $self)
-    or confess "No replace",ref $self," member in DatabaseHandle";
-  my @data;
-  for my $col ($self->columns) {
-    push(@data, ref $self->{$col} ? $self->{$col}{pkey} : $self->{$col})
-      unless $saved{$col};
+  else {
+    my $sth = $dh->stmt('replace'.ref $self)
+      or confess "No replace",ref $self," member in DatabaseHandle";
+    $sth->execute(@$self{$self->columns})
+      or confess "Cannot save ",ref $self,":",$sth->errstr;
   }
-  $sth->execute(@data)
-    or confess "Cannot save ",ref $self,":",$sth->errstr;
 
   $self->{changed} = 0;
 }
@@ -178,6 +168,30 @@ sub AUTOLOAD {
     ref $_[0],'"';
 }
 
+sub _get_bases {
+  my ($class) = @_;
+
+  # make sure we have a class name
+  ref $class and $class = ref $class;
+
+  my @bases;
+  my $parent;
+  my $base = $class;
+  my $bases = $class->bases;
+  while ($bases && keys %$bases) {
+    keys %$bases == 1
+      or confess "I don't know how to handle more than one base for $class";
+    
+    my ($my_col) = keys %$bases;
+    $parent = $base;
+    $base = $bases->{$my_col}{class};
+    unshift @bases, [ $my_col, $base, $parent ];
+    $bases = $base->bases;
+  }
+
+  @bases;
+}
+
 # in case someone tries AUTOLOAD tricks
 sub DESTROY {
 }
index 13def1c..b8e72bd 100644 (file)
@@ -10,6 +10,60 @@ Maybe I'll add some other bits here.
 
 =head1 CHANGES
 
+=head2 0.15_13
+
+This is a development release, not intended for production.
+
+=over
+
+=item *
+
+location maintenance at the basic level is in, accessible via
+http://example.com/cgi-bin/admin/admin_seminar.pl
+
+=item *
+
+you can create seminars, though there's no sessions or related data
+yet.
+
+=item *
+
+the generator field in articles has been expanded in size, so
+BSE::Generate::Whatever will fit in.
+
+=item *
+
+orders now accept delivery and billing organization names, and a
+second street line for the delivery and billing addresses
+
+=item *
+
+members can now have a second street address line in their billing and
+delivery addresses.  They can also have a billing organization.
+
+=item *
+
+the release and expiry dates for articles when creating a new article
+seem to have been handled incorrectly for a long time.  We now
+properly parse them when adding a new article (or product, etc)
+
+=item *
+
+in some cases the date tage %z value would be omitted under Linux.
+I'm not sure why this was happening, and the fix is pretty empirical.
+
+=item *
+
+we now attempt to handle nested non-tag [] when removing tags from
+body text.
+
+=item *
+
+t/t40images.t now uses the links() method instead of the internal
+extract_links() method.
+
+=back
+
 =head2 0.15_12
 
 =over
index a40dceb..9e18bdc 100644 (file)
 <:or New:>
 <:if UserCan edit_add_child:article:>
 <p>
-<form action="/cgi-bin/admin/add.pl" method="POST">
+<form action="/cgi-bin/admin/add.pl" method="get">
 <input type=hidden name="parentid" value="<:article id:>">
 <input type=hidden name="type" value="Catalog">
 
     <input type=submit value="Add Sub-catalog">
 </form>
-<form action="/cgi-bin/admin/add.pl" method="POST">
+<form action="/cgi-bin/admin/add.pl" method="get">
 <input type=hidden name="parentid" value="<:article id:>">
-<input type=hidden name="parentid" value="Product">
+<input type=hidden name="type" value="Product">
     <input type=submit value="Add Product">
+</form>
+<form action="/cgi-bin/admin/add.pl" method="get">
+<input type="hidden" name="parentid" value="<:article id:>">
+<input type="hidden" name="type" value="Seminar">
+    <input type=submit value="Add Seminar">
 </form><:or UserCan:><:eif UserCan:></p>
 <:eif New:> <:or HaveChildType:> <:eif HaveChildType:> 
 <p><font size="-1">BSE Release <:release:></font></p>
diff --git a/site/templates/admin/edit_seminar.tmpl b/site/templates/admin/edit_seminar.tmpl
new file mode 100644 (file)
index 0000000..92ac769
--- /dev/null
@@ -0,0 +1,294 @@
+<html><head><title><:ifNew:>Add<:or:>Edit<:eif:> seminar - Shop administration</title>
+  <link rel="stylesheet" type="text/css" href="/css/admin.css">
+</head>
+<body>
+<h1>Shop Administration</h1>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+<p>| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> | <:if New:><:or New:><a href="<:seminar admin:>">See
+     seminar</a> | <a href="/cgi-bin/admin/add.pl?id=<:seminar parentid:>">Edit
+     parent</a> 
+  |<:eif New:> <a href="/cgi-bin/admin/shopadmin.pl">Manage catalogs</a> |<:if
+  New:><:or New:><:if UserCan edit_save:seminar,edit_field_edit_listed:seminar
+   :> <a href="/cgi-bin/admin/add.pl?id=<:seminar id:>&_t=steps">Manage
+      step parents</a> | <:if Seminar listed:> <a href="<:script:>?id=<:seminar id:>&hide=1&r=<:script:>?id=<:seminar id:>">Hide
+      seminar</a> |<:or Seminar:> <a href="<:script:>?id=<:seminar id:>&unhide=1&r=<:script:>?id=<:seminar id:>">Show
+      seminar</a> |<:eif Seminar:><:or UserCan:><:eif UserCan:><:ifSeminar listed:><:or:> Hidden<:eif:><:eif
+      New:></p>
+  <h2>Seminar Details</h2>
+<:ifNew:><:or:><:if Or [iadminuser_count] [iadmingroup_count]:>
+      <form action="/cgi-bin/admin/adminusers.pl">
+  <input type="hidden" name=id value="<: article id:>" />
+  <table border="0" cellspacing="0" cellpadding="0" bgcolor="#000000" class="table">
+  <tr>
+    <td>
+        <table cellpadding="6" cellspacing="1" border="0" width="100%">
+          <tr>
+            <th nowrap>
+
+        <font size=2>Manage access:</font>
+        </th>
+            <td bgcolor="#FFFFFF">
+              <select name=adminid>
+<:iterator begin adminusers:>
+<option value=<:iadminuser id:>>User <:iadminuser logon:>
+<:iterator end adminusers:>
+<:iterator begin admingroups:>
+<option value=<:iadmingroup id:>>Group <:iadmingroup name:>
+<:iterator end admingroups:>
+              </select>
+              <input type=submit name="a_showobjectart" value="Manage">
+      </td>
+            <td bgcolor="#FFFFFF"><:help access manage:>
+        </td>
+          </tr>
+        </table>
+    </td>
+   </tr>
+  </table>
+      </form>
+<br>
+<:or Or:><:eif Or:><:eif:>
+<form action="<:script:>" enctype="multipart/form-data" method="POST">
+    <input type="hidden" name="id" value="<:seminar id:>" />
+  <input type="hidden" name="type" value="Seminar" />
+  <table border="0" cellspacing="0" cellpadding="0" bgcolor="#000000" class="table">
+    <tr>
+      <td>
+        <table border=0 cellpadding="6" cellspacing="1" width="100%">
+                    <tr> 
+            <th align="left" bgcolor="#FFFFFF">Catalog:</th>
+            <td bgcolor="#FFFFFF">
+<:ifFieldPerm parentid:><select name="parentid"><:list:></select><:or:><:parent title:> (<:parent id:>)<:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product catalog:> <:error_img
+              parentid:></td>
+          </tr>
+                 <tr> 
+            <th align="left" bgcolor="#FFFFFF">Title*:</th>
+            <td bgcolor="#FFFFFF"><:ifFieldPerm title:><input type="text" name="title" value="<:old title default title:>" size="60"><:or:><:seminar title:><:eif:> </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product title:> <:error_img title:></td>
+          </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Summary*:</th>
+            <td nowrap bgcolor="#FFFFFF"><:ifFieldPerm summary:><input type="text" name="summary" value="<:old summary default summary:>" size=60><:or:><:seminar summary:><:eif:> </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product summary:> <:error_img
+            summary:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF" valign="top"> Body:</th>
+            <td bgcolor="#FFFFFF"> 
+              <:ifFieldPerm body:><textarea name=body rows=15 cols=60 wrap=virtual><:old body default body:></textarea><:or:><:bodytext seminar body:><:eif:>
+            </td>
+            <td nowrap bgcolor="#FFFFFF" valign="top"><:help body body:> <:error_img
+            body:></td>
+          </tr>
+                 <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF"><:cfg "seminar fields" duration "Duration":>:</th>
+            <td nowrap bgcolor="#FFFFFF"><:ifFieldPerm duration:><input type="text" name="duration" value="<:old duration default duration:>" size="10" /> minutes<:or:><:seminar duration:><:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product duration:> <:error_img
+            duration:></td>
+          </tr>
+                 <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Template:</th>
+            <td nowrap bgcolor="#FFFFFF"><:ifFieldPerm template:><:templates:><:or:><:seminar template:><:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product template:> <:error_img
+            template:></td>
+          </tr>
+                 <tr> 
+            <th nowrap bgcolor="#FFFFFF" align="left">List article:</th>
+            <td bgcolor="#FFFFFF" width="100%"> <:if FieldPerm listed:><:list listed:><:or FieldPerm:><:if Article listed:><:ifEq [article listed] "1":>Yes<:or:>In Sections, but not menu<:eif:><:or Article:>No<:eif Article:><:eif FieldPerm:> </td>
+            <td bgcolor="#FFFFFF"><:help edit listed:> <:error_img listed:></td>
+          </tr>
+                 <tr>
+            <th nowrap bgcolor="#FFFFFF" align="left">Flags:</th>
+            <td bgcolor="#FFFFFF" width="100%"><:iterator begin flags:><:if FieldPerm flags:>
+              <input type=checkbox name=flags value="<:flag id:>" <:ifFlagSet [flag id]:>checked<:or:>
+              <:eif:>><:or FieldPerm:><:ifFlagSet [flag id]:>Yes<:or:>No<:eif:>
+              <:eif FieldPerm:><:flag desc:><:iterator separator flags:><br /><:iterator end flags:></td>
+            <td bgcolor="#FFFFFF"><:help edit flags:> <:error_img flags:></td>
+          </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Lead time:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+              <:ifFieldPerm leadTime:>
+              <input type="text" name="leadTime" value="<:old leadTime default leadTime:>" size=5><:or:><:seminar leadTime:><:eif:>
+              days</td>
+            <td nowrap bgcolor="#FFFFFF"><:help product leadtime:> <:error_img
+            leadTime:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF"><:cfg "product fields" retailPrice "Retail price":>:</th>
+            <td bgcolor="#FFFFFF">$ 
+              <:ifFieldPerm retailPrice:><input type="text" name="retailPrice" value="<:old retailPrice money default retailPrice:>" size=7>
+              (0.00)<:or:><:money seminar retailPrice:><:eif:> </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product retail:> <:error_img retailPrice:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF">Wholesale price:</th>
+            <td bgcolor="#FFFFFF">$ 
+              <:ifFieldPerm wholesalePrice:><input type="text" name="wholesalePrice" value="<:old wholesalePrice money default wholesalePrice:>" size=7>
+              (0.00)<:or:><:money seminar wholesalePrice:><:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product wholesale:> <:error_img wholesalePrice:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF">GST:</th>
+            <td bgcolor="#FFFFFF">$ 
+              <:ifFieldPerm gst:><input type="text" name="gst" value="<:old gst money default gst:>" size=7>
+              (0.00)<:or:><:money seminar gst:><:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product gst:> <:error_img gst:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF">Release date:</th>
+            <td bgcolor="#FFFFFF"> 
+              <:ifFieldPerm release:><input type="text" name="release" value="<:old release date "%d/%m/%Y" default release:>" size=11>
+              (dd/mm/yyyy)<:or:><:date "%d/%m/%Y" seminar release:><:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product release:> <:error_img
+            release:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF">Expiry date:</th>
+            <td bgcolor="#FFFFFF"> 
+              <:ifFieldPerm expire:><input type="text" name="expire" value="<:old expire date "%d/%m/%Y" default expire:>" size=11>
+              (dd/mm/yyyy)<:or:><:date "%d/%m/%Y" seminar expire:><:eif:></td>
+            <td nowrap bgcolor="#FFFFFF"><:help product expire:> <:error_img
+            expire:></td>
+          </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Summary length:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+              <:ifFieldPerm summaryLength:><input type="text" name="summaryLength" size="10" maxlength="10" value="<:old summaryLength default summaryLength:>"><:or:><:seminar summaryLength:><:eif:>
+            </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product summary:> <:error_img
+            summaryLength:></td>
+          </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Display threshold:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+              <:ifFieldPerm threshold:><input type="text" name="threshold" size=10 maxlength=10 value="<:old threshold default threshold:>"><:or:><:seminar threshold:><:eif:>
+            </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product threshold:> <:error_img
+            threshold:></td>
+          </tr>
+          <tr> 
+            <th align="left" bgcolor="#FFFFFF">Options:</th>
+            <td bgcolor="#FFFFFF"> 
+              <:ifFieldPerm options:><input type="text" name="options" value="<:old options default options:>" size=30>
+              (<:alloptions:>)<:or:><:seminar options:><:eif:> </td>
+            <td bgcolor="#FFFFFF"><:help product options:> <:error_img options:></td>
+          </tr>
+<:include admin/seminar_custom.tmpl optional:>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF" valign="top">Thumbnail image:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+              <:ifFieldPerm thumbImage:><input type="file" name="thumbnail"><:or:><:eif:>
+              <:ifSeminar thumbImage:><img src="/images/<:seminar thumbImage:>"> 
+              <:if FieldPerm thumbImage:><input type=checkbox name="remove_thumb">
+              Remove<:or FieldPerm:><:eif FieldPerm:><:or:><:eif:> </td>
+            <td nowrap bgcolor="#FFFFFF" valign="top"><:help product thumb:> <:error_img
+            thumbImage:></td>
+          </tr>
+                 <:if Article id:>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF" valign="top"><a name="files"></a>Files:</th>
+            <td nowrap bgcolor="#FFFFFF"> <:if Files:> 
+              <table cellpadding="0" cellspacing="0" border="0" bgcolor="#333333">
+                <tr> 
+                  <td> 
+                    <table cellpadding="5" cellspacing="1" border="0">
+                      <tr bgcolor="#FFFFFF"> 
+                        <th>Filename</th>
+                        <th>Size</th>
+                        <th>Type</th>
+                        <th>D/l</th>
+                        <th>Pay</th>
+                        <th>User</th>
+                      </tr>
+                      <:iterator begin files:> 
+                      <tr bgcolor="#FFFFFF"> 
+                        <td><:file displayName:></td>
+                        <td align="center"><:kb file sizeInBytes:></td>
+                        <td><:file contentType:></td>
+                        <td align=center><:ifFile download:>Yes<:or:>No<:eif:></td>
+                        <td align=center><:ifFile forSale:>Yes<:or:>No<:eif:></td>
+                        <td align=center><:ifFile requireUser:>Yes<:or:>No<:eif:></td>
+                      </tr>
+                      <:iterator end files:> 
+                    </table>
+                  </td>
+                </tr>
+              </table>
+              <p><a href="<:script:>?filelist=1&id=<:article id:>"><b>Manage Files</b></a>
+              </p>
+              <:or Files:>
+              <p>No files are attached to this article. <a href="<:script:>?filelist=1&id=<:article id:>"><b>Manage Files</b></a></p><:eif Files:>
+            </td>
+            <td nowrap bgcolor="#FFFFFF" valign="top"><:help product 
+              files:> <:error_img files:></td>
+          </tr>
+          <tr> 
+            <th valign="top" nowrap bgcolor="#FFFFFF" align="left"> Images: 
+            </th>
+            <td align="center" bgcolor="#FFFFFF"> <:if Images:> <:iterator begin 
+              images:> <img src="/images/<: image image :>" alt="<:image alt :>" width= 
+              <:image width:> height=<:image height:>> <:iterator separator images:> 
+              <hr noshade size="1">
+              <: iterator end images :>
+              <p align="left"><a href="<:script:>?id=<:article id:>&showimages=1"><b>Manage Images</b></a></p>
+             <:or Images:><p align="left">No images are attached to this article.  <a href="<:script:>?id=<:article id:>&showimages=1"><b>Manage Images</b></a></p>
+             <:eif Images:>
+            </td>
+            <td valign="top" bgcolor="#FFFFFF"><:help product images:> <:error_img
+            images:></td>
+          </tr>
+                 <:or Article:><:eif Article:>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Purchase subscribes to:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+             <select name="subscription_id">
+                <option value="-1"<:ifEq [old subscription_id] "-1":> selected="selected"<:or:><:eif:>>(nothing)</option>
+<:iterator begin subscriptions:>
+                <option value="<:subscription subscription_id:>"<:ifEq [old subscription_id] [subscription subscription_id]:> selected="selected"<:or:><:eif:>><:subscription title:></option>
+<:iterator end subscriptions:>
+             </select> for <input type="text" name="subscription_period" value="<:ifEq [old subscription_period] "":><:default subscription_period:><:or:><:old subscription_period:><:eif:>" size="3" /><:error_img subscription_period:> months.
+            </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product subscription_id:></td>
+          </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">Can be used to:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+             <select name="subscription_usage">
+                <option value="3"<:ifEq [old subscription_usage] "3":> selected="selected"<:or:><:eif:>>Start or renew a subscription</option>
+                <option value="1"<:ifEq [old subscription_usage] "1":> selected="selected"<:or:><:eif:>>Start a subscription only</option>
+                <option value="2"<:ifEq [old subscription_usage] "2":> selected="selected"<:or:><:eif:>>Renew a subscription only</option>
+             </select>
+            </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product subscription_usage:></td>
+          </tr>
+          <tr> 
+            <th nowrap align="left" bgcolor="#FFFFFF">User must be subscribed to:</th>
+            <td nowrap bgcolor="#FFFFFF"> 
+             <select name="subscription_required">
+                <option value="-1"<:ifEq [old subscription_required] "-1":> selected="selected"<:or:><:eif:>>(nothing)</option>
+<:iterator begin subscriptions:>
+                <option value="<:subscription subscription_id:>"<:ifEq [old subscription_required] [subscription subscription_id]:> selected="selected"<:or:><:eif:>><:subscription title:></option>
+<:iterator end subscriptions:>
+             </select> to purchase this product
+            </td>
+            <td nowrap bgcolor="#FFFFFF"><:help product subscription_id:></td>
+          </tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+  <p><font size="-1">*These fields cannot be modified once this seminar has been
+      included in an order.</font></p>
+  <:if UserCan edit_save:article:>
+  <p>
+    <:ifNew:><input type=submit name="save" value="Add Seminar"><:or:><input type=submit name="save" value="Update Seminar"><:eif:>
+  </p><:or UserCan:><:eif UserCan:>
+</form>
+
+<p><font size="-1">BSE Release <:release:></font></p>
+</body>
+</html>
diff --git a/site/templates/admin/locations/add.tmpl b/site/templates/admin/locations/add.tmpl
new file mode 100644 (file)
index 0000000..d5025ce
--- /dev/null
@@ -0,0 +1,105 @@
+<:wrap admin/xbase.tmpl title=>"Add Location":>
+<h1>Add Location</h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<:if UserCan bse_location_list :>
+<a href="<:script:>?a_loclist=1">List Locations</a> |<:or UserCan:><:eif UserCan:>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+
+<form action="<:script:>" method="post" name="addlocation">
+<table>
+<tr>
+  <th><:field description description:>:</th>
+  <td><input type="text" name="description" value="<:old description:>" maxlength="<:field description maxlength:>" size="<:field description width:>" />*</td>
+  <td><:error_img description:><:help addlocation description:></td>
+</tr>
+<tr>
+  <th><:field room description:>:</th>
+  <td><input type="text" name="room" value="<:old room:>" maxlength="<:field room maxlength:>" size="<:field room width:>" /><:ifField room required:>*<:or:><:eif:></td>
+  <td><:error_img room:><:help addlocation room:></td>
+</tr>
+<tr>
+  <th><:field street1 description:>:</th>
+  <td><input type="text" name="street1" value="<:old street1:>" maxlength="<:field street1 maxlength:>" size="<:field street1 width:>" /><:ifField street1 required:>*<:or:><:eif:></td>
+  <td><:error_img street1:><:help addlocation street1:></td>
+</tr>
+<tr>
+  <th><:field street2 description:>:</th>
+  <td><input type="text" name="street2" value="<:old street2:>" maxlength="<:field street2 maxlength:>" size="<:field street2 width:>" /><:ifField street2 required:>*<:or:><:eif:></td>
+  <td><:error_img street2:><:help addlocation street2:></td>
+</tr>
+<tr>
+  <th><:field suburb description:>:</th>
+  <td><input type="text" name="suburb" value="<:old suburb:>" maxlength="<:field suburb maxlength:>" size="<:field suburb width:>" /><:ifField street2 required:>*<:or:><:eif:></td>
+  <td><:error_img suburb:><:help addlocation suburb:></td>
+</tr>
+<tr>
+  <th><:field state description:>:</th>
+  <td><input type="text" name="state" value="<:old state:>" maxlength="<:field state maxlength:>" size="<:field state width:>" /><:ifField state required:>*<:or:><:eif:></td>
+  <td><:error_img state:><:help addlocation state:></td>
+</tr>
+<tr>
+  <th><:field country description:>:</th>
+  <td><input type="text" name="country" value="<:old country:>" maxlength="<:field country maxlength:>" size="<:field country width:>" /><:ifField country required:>*<:or:><:eif:></td>
+  <td><:error_img country:><:help addlocation country:></td>
+</tr>
+<tr>
+  <th><:field postcode description:>:</th>
+  <td><input type="text" name="postcode" value="<:old postcode:>" maxlength="<:field postcode maxlength:>" size="<:field postcode width:>" /><:ifField postcode required:>*<:or:><:eif:></td>
+  <td><:error_img postcode:><:help addlocation postcode:></td>
+</tr>
+<tr>
+  <th><:field public_notes description:>:</th>
+  <td><textarea name="public_notes" rows="<:field public_notes height:>" cols="<:field public_notes width:>" wrap="virtual"><:old public_notes:></textarea></td>
+  <td><:error_img public_notes:><:help addlocation public_notes:></td>
+</tr>
+<tr>
+  <th colspan="2">Bookings</th>
+</tr>
+<tr>
+  <th><:field bookings_name description:>:</th>
+  <td><input type="text" name="bookings_name" value="<:old bookings_name:>" maxlength="<:field bookings_name maxlength:>" size="<:field bookings_name width:>" /><:ifField bookings_name required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_name:><:help addlocation bookings_name:></td>
+</tr>
+<tr>
+  <th><:field bookings_phone description:>:</th>
+  <td><input type="text" name="bookings_phone" value="<:old bookings_phone:>" maxlength="<:field bookings_phone maxlength:>" size="<:field bookings_phone width:>" /><:ifField bookings_phone required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_phone:><:help addlocation bookings_phone:></td>
+</tr>
+<tr>
+  <th><:field bookings_fax description:>:</th>
+  <td><input type="text" name="bookings_fax" value="<:old bookings_fax:>" maxlength="<:field bookings_fax maxlength:>" size="<:field bookings_fax width:>" /><:ifField bookings_fax required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_fax:><:help addlocation bookings_fax:></td>
+</tr>
+<tr>
+  <th><:field bookings_url description:>:</th>
+  <td><input type="text" name="bookings_url" value="<:old bookings_url:>" maxlength="<:field bookings_url maxlength:>" size="<:field bookings_url width:>" /><:ifField bookings_url required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_url:><:help addlocation bookings_url:></td>
+</tr>
+<tr>
+  <th colspan="2">Facilities</th>
+</tr>
+<tr>
+  <th><:field facilities_name description:>:</th>
+  <td><input type="text" name="facilities_name" value="<:old facilities_name:>" maxlength="<:field facilities_name maxlength:>" size="<:field facilities_name width:>" /><:ifField facilities_name required:>*<:or:><:eif:></td>
+  <td><:error_img facilities_name:><:help addlocation facilities_name:></td>
+</tr>
+<tr>
+  <th><:field facilities_phone description:>:</th>
+  <td><input type="text" name="facilities_phone" value="<:old facilities_phone:>" maxlength="<:field facilities_phone maxlength:>" size="<:field facilities_phone width:>" /><:ifField facilities_phone required:>*<:or:><:eif:></td>
+  <td><:error_img facilities_phone:><:help addlocation facilities_phone:></td>
+</tr>
+<tr>
+  <th><:field admin_notes description:>:</th>
+  <td><textarea name="admin_notes" rows="<:field admin_notes height:>" cols="<:field admin_notes width:>" wrap="virtual"><:old admin_notes:></textarea></td>
+  <td><:error_img admin_notes:><:help addlocation admin_notes:></td>
+</tr>
+
+<tr>
+  <td colspan="2"><input type="submit" name="a_locadd" value="Add Location" /></td>
+  <td>&nbsp;</td>
+</tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/site/templates/admin/locations/delete.tmpl b/site/templates/admin/locations/delete.tmpl
new file mode 100644 (file)
index 0000000..ab98575
--- /dev/null
@@ -0,0 +1,25 @@
+<:wrap admin/xbase.tmpl title=>"Delete Location: [location description]":>
+<h1>Delete Location: <:location description:></h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<:if UserCan bse_location_list :><a href="<:script:>?a_loclist=1">List Locations</a> |<:or UserCan:><:eif UserCan:>
+<:if UserCan bse_location_edit :>
+<a href="<:script:>?a_locedit=1&amp;id=<:location id:>">Edit Location</a> |<:or UserCan:><:eif UserCan:>
+<:if UserCan bse_location_add :>
+<a href="<:script:>?a_locaddform=1">Add Location</a> |<:or UserCan:><:eif UserCan:>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+
+<form action="<:script:>" method="POST">
+<input type="hidden" name="id" value="<:location id:>" />
+<table>
+<tr>
+  <th><:field description description:>:</th>
+  <td><:location description:></td>
+</tr>
+<tr>
+  <td colspan="2"><input type="submit" name="a_locdelete" value="Delete Location" /></td>
+</tr>
+</table>
+</form>
diff --git a/site/templates/admin/locations/edit.tmpl b/site/templates/admin/locations/edit.tmpl
new file mode 100644 (file)
index 0000000..917f505
--- /dev/null
@@ -0,0 +1,111 @@
+<:wrap admin/xbase.tmpl title=>"Edit Location":>
+<h1>Edit Location</h1>
+<p>
+| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |
+<:if UserCan bse_location_list :>
+<a href="<:script:>?a_loclist=1">List Locations</a> |<:or UserCan:><:eif UserCan:>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+
+<form action="<:script:>" method="post" name="editlocation">
+<input type="hidden" name="id" value="<:location id:>" />
+<table>
+<tr>
+  <th><:field description description:>:</th>
+  <td><input type="text" name="description" value="<:old description location description:>" maxlength="<:field description maxlength:>" size="<:field description width:>" />*</td>
+  <td><:error_img description:><:help addlocation description:></td>
+</tr>
+<tr>
+  <th><:field room description:>:</th>
+  <td><input type="text" name="room" value="<:old room location room:>" maxlength="<:field room maxlength:>" size="<:field room width:>" /><:ifField room required:>*<:or:><:eif:></td>
+  <td><:error_img room:><:help addlocation room:></td>
+</tr>
+<tr>
+  <th><:field street1 description:>:</th>
+  <td><input type="text" name="street1" value="<:old street1 location street1:>" maxlength="<:field street1 maxlength:>" size="<:field street1 width:>" /><:ifField street1 required:>*<:or:><:eif:></td>
+  <td><:error_img street1:><:help addlocation street1:></td>
+</tr>
+<tr>
+  <th><:field street2 description:>:</th>
+  <td><input type="text" name="street2" value="<:old street2 location street2:>" maxlength="<:field street2 maxlength:>" size="<:field street2 width:>" /><:ifField street2 required:>*<:or:><:eif:></td>
+  <td><:error_img street2:><:help addlocation street2:></td>
+</tr>
+<tr>
+  <th><:field suburb description:>:</th>
+  <td><input type="text" name="suburb" value="<:old suburb location suburb:>" maxlength="<:field suburb maxlength:>" size="<:field suburb width:>" /><:ifField street2 required:>*<:or:><:eif:></td>
+  <td><:error_img suburb:><:help addlocation suburb:></td>
+</tr>
+<tr>
+  <th><:field state description:>:</th>
+  <td><input type="text" name="state" value="<:old state location state:>" maxlength="<:field state maxlength:>" size="<:field state width:>" /><:ifField state required:>*<:or:><:eif:></td>
+  <td><:error_img state:><:help addlocation state:></td>
+</tr>
+<tr>
+  <th><:field country description:>:</th>
+  <td><input type="text" name="country" value="<:old country location country:>" maxlength="<:field country maxlength:>" size="<:field country width:>" /><:ifField country required:>*<:or:><:eif:></td>
+  <td><:error_img country:><:help addlocation country:></td>
+</tr>
+<tr>
+  <th><:field postcode description:>:</th>
+  <td><input type="text" name="postcode" value="<:old postcode location postcode:>" maxlength="<:field postcode maxlength:>" size="<:field postcode width:>" /><:ifField postcode required:>*<:or:><:eif:></td>
+  <td><:error_img postcode:><:help addlocation postcode:></td>
+</tr>
+<tr>
+  <th><:field public_notes description:>:</th>
+  <td><textarea name="public_notes" rows="<:field public_notes height:>" cols="<:field public_notes width:>" wrap="virtual"><:old public_notes location public_notes:></textarea></td>
+  <td><:error_img public_notes:><:help addlocation public_notes:></td>
+</tr>
+<tr>
+  <th>Disabled:</th>
+  <td><input type="checkbox" name="disabled" value="1" <:ifLocation disabled:>checked="checked" <:or:><:eif:>/><input type="hidden" name="save_disabled" value="1" /></td>
+  <td><:help addlocation disabled:></td>
+</tr>
+<tr>
+  <th colspan="2">Bookings</th>
+</tr>
+<tr>
+  <th><:field bookings_name description:>:</th>
+  <td><input type="text" name="bookings_name" value="<:old bookings_name location bookings_name:>" maxlength="<:field bookings_name maxlength:>" size="<:field bookings_name width:>" /><:ifField bookings_name required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_name:><:help addlocation bookings_name:></td>
+</tr>
+<tr>
+  <th><:field bookings_phone description:>:</th>
+  <td><input type="text" name="bookings_phone" value="<:old bookings_phone location bookings_phone:>" maxlength="<:field bookings_phone maxlength:>" size="<:field bookings_phone width:>" /><:ifField bookings_phone required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_phone:><:help addlocation bookings_phone:></td>
+</tr>
+<tr>
+  <th><:field bookings_fax description:>:</th>
+  <td><input type="text" name="bookings_fax" value="<:old bookings_fax location bookings_fax:>" maxlength="<:field bookings_fax maxlength:>" size="<:field bookings_fax width:>" /><:ifField bookings_fax required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_fax:><:help addlocation bookings_fax:></td>
+</tr>
+<tr>
+  <th><:field bookings_url description:>:</th>
+  <td><input type="text" name="bookings_url" value="<:old bookings_url location bookings_url:>" maxlength="<:field bookings_url maxlength:>" size="<:field bookings_url width:>" /><:ifField bookings_url required:>*<:or:><:eif:></td>
+  <td><:error_img bookings_url:><:help addlocation bookings_url:></td>
+</tr>
+<tr>
+  <th colspan="2">Facilities</th>
+</tr>
+<tr>
+  <th><:field facilities_name description:>:</th>
+  <td><input type="text" name="facilities_name" value="<:old facilities_name location facilities_name:>" maxlength="<:field facilities_name maxlength:>" size="<:field facilities_name width:>" /><:ifField facilities_name required:>*<:or:><:eif:></td>
+  <td><:error_img facilities_name:><:help addlocation facilities_name:></td>
+</tr>
+<tr>
+  <th><:field facilities_phone description:>:</th>
+  <td><input type="text" name="facilities_phone" value="<:old facilities_phone location facilities_phone:>" maxlength="<:field facilities_phone maxlength:>" size="<:field facilities_phone width:>" /><:ifField facilities_phone required:>*<:or:><:eif:></td>
+  <td><:error_img facilities_phone:><:help addlocation facilities_phone:></td>
+</tr>
+<tr>
+  <th><:field admin_notes description:>:</th>
+  <td><textarea name="admin_notes" rows="<:field admin_notes height:>" cols="<:field admin_notes width:>" wrap="virtual"><:old admin_notes location admin_notes:></textarea></td>
+  <td><:error_img admin_notes:><:help addlocation admin_notes:></td>
+</tr>
+
+<tr>
+  <td colspan="2"><input type="submit" name="a_locsave" value="Save Location" /></td>
+  <td>&nbsp;</td>
+</tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/site/templates/admin/locations/list.tmpl b/site/templates/admin/locations/list.tmpl
new file mode 100644 (file)
index 0000000..08bdfea
--- /dev/null
@@ -0,0 +1,34 @@
+<:wrap admin/xbase.tmpl title=>"Location List":>
+<h1>Location List</h1>
+
+<:ifMessage:><p><b><:message:></b></p><:or:><:eif:>
+
+<p>| <a href="/cgi-bin/admin/menu.pl">Admin menu</a> |<:ifUserCan bse_location_add:>
+<a href="<:script:>?a_locaddform=1">Add Location</a> |<:or:><:eif:></p>
+
+<table cellpadding="6" border="0" cellspacing="1">
+   <tr bgcolor="#FFFFFF">
+      <th>Description</th>
+      <th>Room</th>
+      <th>Street</th>
+      <th>Suburb</th>
+      <th>Disabled?</th>
+          <th>Modify</th>
+    </tr>
+<:if Locations:>
+<:iterator begin locations:>
+    <tr bgcolor="#FFFFFF">
+      <td><:ilocation description:></td>
+      <td><:ilocation room:></td>
+      <td><:ilocation street1:> <:ilocation street2:></td>
+      <td><:ilocation suburb:></td>
+      <td align="center"><:ifIlocation disabled:>Disabled<:or:><:eif:></td>
+      <td><:ifUserCan bse_location_edit:><a href="<:script:>?a_locedit=1&amp;id=<:ilocation id:>">Edit</a><:or:><:eif:> <:ifAnd [ifRemovable] [ifUserCan bse_location_delete]:><a href="<:script:>?a_locdelask=1&amp;id=<:ilocation id:>">Delete</a><:or:><:eif:></td>
+    </tr>
+<:iterator end locations:>
+<:or Locations:>
+    <tr bgcolor="#FFFFFF">
+      <td colspan="7" align="center">You don't have any locations defined</td>
+    </tr>
+<:eif Locations:>
+</table>
index ba0e215..61f3ea2 100644 (file)
   <tr> 
     <td align="left"><b>Delivery:</b></td>
     <td><:order delivFirstName:> <:order delivLastName:></td>
-    <td rowspan=4>&nbsp;&nbsp;</td>
+    <td rowspan="6">&nbsp;&nbsp;</td>
     <td align="left"><b>Billing:</b></td>
     <td><:order billFirstName:> <:order billLastName:></td>
   </tr>
   <tr> 
-    <td rowspan=3>&nbsp;</td>
+    <td rowspan="5">&nbsp;</td>
+    <td><:order delivOrganization:></td>
+    <td rowspan="5">&nbsp;</td>
+    <td><:order billOrganization:></td>
+  </tr>
+  <tr> 
     <td><:order delivStreet:></td>
-    <td rowspan=3>&nbsp;</td>
     <td><:order billStreet:></td>
   </tr>
+  <tr> 
+    <td><:order delivStreet2:></td>
+    <td><:order billStreet2:></td>
+  </tr>
   <tr> 
     <td><:order delivSuburb:> <:order delivState:> <:order delivPostCode:></td>
     <td><:order billSuburb:> <:order billState:> <:order billPostCode:></td>
index 7df047b..6c57f3e 100644 (file)
@@ -161,12 +161,24 @@ function BSE_validateForm {
         <input type="Text" name="delivLastName" size=34 value="<:old delivLastName:>"><:error_img delivLastName:>
         *</font></td>
     </tr>
+    <tr> 
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> Organization:</font></td>
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
+        <input type="Text" name="delivOrganization" size=34 value="<:old delivOrganization:>" /><:error_img delivOrganization:>
+        *</font></td>
+    </tr>
     <tr> 
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> Address:</font></td>
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
         <input type="Text" name="delivStreet" size=34 value="<:old delivStreet:>" /><:error_img delivStreet:>
         *</font></td>
     </tr>
+    <tr> 
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> </font></td>
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
+        <input type="Text" name="delivStreet2" size=34 value="<:old delivStreet2:>" /><:error_img delivStreet2:>
+        </font></td>
+    </tr>
     <tr> 
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> City:</font></td>
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
@@ -244,12 +256,24 @@ function BSE_validateForm {
         <input type="Text" name="billLastName" size=34 value="<:old billLastName:>"><:error_img billLastName:>
         *</font></td>
     </tr>
+    <tr> 
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> Organization:</font></td>
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
+        <input type="Text" name="billOrganization" size=34 value="<:old billOrganization:>"><:error_img billOrganization:>
+        *</font></td>
+    </tr>
     <tr> 
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> Address:</font></td>
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
         <input type="Text" name="billStreet" size=34 value="<:old billStreet:>"><:error_img billStreet:>
         *</font></td>
     </tr>
+    <tr> 
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> </font></td>
+      <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
+        <input type="Text" name="billStreet2" size=34 value="<:old billStreet2:>"><:error_img billStreet2:>
+        </font></td>
+    </tr>
     <tr> 
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> City:</font></td>
       <td> <font face="Verdana, Arial, Helvetica, sans-serif" size="2"> 
index e9f4622..51d3a5c 100644 (file)
           <input type="text" name="address" value="<:last address:>" size="40" maxlength="127" /><:error_img address:>
         </td>
       </tr>
+      <tr> 
+        <th nowrap="nowrap" align="left"><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"></font></b></th>
+        <td width="100%" nowrap="nowrap"> 
+          <input type="text" name="delivStreet2" value="<:last delivStreet2:>" size="40" maxlength="127" /><:error_img delivStreet2:>
+        </td>
+      </tr>
       <tr> 
         <th nowrap="nowrap" align="left"><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2">Suburb:</font></b></th>
         <td width="100%" nowrap="nowrap"> 
index efe0568..2910bae 100644 (file)
           <input type="text" name="billLastName" value="<:last billLastName:>" size="40" maxlength="127" /><:error_img billLastName:>
         </td>
       </tr>
+      <tr> 
+        <th>Organization:</th>
+        <td width="100%" nowrap="nowrap"> 
+          <input type="text" name="billOrganization" value="<:last billOrganization:>" size="40" maxlength="127" /><:error_img billOrganization:>
+        </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></th>
+        <td width="100%" nowrap="nowrap"> 
+          <input type="text" name="billStreet2" value="<:last billStreet2:>" size="40" maxlength="127" /><:error_img billStreet2:>
+        </td>
+      </tr>
       <tr> 
         <th>Suburb:</th>
         <td width="100%" nowrap="nowrap"> 
index 0b8e810..dcb21ff 100644 (file)
           <input type="text" name="address" value="<:old address:>" size="40" maxlength="127" /> <:error_img address:><:ifRequired address:><font face="Verdana, Arial, Helvetica, sans-serif" size="-2">*</font><:or:><:eif:>
         </td>
       </tr>
+      <tr> 
+        <th nowrap align="left"><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"></font></b></th>
+        <td width="100%" nowrap="nowrap"> 
+          <input type="text" name="delivStreet2" value="<:old delivStreet2:>" size="40" maxlength="127" /> <:error_img delivStreet2:><:ifRequired delivStreet2:><font face="Verdana, Arial, Helvetica, sans-serif" size="-2">*</font><:or:><:eif:>
+        </td>
+      </tr>
       <tr> 
         <th nowrap align="left"><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2">Suburb:</font></b></th>
         <td width="100%" nowrap="nowrap"> 
index 80fc610..2e12d81 100644 (file)
@@ -1,13 +1,14 @@
 #!perl -w
 use strict;
-use Test::More tests => 62;
+use Test::More tests => 64;
 
 sub format_test($$$;$);
+sub noformat_test($$$;$);
 
 my $gotmodule = require_ok('DevHelp::Formatter');
 
 SKIP: {
-  skip "couldn't load module", 41 unless $gotmodule;
+  skip "couldn't load module", 63 unless $gotmodule;
   format_test 'acronym[hello]', '<p><acronym>hello</acronym></p>', 'acronym';
   format_test 'acronym[|hello]', '<p><acronym>hello</acronym></p>', 'acronym with empty title';
   format_test 'acronym[foo|hello]', '<p><acronym title="foo">hello</acronym></p>', 'acronym with title';
@@ -259,6 +260,10 @@ EOS
 <p>bar</p>
 <h2>quux</h2></div>
 EOS
+
+  # remove_format() tests
+  noformat_test 'image[foo]', '', 'image';
+  noformat_test 'code[something [bar]]', 'something [bar]', 'nested []';
 }
 
 sub format_test ($$$;$) {
@@ -274,3 +279,17 @@ sub format_test ($$$;$) {
 
   is($result, $out, $desc);
 }
+
+sub noformat_test($$$;$) {
+  my ($in, $out, $desc, $stripnl) = @_;
+
+  $stripnl ||= 'none';
+  $in =~ s/\n$// if $stripnl eq 'in' || $stripnl eq 'both';
+  $out =~ s/\n$// if $stripnl eq 'out' || $stripnl eq 'both';
+
+  my $formatter = DevHelp::Formatter->new;
+
+  my $result = $formatter->remove_format($in);
+
+  is($result, $out, $desc);
+}
index 99cc62b..c8d3685 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 use strict;
 use BSE::Test ();
-use Test::More tests=>64;
+use Test::More tests=>67;
 use File::Spec;
 use FindBin;
 my $cgidir = File::Spec->catdir(BSE::Test::base_dir, 'cgi-bin');
@@ -214,6 +214,18 @@ TEMPLATE
 
 EXPECTED
 
+template_test "quotedreplace", $parent, <<'TEMPLATE', <<EXPECTED;
+<:date "%FT%T%z" article lastModified:>
+<meta name="DC.title" content="<:article title:>" />
+<meta name="DC.date" content="<:replace [date "%FT%T%z" article lastModified] "(\d\d)$" ":$1":>" />
+<meta name="DC.format" content="<:cfg site format "text/html":>" />
+TEMPLATE
+2004-09-23T06:00:00+1000
+<meta name="DC.title" content="Parent" />
+<meta name="DC.date" content="2004-09-23T06:00:00+10:00" />
+<meta name="DC.format" content="text/html" />
+EXPECTED
+
 BSE::Admin::StepParents->del($parent, $parent);
 for my $kid (reverse @kids) {
   my $name = $kid->{title};
index e125935..88b0c44 100644 (file)
@@ -201,8 +201,8 @@ follow_ok($ua, "global images", "Global Images", qr/Global Image Wizard/);
 
 # since there may have been other global images, we need to be a bit 
 # more careful here
-my $links = $ua->extract_links;
-my @links = grep $_->[1] eq 'Delete', @$links;
+my $links = $ua->links;
+my @links = grep $_->text eq 'Delete', @$links;
 print "# link #", scalar(@links), "\n";
 follow_ok($ua, "delete global image", 
          { n=>scalar(@links), text=>"Delete" }, 
index 971a904..72d99dd 100644 (file)
--- a/test.cfg
+++ b/test.cfg
@@ -59,7 +59,7 @@ payment type names.11=FaxProForma
 payment type names.12=EmailProForma
 payment type required.11=facsimile
 
-#paths.local_templates=/home/tony/dev/bse/work/templates/
+paths.local_templates=/home/tony/dev/bse/work/templates/
 
 affiliate.prompt_name=1
 site users.require_affiliate_name=0