# $Id: attendees.pm,v 1.22 2004/11/12 05:44:52 gwolf Exp $
######################################
# Comas - Conference Management System
######################################
# Copyright 2003 CONSOL
# Congreso Nacional de Software Libre (http://www.consol.org.mx/)
#   Gunnar Wolf <gwolf@gwolf.cx>
#   Manuel Rabade <mig@mig-29.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
######################################

######################################
# Module: Comas::HTML::attendees
# Manage attendees registration.
######################################
# Depends on:
#
# Comas::Person - Handles the interaction with a person for Comas
# Comas::HTML::common - Common functions for the HTML FrontEnd
# Comas::Conf - Comas' configuration management routines
# Data::FormValidator - Validates user input based on input profile

# This module is not meant to be called on its own, it should be called from
# Comas::HTML.

package Comas::HTML::attendees;

use strict;
use warnings;
use Carp;

use Comas::Person;
use Comas::Conf qw(:read);
use Comas::HTML::common qw(:all);
use Data::FormValidator;

use Exporter;

our @ISA = qw(Exporter);
our @EXPORT_OK = qw(attendees_menu attendees_login attendees_logout
                    attendees_new attendees_data attendees_passwd
                    attendees_recover_passwd);
our %EXPORT_TAGS = (all => [@EXPORT_OK]);

=head1 NAME

Comas::HTML::attendees - Front End to manage persons.

=head1 SYNOPSIS

This module is not meant to be used by itself, but as an auxiliary to
L<Comas::HTML|Comas::HTML>. Please check L<Comas::HTML|Comas::HTML>'s
documentation, in the B<Front End Tasks> section, for further details on the
usage for this functions.

=head1 INTERNALS

=head2 METHODS

=head3 $res = $html->attendees_new;

Process a new attendee and put the id in the session (C<-person>).

=cut

sub attendees_new {
    my ($input_profile, $template, $validator, $val_results, %new_person,
        $person);
    my $h = shift;
    $input_profile = {
        required => [ qw( -firstname -famname -login -passwd -passwd2 -email
                          -state_id -country_id -person_type_id) ],
        optional => [ qw( -nickname -org -dept -title -studies_id
                          -studies -postal_addr -zip -city
                          -phone -fax -b_day -b_month -b_year ) ],
        constraints => { -email => 'email',
                         -zip => 'zip',
                         -phone => 'phone',
                         -fax => 'phone'},
        dependency_groups => { -b_date => [qw(-b_day -b_month -b_year)]},
        filters => [ qw( trim ) ] };

    $template = $h->new_template('attendees/new.tmpl');
    
    # This is all the validation if the form have been posted.
    if (defined($h->{-params}->{validate})) {
        $validator = Data::FormValidator->new;
        $val_results = $validator->check ( $h->{-params}, $input_profile);

	use Data::Dumper; warn Dumper $h->{-params};
        if ($val_results->has_missing ||
            $val_results->has_invalid ||
            $h->{-params}->{-passwd} ne $h->{-params}->{-passwd2} ) {            
            # Something is wrong.
            $template->param(_validator2html($val_results));
            if ($h->{-params}->{-passwd} ne $h->{-params}->{-passwd2}) {
                # If the passwords are not equal.
                $template->param(DIFERENT_PASSWORDS => '1');
            }
            
        } else {
            # If we are here, everything is fine, lets create the new
            # person.
            %new_person = %{$val_results->valid};

            if (defined $new_person{-b_day}) {
                $new_person{-birth}="$new_person{-b_year}-$new_person{-b_month}";
                $new_person{-birth}.="-$new_person{-b_day}";
                delete $new_person{-b_year};
                delete $new_person{-b_month};
                delete $new_person{-b_day};
            }
            delete $new_person{-passwd2};
            $person = Comas::Person->new(-db=>$h->{-db}, %new_person);
            if (defined $person) {
                $h->{-session}->{-person} = $person->{-id};
                return $h->attendees_menu;
            } else {
                if (Comas::Person->lastError == 205) {
                    $template->param(LOGIN_USED => '1');
                }                    
            }
        }
    }
    
    # If we are here, something is wrong with the form or is the first time that
    # the user see the form.
    
    # All the things that we have to put in the Template in case that we can't
    # register the person.
    $template->param(
                     -person_type_list => 
                     _catalog2html_opts($h->{-db},"person_type",
                                        $h->{-params}->{-person_type_id},
                                        'priv_person_types'),
                     -studies_list => 
                     _catalog2html_opts($h->{-db},"studies",
                                        $h->{-params}->{-studies_id}),
                     -state_list =>
                     _catalog2html_opts($h->{-db},"state",
                                        $h->{-params}->{-state_id}),
                     -country_list =>
                     _catalog2html_opts($h->{-db},"country",
                                        $h->{-params}->{-country_id},'country'),
                     -b_day_list => 
                     _list2html_opts([1..31],$h->{-params}->{-b_day}),
                     -b_month_list =>
                     _list2html_opts([1..12], $h->{-params}->{-b_month}),
                     -b_year_list => 
                     _list2html_opts([1900..2004],$h->{-params}->{-b_year}));

    $template->param($h->{-params});
    return $template->output;
}

=pod

=head3 $res = $html->attendees_menu($message);

Attendee's main menu. Can receive an optional explanatory message that will be
passed to the template in case there was an error in some other method.

=cut

sub attendees_menu {
    my ($template, $person);
    my $h = shift;
    my $message = shift;

    $template = $h->new_template('attendees/menu.tmpl');

    $person = Comas::Person->new(-db=>$h->{-db},
                                 -id=>$h->{-session}->{-person});

    $template->param($person->get_data);

    if (defined $message) {
        $template->param($message => 1);
    }

    if (Comas::Proposal->in_submission_period(-db=>$h->{-db})) {
	$template->param(can_submit_proposal => $person->can_submit_proposals,
			 days_for_submitting => 
		   Comas::Proposal->days_left_for_submission(-db=>$h->{-db}));
    } else {
	$template->param(can_submit_proposal => 0,
			 days_for_submitting => 0);
    }
    return $template->output;
}

=pod

=head3 $res = $html->attendees_login;

Login for the attendees, if successful it will store the attendee's id in the
session (C<-person>).

=cut

sub attendees_login {
    my ($input_profile, $template, $validator, $val_results, $person);
    my $h = shift;

    $input_profile = {
        required => [ qw( -login -passwd ) ],
        filters => [ qw( trim ) ] };
    
    $template = $h->new_template('attendees/login.tmpl');

    if (defined($h->{-params}->{validate})) {
        # If the form have been posted we check the values
        $validator = Data::FormValidator->new;
        $val_results = $validator->check ( $h->{-params}, $input_profile);

        if ($val_results->has_missing || $val_results->has_invalid) {
            # Wops, something is worng with the parameters
            $template->param(_validator2html($val_results));
            $template->param($h->{-params});
            return $template->output;
        } else {
            # We create a new person
            $person = Comas::Person->new(-db=>$h->{-db},
                                         %{$val_results->valid});
            if (defined $person) {
                $h->{-session}->{-person} = $person->{-id};
                return $h->attendees_menu;
            } else {
                # Or we return the login form.
                if (Comas::Person->lastError == 203) {
                    $template->param(_validator2html($val_results));
                    $template->param($h->{-params});
                    $template->param(UNKNOWN_USER => '1');
                    return $template->output;
                }                    
            }
        }
    } else {
        # The form hasn't been posted.
        return $template->output;
    }
}

=pod

=head3 $res = $html->attendees_logout;

Logout for the attendees, it will delete the attendee's id in the session
(C<-person>).

=cut

sub attendees_logout {
    my $h = shift;
    delete $h->{-session}->{-person};
    return $h->main_menu;
}    

=pod

=head3 $res = $html->attendees_data;

Modify attendee's personal information.

=cut

sub attendees_data {
    my ($input_profile, $template, $validator, $val_results, %new_data,
        $person);
    my $h = shift;

    $input_profile = {
        required => [ qw( -firstname -famname -email -state_id -country_id
                          -person_type_id) ],
        optional => [ qw( -nickname -org -dept -title -studies_id
                          -postal_addr -zip -city
                          -phone -fax -b_day -b_month -b_year ) ],
        constraints => { -email => 'email',
                         -zip => 'zip',
                         -phone => 'phone',
                         -fax => 'phone' },
        dependency_groups => { -b_date => [qw/-b_day -b_month -b_year/]},
        filters => [ qw( trim ) ] };

    $template = $h->new_template('attendees/data.tmpl');

    $person = Comas::Person->new(-db=>$h->{-db},
                                 -id=>$h->{-session}->{-person});

    if (_is_privileged($h->{-db}, $person->get_person_type_id,
                       'priv_person_types')) {
        $input_profile->{required} = [ grep {$_ ne '-person_type_id'}
                                       @{$input_profile->{required}} ];
    }

    # This is all the validation if the form have been posted.
    if (defined($h->{-params}->{validate})) {
        $validator = Data::FormValidator->new;
        $val_results = $validator->check ( $h->{-params}, $input_profile);
        
        if ($val_results->has_missing || $val_results->has_invalid) {
            # Something is wrong.
            $template->param(_validator2html($val_results));

            if (_is_privileged($h->{-db}, $person->get_person_type_id,
                               'priv_person_types')) {
                $template->param(-person_type =>
				          $h->{-db}->getCatDescr('person_type',
                                                  $person->get_person_type_id),
                                 PRIV_PERSON_TYPE => 1);
            } else {
                $template->param(-person_type_list => 
                                 _catalog2html_opts($h->{-db},'person_type',
                                                    $h->{-params}->
                                                    {-person_type_id},
                                                    'priv_person_types'));
            }

            $template->param(
                             -studies_list => 
                             _catalog2html_opts($h->{-db},"studies",
                                                $h->{-params}->{-studies_id}),
                             -state_list =>
                             _catalog2html_opts($h->{-db},"state",
                                                $h->{-params}->{-state_id}),
                             -country_list =>
                             _catalog2html_opts($h->{-db},"country",
                                                $h->{-params}->{-country_id}),
                             -b_day_list => 
                             _list2html_opts([1..31],$h->{-params}->{-b_day}),
                             -b_month_list =>
                             _list2html_opts([1..12], $h->{-params}->{-b_month}),
                             -b_year_list => 
                             _list2html_opts([1900..2004],
                                             $h->{-params}->{-b_year}));
            
            $template->param($h->{-params});
            return $template->output;
        } else {
            # If we are here, everything is fine, lets update the person.
            %new_data = %{$val_results->valid};
            if (defined $new_data{-b_day}) {
                $new_data{-birth}="$new_data{-b_year}-$new_data{-b_month}";
                $new_data{-birth}.="-$new_data{-b_day}";
                delete $new_data{-b_year};
                delete $new_data{-b_month};
                delete $new_data{-b_day};
            } else {
                $new_data{-birth} = '';
            }

            foreach my $temp (@{$input_profile->{optional}}) {
                next if ($temp =~ /-b_day|-b_month|-b_year/);
                unless($new_data{$temp}) {
                    $new_data{$temp} = '';
                }
            }

            $person = Comas::Person->new(-db => $h->{-db},
                                         -id => $h->{-session}->{-person});
            $person->set_data(%new_data);
            return $h->attendees_menu('DATA_CHANGED');
        }
    } else {
        # If we are here, the form haven't been posted, lets put the current
        # values of the person
        my ($b_year, $b_month, $b_day);
        if (defined $person->get_birth) {
            ($b_year, $b_month, $b_day) = split('-', $person->get_birth);
        }

        if (_is_privileged($h->{-db}, $person->get_person_type_id,
                           'priv_person_types')) {
            $template->param(-person_type => 
			               $h->{-db}->getCatDescr('person_type',
						 $person->get_person_type_id),
                             PRIV_PERSON_TYPE => 1);
        } else {
            $template->param(-person_type_list => 
                             _catalog2html_opts($h->{-db},'person_type',
                                                $person->get_person_type_id,
                                                'priv_person_types'));
        }

        $template->param(-studies_list => 
                         _catalog2html_opts($h->{-db},'studies',
                                            $person->get_studies_id),
                         -state_list =>
                         _catalog2html_opts($h->{-db},'state',
                                            $person->get_state_id),
                         -country_list =>
                         _catalog2html_opts($h->{-db},'country',
                                            $person->get_country_id),
                         -b_day_list => _list2html_opts([1..31],$b_day),
                         -b_month_list => _list2html_opts([1..12],$b_month),
                         -b_year_list =>  _list2html_opts([1900..1990],$b_year));
        $template->param($person->get_data);
        return $template->output;        
    }
}

=pod

=head3 $res = $html->attendees_passwd;

Modify attendee's personal password.

=cut

sub attendees_passwd {
    my ($input_profile, $template, $validator, $val_results, $person);
    my $h = shift;
    $input_profile = {
        required => [ qw( -current_passwd -new_passwd  -new_passwd2) ],
        filters => [ qw( trim ) ] };
    
    $template = $h->new_template('attendees/passwd.tmpl');
    
    # This is all the validation if the form have been posted.
    if (defined($h->{-params}->{validate})) {
        $validator = Data::FormValidator->new;
        $val_results = $validator->check ( $h->{-params}, $input_profile);
        $template->param($h->{-params});        
        if ($val_results->has_missing || $val_results->has_invalid) {
            # If something is invalid
            $template->param(_validator2html($val_results));
            return $template->output;
        } elsif ($h->{-params}->{-new_passwd} ne $h->{-params}->{-new_passwd2}) {
            # If the passwords are diferent
            $template->param(DIFERENT_PASSWD => 1);
            return $template->output;
        } else {
            # Everything is fine ;)
            $person = Comas::Person->new(-db=>$h->{-db}, 
                                         -id => $h->{-session}->{-person});
            if(defined $person->ck_passwd(-login=>$person->get_login,
                                          -passwd =>
                                          $h->{-params}->{-current_passwd})) {
                $person->set_passwd($h->{-params}->{-new_passwd});
                return $h->attendees_menu('PASSWD_CHANGED');
            } else {
                # Whops .. the current password is bad :(
                $template->param(BAD_CURRENT_PASSWD => 1);
                return $template->output;
            }
        }
    } else {
        # If the form hasn't been posted
        return $template->output;
    }
}

=pod

=head3 $res = $html->attendees_recover_passwd;

Generate a new attendee's password and send it via email.

=cut

sub attendees_recover_passwd {
    my ($input_profile, $template, $validator, $val_results, $person);
    my $h = shift;
    $input_profile = {
        required => [ qw( -login -email ) ],
        filters => [ qw( trim ) ] };
    
    $template = $h->new_template('attendees/recover_passwd.tmpl');
    
    # This is all the validation if the form have been posted.
    if (defined($h->{-params}->{validate})) {
        $validator = Data::FormValidator->new;
        $val_results = $validator->check ( $h->{-params}, $input_profile);
        $template->param($h->{-params});        
        if ($val_results->has_missing || $val_results->has_invalid) {
            # If something is invalid
            $template->param(_validator2html($val_results));
            return $template->output;
        } else {
            # Everything is fine, let's see if the data are correct ;)
            if($person = Comas::Person->new(-db=>$h->{-db}, 
                                            -login => $h->{-params}->{-login},
                                            -email => $h->{-params}->{-email}))
            {
                my $new_passwd;
                my @a=('a'..'z');
                for (1..8) {
                    $new_passwd .= $a[rand @a];
                }
                if(_send_recover_passwd_mail($h->{-config}->{-tmpl_path} .
                                             'mail/recover_passwd.txt', 
                                             $person, $new_passwd)) {
                    $person->set_passwd($new_passwd);
                    $template->param(PASSWD_RECOVERED => 1);
                    return $template->output;
                }
            } else {
                $template->param(UNKNOWN_USER => 1);
                return $template->output;
            }
        }
    } else {
        # If the form hasn't been posted
        return $template->output;
    }
}

=pod

=head2 LOCAL FUNCTIONS

=head3 $res = _send_recover_passwd_mail($mail_tmpl, $person, $new_passwd);

Send via email a new password (already generated) to an attendee.

The arguments are:

=over 2

=item - $mail_tmpl

Filename of the email template with full path.

=item - $person

L<Comas::Person|Comas::Person> object of the attendee.

=item - $new_passwd

String with the new password to be sent.

=back

This function also needs the following configuration values: smtp_host (if isn't
defined 127.0.0.1 is used) and attendees_recover_passwd_from (sender of
notifications).

The C<$res> retrun value is undef in case of an error or true if successful.

=cut

sub _send_recover_passwd_mail {
    my (%mail_subst, $smtp_host, $mail_from, %person_data, $smtp, $mail);
    my $mail_tmpl = shift;
    my $person = shift;
    my $new_passwd = shift;

    unless (-r $mail_tmpl and open(TMPL, '<', $mail_tmpl)) {
	warn "Could not open mail template ($mail_tmpl): $!\n";
	warn "The password of the person will not be changed.\n";
	return undef;
    }
    while (<TMPL>) {
        $mail .= $_;
    }
    close(TMPL);

    %person_data = $person->get_data;

    $mail_subst{FROM}=get_conf($person->{-db},'attendees_recover_passwd_from');
    $mail_subst{NAME} = $person_data{-firstname} . ' ' . $person_data{-famname};
    $mail_subst{NEW_PASSWD} = $new_passwd;
    $mail_subst{MAIL} = $person_data{-email};
    $mail_subst{PERSON_ID} = $person->{-id};
    
    $smtp_host = get_conf($person->{-db}, 'smtp_host') || '127.0.0.1';

    unless($mail_from = get_conf($person->{-db}, 'attendees_recover_passwd_from')) {
        warn "Couldn't get sender address, please check your configuration.\n";
	return undef;
    }    
    
    # Here goes my mail-processing mini-template-engine!
    for my $key (keys %mail_subst) {
        $mail =~ s/__${key}__/$mail_subst{$key}/gs;
    }
    unless ($smtp = Net::SMTP->new($smtp_host)) {
        warn "Couldn't contact SMTP server\n";
	warn "The password of the person will not be changed.\n";
        return undef;
    }
    
    $smtp->mail($mail_from);
    $smtp->to($person_data{-email});
    $smtp->data();
    $smtp->datasend($mail);
    $smtp->dataend();
    $smtp->quit();
    return 1;
}

=pod

=head1 SEE ALSO

L<Comas::HTML|Comas::HTML> module documentation

=head1 AUTHOR

Gunnar Wolf, gwolf@gwolf.cx

Manuel Rabade, mig@mig-29.net

Comas has been developed for CONSOL, Congreso Nacional de Software Libre,
http://www.consol.org.mx/

=head1 COPYRIGHT

Copyright 2003 Gunnar Wolf and Manuel Rabade

This library is free software, you can redistribute it and/or modify it
under the terms of the GPL version 2 or later.

=cut

  
1;

# $Log: attendees.pm,v $
# Revision 1.22  2004/11/12 05:44:52  gwolf
# Added the functionality to be able to handle more than one file per proposal and store it in the DB, not in separate files.
#
# Revision 1.21  2004/07/09 06:24:02  mig
# Added the documentation for the methods and functions.
#
# Revision 1.20  2004/07/08 00:15:42  gwolf
# Handling Comas' and the conference's base URL in the configuration,
# not hard-coded in the HTML templates themselves
#
# Revision 1.19  2004/06/25 03:15:48  gwolf
# The system now notifies a person that he has submitted the maximum
# allowed number of proposals from the attendee's main menu
#
# Revision 1.18  2004/06/24 23:04:40  gwolf
# When displaying the attendee's menu, the option to submit a new
# proposal is only shown if they can still be received (this means,
# before the submission period is over).
#
# I am still missing a little bit here: It should also be hidden if the
# person has the maximum number of proposals registered.
#
# Revision 1.17  2004/06/14 16:58:03  gwolf
# Minor modifications for consistency sake (all of them regarding field
# names in templates, or at least something terribly similar to it)
#
# Revision 1.16  2004/04/08 02:54:23  gwolf
# Created the administrative interface to list/modify attendees
#
# Revision 1.15  2004/04/07 21:32:41  gwolf
# Created Comas::DB methods getCatalog and getCatDescr, modified some of
# Comas::DB modules in order to use this interface instead of
# Comas::HTML::common::_id2descr (and removed that function, of course)
#
# Revision 1.14  2004/02/03 19:34:50  mig
# - Listo la recuperacion de contraseñas.
#
# Revision 1.13  2003/12/21 07:29:49  mig
# - Sincronizo con las templates que modifique del campo hidden 'validate'
#
# Revision 1.12  2003/12/20 04:14:51  mig
# - Agrego tags Id y Log que expanda el CVS
#
