0.15_48 commit r0_15_48
authorTony Cook <tony@develop-help.com>
Thu, 19 Oct 2006 01:31:54 +0000 (01:31 +0000)
committertony <tony@45cb6cf1-00bc-42d2-bb5a-07f51df49f94>
Thu, 19 Oct 2006 01:31:54 +0000 (01:31 +0000)
25 files changed:
MANIFEST
Makefile
site/cgi-bin/admin/admin_seminar.pl
site/cgi-bin/bse.cfg
site/cgi-bin/modules/BSE/AdminLogon.pm
site/cgi-bin/modules/BSE/Edit/Seminar.pm
site/cgi-bin/modules/BSE/Request.pm
site/cgi-bin/modules/BSE/TB/Seminar.pm
site/cgi-bin/modules/BSE/TB/SeminarSession.pm
site/cgi-bin/modules/BSE/UI/AdminSeminar.pm
site/cgi-bin/modules/BSE/UI/Dispatch.pm
site/cgi-bin/modules/BSE/UI/NUser.pm [new file with mode: 0644]
site/cgi-bin/modules/BSE/UI/User.pm [new file with mode: 0644]
site/cgi-bin/modules/Product.pm
site/cgi-bin/nuser.fcgi [new file with mode: 0755]
site/cgi-bin/nuser.pl [new file with mode: 0755]
site/data/db/sql_statements.data
site/docs/bse.pod
site/templates/admin/user_book_seminar.tmpl [new file with mode: 0644]
site/templates/seminars/seminar.tmpl
site/templates/shopitem.tmpl
site/templates/user/base_bookcomplete.tmpl [new file with mode: 0644]
site/templates/user/base_bookconfirm.tmpl [new file with mode: 0644]
site/templates/user/base_bookseminar.tmpl [new file with mode: 0644]
test.cfg

index c87c5fd..824b516 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -134,11 +134,13 @@ site/cgi-bin/modules/BSE/UI/Affiliate.pm
 site/cgi-bin/modules/BSE/UI/Dispatch.pm
 site/cgi-bin/modules/BSE/UI/Formmail.pm
 site/cgi-bin/modules/BSE/UI/Image.pm
+site/cgi-bin/modules/BSE/UI/NUser.pm
 site/cgi-bin/modules/BSE/UI/Page.pm
 site/cgi-bin/modules/BSE/UI/Shop.pm
 site/cgi-bin/modules/BSE/UI/SiteuserCommon.pm
 site/cgi-bin/modules/BSE/UI/SiteUserUpdate.pm
 site/cgi-bin/modules/BSE/UI/SubAdmin.pm
+site/cgi-bin/modules/BSE/UI/User.pm
 site/cgi-bin/modules/BSE/UserReg.pm
 site/cgi-bin/modules/BSE/Util/DynSort.pm
 site/cgi-bin/modules/BSE/Util/DynamicTags.pm
@@ -186,6 +188,8 @@ site/cgi-bin/modules/Squirrel/Row.pm
 site/cgi-bin/modules/Squirrel/Table.pm
 site/cgi-bin/modules/Squirrel/Template.pm
 site/cgi-bin/modules/Util.pm
+site/cgi-bin/nuser.fcgi
+site/cgi-bin/nuser.pl
 site/cgi-bin/page.fcgi
 site/cgi-bin/page.pl
 site/cgi-bin/printable.pl
@@ -383,6 +387,7 @@ site/templates/admin/users/groupmembers.tmpl
 site/templates/admin/users/list.tmpl
 site/templates/admin/users/view.tmpl
 site/templates/admin/users/view_bookings.tmpl
+site/templates/admin/user_book_seminar.tmpl
 site/templates/admin/xbase.tmpl
 site/templates/affiliate.tmpl
 site/templates/base.tmpl
@@ -452,6 +457,9 @@ site/templates/user/admin_edit_seminar.tmpl
 site/templates/user/admin_unbook_seminar.tmpl
 site/templates/user/alreadyblacklisted_base.tmpl
 site/templates/user/base_orderdetail.tmpl
+site/templates/user/base_bookcomplete.tmpl
+site/templates/user/base_bookconfirm.tmpl
+site/templates/user/base_bookseminar.tmpl
 site/templates/user/blacklistdone_base.tmpl
 site/templates/user/cantunsub_base.tmpl
 site/templates/user/confirmed_base.tmpl
index d57bd5d..b4847ff 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-VERSION=0.15_47
+VERSION=0.15_48
 DISTNAME=bse-$(VERSION)
 DISTBUILD=$(DISTNAME)
 DISTTAR=../$(DISTNAME).tar
index d44ce96..5407112 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.50:0.0' }
 use strict;
 use FindBin;
 use lib "$FindBin::Bin/../modules";
index 0f62549..bd14ab3 100644 (file)
@@ -40,6 +40,9 @@ user/toosoon.tmpl = user,user/toosoon_base.tmpl
 user/unsuball.tmpl = user,user/unsuball_base.tmpl
 user/unsubone.tmpl = user,user/unsubone_base.tmpl
 user/userpage.tmpl = user,user/userpage_base.tmpl
+user/bookseminar.tmpl = user,user/base_bookseminar.tmpl
+user/bookconfirm.tmpl = user,user/base_bookconfirm.tmpl
+user/bookcomplete.tmpl = user,user/base_bookcomplete.tmpl
 interest/confirm.tmpl = interest,interest/confirm_base.tmpl
 interest/askagain.tmpl = interest,interest/askagain_base.tmpl
 interest/error.tmpl = interest,interest/error_base.tmpl
@@ -356,6 +359,9 @@ mozilla=^Mozilla/5.0
 [ajax definitions]
 includes=inline:<script type="text/javascript" src="/js/prototype.js"></script>
 
+[nuser controllers]
+user=BSE::UI::User
+
 [includes]
 00bsecfg_d=bsecfg_d/
 50local=bse-local.cfg
\ No newline at end of file
index 8954d40..ce03870 100644 (file)
@@ -54,7 +54,7 @@ sub req_logon_form {
      error_img => [ \&tag_error_img, $req->cfg, $errors ],
     );
 
-  return BSE::Template->get_response('admin/logon', $req->cfg, \%acts);
+  return $req->dyn_response('admin/logon', \%acts);
 }
 
 sub req_logon {
index 9476a0f..8ad1cff 100644 (file)
@@ -2,7 +2,7 @@ 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);
+use BSE::Util::Tags qw(tag_hash tag_hash_mbcs tag_hash_plain);
 use BSE::Util::SQL qw(now_sqldatetime);
 use DevHelp::Date qw(dh_parse_date_sql dh_parse_time_sql);
 use constant SECT_SEMSESSION_VALIDATION => 'BSE Seminar Session Validation';
@@ -459,7 +459,7 @@ sub req_delsemsession {
   my $other_session;
   if ($other_session_id) {
     if ($other_session_id != -1) {
-      $other_session = BSE::TB::SeminarSession->getByPkey($other_session_id);
+      $other_session = BSE::TB::SeminarSessions->getByPkey($other_session_id);
       if (!$other_session 
          || $other_session->{seminar_id} != $article->{id}
          || $other_session->{id} == $session->{id}) {
index 62204ea..5145009 100644 (file)
@@ -169,6 +169,7 @@ sub dyn_response {
 
   my $base_template = $template;
   my $t = $req->cgi->param('t');
+  $t or $t = $req->cgi->param('_t');
   if ($t && $t =~ /^\w+$/) {
     $template .= "_$t";
   }
@@ -395,4 +396,10 @@ sub get_article {
   $article;
 }
 
+sub text {
+  my ($self, $id, $default) = @_;
+
+  $default;
+}
+
 1;
index b3ab494..a309131 100644 (file)
@@ -35,6 +35,12 @@ sub session_info {
   BSE::DB->query(seminarSessionInfo => $self->{id});
 }
 
+sub session_info_unbooked {
+  my ($self, $user) = @_;
+
+  BSE::DB->query(seminarSessionInfoUnbooked => $user->{id}, $self->{id});
+}
+
 sub future_session_info {
   my ($self) = @_;
 
index da1096f..4119d10 100644 (file)
@@ -112,6 +112,7 @@ sub get_booking {
   require BSE::TB::SeminarBookings;
   my @result = BSE::TB::SeminarBookings->
     getBy(session_id => $self->{id}, siteuser_id => $siteuser_id);
+  @result or return;
 
   return $result[0];
 }
index cc7b905..f0f3e4f 100644 (file)
@@ -179,11 +179,6 @@ sub _loc_show_common {
                        'session', 'sessions'),
     );
 
-  my $t = $req->cgi->param('_t');
-  if ($t && $t =~ /^\w+$/) {
-    $template .= "_$t";
-  }
-
   return $req->dyn_response($template, \%acts);
 }
 
@@ -324,7 +319,7 @@ sub req_addattendseminar {
      error_img => [ \&tag_error_img, $req->cfg, $errors ],
     );
   
-  return $req->dyn_response('admin/addattendee1.tmpl', \%acts);
+  return $req->dyn_response('admin/addattendee1', \%acts);
 }
 
 sub req_addattendsession {
@@ -380,7 +375,7 @@ sub req_addattendsession {
      option_popup => [ \&tag_option_popup, $req->cgi, \$current_option ],
     );
   
-  return $req->dyn_response('admin/addattendee2.tmpl', \%acts);
+  return $req->dyn_response('admin/addattendee2', \%acts);
 }
 
 sub tag_option_popup {
@@ -746,23 +741,7 @@ sub req_savebooking {
 sub _get_sem_options {
   my ($cfg, $seminar, @values) = @_;
 
-  my $avail_options = product_options($cfg);
-  my @opt_names = split /,/, $seminar->{options};
-  push @values, '' while @values < @opt_names;
-  my %values;
-  @values{@opt_names} = @values;
-
-  my @sem_options = map 
-    +{ 
-      id => $_, 
-      %{$avail_options->{$_}},
-      value => $values{$_},
-     }, @opt_names;
-  for my $option (@sem_options) {
-    $option->{display} = $option->{labels}{$option->{value}};
-  }
-
-  return @sem_options;
+  $seminar->option_descs($cfg, \@values);
 }
 
 sub tag_session_popup {
index 61abeb0..f98ba81 100644 (file)
@@ -2,43 +2,55 @@ package BSE::UI::Dispatch;
 use strict;
 use Carp 'confess';
 
+sub new {
+  my ($class, %opts) = @_;
+
+  bless \%opts, $class;
+}
+
 sub dispatch {
-  my ($class, $req) = @_;
+  my ($self, $req) = @_;
 
   my $result;
-  $class->check_secure($req, \$result)
+  $self->check_secure($req, \$result)
     or return $result;
 
-  my $actions = $class->actions;
+  my $actions = $self->actions;
 
-  my $prefix = $class->action_prefix;
+  my $prefix = $self->action_prefix;
   my $cgi = $req->cgi;
   my $action;
-  for my $check (keys %$actions) {
-    if ($cgi->param("$prefix$check") || $cgi->param("$prefix$check.x")) {
-      $action = $check;
-      last;
-    }
+  if (ref $self) {
+    $action = $self->action;
+    $actions->{$action} or undef $action;
   }
-  if (!$action && $prefix ne 'a_') {
+  unless ($action) {
     for my $check (keys %$actions) {
-      if ($cgi->param("a_$check") || $cgi->param("a_$check.x")) {
+      if ($cgi->param("$prefix$check") || $cgi->param("$prefix$check.x")) {
        $action = $check;
        last;
       }
     }
+    if (!$action && $prefix ne 'a_') {
+      for my $check (keys %$actions) {
+       if ($cgi->param("a_$check") || $cgi->param("a_$check.x")) {
+         $action = $check;
+         last;
+       }
+      }
+    }
   }
   my @extras;
   unless ($action) {
-    ($action, @extras) = $class->other_action($cgi);
+    ($action, @extras) = $self->other_action($cgi);
   }
-  $action ||= $class->default_action;
+  $action ||= $self->default_action;
 
-  $class->check_action($req, $action, \$result)
+  $self->check_action($req, $action, \$result)
     or return $result;
 
   my $method = "req_$action";
-  $class->$method($req, @extras);
+  $self->$method($req, @extras);
 }
 
 sub check_secure {
@@ -86,4 +98,12 @@ sub error {
   return $req->response($template, \%acts);
 }
 
+sub controller_id {
+  $_[0]{controller_id};
+}
+
+sub action {
+  $_[0]{action};
+}
+
 1;
diff --git a/site/cgi-bin/modules/BSE/UI/NUser.pm b/site/cgi-bin/modules/BSE/UI/NUser.pm
new file mode 100644 (file)
index 0000000..fdc788b
--- /dev/null
@@ -0,0 +1,50 @@
+package BSE::UI::NUser;
+use strict;
+use base 'BSE::UI::Dispatch';
+
+sub dispatch {
+  my ($class, $req) = @_;
+
+  my $controller_id = $req->cgi->param('_p');
+  my ($action, $rest);
+  unless ($controller_id) {
+    my @components = split '/', $ENV{PATH_INFO}, -1;
+    shift @components if @components && $components[0] eq '';
+    my @rest;
+    ($controller_id, $action, @rest) = @components;
+    $rest = join '/', @rest;
+  }
+
+  $controller_id
+    or return $class->error($req, "No controller found in path");
+
+  my $controller_class = 
+    $req->cfg->entry('nuser controllers', $controller_id)
+      or return $class->error($req, "No class found for controller $controller_id");
+  (my $controller_file = $controller_class . ".pm") =~ s!::!/!g;
+  eval {
+    require $controller_file;
+  };
+  if ($@) {
+    print STDERR "Error loading controller $controller_file: $@";
+    return $class->error($req, "Internal error: Could not load controller class");
+  }
+  my %opts;
+  $action and $opts{action} = $action;
+  defined $rest or $rest = '';
+  $opts{rest} = $rest;
+  $opts{controller_id} = $controller_id;
+  my $controller = $controller_class->new(%opts);
+
+  return $controller->dispatch($req);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+BSE::UI::NUser - dispatcher for user side functionality.
+
+=cut
diff --git a/site/cgi-bin/modules/BSE/UI/User.pm b/site/cgi-bin/modules/BSE/UI/User.pm
new file mode 100644 (file)
index 0000000..e6fe74c
--- /dev/null
@@ -0,0 +1,295 @@
+package BSE::UI::User;
+use strict;
+use base 'BSE::UI::Dispatch';
+use BSE::Util::Tags qw(tag_hash tag_error_img tag_hash_plain);
+use DevHelp::HTML qw(:default popup_menu);
+use BSE::Util::Iterate;
+
+my %actions =
+  (
+   bookseminar => 1,
+   bookconfirm => 1,
+   book => 1,
+   bookcomplete => 1,
+  );
+
+sub default_action {
+  'error';
+}
+
+sub actions {
+  \%actions;
+}
+
+sub req_bookseminar {
+  my ($self, $req, $errors) = @_;
+
+  my $cgi = $req->cgi;
+  my $seminar_id = $cgi->param('id');
+  defined $seminar_id && $seminar_id =~ /^\d+$/
+    or return $self->error($req, "Seminar id parameter missing");
+
+  require BSE::TB::Seminars;
+  my $seminar = BSE::TB::Seminars->getByPkey($seminar_id)
+    or return $self->error($req, "Unknown seminar");
+
+  $seminar->{retailPrice} == 0
+    or return $self->error($req, "This is only available for free seminars");
+
+  my @sessions = $seminar->session_info_unbooked($req->siteuser);
+  my $message = $req->message($errors);
+  @sessions
+    or $message = "You are already booked for every session of this seminar";
+  my @opt_names = split /,/, $seminar->{options};
+  my @opt_values = map { ($cgi->param($_))[0] } @opt_names;
+  my @options = $seminar->option_descs($req->cfg, \@opt_values);
+
+  my $it = BSE::Util::Iterate->new;
+  my $current_option;
+  my %acts;
+  %acts =
+    (
+     $req->dyn_user_tags(),
+     seminar => [ \&tag_hash, $seminar ],
+     message => escape_html($message),
+     error_img => [ \&tag_error_img, $req->cfg, $errors ],
+     $it->make_iterator(undef, 'session', 'sessions', \@sessions),
+     $it->make_iterator(undef, 'option', 'options', \@options, 
+                       undef, undef, \$current_option),
+     option_popup => [ \&tag_option_popup, \$current_option ],
+    );
+
+  return $req->dyn_response('user/bookseminar', \%acts);
+}
+
+sub tag_option_popup {
+  my ($roption) = @_;
+
+  $$roption 
+    or return '** popup_option not in options iterator **';
+
+  my $option = $$roption;
+
+  my $value = $option->{value} || $option->{values}[0];
+
+  return popup_menu(-name => $option->{id},
+                   -values => $option->{values},
+                   -labels => $option->{labels},
+                   -default => $value);
+}
+
+sub req_bookconfirm {
+  my ($self, $req) = @_;
+
+  my $cgi = $req->cgi;
+  my $seminar_id = $cgi->param('id');
+  defined $seminar_id && $seminar_id =~ /^\d+$/
+    or return $self->error($req, "Seminar id parameter missing");
+
+  require BSE::TB::Seminars;
+  my $seminar = BSE::TB::Seminars->getByPkey($seminar_id)
+    or return $self->error($req, "Unknown seminar");
+
+  $seminar->{retailPrice} == 0
+    or return $self->error($req, "This is only available for free seminars");
+
+  my $session_id = $cgi->param('session_id');
+  defined $session_id && $session_id =~ /^\d+$/
+    or return $self->req_bookseminar($req, { session_id => "Please select a session" });
+
+  require BSE::TB::SeminarSessions;
+  my $session = BSE::TB::SeminarSessions->getByPkey($session_id)
+    or return $self->req_bookseminar($req, { session_id => "Unknown session id" });
+
+  # make sure the user isn't already booked for this
+  $session->get_booking($req->siteuser)
+    and return $self->req_bookseminar($req, { session_id => "You are already booked for this session"} );
+
+  my @opt_names = split /,/, $seminar->{options};
+  my @opt_values = map { ($cgi->param($_))[0] } @opt_names;
+  my @options = $seminar->option_descs($req->cfg, \@opt_values);
+
+  my $it = BSE::Util::Iterate->new;
+  my $current_option;
+  my %acts;
+  %acts =
+    (
+     $req->dyn_user_tags(),
+     seminar => [ \&tag_hash, $seminar ],
+     session => [ \&tag_hash, $session ],
+     location => [ \&tag_hash, $session->location ],
+     $it->make_iterator(undef, 'option', 'options', \@options, 
+                       undef, undef, \$current_option),
+    );
+
+  return $req->dyn_response('user/bookconfirm', \%acts);
+}
+
+sub req_book {
+  my ($self, $req) = @_;
+
+  my $cgi = $req->cgi;
+  my $cfg = $req->cfg;
+  my $seminar_id = $cgi->param('id');
+  defined $seminar_id && $seminar_id =~ /^\d+$/
+    or return $self->error($req, "Seminar id parameter missing");
+
+  require BSE::TB::Seminars;
+  my $seminar = BSE::TB::Seminars->getByPkey($seminar_id)
+    or return $self->error($req, "Unknown seminar");
+
+  $seminar->{retailPrice} == 0
+    or return $self->error($req, "This is only available for free seminars");
+
+  my $session_id = $cgi->param('session_id');
+  defined $session_id && $session_id =~ /^\d+$/
+    or return $self->req_bookseminar($req, { session_id => "Please select a session" });
+
+  require BSE::TB::SeminarSessions;
+  my $session = BSE::TB::SeminarSessions->getByPkey($session_id)
+    or return $self->req_bookseminar($req, { session_id => "Unknown session id" });
+
+  # make sure the user isn't already booked for this
+  require BSE::TB::SeminarBookings;
+  $session->get_booking($req->siteuser)
+    and return $self->req_bookseminar($req, { session_id => "You are already booked for this session"} );
+
+  my @opt_names = split /,/, $seminar->{options};
+  my @opt_values = map { ($cgi->param($_))[0] } @opt_names;
+
+  my %more;
+  for my $name (qw/customer_instructions support_notes roll_present/) {
+    my $value = $cgi->param($name);
+    $value and $more{$name} = $value;
+  }
+  $more{roll_present} = 0;
+  $more{options}      = join(',', @opt_values);
+
+  my $booking;
+  eval {
+    $booking = $session->add_attendee($req->siteuser, %more);
+  };
+  if ($@) {
+    if ($@ =~ /duplicate/) {
+      return $self->req_bookseminar($req, { session_id => "You appear to be already booked for this seminar" });
+    }
+    else {
+      return $self->req_bookseminar($req, { _error => $@ });
+    }
+  }
+
+  my $message;
+  if ($cfg->entry('seminars', 'notify_user_booking', 1)) {
+    my $email = $cfg->entry('seminar', 'notify_user_booking_email')
+      || $cfg->entry('shop', 'from', $Constants::SHOP_FROM);
+    my $from = $cfg->entry('shop', 'from', $Constants::SHOP_FROM);
+    my $subject = $cfg->entry('seminar', 'notify_user_booking_subject',
+                             'A user has booked a seminar session');
+    my @sem_options = $seminar->option_descs($cfg, \@opt_values);
+    my $it = DevHelp::Tags::Iterate->new;
+    my %acts =
+      (
+       BSE::Util::Tags->static(undef, $cfg),
+       user => [ \&tag_hash_plain, $req->siteuser ],
+       seminar => [ \&tag_hash_plain, $seminar ],
+       session => [ \&tag_hash_plain, $session ],
+       location => [ \&tag_hash_plain, $session->location ],
+       booking => [ \&tag_hash_plain, $booking ],
+       $it->make_iterator(undef, 'option', 'options', \@sem_options),
+      );
+    require BSE::ComposeMail;
+    my $mailer = BSE::ComposeMail->new(cfg => $cfg);
+    unless ($mailer->send(to      => $email,
+                         from     => $from,
+                         subject  => $subject,
+                         template => 'admin/user_book_seminar',
+                         acts     => \%acts)) {
+      $message = "Your seminar has been booked but there was an error sending an email notification to the system administrator:".$mailer->errstr;
+    }
+  }
+  
+  # refresh to the completion page
+  my %params =
+    (
+     a_bookcomplete => 1,
+     _p => $self->controller_id,
+     id => $booking->{id},
+    );
+  $message and $params{m} = $message;
+  my $url = $ENV{SCRIPT_NAME} . '?' .
+    join '&', map { "$_=". escape_uri($params{$_}) } keys %params;
+
+  return BSE::Template->get_refresh($url, $req->cfg);
+}
+
+sub req_bookcomplete {
+  my ($self, $req) = @_;
+
+  my $id = $req->cgi->param('id');
+  defined $id || $id =~ /^\d+$/
+    or return $self->error($req, "No booking id supplied");
+
+  require BSE::TB::SeminarBookings;
+  my $booking = BSE::TB::SeminarBookings->getByPkey($id);
+  my $user = $req->siteuser;
+  $booking && $booking->{siteuser_id} == $user->{id}
+    or return $self->error($req, "No such booking found");
+
+  my $session = $booking->session;
+  my $seminar = $session->seminar;
+  my @opt_values = split /,/, $booking->{options};
+  my @options = $seminar->option_descs($req->cfg, \@opt_values);
+  my %acts;
+  my $message = $req->message();
+  my $it = BSE::Util::Iterate->new;
+  %acts =
+    (
+     $req->dyn_user_tags(),
+     seminar => [ \&tag_hash, $seminar ],
+     session => [ \&tag_hash, $session ],
+     location => [ \&tag_hash, $session->location ],
+     booking => [ \&tag_hash, $booking ],
+     message => escape_html($message),
+     $it->make_iterator(undef, 'option', 'options', \@options),
+    );
+
+  return $req->dyn_response('user/bookcomplete', \%acts);
+}
+
+sub check_action {
+  my ($self, $req, $action, $rresult) = @_;
+
+  unless ($req->siteuser) {
+    my %logon_params;
+    if ($ENV{REQUEST_METHOD} eq 'GET') {
+      my @params =
+       (
+        "a_$action" => 1,
+        _p => $self->controller_id,
+       );
+      my $cgi = $req->cgi;
+      for my $name ($cgi->param) {
+       my @values = $cgi->param($name);
+       push @params, $name => $_ for @values;
+      }
+      my @param_args;
+      while (@params) {
+       my ($name, $value) = splice @params, 0, 2;
+       push @param_args, $name . '=' . escape_uri($value);
+      }
+      my $r = $ENV{SCRIPT_NAME} . '?' . join '&', @param_args;
+      $logon_params{r} = $r;
+    }
+    $logon_params{message} = 
+      $req->text('needlogongen', "You need to logon to use this function");
+    $logon_params{show_logon} = 1;
+    my $logon = '/cgi-bin/user.pl?' . join '&',
+      map { $_ . '=' . escape_uri($logon_params{$_}) } keys %logon_params;
+    $$rresult = BSE::Template->get_refresh($logon, $req->cfg);
+    return;
+  }
+
+  return 1;
+}
+
+1;
index 67c2588..29bdb75 100644 (file)
@@ -53,4 +53,35 @@ sub is_start_sub_only {
   $self->{subscription_usage} == SUBUSAGE_START_ONLY;
 }
 
+sub _get_prod_options {
+  my ($product, $cfg, @values) = @_;
+
+  require BSE::CfgInfo;
+  my $avail_options = BSE::CfgInfo::product_options($cfg);
+  my @opt_names = split /,/, $product->{options};
+  push @values, '' while @values < @opt_names;
+  my %values;
+  @values{@opt_names} = @values;
+
+  my @sem_options = map 
+    +{ 
+      id => $_, 
+      %{$avail_options->{$_}},
+      value => $values{$_},
+     }, @opt_names;
+  for my $option (@sem_options) {
+    $option->{display} = $option->{labels}{$option->{value}};
+  }
+
+  return @sem_options;
+}
+
+sub option_descs {
+  my ($self, $cfg, $rvalues) = @_;
+
+  $rvalues or $rvalues = [ ];
+
+  return $self->_get_prod_options($cfg, @$rvalues);
+}
+
 1;
diff --git a/site/cgi-bin/nuser.fcgi b/site/cgi-bin/nuser.fcgi
new file mode 100755 (executable)
index 0000000..d524bf1
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/perl -w
+# -d:ptkdb
+BEGIN { $ENV{DISPLAY} = '192.168.32.15:0.0' }
+use strict;
+use FindBin;
+use CGI::Fast;
+use lib "$FindBin::Bin/modules";
+use BSE::DB;
+use BSE::Request;
+use BSE::Template;
+use Carp 'confess';
+use BSE::UI::NUser;
+
+my $cfg = BSE::Cfg->new; # only do this once
+
+$SIG{__DIE__} = sub { confess $@ };
+
+while(my $cgi = CGI::Fast->new) {
+  my $req = BSE::Request->new(cfg=>$cfg, cgi=>$cgi);
+
+  my $result = BSE::UI::NUser->dispatch($req);
+
+  BSE::Template->output_result($req, $result);
+}
+
diff --git a/site/cgi-bin/nuser.pl b/site/cgi-bin/nuser.pl
new file mode 100755 (executable)
index 0000000..5b14f67
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+# -d:ptkdb
+BEGIN { $ENV{DISPLAY} = '192.168.32.50: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::NUser;
+
+$SIG{__DIE__} = sub { confess $@ };
+
+my $req = BSE::Request->new;
+
+my $result = BSE::UI::NUser->dispatch($req);
+
+BSE::Template->output_result($req, $result);
+
index 037befe..380949e 100644 (file)
@@ -74,3 +74,14 @@ select ss.* from bse_seminar_sessions ss left join bse_seminar_bookings sb
   on ss.id = sb.session_id and sb.siteuser_id = ?
 where ss.seminar_id = ? and sb.siteuser_id is null
 SQL
+
+name: seminarSessionInfoUnbooked
+sql_statement: <<SQL
+select ss.*, lo.*, ss.id as session_id 
+  from bse_locations lo 
+  right join bse_seminar_sessions ss on lo.id = ss.location_id
+  left join bse_seminar_bookings sb
+  on ss.id = sb.session_id and sb.siteuser_id = ?
+where ss.seminar_id = ? and sb.siteuser_id is null and lo.id = ss.location_id
+SQL
+
index 573f16b..6c49a1d 100644 (file)
@@ -30,6 +30,49 @@ the session_popup tag on the edit booking page now uses a different
 date format.  This can also be configured with
 [seminars].popup_date_format
 
+=item *
+
+BSE::Edit::Seminar wasn't importing tag_hash_plain, this caused some
+errors in emails sent when cancelling a session for example.
+
+=item *
+
+deleting a seminar session was calling a class method on the wrong
+class.
+
+=item *
+
+the admin logon form target now accept the t template selection option
+
+=item *
+
+the t paremeter is now recognized for the addattendee1 and
+addattendee2 templates
+
+=item *
+
+a new user side action dispatching mechanism is in place, all new user
+side functionality will be going via cgi-bin/nuser.pl.  This splits
+the requests it takes into 2 parts, one is a controller id and the
+second is the action to take on that controller.  These can either be
+supplied as part of the path if your web server supports PATH_INFO or
+in parameters.  Eg. to use the bookseminar target of the user
+controller you can either request:
+
+ /cgi-bin/nuser.pl/user/bookseminar?id=50
+
+or
+
+  /cgi-bin/nuser.pl?_p=user&a_bookseminar=1&id=50
+
+=item *
+
+a user can now book seminar sessions without going via the shop if
+they're free.  The script is /cgi-bin/nuser.pl/user - if your server
+doesn't support PATH_INFO then you can use /cgi-bin/nuser.pl and set
+_p to user.  The targets under this script are: bookseminar,
+bookconfirm, book, and bookcomplete.
+
 =back
 
 =head2 0.15_47
diff --git a/site/templates/admin/user_book_seminar.tmpl b/site/templates/admin/user_book_seminar.tmpl
new file mode 100644 (file)
index 0000000..d498099
--- /dev/null
@@ -0,0 +1,10 @@
+The following user has booked a seminar session:
+
+  User     : <:user name1:> <:user name2:> (<:user userId:>)
+  Course   : <:seminar title:>
+  Location : <:location description:>
+  Date     : <:date session when_at:>
+  Time     : <:date "%I:%M %P" session when_at:>
+<:iterator begin options
+:>  <:option desc:>: <:option value:>
+<:iterator end options:>
index 6929242..4d60c06 100644 (file)
@@ -1,4 +1,4 @@
- <:wrap base.tmpl:> <:embed start:><:admin:>
+<:wrap base.tmpl:> <:embed start:><:admin:>
 <script>
 function show_location(locationid) {
   window.open('/cgi-bin/shop.pl?a_location=1&amp;location_id='+locationid, 
@@ -39,8 +39,8 @@ function show_location(locationid) {
 <:iterator end stepcats:>
 </ul></font>
 <:or StepCats:><:eif StepCats:>
-<:if Product retailPrice:>
-<form name="ff" method="POST" action="/cgi-bin/shop.pl">
+<:if Or [seminar retailPrice] [cfg seminars free_bookings 1]:>
+<form name="ff" action="<:ifSeminar retailPrice:>/cgi-bin/shop.pl<:or:>/cgi-bin/nuser.pl/user/bookconfirm<:eif:>">
 <p><font face="Verdana, Arial, Helvetica, sans-serif" size="2">Select a seminar session to attend:</font></p>              
      <:if Cfg seminar locations:>
        <:iterator begin locations:>
@@ -63,6 +63,12 @@ function show_location(locationid) {
 </tr>
 </table>
   <:or Options:><:eif Options:>
+<:if Eq [seminar retailPrice] "0":>
+<p>
+         <font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>Special Instructions:</b></font><br />
+         <textarea name="customer_instructions" cols="70" rows="10"></textarea>
+      </p>
+<:or Eq:><:eif Eq:>      
   <:ifProduct leadTime:> 
    
   <p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="-2" color="#FE7F00">Usually ships in:</font><font face="Verdana, Arial, Helvetica, sans-serif" size="-2"> <:seminar leadTime:> <:if Eq [product leadTime] "1":>day<:or Eq:>days<:eif Eq:></font></b>
@@ -71,6 +77,7 @@ function show_location(locationid) {
 
 <table border="0" cellspacing="0" cellpadding="0">
     <tr valign="middle" align="center"> 
+<:if Seminar retailPrice:>
       <td bgcolor="#666666"> 
         <table width="100%" border="0" cellspacing="1" cellpadding="4" height="30">
           <tr valign="middle" align="center"> 
@@ -81,16 +88,22 @@ function show_location(locationid) {
           </tr>
         </table>
       </td>
+<:or Seminar:><:eif Seminar:>
       <td nowrap> &nbsp; 
         <input type="hidden" name="id" value="<:seminar id:>">
+<:if Seminar retailPrice:>
         <input type="hidden" name="quantity" value="1">
         <input type="submit" name="add" value="Add to cart">
         <input type="submit" name="cart" value="View cart">
+<:or Seminar:>
+        <input type="submit" name="book" value="Book this seminar" />
+<:eif Seminar:>
       </td>
     </tr>
   </table>
 </form>
-<:or Product:><br>
+<:or Or:>
+<br>
 <br>
 <table border="0" cellspacing="0" cellpadding="1" bgcolor="#CCCCCC">
   <tr>
@@ -123,7 +136,7 @@ function show_location(locationid) {
   </tr>
 </table>
 <font face="Arial, Helvetica, sans-serif"><b><font size="4"> </font></b></font><:eif 
-Product:> 
+Or:> 
 <p><br>
   <a href="/shop/index.html"><img src="/images/store/browse_more.gif" width="133" height="21" border="0"></a>
 </p><br>
index 21c2e5b..1cbc9de 100644 (file)
@@ -1,4 +1,4 @@
- <:wrap base.tmpl:> <:embed start:><:admin:> 
+<:wrap base.tmpl:> <:embed start:><:admin:> 
 <table width="100%" border="0" cellspacing="0" cellpadding="0">
   <tr> 
     <td width="80%" height="24">&nbsp;&nbsp;<font face="Arial, Helvetica, sans-serif" size="4" color="#FF7F00"><b><:product 
diff --git a/site/templates/user/base_bookcomplete.tmpl b/site/templates/user/base_bookcomplete.tmpl
new file mode 100644 (file)
index 0000000..14aae86
--- /dev/null
@@ -0,0 +1,25 @@
+<:wrap base.tmpl title=>"Booking Complete":>
+<h1>Booking Complete</h1>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+<table>
+  <tr>
+    <th>Seminar:</th>
+    <td><:seminar title:></td>
+  </tr>
+  <tr>
+    <th>Session:</th>
+    <td><:location description:> <:date "%H:%M %d/%m/%Y" session when_at:></td>
+  </tr>
+<:iterator begin options:>
+  <tr>
+   <th><:option desc:></th>
+   <td><:option display:></td>
+  </tr>
+<:iterator end options:>
+  <tr>
+   <th>Customer<br />Instructions:</th>
+   <td><textarea name="customer_instructions" cols="70" rows="10" readonly="readonly"/><:booking customer_instructions:></textarea></td>
+  </tr>
+</table>
diff --git a/site/templates/user/base_bookconfirm.tmpl b/site/templates/user/base_bookconfirm.tmpl
new file mode 100644 (file)
index 0000000..d4ac95f
--- /dev/null
@@ -0,0 +1,32 @@
+<:wrap base.tmpl title=>"Confirm Booking":>
+<h1>Confirm Booking</h1>
+<form action="<:script:>/user/book" method="post" name="book">
+<input type="hidden" name="id" value="<:seminar id:>" />
+<input type="hidden" name="session_id" value="<:session id:>" />
+<:iterator begin options:>
+<input type="hidden" name="<:option id:>" value="<:option value:>" />
+<:iterator end options:>
+
+<table>
+  <tr>
+    <th>Seminar:</th>
+    <td><:seminar title:></td>
+  </tr>
+  <tr>
+    <th>Session:</th>
+    <td><:location description:> <:date "%H:%M %d/%m/%Y" session when_at:></td>
+  </tr>
+<:iterator begin options:>
+  <tr>
+   <th><:option desc:></th>
+   <td><:option display:></td>
+  </tr>
+<:iterator end options:>
+  <tr>
+   <th>Customer<br />Instructions:</th>
+   <td><textarea name="customer_instructions" cols="70" rows="10" readonly="readonly"/><:old customer_instructions:></textarea></td>
+  </tr>
+  <tr>
+    <td colspan="2"><input type="submit" value="Book Session &gt;&gt;" /></td>
+  </tr>
+</table>
diff --git a/site/templates/user/base_bookseminar.tmpl b/site/templates/user/base_bookseminar.tmpl
new file mode 100644 (file)
index 0000000..13da921
--- /dev/null
@@ -0,0 +1,39 @@
+<:wrap base.tmpl title=>"Book Seminar Session":>
+<h1>Book Seminar Session</h1>
+<:ifMessage:>
+<p><b><:message:></b></p>
+<:or:><:eif:> 
+<!-- leave this as get, since submitting it just displays a new page -->
+<form action="<:script:>/user/bookconfirm" method="get" name="bookconfirm">
+<input type="hidden" name="id" value="<:seminar id:>" />
+<table>
+  <tr>
+    <th>Seminar:</th>
+    <td><:seminar title:></td>
+  </tr>
+  <tr>
+    <th>Session:</th>
+    <td><select name="session_id">
+<option value="">(select a session)</option>
+<:iterator begin sessions:>
+<option value="<:session session_id:>" <:ifEq [old session_id] [session session_id]:>selected="selected"<:or:><:eif:>><:session description:> <:date "%H:%M %d/%m/%Y" session when_at:></option>
+<:iterator end sessions:>
+        </select></td>
+    <td><:error_img session_id:></td>
+  </tr>
+<:iterator begin options:>
+  <tr>
+   <th><:option desc:></th>
+   <td><:option_popup:></td>
+   <td></td>
+  </tr>
+<:iterator end options:>
+  <tr>
+   <th>Customer<br />Instructions:</th>
+   <td><textarea name="customer_instructions" cols="70" rows="10"><:old customer_instructions:></textarea></td>
+   <td><:error_img customer_instructions:></td>
+  </tr>
+  <tr>
+    <td colspan="3"><input type="submit" value="Book Session &gt;&gt;" /></td>
+  </tr>
+</table>
index 1cef8c0..a87647a 100644 (file)
--- a/test.cfg
+++ b/test.cfg
@@ -235,3 +235,4 @@ valid child types.Article=Article,Seminar
 
 shop product options.book_flight=Flight Booking;Book a flight;Don't book a flight
 ;seminars.popup_date_format=%H:%M %d/%m/%Y
+seminars.free_bookings=1
\ No newline at end of file