1 package BSE::UI::AdminSeminar;
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);
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);
14 our $VERSION = "1.002";
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',
37 sub actions { \%rights }
39 sub rights { \%rights }
41 sub default_action { 'loclist' }
44 my ($class, $req, $errors) = @_;
46 my $msg = $req->message($errors);
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;
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 ],
68 ifRemovable => [ \&tag_ifRemovable, \$current_loc ],
71 return $req->dyn_response('admin/locations/list', \%acts);
77 $$rlocation or return;
79 $$rlocation->is_removable;
83 my ($fields, $args) = @_;
85 my ($name, $parm) = split ' ', $args;
87 exists $fields->{$name}
88 or return "** Unknown field $name **";
89 exists $fields->{$name}{$parm}
92 return escape_html($fields->{$name}{$parm});
96 my ($class, $req, $errors) = @_;
98 my $msg = $req->message($errors);
100 my %fields = BSE::TB::Location->valid_fields();
101 my $cfg_fields = $req->configure_fields(\%fields, SECT_LOCATION_VALIDATION);
109 error_img => [ \&tag_error_img, $req->cfg, $errors ],
110 field => [ \&tag_field, $cfg_fields ],
113 return $req->dyn_response('admin/locations/add', \%acts);
117 my ($class, $req) = @_;
121 my %fields = BSE::TB::Location->valid_fields($cfg);
122 my %rules = BSE::TB::Location->valid_rules($cfg);
124 $req->validate(errors=> \%errors,
127 section => SECT_LOCATION_VALIDATION);
130 and return $class->req_locaddform($req, \%errors);
133 for my $field (keys %fields) {
134 $location{$field} = $cgi->param($field);
136 $location{disabled} = 0;
137 my @cols = BSE::TB::Location->columns;
139 my $loc = BSE::TB::Locations->add(@location{@cols});
141 my $r = $class->_loclist_refresh($req, "Location $location{description} added");
143 return BSE::Template->get_refresh($r, $req->cfg);
146 sub _loc_show_common {
147 my ($class, $req, $errors, $template) = @_;
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);
155 or return $class->req_loclist
156 ($req, { id=>'Unknown location id' });
158 my $msg = $req->message($errors);
160 my %fields = BSE::TB::Location->valid_fields();
161 my $cfg_fields = $req->configure_fields(\%fields, SECT_LOCATION_VALIDATION);
163 my $it = BSE::Util::Iterate->new;
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'),
178 return $req->dyn_response($template, \%acts);
181 sub iter_locsessions {
184 $location->sessions_detail;
188 my ($class, $req, $errors) = @_;
190 return $class->_loc_show_common($req, $errors, 'admin/locations/edit');
194 my ($class, $req) = @_;
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);
202 or return $class->req_loclist
203 ($req, { id=>'Unknown location id' });
207 my %fields = $location->valid_fields($cfg);
208 my %rules = $location->valid_rules($cfg);
210 $req->validate(errors=> \%errors,
213 section => SECT_LOCATION_VALIDATION);
216 and return $class->req_locedit($req, \%errors);
218 for my $field (keys %fields) {
219 my $value = $cgi->param($field);
220 $location->{$field} = $value if defined $value;
223 if ($cgi->param('save_disabled')) {
224 $location->{disabled} = $cgi->param('disabled') ? 1 : 0;
229 my $r = $class->_loclist_refresh($req,
230 "Location $location->{description} saved");
232 return BSE::Template->get_refresh($r, $req->cfg);
236 my ($class, $req, $errors) = @_;
238 return $class->_loc_show_common($req, $errors, 'admin/locations/view');
242 my ($class, $req, $errors) = @_;
244 return $class->_loc_show_common($req, $errors, 'admin/locations/delete');
248 my ($class, $req) = @_;
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);
256 or return $class->req_loclist
257 ($req, { id=>'Unknown location id' });
259 $location->is_removable
260 or return $class->req_loclist
261 ($req, { id=>"Location $location->{description} cannot be removed" });
263 my $description = $location->{description};
266 my $r = $class->_loclist_refresh($req,
267 "Location $description removed");
269 return BSE::Template->get_refresh($r, $req->cfg);
272 sub _loclist_refresh {
273 my ($class, $req, $msg) = @_;
275 my $r = $req->cgi->param('r') || $req->cgi->param('refreshto');
277 $r = "/cgi-bin/admin/admin_seminar.pl";
279 if ($msg && $r !~ /[&?]m=/) {
280 my $sep = $r =~ /\?/ ? '&' : '?';
282 $r .= $sep . "m=" . escape_uri($msg);
288 sub req_addattendseminar {
289 my ($class, $req, $errors) = @_;
291 # make sure we're passed a valid siteuser_id
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;
304 my $it = BSE::Util::Iterate->new;
309 $it->make_iterator(undef, 'seminar', 'seminars', \@seminars),
310 siteuser => [ \&tag_hash, $siteuser ],
313 error_img => [ \&tag_error_img, $req->cfg, $errors ],
316 return $req->dyn_response('admin/addattendee1', \%acts);
319 sub req_addattendsession {
320 my ($class, $req, $errors) = @_;
322 # make sure we're passed a valid siteuser_id
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" });
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" });
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);
346 my @sem_options = _get_sem_options($req->cfg, $seminar);
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;
354 my $it = BSE::Util::Iterate->new;
359 siteuser => [ \&tag_hash, $siteuser ],
360 seminar => [ \&tag_hash, $seminar ],
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 ],
370 return $req->dyn_response('admin/addattendee2', \%acts);
373 sub tag_option_popup {
374 my ($cgi, $roption) = @_;
377 or return '** popup_option not in options iterator **';
379 my $option = $$roption;
382 my $value = $cgi->param($option->{id});
383 defined $value or $value = $option->{value};
385 push @extras, -default => $value;
388 return popup_menu(-name => $option->{id},
389 -values => $option->{values},
390 -labels => $option->{labels},
394 sub req_addattendsave {
395 my ($class, $req, $errors) = @_;
397 # make sure we're passed a valid siteuser_id
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" });
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" });
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);
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" });
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" });
434 # accumulate the options
437 for my $opt_name (split /,/, $seminar->{options}) {
438 my $value = $cgi->param($opt_name);
440 or return $class->req_addattendsession
441 ($req, { opt_name => "Missing value for $opt_name" });
443 push @options, $value;
447 for my $name (qw/customer_instructions support_notes roll_present/) {
448 my $value = $cgi->param($name);
449 $value and $more{$name} = $value;
451 $more{roll_present} ||= 0;
452 $more{options} = join(',', @options);
455 $session->add_attendee($siteuser, %more);
458 if ($@ =~ /duplicate/i) {
459 return $class->req_addattendsession
460 ($req, { session_id => "User already booked for this session" });
463 return $class->req_addattendsession
464 ($req, { _error => $@ });
468 require BSE::ComposeMail;
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;
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),
487 unless ($mailer->send(to => $siteuser,
489 template => 'user/admin_book_seminar',
491 return $class->req_addattendsession
492 ($req, { _email => "The user has been booked, but there was an error seding the email notification:".$mailer->errstr });
496 my $r = $cgi->param('r');
498 $r = "/cgi-bin/admin/siteusers.pl?a_edit=1&id=" . $siteuser->{id};
501 return BSE::Template->get_refresh($r, $req->cfg);
504 sub req_cancelbookingconfirm {
505 my ($class, $req, $message) = @_;
509 my $id = $cgi->param('id');
510 defined $id && $id =~ /^\d+$/
511 or return $class->error($req, "id parameter invalid");
513 require BSE::TB::SeminarBookings;
514 my $booking = BSE::TB::SeminarBookings->getByPkey($id)
515 or return $class->error($req, "booking $id not found");
517 my $session = $booking->session;
518 my $siteuser = $booking->siteuser;
520 my $seminar = $session->seminar;
521 my @sem_options = _get_sem_options($req->cfg, $seminar,
522 split /,/, $session->{options});
524 defined $message or $message = '';
525 $message = escape_html($message);
527 my $it = BSE::Util::Iterate->new;
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 ],
538 $it->make_iterator(undef, 'option', 'options', \@sem_options),
541 return $req->dyn_response('admin/semcancelbooking', \%acts);
544 sub req_cancelbooking {
545 my ($class, $req) = @_;
549 my $id = $cgi->param('id');
550 defined $id && $id =~ /^\d+$/
551 or return $class->error($req, "id parameter invalid");
553 require BSE::TB::SeminarBookings;
554 my $booking = BSE::TB::SeminarBookings->getByPkey($id)
555 or return $class->error($req, "booking $id not found");
557 my $session = $booking->session;
558 my $siteuser = $booking->siteuser;
560 my @options = split /,/, $booking->{options};
563 $session->remove_booking($siteuser);
565 $@ and return $class->req_cancelbookingconfirm
566 ($req, "Could not remove booking $@");
568 my $seminar = $session->seminar;
570 my @sem_options = _get_sem_options($req->cfg, $seminar, @options);
572 require BSE::ComposeMail;
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;
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),
590 unless ($mailer->send(to => $siteuser,
592 template => 'user/admin_unbook_seminar',
594 return $class->req_cancelbookingconfirm
595 ($req, "The user booking was cancelled, but there was an error sending the email notification:".$mailer->errstr );
598 my $r = $cgi->param('r');
600 $r = '/cgi-bin/admin/siteusers.pl?a_view=1&id='.$siteuser->{id};
602 $r .= $r =~ /\?/ ? '&' : '?';
603 $r .= "m=Booking+cancelled";
605 return BSE::Template->get_refresh($r, $req->cfg);
608 sub req_editbooking {
609 my ($class, $req, $errors) = @_;
613 my $id = $cgi->param('id');
614 defined $id && $id =~ /^\d+$/
615 or return $class->error($req, "id parameter invalid");
617 require BSE::TB::SeminarBookings;
618 my $booking = BSE::TB::SeminarBookings->getByPkey($id)
619 or return $class->error($req, "booking $id not found");
621 my $session = $booking->session;
623 my $siteuser = $booking->siteuser;
625 my $message = $req->message($errors);
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 );
634 my $it = BSE::Util::Iterate->new;
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 ],
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),
651 [ \&tag_session_popup, $req->cfg, $booking, $req->cgi, \@unbooked ],
654 return $req->dyn_response('admin/semeditbooking', \%acts);
657 sub req_savebooking {
658 my ($class, $req, $message) = @_;
662 my $id = $cgi->param('id');
663 defined $id && $id =~ /^\d+$/
664 or return $class->error($req, "id parameter invalid");
666 require BSE::TB::SeminarBookings;
667 my $booking = BSE::TB::SeminarBookings->getByPkey($id)
668 or return $class->error($req, "booking $id not found");
670 my @cols = $booking->columns;
672 for my $name (@cols) {
673 my $value = $cgi->param($name);
674 defined $value and $booking->set($name => $value);
676 my $seminar = $booking->session->seminar;
678 for my $name (split /,/, $seminar->{options}) {
679 push @options, ($cgi->param($name))[0];
681 $booking->{options} = join ',', @options;
687 and return $class->req_editbooking($req, { error => $@ });
689 my @sem_options = _get_sem_options($req->cfg, $seminar, @options);
691 my $session = $booking->session;
692 my $siteuser = $booking->siteuser;
693 require BSE::ComposeMail;
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;
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),
711 unless ($mailer->send(to => $siteuser,
713 template => 'user/admin_edit_seminar',
715 return $class->req_editbooking
716 ($req, _email => "The user booking was changed, but there was an error sending the email notification:".$mailer->errstr );
719 my $r = $cgi->param('r');
721 $r = '/cgi-bin/admin/siteusers.pl?a_view=1&id='.$siteuser->{id};
723 $r .= $r =~ /\?/ ? '&' : '?';
724 $r .= "m=Booking+updated";
726 return BSE::Template->get_refresh($r, $req->cfg);
729 sub _get_sem_options {
730 my ($cfg, $seminar, @values) = @_;
732 $seminar->option_descs($cfg, \@values);
735 sub tag_session_popup {
736 my ($cfg, $booking, $cgi, $unbooked) = @_;
738 my $default = $cgi->param('session_id');
739 defined $default or $default = $booking->{session_id};
741 for my $session (@$unbooked) {
742 unless ($locations{$session->{location_id}}) {
743 $locations{$session->{location_id}} = $session->location;
747 my $date_fmt = $cfg->entry('seminars', 'popup_date_format',
748 "%I:%M %p %d %b %Y");
750 (-name => 'session_id',
751 -values => [ map $_->{id}, @$unbooked ],
755 _session_desc($_, $locations{$_->{location_id}}, $date_fmt)
758 -default => $default);
762 my ($session, $location, $date_fmt) = @_;
764 dh_strftime_sql_datetime($date_fmt, $session->{when_at}) . ' - ' .
765 $location->{description};