RSS Git Download  Clone
Raw Blame History
package Reporter::Routes;

use Dancer2;
use Dancer2::Plugin::Deferred;
use Dancer2::Plugin::Auth::Extensible;
# 'p' generates error (maybe conflict with DPP from another place?)
use Data::Printer alias => 'ddp';

use Reporter;
# use Reporter::Routes::Test; # do we need this?

=begin # seems to work the same setting $Local::QueryLogger::CurrentUsername directly
use Tie::RefHash::Weak;
use Scope::Guard qw(guard);
{
    tie my %refhash, 'Tie::RefHash::Weak';
    sub set_username {
        my ($request, $username) = @_; # ddp $request; ddp $username;
	    $refhash{$request} = guard { undef $Local::QueryLogger::CurrentUsername };
	    $Local::QueryLogger::CurrentUsername = $username;
	    return;
    }
}
=cut

# switch on sql_trace for test scripts (or though connamd-line with SQL_TRACE=1):
# set enable_sql_trace => 1 if $ENV{HARNESS_VERSION}; # ddp \%ENV;

set auto_page => 1; # eg /login, /denied (DPAE)

# app, contains model & db classes:
my $app = Reporter->new( config => config() );

sub flash { deferred @_ } # to use old term flash instead of deferred

hook before => sub {
    # discard old db connection if mysql has gone away since last request:
    $app->check_db_connection; # Local::DB - creates new DBIx::Simple object on demand
    # sql query output to stdout:
    $app->enable_sql_trace if setting('enable_sql_trace'); # development env

    # all routes need login (except /login):
    my $session = session('user_profile'); # ddp $session; # beware auto-vivification here
    unless ( $session ) {
        redirect '/login' unless request->path_info =~ m!^/login!;
    }
    # set_username(request(), $session->{username}); # works the same setting directly:
    $Local::QueryLogger::CurrentUsername = $session->{username};
};

hook before_template_render => sub { # do NOT use 'var' in here or 500.tt fails
    my $tokens = shift;
    $tokens->{app_version} = $app->version; # warn $app->version;
    $tokens->{symbolise}   = sub { $app->symbolise(@_) };
    if ( session->read('logged_in_user_realm') ) { # fatal if called before login
        $tokens->{user_roles} = user_roles; # ddp user_roles; # DPAE
    }
};

# fix for broken 500.tt rendering if use of 'var' in before_template_render() :
# hook on_route_exception => sub { send_error($_[1]) }; # err msg available in [% content %]

# default route:
get '/' => sub {
    my $page = param('page') || 1;
    # get list of reported requests for this user:
    my $user = session('user_profile'); # ddp $user;
    my %args = (
        user_id => $user->{id},
        page    => $page,
        uri     => uri_for('/'),
    );
    my $data = $app->model->get_reports_for_user(\%args);
    template load => { data => $data };
};

# submitted user credentials:
post '/login' => sub {
    my $params = params; # ddp $params;

    # validate username/password
    if ( my $user = $app->model->validate_user_credentials($params) ) {  ddp $user;
        session user_profile => $user;
        my @user_details = ( $user->{username}, $user->{password} );
        # authenticate using DPAE authenticate_user() method:
        my ($success, $realm) = authenticate_user(@user_details); # ddp $success; ddp $realm;
        if ($success) { # ddp $realm; # for DPAE:
            session logged_in_user => $user->{username};
            session logged_in_user_realm => $realm;
        }
        redirect '/';
    }
    else {
        my $failed_logins = session('failed_logins') || 0;
        session failed_logins => $failed_logins + 1; # increment
        redirect '/login';
    }
};

get '/load' => sub {
    my $params = params; # ddp $params;

    # validate lab number format:
    my $validation = $app->model->validator->validate( search => $params );
        # ddp $validation;
    if ( $validation->{failed} ) { # ddp $result->{errors};
        return template load => $validation; # href containing dfv_errs key
    }
    my $lab_number = param('lab_number');
    my $session    = session('user_profile');
    { # get request for lab_number:
        my %h = (
            lab_number => $lab_number,
            user_id    => $session->{id},
        );
        my $data = $app->model->load_request(\%h); # ddp $data;
        if (! $data) {
            my $msg = 'request with lab number %s not found';
            flash warning => sprintf $msg, $lab_number;
            redirect '/';
        }
        # store data in session for tt, and in case needed for re-display to report.tt:
        session record => $data;
    }
    { # get validation profile to configure required fields:
        my $profile = $app->model->get_validation_profile('report'); # ddp $profile;
        session validation_profile => $profile;
    }
    # tt gets data from session.record:
    template report => { target => uri_for('/report') };
};

post '/report/:request_id' => sub {
    my $params = params; # ddp $params;
    my $validation = $app->model->validator->validate( report => $params );
        # ddp $validation;
    my $record = session('record');
    $record->{report} = $params; # in case validation fails

    if ( $validation->{failed} ) { # ddp $validation->{errors};
        my %args = (
            target => uri_for('/report'),
            dfv_errs => $validation->{dfv_errs}, # href containing dfv_errs key
        );
        return template report => \%args;
    }
    my $data = $validation->{valid}; # ddp $data;
    my $user = session('user_profile'); # ddp $user;

    # add user_id & request_id to $data:
    $data->{request_id} = param('request_id');
    $data->{user_id}    = $user->{id}; # ddp $data;

    my $result = $app->model->save_report($data);
    if ( my $err = $result->{error} ) { # ddp $result;
        my %args = ( target => uri_for('/report'), error => $err );
        return template report => \%args;
    } # ddp $params->{request_id};
    else {
        flash info => $result->{message};
    }
    my $lab_number = $app->model->get_lab_number($params->{request_id}); # ddp $lab_number;
    redirect '/load?lab_number='.$lab_number;
};

get '/register' => require_role qr/admin/ => sub { # ddp logged_in_user; ddp user_roles;
    # return 'access_denied' unless user_has_role('admin'); # permission_denied_page_handler
    return template register => { target => uri_for('/register') };
};

post '/register' => require_role qr/admin/ => sub {
    my $params = params; # ddp $params;
    my $validation = $app->model->validator->validate( register => $params );

    if ( $validation->{failed} ) { # ddp $validation;
        my %args = (
            target => uri_for('/register'),
            dfv_errs => $validation->{dfv_errs}, # href containing dfv_errs key
        );
        return template register => \%args;
    }
    my $data = $validation->{valid}; # ddp $data;
    { # register validated user:
        my $result = $app->model->register_user($data);
        if ( my $err = $result->{error} ) { # ddp $result;
            my %args = ( target => uri_for('/register'), error => $err );
            return template register => \%args;
        }
    }
    { # OK, registration success, email message to user:
        my $result = $app->model->email_registration($data);
        return template registration => $result; # hand $result straight to tt
    }
};

get '/report_summary' => sub {
    my $user_id = session('user_profile')->{id}; # ddp $user_id;
    my $data = $app->model->user_reports_list($user_id);

    { # set headers & return PDF form for user to print & send:
        my $content = template 'report_summary', { data => $data },
            { layout => undef }; # override default 'main' layout
        return $content if dancer_app->environment eq 'development';

        my $pdf = $app->model->render_pdf($content);
        header 'Content-Type' => 'application/pdf';
        return $pdf;
    }
};

# ajax calls (also available as separate app in Routes::AJAX if loaded by app.psgi)
prefix '/ajax' => sub {
    get '/get_diagnoses' => sub {
        my $diagnoses = $app->model->get_diagnoses( param('diagnosis') )
        || return; # p $diagnoses; # doesn't do query unless str >= 3 chars

        my @results = map {
            {
                id    => $_->{id},
                value => $_->{diagnosis},
                info  => $_->{icdo3} || '[non-ICDO3]',
            }
        } @$diagnoses; # ddp \@results;

        my $ref = { results => \@results }; # p $ref;
        encode_json($ref);
    };
};

# logout route:
get '/logout' => sub {
    app->destroy_session;
    redirect('/'); # should redirect to /login
};

get '/gitlog' => sub { template gitlog => { log => $app->gitlog } };

# default Dancer2 app index page:
get '/index' => sub { template index => {}, { layout => 'index' } };

#===============================================================================
# http://blog.geekuni.com/2015/06/three-ways-to-implement-sessions.html
=begin
get '/count' => sub {
  if ( my $user = session('user') ) {
    session count => session('count') + 1;
    my $ordinal = $app->model->num2ordinal(session('count')); ddp $ordinal;
    return
        '<a href="/count">Click here</a> for the name of the eldest son of ' .
        '<b>Count von '. ucfirst $user.' the '. ucfirst $ordinal .'</b>';
  }
  return 'Cookie Monster!';
};

get '/do_login/:user' => sub {
    session user   => params->{user};
    session count  => 0;
    redirect '/count';
};
=cut
#===============================================================================

true;