]> git.imager.perl.org - bse.git/blob - site/cgi-bin/modules/BSE/UI/AdminSeminar.pm
optional case-insensitivity for searching
[bse.git] / site / cgi-bin / modules / BSE / UI / AdminSeminar.pm
1 package BSE::UI::AdminSeminar;
2 use strict;
3 use base qw(BSE::UI::AdminDispatch);
4 use BSE::Util::Tags qw(tag_hash tag_error_img tag_hash_plain);
5 use BSE::Util::DynSort qw(sorter tag_sorthelp);
6 use BSE::Template;
7 use BSE::Util::Iterate;
8 use BSE::TB::Locations;
9 use BSE::Util::HTML qw(:default popup_menu);
10 use constant SECT_LOCATION_VALIDATION => "BSE Location Validation";
11 use BSE::CfgInfo 'product_options';
12 use DevHelp::Date qw(dh_strftime_sql_datetime);
13
14 our $VERSION = "1.002";
15
16 my %rights =
17   (
18    loclist               => 'bse_location_list',
19    locaddform            => 'bse_location_add',
20    locadd                => 'bse_location_add',
21    locedit               => 'bse_location_edit',
22    locsave               => 'bse_location_edit',
23    locview               => 'bse_location_view',
24    locdelask             => 'bse_location_delete',
25    locdelete             => 'bse_location_delete',
26    #    detail           => 'bse_subscr_detail',
27    #    update           => 'bse_subscr_update',
28    addattendseminar      => 'bse_session_booking_add',
29    addattendsession      => 'bse_session_booking_add',
30    addattendsave         => 'bse_session_booking_add',
31    cancelbookingconfirm  => 'bse_session_booking_cancel',
32    cancelbooking         => 'bse_session_booking_cancel',
33    editbooking           => 'bse_session_booking_edit',
34    savebooking           => 'bse_session_booking_edit',
35   );
36
37 sub actions { \%rights }
38
39 sub rights { \%rights }
40
41 sub default_action { 'loclist' }
42
43 sub req_loclist {
44   my ($class, $req, $errors) = @_;
45
46   my $msg = $req->message($errors);
47   my $cgi = $req->cgi;
48   my @locations = BSE::TB::Locations->all;
49   my ($sortby, $reverse) =
50     sorter(data=>\@locations, cgi=>$cgi, sortby=>'description', 
51            session=>$req->session,
52            name=>'locations', fields=> { id => {numeric => 1 } });
53   my $it = BSE::Util::Iterate->new;
54   my $current_loc;
55
56   my %acts;
57   %acts =
58     (
59      $req->admin_tags,
60      msg => $msg,
61      message => $msg,
62      $it->make_paged_iterator('ilocation', 'locations', \@locations, undef,
63                               $cgi, undef, 'pp=20', $req->session, 
64                               'locations', \$current_loc),
65      sorthelp => [ \&tag_sorthelp, $sortby, $reverse ],
66      sortby=>$sortby,
67      reverse=>$reverse,
68      ifRemovable => [ \&tag_ifRemovable, \$current_loc ],
69     );
70
71   return $req->dyn_response('admin/locations/list', \%acts);
72 }
73
74 sub tag_ifRemovable {
75   my ($rlocation) = @_;
76
77   $$rlocation or return;
78
79   $$rlocation->is_removable;
80 }
81
82 sub tag_field {
83   my ($fields, $args) = @_;
84
85   my ($name, $parm) = split ' ', $args;
86
87   exists $fields->{$name}
88     or return "** Unknown field $name **";
89   exists $fields->{$name}{$parm}
90     or return '';
91
92   return escape_html($fields->{$name}{$parm});
93 }
94
95 sub req_locaddform {
96   my ($class, $req, $errors) = @_;
97
98   my $msg = $req->message($errors);
99
100   my %fields = BSE::TB::Location->valid_fields();
101   my $cfg_fields = $req->configure_fields(\%fields, SECT_LOCATION_VALIDATION);
102
103   my %acts;
104   %acts =
105     (
106      $req->admin_tags,
107      msg => $msg,
108      message => $msg,
109      error_img => [ \&tag_error_img, $req->cfg, $errors ],
110      field => [ \&tag_field, $cfg_fields ],
111     );
112
113   return $req->dyn_response('admin/locations/add', \%acts);
114 }
115
116 sub req_locadd {
117   my ($class, $req) = @_;
118
119   my $cgi = $req->cgi;
120   my $cfg = $req->cfg;
121   my %fields = BSE::TB::Location->valid_fields($cfg);
122   my %rules = BSE::TB::Location->valid_rules($cfg);
123   my %errors;
124   $req->validate(errors=> \%errors, 
125                  fields=> \%fields,
126                  rules => \%rules,
127                  section => SECT_LOCATION_VALIDATION);
128
129   keys %errors
130     and return $class->req_locaddform($req, \%errors);
131
132   my %location;
133   for my $field (keys %fields) {
134     $location{$field} = $cgi->param($field);
135   }
136   $location{disabled} = 0;
137   my @cols = BSE::TB::Location->columns;
138   shift @cols;
139   my $loc = BSE::TB::Locations->add(@location{@cols});
140
141   my $r = $class->_loclist_refresh($req, "Location $location{description} added");
142
143   return BSE::Template->get_refresh($r, $req->cfg);
144 }
145
146 sub _loc_show_common {
147   my ($class, $req, $errors, $template) = @_;
148
149   my $loc_id = $req->cgi->param('id');
150   $loc_id && $loc_id =~ /^\d+/
151     or return $class->req_loclist
152       ($req, { id=>'Missing or invalid location id' });
153   my $location = BSE::TB::Locations->getByPkey($loc_id);
154   $location
155     or return $class->req_loclist
156       ($req, { id=>'Unknown location id' });
157
158   my $msg = $req->message($errors);
159
160   my %fields = BSE::TB::Location->valid_fields();
161   my $cfg_fields = $req->configure_fields(\%fields, SECT_LOCATION_VALIDATION);
162
163   my $it = BSE::Util::Iterate->new;
164
165   my %acts;
166   %acts =
167     (
168      $req->admin_tags,
169      msg => $msg,
170      message => $msg,
171      error_img => [ \&tag_error_img, $req->cfg, $errors ],
172      location => [ \&tag_hash, $location ],
173      field => [ \&tag_field, $cfg_fields ],
174      $it->make_iterator([ \&iter_locsessions, $location ],
175                         'session', 'sessions'),
176     );
177
178   return $req->dyn_response($template, \%acts);
179 }
180
181 sub iter_locsessions {
182   my ($location) = @_;
183
184   $location->sessions_detail;
185 }
186
187 sub req_locedit {
188   my ($class, $req, $errors) = @_;
189
190   return $class->_loc_show_common($req, $errors, 'admin/locations/edit');
191 }
192
193 sub req_locsave {
194   my ($class, $req) = @_;
195
196   my $loc_id = $req->cgi->param('id');
197   $loc_id && $loc_id =~ /^\d+/
198     or return $class->req_loclist
199       ($req, { id=>'Missing or invalid location id' });
200   my $location = BSE::TB::Locations->getByPkey($loc_id);
201   $location
202     or return $class->req_loclist
203       ($req, { id=>'Unknown location id' });
204
205   my $cgi = $req->cgi;
206   my $cfg = $req->cfg;
207   my %fields = $location->valid_fields($cfg);
208   my %rules = $location->valid_rules($cfg);
209   my %errors;
210   $req->validate(errors=> \%errors, 
211                  fields=> \%fields,
212                  rules => \%rules,
213                  section => SECT_LOCATION_VALIDATION);
214
215   keys %errors
216     and return $class->req_locedit($req, \%errors);
217
218   for my $field (keys %fields) {
219     my $value = $cgi->param($field);
220     $location->{$field} = $value if defined $value;
221   }
222
223   if ($cgi->param('save_disabled')) {
224     $location->{disabled} = $cgi->param('disabled') ? 1 : 0;
225   }
226
227   $location->save;
228
229   my $r = $class->_loclist_refresh($req, 
230                                    "Location $location->{description} saved");
231
232   return BSE::Template->get_refresh($r, $req->cfg);
233 }
234
235 sub req_locview {
236   my ($class, $req, $errors) = @_;
237
238   return $class->_loc_show_common($req, $errors, 'admin/locations/view');
239 }
240
241 sub req_locdelask {
242   my ($class, $req, $errors) = @_;
243
244   return $class->_loc_show_common($req, $errors, 'admin/locations/delete');
245 }
246
247 sub req_locdelete {
248   my ($class, $req) = @_;
249
250   my $loc_id = $req->cgi->param('id');
251   $loc_id && $loc_id =~ /^\d+/
252     or return $class->req_loclist
253       ($req, { id=>'Missing or invalid location id' });
254   my $location = BSE::TB::Locations->getByPkey($loc_id);
255   $location
256     or return $class->req_loclist
257       ($req, { id=>'Unknown location id' });
258
259   $location->is_removable
260     or return $class->req_loclist
261       ($req, { id=>"Location $location->{description} cannot be removed" });
262
263   my $description = $location->{description};
264   $location->remove;
265
266   my $r = $class->_loclist_refresh($req, 
267                                    "Location $description removed");
268
269   return BSE::Template->get_refresh($r, $req->cfg);
270 }
271
272 sub _loclist_refresh {
273   my ($class, $req, $msg) = @_;
274
275   my $r = $req->cgi->param('r') || $req->cgi->param('refreshto');
276   unless ($r) {
277     $r = "/cgi-bin/admin/admin_seminar.pl";
278   }
279   if ($msg && $r !~ /[&?]m=/) {
280     my $sep = $r =~ /\?/ ? '&' : '?';
281
282     $r .= $sep . "m=" . escape_uri($msg);
283   }
284
285   return $r;
286 }
287
288 sub req_addattendseminar {
289   my ($class, $req, $errors) = @_;
290
291   # make sure we're passed a valid siteuser_id
292   my $cgi = $req->cgi;
293   my $siteuser_id = $cgi->param('siteuser_id');
294   defined $siteuser_id && $siteuser_id =~ /^\d+$/
295     or return $class->req_loclist($req, { siteuser_id => 
296                                           "Missing or invalid siteuser_id" });
297   require BSE::TB::SiteUsers;
298   my $siteuser = BSE::TB::SiteUsers->getByPkey($siteuser_id)
299     or return $class->req_loclist($req, { siteuser_id => "Unknown siteuser_id" });
300   my $msg = $req->message($errors);
301   require BSE::TB::Seminars;
302   my @seminars = BSE::TB::Seminars->all;
303
304   my $it = BSE::Util::Iterate->new;
305   my %acts;
306   %acts =
307     (
308      $req->admin_tags,
309      $it->make_iterator(undef, 'seminar', 'seminars', \@seminars),
310      siteuser => [ \&tag_hash, $siteuser ],
311      msg => $msg,
312      message => $msg,
313      error_img => [ \&tag_error_img, $req->cfg, $errors ],
314     );
315   
316   return $req->dyn_response('admin/addattendee1', \%acts);
317 }
318
319 sub req_addattendsession {
320   my ($class, $req, $errors) = @_;
321
322   # make sure we're passed a valid siteuser_id
323   my $cgi = $req->cgi;
324   my $siteuser_id = $cgi->param('siteuser_id');
325   defined $siteuser_id && $siteuser_id =~ /^\d+$/
326     or return $class->req_loclist($req, { siteuser_id => 
327                                           "Missing or invalid siteuser_id" });
328   require BSE::TB::SiteUsers;
329   my $siteuser = BSE::TB::SiteUsers->getByPkey($siteuser_id)
330     or return $class->req_loclist($req, { siteuser_id => "Unknown siteuser_id" });
331
332   # make sure we got a valid seminar
333   require BSE::TB::Seminars;
334   my $seminar_id = $cgi->param('seminar_id');
335   defined $seminar_id && $seminar_id =~ /^\d*$/
336     or return $class->req_addattendseminar
337       ($req, { seminar_id => "Missing or invalid seminar_id" });
338   $seminar_id
339     or return $class->req_addattendseminar
340       ($req, { seminar_id => "Please select a seminar" });
341   my $seminar = BSE::TB::Seminars->getByPkey($seminar_id)
342     or return $class->req_addattendseminar
343       ($req, { seminar_id => "Unknown seminar_id" });
344   my $msg = $req->message($errors);
345
346   my @sem_options = _get_sem_options($req->cfg, $seminar);
347   my $current_option;
348
349   my @sessions = $seminar->session_info;
350   my %user_booked = map { $_=>1 } 
351     $siteuser->seminar_sessions_booked($seminar_id);
352   @sessions = grep !$user_booked{$_->{id}}, @sessions;
353
354   my $it = BSE::Util::Iterate->new;
355   my %acts;
356   %acts =
357     (
358      $req->admin_tags,
359      siteuser => [ \&tag_hash, $siteuser ],
360      seminar => [ \&tag_hash, $seminar ],
361      msg => $msg,
362      message => $msg,
363      error_img => [ \&tag_error_img, $req->cfg, $errors ],
364      $it->make_iterator(undef, 'session', 'sessions', \@sessions),
365      $it->make_iterator(undef, 'option', 'options', \@sem_options, 
366                         undef, undef, \$current_option),
367      option_popup => [ \&tag_option_popup, $req->cgi, \$current_option ],
368     );
369   
370   return $req->dyn_response('admin/addattendee2', \%acts);
371 }
372
373 sub tag_option_popup {
374   my ($cgi, $roption) = @_;
375
376   $$roption 
377     or return '** popup_option not in options iterator **';
378
379   my $option = $$roption;
380
381   my @extras;
382   my $value = $cgi->param($option->{id});
383   defined $value or $value = $option->{value};
384   if ($value) {
385     push @extras, -default => $value;
386   }
387
388   return popup_menu(-name => $option->{id},
389                     -values => $option->{values},
390                     -labels => $option->{labels},
391                     @extras);
392 }
393
394 sub req_addattendsave {
395   my ($class, $req, $errors) = @_;
396
397   # make sure we're passed a valid siteuser_id
398   my $cgi = $req->cgi;
399   my $siteuser_id = $cgi->param('siteuser_id');
400   defined $siteuser_id && $siteuser_id =~ /^\d+$/
401     or return $class->req_loclist($req, { siteuser_id => 
402                                           "Missing or invalid siteuser_id" });
403   require BSE::TB::SiteUsers;
404   my $siteuser = BSE::TB::SiteUsers->getByPkey($siteuser_id)
405     or return $class->req_loclist($req, { siteuser_id => "Unknown siteuser_id" });
406
407   # make sure we got a valid seminar
408   require BSE::TB::Seminars;
409   my $seminar_id = $cgi->param('seminar_id');
410   defined $seminar_id && $seminar_id =~ /^\d*$/
411     or return $class->req_addattendseminar
412       ($req, { seminar_id => "Missing or invalid seminar_id" });
413   $seminar_id
414     or return $class->req_addattendseminar
415       ($req, { seminar_id => "Please select a seminar" });
416   my $seminar = BSE::TB::Seminars->getByPkey($seminar_id)
417     or return $class->req_addattendseminar
418       ($req, { seminar_id => "Unknown seminar_id" });
419   my $msg = $req->message($errors);
420
421   # make sure we get a valid session
422   require BSE::TB::SeminarSessions;
423   my $session_id = $cgi->param('session_id');
424   defined $session_id && $session_id =~ /^\d*$/
425     or return $class->req_addattendsession
426       ($req, { session_id => "Missing or invalid session_id" });
427   $session_id
428     or return $class->req_addattendsession
429       ($req, { session_id => "Please select a session" });
430   my $session = BSE::TB::SeminarSessions->getByPkey($session_id)
431     or return $class->req_addattendsession
432       ($req, { session_id => "Unknown session_id" });
433
434   # accumulate the options
435   my %errors;
436   my @options;
437   for my $opt_name (split /,/, $seminar->{options}) {
438     my $value = $cgi->param($opt_name);
439     defined $value
440       or return $class->req_addattendsession
441         ($req, { opt_name => "Missing value for $opt_name" });
442     
443     push @options, $value;
444   }
445
446   my %more;
447   for my $name (qw/customer_instructions support_notes roll_present/) {
448     my $value = $cgi->param($name);
449     $value and $more{$name} = $value;
450   }
451   $more{roll_present} ||= 0;
452   $more{options}      = join(',', @options);
453
454   eval {
455     $session->add_attendee($siteuser, %more);
456   };
457   if ($@) {
458     if ($@ =~ /duplicate/i) {
459       return $class->req_addattendsession
460         ($req, { session_id => "User already booked for this session" });
461     }
462     else {
463       return $class->req_addattendsession
464         ($req, { _error => $@ });
465     }
466   }
467
468   require BSE::ComposeMail;
469   my $cfg = $req->cfg;
470   my $mailer = BSE::ComposeMail->new(cfg => $cfg);
471   my $subject = $cfg->entry('seminars', 'booked_notify_subject',
472                             'You have been booked for seminar');
473   my @sem_options = _get_sem_options($cfg, $seminar, @options);
474   my $it = DevHelp::Tags::Iterate->new;
475   my %acts;
476   %acts =
477     (
478      BSE::Util::Tags->static(undef, $cfg),
479      user     => [ \&tag_hash_plain, $siteuser ],
480      seminar  => [ \&tag_hash_plain, $seminar  ],
481      session  => [ \&tag_hash_plain, $session  ],
482      booking  => [ \&tag_hash_plain, \%more    ],
483      location => [ \&tag_hash_plain, $session->location ],
484      $it->make_iterator(undef, 'option', 'options', \@sem_options),
485     );
486
487   unless ($mailer->send(to        => $siteuser,
488                         subject   => $subject,
489                         template  => 'user/admin_book_seminar',
490                         acts      => \%acts)) {
491     return $class->req_addattendsession
492         ($req, { _email => "The user has been booked, but there was an error seding the email notification:".$mailer->errstr });
493   }
494
495   
496   my $r = $cgi->param('r');
497   unless ($r) {
498     $r = "/cgi-bin/admin/siteusers.pl?a_edit=1&id=" . $siteuser->{id};
499   }
500
501   return BSE::Template->get_refresh($r, $req->cfg);
502 }
503
504 sub req_cancelbookingconfirm {
505   my ($class, $req, $message) = @_;
506
507   my $cgi = $req->cgi;
508
509   my $id = $cgi->param('id');
510   defined $id && $id =~ /^\d+$/
511     or return $class->error($req, "id parameter invalid");
512
513   require BSE::TB::SeminarBookings;
514   my $booking = BSE::TB::SeminarBookings->getByPkey($id)
515     or return $class->error($req, "booking $id not found");
516
517   my $session = $booking->session;
518   my $siteuser = $booking->siteuser;
519
520   my $seminar = $session->seminar;
521   my @sem_options = _get_sem_options($req->cfg, $seminar, 
522                                      split /,/, $session->{options});
523
524   defined $message or $message = '';
525   $message = escape_html($message);
526
527   my $it = BSE::Util::Iterate->new;
528   my %acts;
529   %acts =
530     (
531      $req->admin_tags,
532      siteuser => [ \&tag_hash, $siteuser ],
533      session  => [ \&tag_hash, $session  ],
534      seminar  => [ \&tag_hash, $seminar ],
535      location => [ \&tag_hash, $session->location ],
536      booking  => [ \&tag_hash, $booking  ],
537      message  => $message,
538      $it->make_iterator(undef, 'option', 'options', \@sem_options),
539     );
540
541   return $req->dyn_response('admin/semcancelbooking', \%acts);
542 }
543
544 sub req_cancelbooking {
545   my ($class, $req) = @_;
546
547   my $cgi = $req->cgi;
548
549   my $id = $cgi->param('id');
550   defined $id && $id =~ /^\d+$/
551     or return $class->error($req, "id parameter invalid");
552
553   require BSE::TB::SeminarBookings;
554   my $booking = BSE::TB::SeminarBookings->getByPkey($id)
555     or return $class->error($req, "booking $id not found");
556
557   my $session = $booking->session;
558   my $siteuser = $booking->siteuser;
559
560   my @options = split /,/, $booking->{options};
561   local $SIG{__DIE__};
562   eval {
563     $session->remove_booking($siteuser);
564   };
565   $@ and return $class->req_cancelbookingconfirm
566     ($req, "Could not remove booking $@");
567
568   my $seminar = $session->seminar;
569
570   my @sem_options = _get_sem_options($req->cfg, $seminar, @options);
571
572   require BSE::ComposeMail;
573   my $cfg = $req->cfg;
574   my $mailer = BSE::ComposeMail->new(cfg => $cfg);
575   my $subject = $cfg->entry('seminars', 'unbooked_notify_subject',
576                             'Your seminar booking has been cancelled');
577   my $it = DevHelp::Tags::Iterate->new;
578   my %acts;
579   %acts =
580     (
581      BSE::Util::Tags->static(undef, $cfg),
582      user     => [ \&tag_hash_plain, $siteuser ],
583      seminar  => [ \&tag_hash_plain, $seminar  ],
584      session  => [ \&tag_hash_plain, $session  ],
585      booking  => [ \&tag_hash_plain, $booking  ],
586      location => [ \&tag_hash_plain, $session->location ],
587      $it->make_iterator(undef, 'option', 'options', \@sem_options),
588     );
589
590   unless ($mailer->send(to        => $siteuser,
591                         subject   => $subject,
592                         template  => 'user/admin_unbook_seminar',
593                         acts      => \%acts)) {
594     return $class->req_cancelbookingconfirm
595       ($req, "The user booking was cancelled, but there was an error sending the email notification:".$mailer->errstr );
596   }
597
598   my $r = $cgi->param('r');
599   unless ($r) {
600     $r = '/cgi-bin/admin/siteusers.pl?a_view=1&id='.$siteuser->{id};
601   }
602   $r .= $r =~ /\?/ ? '&' : '?';
603   $r .= "m=Booking+cancelled";
604
605   return BSE::Template->get_refresh($r, $req->cfg);
606 }
607
608 sub req_editbooking {
609   my ($class, $req, $errors) = @_;
610
611   my $cgi = $req->cgi;
612
613   my $id = $cgi->param('id');
614   defined $id && $id =~ /^\d+$/
615     or return $class->error($req, "id parameter invalid");
616
617   require BSE::TB::SeminarBookings;
618   my $booking = BSE::TB::SeminarBookings->getByPkey($id)
619     or return $class->error($req, "booking $id not found");
620
621   my $session = $booking->session;
622
623   my $siteuser = $booking->siteuser;
624
625   my $message = $req->message($errors);
626
627   my $seminar = $session->seminar;
628   my @sem_options = _get_sem_options($req->cfg, $seminar, 
629                                      split /,/,  $booking->{options});
630   my @unbooked = $seminar->get_unbooked_by_user($siteuser);
631   @unbooked = sort { $b->{when_at} cmp $a->{when_at} } ( @unbooked, $session );
632     
633   my $current_option;
634   my $it = BSE::Util::Iterate->new;
635   my %acts;
636   %acts =
637     (
638      $req->admin_tags,
639      siteuser => [ \&tag_hash, $siteuser ],
640      session  => [ \&tag_hash, $session  ],
641      seminar  => [ \&tag_hash, $seminar ],
642      location => [ \&tag_hash, $session->location ],
643      booking  => [ \&tag_hash, $booking  ],
644      message  => $message,
645      error_img => [ \&tag_error_img, $req->cfg, $errors ],
646      $it->make_iterator(undef, 'option', 'options', \@sem_options, 
647                         undef, undef, \$current_option),
648      option_popup => [ \&tag_option_popup, $req->cgi, \$current_option ],
649      $it->make_iterator(undef, 'isession', 'sessions', \@unbooked),
650      session_popup => 
651      [ \&tag_session_popup, $req->cfg, $booking, $req->cgi, \@unbooked ],
652     );
653
654   return $req->dyn_response('admin/semeditbooking', \%acts);
655 }
656
657 sub req_savebooking {
658   my ($class, $req, $message) = @_;
659
660   my $cgi = $req->cgi;
661
662   my $id = $cgi->param('id');
663   defined $id && $id =~ /^\d+$/
664     or return $class->error($req, "id parameter invalid");
665
666   require BSE::TB::SeminarBookings;
667   my $booking = BSE::TB::SeminarBookings->getByPkey($id)
668     or return $class->error($req, "booking $id not found");
669
670   my @cols = $booking->columns;
671   shift @cols;
672   for my $name (@cols) {
673     my $value = $cgi->param($name);
674     defined $value and $booking->set($name => $value);
675   }
676   my $seminar = $booking->session->seminar;
677   my @options;
678   for my $name (split /,/, $seminar->{options}) {
679     push @options, ($cgi->param($name))[0];
680   }
681   $booking->{options} = join ',', @options;
682
683   eval {
684     $booking->save;
685   };
686   $@
687     and return $class->req_editbooking($req, { error => $@ });
688
689   my @sem_options = _get_sem_options($req->cfg, $seminar, @options);
690
691   my $session = $booking->session;
692   my $siteuser = $booking->siteuser;
693   require BSE::ComposeMail;
694   my $cfg = $req->cfg;
695   my $mailer = BSE::ComposeMail->new(cfg => $cfg);
696   my $subject = $cfg->entry('seminars', 'edit_notify_subject',
697                             'Your seminar booking has been changed');
698   my $it = DevHelp::Tags::Iterate->new;
699   my %acts;
700   %acts =
701     (
702      BSE::Util::Tags->static(undef, $cfg),
703      user     => [ \&tag_hash_plain, $siteuser ],
704      seminar  => [ \&tag_hash_plain, $seminar  ],
705      session  => [ \&tag_hash_plain, $session  ],
706      booking  => [ \&tag_hash_plain, $booking  ],
707      location => [ \&tag_hash_plain, $session->location ],
708      $it->make_iterator(undef, 'option', 'options', \@sem_options),
709     );
710
711   unless ($mailer->send(to        => $siteuser,
712                         subject   => $subject,
713                         template  => 'user/admin_edit_seminar',
714                         acts      => \%acts)) {
715     return $class->req_editbooking
716       ($req, _email => "The user booking was changed, but there was an error sending the email notification:".$mailer->errstr );
717   }
718
719   my $r = $cgi->param('r');
720   unless ($r) {
721     $r = '/cgi-bin/admin/siteusers.pl?a_view=1&id='.$siteuser->{id};
722   }
723   $r .= $r =~ /\?/ ? '&' : '?';
724   $r .= "m=Booking+updated";
725
726   return BSE::Template->get_refresh($r, $req->cfg);
727 }
728
729 sub _get_sem_options {
730   my ($cfg, $seminar, @values) = @_;
731
732   $seminar->option_descs($cfg, \@values);
733 }
734
735 sub tag_session_popup {
736   my ($cfg, $booking, $cgi, $unbooked) = @_;
737
738   my $default = $cgi->param('session_id');
739   defined $default or $default = $booking->{session_id};
740   my %locations;
741   for my $session (@$unbooked) {
742     unless ($locations{$session->{location_id}}) {
743       $locations{$session->{location_id}} = $session->location;
744     }
745   }
746
747   my $date_fmt = $cfg->entry('seminars', 'popup_date_format', 
748                              "%I:%M %p %d %b %Y");
749   return popup_menu
750     (-name => 'session_id',
751      -values => [ map $_->{id}, @$unbooked ],
752      -labels => 
753      { map 
754        { $_->{id} => 
755            _session_desc($_, $locations{$_->{location_id}}, $date_fmt) 
756          } @$unbooked 
757      },
758      -default => $default);
759 }
760
761 sub _session_desc {
762   my ($session, $location, $date_fmt) = @_;
763
764   dh_strftime_sql_datetime($date_fmt, $session->{when_at}) . ' - ' .
765     $location->{description};
766 }
767
768 1;