RSS Git Download  Clone
Raw Blame History
package Routes::DPW;
use Dancer2 appname => 'DocsLib';

use v5.34.0;
use Path::Tiny;
use Data::Printer;

use App::Model; # D2 plugin, provides 'model' keyword
use Dancer2::Plugin::Deferred;
use Dancer2::Plugin::Auth::Tiny;

my $PREFIX = 'dpw'; 
prefix "/$PREFIX";

# TODO: make this path( config->{documents_path}, $PREFIX );
my $docs_path = path( config->{documents_path}, 'household' ); # debug $docs_path;
# set path to documents folder:
model->household->set_docs_path($docs_path);

my %cache; # for categories

hook before => sub { };

hook before_template_render => sub {
    my $tokens = shift; # need to have unique token name across all route files 
    $cache{categories} //= model->household->get_categories; # only need once
    $tokens->{dpw_categories} = $cache{categories}; # p $tokens;

    my %h = (
        home     => '/',
        new      => '/new_document',
        edit     => '/edit',
        create   => '/create',
        search   => '/search',
        update   => '/update',
        summary  => '/summary',
        document => '/id',
        download => '/download',
    );
    $tokens->{uri_for_section}->{$PREFIX}->{$_}
        = uri_for('/'. $PREFIX . $h{$_}) for keys %h; # p $tokens;
};

=begin # rules for route names & uri_for_route:
cannot use method 'any' with route names: "Route with this name ($name) already exists"
   probably when route name is registered for GET, then for POST/HEAD/etc
cannot use uri_for_route in any template loaded after forward (data is deleted)
=cut

# these routes need unique (to DocsLib app) route-names (for redirect):
get dpw_home   => '/' => needs login  => \&home; 
get dpw_entry  => '/id/:id[Int]'      => \&get_document;
# these routes need permissions:
get  '/edit/:id[Int]'   => needs admin => \&edit_document;
post '/create'          => needs login => \&create_document;
post '/update/:id[Int]' => needs login => \&update_document;
# these routes don't need permissions or names (so can use 'any')
get  '/new_document'           => \&new_document;
get  '/download/:category/:id' => \&download;
any  '/summary'                => \&summary; # GET, POST with filter
post '/search'                 => \&search;

# ==============================================================================

sub flash { deferred @_ }

sub home { template home => { title => 'DPW Filing Cabinet' } }

sub new_document {
    var next_route => 'create';
    template $PREFIX.'/record.tt', { prefix => $PREFIX };
}

sub get_document {
    my $id = route_parameters->get('id');
    my $rec = model->household->get_document($id); # p $rec; # AoH
    vars->{next_route} ||= 'edit/'.$id; # may already be set by edit_document()
    template $PREFIX.'/record.tt', { entry => $rec };
}

sub edit_document {
	my $id = route_parameters->get('id');
    var next_route => 'update/'.$id;
    forward '/'.$PREFIX.'/id/'.$id;
}

sub summary {
	my $category = body_parameters->get('category'); # warn $category; # optional
    my $rec = model->household->get_all_documents($category); # p $rec; # AoH
    template $PREFIX.'/summary.tt', { records => $rec };
}

sub search { # my $p = params;  p $p;
	my $str = body_parameters->get('search'); # =~ s{\s+}{}gr; # sometimes need space
	my $rec = model->household->find_documents($str); # p $rec; # AoH
    template $PREFIX.'/summary.tt', { records => $rec };
}

sub update_document { # just captures document_id & forwards:
	forward '/'.$PREFIX.'/create', { id => route_parameters->get('id') };
}

sub create_document {
    my $params = request->parameters->as_hashref; # p $params;
    # get upload data - new entry only, passed as param if edit:
    my $data_file = upload('filename'); # p $data_file;

    # if params.id present must be edit; if data_file submitted, must be replacement,
    # or if original record had filename but no filename submitted, get old filename
    # and store in var for later deletion:
    if ( $params->{id} ) {
        my $rec = model->household->get_document($params->{id}); # p $rec; # AoH
        if ( $data_file || ( $rec->{filename} && ! $params->{filename} ) ) {
            var file_to_delete => $rec->{filename} || undef; # may not be one
        } # my $v = vars; p $v;
    }
	my $res = model->household->save_document($params, $data_file); # p $res;
	if ( $res->{error} ) {
		var input_error => $res->{error};
		return template $PREFIX.'/record.tt', { entry => $params };
    }
    # no error:
    my $action = $params->{id} ? 'update_success' : 'input_success'; # update if id supplied
	flash ( $action => 1 ); # doesn't need message
    if ($data_file) { # upload file to docs dir if uploaded:
        # _upload_dir set in model _check_filepath() method:
        my $filepath = path( @{ $params }{ qw/_upload_dir filename/ } ); # p $filepath;
        unless ( $data_file->copy_to($filepath) ) { # true or success, otherwise sets $!
            my $err = $!;
            warning "upload $filepath failed: $err";
            send_error "upload $filepath failed: $err", 500;
        }
        debug "upload $filepath success";
    }
    # delete old file if flagged for deletion
    if ( my $old_filename = vars->{file_to_delete} ) { # p $params;
        my $filepath = path( $docs_path, $params->{category}, $old_filename ); # p $filepath;
        unless ( unlink( $filepath ) ) {
            my $err = $!;
            warning "deleting $filepath failed: $err";
            send_error "deleting $filepath failed: $err", 500;
        }
        debug "deleted old file $filepath";
    }
	redirect uri_for_route( 'dpw_entry', { id => $res->{id} } );
}

sub download {
    my $category  = route_parameters->get('category');
    my $doc_id    = route_parameters->get('id');
    my $directory = Path::Tiny->new( join '/', $docs_path, $category );
    var document_id  => $doc_id;
    var download_dir => $directory;
    forward '/download';
}

true;