package Rapi::Fs;

use strict;
use warnings;

# ABSTRACT: Plack-compatible, instant ExtJS file browser

use RapidApp 1.0204;

use Moose;
extends 'RapidApp::Builder';

use Types::Standard qw(:all);

use RapidApp::Util ':all';
use File::ShareDir qw(dist_dir);
use FindBin;
use Module::Runtime;

our $VERSION = '1.102';

has '+base_appname', default => sub { 'Rapi::Fs::App' };

has 'mounts', is => 'ro', isa => ArrayRef, required => 1;
has 'filetree_class', is => 'ro', isa => Str, default => sub {'Rapi::Fs::Module::FileTree'};
has 'filetree_params', is => 'ro', isa => HashRef, default => sub {{}};

has 'share_dir', is => 'ro', isa => Str, lazy => 1, default => sub {
  my $self = shift;
  $ENV{RAPIFS_SHARE_DIR} || (
    try{dist_dir('Rapi-Fs')} || (
      -d "$FindBin::Bin/share" ? "$FindBin::Bin/share" : "$FindBin::Bin/../share" 
    )
  )
};

sub _build_version { $VERSION }
sub _build_plugins { ['RapidApp::TabGui'] }

sub _build_config {
  my $self = shift;
  
  Module::Runtime::require_module( $self->filetree_class );
  
  my $tpl_dir = join('/',$self->share_dir,'templates');
  -d $tpl_dir or die "template dir ($tpl_dir) not found; Rapi-Fs dist may not be installed properly.\n";
  
  my $loc_assets_dir = join('/',$self->share_dir,'assets');
  -d $loc_assets_dir or die "assets dir ($loc_assets_dir) not found; Rapi-Fs dist may not be installed properly.\n";
  
  exists $self->filetree_params->{mounts} and die join('',
    "'mounts' param is configured in its own attr",
    "-- don't also suppy in 'filetree_params'"
  );
  
  return {
    'RapidApp' => {
      load_modules => {
        files => {
          class  => $self->filetree_class,
          params => { 
            %{ $self->filetree_params },
            mounts => $self->mounts 
          }
        }
      },
      local_assets_dir => $loc_assets_dir,
      default_favicon_url => '/assets/local/misc/static/folder_explore.ico'
    },
    'Plugin::RapidApp::TabGui' => {
      title              => (ref $self),
      right_footer       => join('','Generated by ',(ref $self),' v',$VERSION),      
      nav_title_iconcls  => 'icon-folder-explore',
      navtrees => [{
        module => '/files',
      }]  
    },
    'Controller::RapidApp::Template' => {
      include_paths => [ $tpl_dir ]
    },
  }
}

1;

__END__

=head1 NAME

Rapi::Fs - Plack-compatible, instant ExtJS file browser

=head1 SYNOPSIS

 use Rapi::Fs;
 
 my $app = Rapi::Fs->new({
   mounts  => [ '/some/path', '/foo/blah' ]
 });

 # Plack/PSGI app:
 $app->to_app

Or, using L<rapi-fs.pl> utility script:

 rapi-fs.pl /some/path /foo/blah

=head1 DESCRIPTION

This is a L<Plack>-compatible file browser application written using L<RapidApp>. It generates a 
nice-looking dynamic web interface to browse and view arbitrary files and directories for the 
configured "mounts" using a standard, split-panel tree layout:

=begin HTML

  <p><img 
     src="https://raw.githubusercontent.com/vanstyn/Rapi-Fs/master/share/screenshot.png" 
     width="600"
     alt="Rapi::Fs screenshot"
  /></p>

=end HTML

For a live demo, see: L<rapi.io/fs|http://rapi.io/fs>

Internally, the RapidApp module class L<Rapi::Fs::Module::FileTree> does the heavy lifting, and that
module can be configured and used directly within an existing RapidApp.

The convenience utility script L<rapi-fs.pl> is also available, which is a simple wrapper around this
module, to be able to fire up a fully working and self-contained app on-the-fly (including launching a
built-in webserver) from the command-line.

=head1 CONFIGURATION

C<Rapi::Fs> extends L<RapidApp::Builder> and supports all of its options, as well as the following
params specific to this module:

=head2 mounts

List of directory "mount" points to use/show in the application. This is the only required parameter
and supports a flexible syntax. Mounts can be supplied as simple directory string paths, HashRef configs,
or "driver" objects (consuming the L<Rapi::Fs::Role::Driver> role). The default driver class which 
ships with the C<Rapi::Fs> package is L<Rapi::Fs::Driver::Filesystem>.

The following mount specifications are all equivalent:

 $app = Rapi::Fs->new({
   mounts  => [ '/some/path' ]
 });

 $app = Rapi::Fs->new({
   mounts  => [{
     driver => 'Filesystem',
     name   => 'path',
     args   => '/some/path'
   }]
 });

 $app = Rapi::Fs->new({
   mounts  => [ 
     Rapi::Fs::Driver::Filesystem->new({
       name   => 'path',
       args   => '/some/path'
     })
   ]
 });

When using the HashRef syntax, the C<'driver'> param is a class name relative to the 
C<Rapi::Fs::Driver::*> namespace. To supply a full class name, prefix with C<'+'>, for instance:

 $app = Rapi::Fs->new({
   mounts  => [{
     driver => '+My::Fs::Driver',
     name   => 'Foobar',
     args   => 'something understood by My::Fs::Driver'
   }]
 });

Different forms can also be mix/matched:

 $app = Rapi::Fs->new({
   mounts  => [
     {
       driver => '+My::Fs::Driver',
       name   => 'Foobar',
       args   => 'something understood by My::Fs::Driver'
     },
     '/path/to/something',
     {
       name  => 'perl5-lib',
       args  => '/usr/lib/perl5'
     },
     Some::Other::Driver->new({
       name   => 'larry',
       args   => 'bla',
       foo    => 'bar'
     })
   ]
 });

L<Rapi::Fs::Driver::Filesystem> is the only driver which has been implemented so far, but this module
was written with the idea of implementing other drivers in mind, both as possible additional 
core-modules as well as user-defined drivers. See L<Rapi::Fs::Role::Driver> for more info on the 
driver API.

=head2 filetree_class

Defaults to L<Rapi::Fs::Module::FileTree>.

=head2 filetree_params

Optional extra params to supply to the C<filetree_class> constructor.

=head1 METHODS

=head2 to_app

PSGI C<$app> CodeRef. Derives from L<Plack::Component>

=head1 TODO

=over

=item * 

Add write support (move/rename/copy/delete/edit)

=item * 

Add "Mount" as a 4th Node type, to allow nesting other kinds of drivers within
a structure.

=back


=head2 Planned additional drivers

=over

=item * 

SSH/SFTP

=item * 

IMAP

=item * 

JSON/YAML files (i.e. browse data structure)

=item * 

Zip/archive

=item * 

Multipart/MIME

=back

=head1 SEE ALSO

=over

=item * 

L<rapi-fs.pl>

=item * 

L<RapidApp>

=item * 

L<RapidApp::Builder>

=item * 

L<Plack>

=item *

L<http://rapi.io/fs>

=back


=head1 AUTHOR

Henry Van Styn <vanstyn@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by IntelliTree Solutions llc.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut



