package Routes::Moongate; 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 = 'moongate'; prefix "/$PREFIX"; my $docs_path = path( config->{documents_path}, $PREFIX ); # debug $docs_path; # set path to documents folder: model->moongate->set_docs_path($docs_path); hook before_template_render => sub { my $tokens = shift; # need to have unique token name across all route files 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 moongate_home => '/' => needs login => \&home; get moongate_entry => '/id/:id[Int]' => \&get_document; # these routes need permissions: get '/edit/:id[Int]' => needs login => \&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 '/summary' => \&summary; get '/download/:id[Str]' => \&download; post '/search' => \&search; # ============================================================================== sub flash { deferred @_ } sub home { template home => { title => 'Moongate' } } 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->moongate->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 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->moongate->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 $res = model->moongate->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: my $filepath = path($docs_path, $params->{filename}); 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} ) { my $filepath = path( $docs_path, $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( 'moongate_entry', { id => $res->{id} } ); } sub update_document { # just captures document_id & forwards: forward '/'.$PREFIX.'/create', { id => route_parameters->get('id') }; } sub summary { my $rec = model->moongate->get_all_documents; # p $rec; # AoH template $PREFIX.'/summary.tt', { records => $rec }; } sub search { my $str = body_parameters->get('search'); # =~ s{\s+}{}gr; sometimes need space my $rec = model->moongate->find_documents($str); # p $rec; # AoH template $PREFIX.'/summary.tt', { records => $rec }; } sub download { my $doc_id = route_parameters->get('id'); var document_id => $doc_id; my $directory = Path::Tiny->new($docs_path); var download_dir => $directory; forward '/download'; } true;