The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package MyApp::Controller::Books;

use strict;
use warnings;
use base 'Catalyst::Controller';
use FormElementContainer;


=head1 NAME

MyApp::Controller::Books - Catalyst Controller

=head1 DESCRIPTION

Catalyst Controller.

=head1 METHODS

=cut


=head2 index 

=cut

sub index : Private {
    my ( $self, $c ) = @_;

    $c->response->body('Matched MyApp::Controller::Books in Books.');
}


=head2 list

Fetch all book objects and pass to books/list.tt2 in stash to be displayed

=cut
 
sub list : Local {
    # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst
    # 'Context' that's used to 'glue together' the various components
    # that make up the application
    my ($self, $c) = @_;

    # Retrieve all of the book records as book model objects and store in the
    # stash where they can be accessed by the TT template
    $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
    
    # Set the TT template to use.  You will almost always want to do this
    # in your action methods (actions methods respond to user input in
    # your controllers).
    $c->stash->{template} = 'books/list.tt2';
}



=head2 url_create

Create a book with the supplied title and rating,
with manual authorization

=cut

sub url_create : Local {
    # In addition to self & context, get the title, rating & author_id args
    # from the URL.  Note that Catalyst automatically puts extra information
    # after the "/<controller_name>/<action_name/" into @_
    my ($self, $c, $title, $rating, $author_id) = @_;

    # Check the user's roles
    if ($c->check_user_roles('admin')) {
        # Call create() on the book model object. Pass the table 
        # columns/field values we want to set as hash values
        my $book = $c->model('MyAppDB::Book')->create({
                title   => $title,
                rating  => $rating
            });
        
        # Add a record to the join table for this book, mapping to 
        # appropriate author
        $book->add_to_book_authors({author_id => $author_id});
        # Note: Above is a shortcut for this:
        # $book->create_related('book_authors', {author_id => $author_id});
        
        # Assign the Book object to the stash for display in the view
        $c->stash->{book} = $book;
    
        # This is a hack to disable XSUB processing in Data::Dumper
        # (it's used in the view).  This is a work-around for a bug in
        # the interaction of some versions or Perl, Data::Dumper & DBIC.
        # You won't need this if you aren't using Data::Dumper (or if
        # you are running DBIC 0.06001 or greater), but adding it doesn't 
        # hurt anything either.
        $Data::Dumper::Useperl = 1;
    
        # Set the TT template to use
        $c->stash->{template} = 'books/create_done.tt2';
    } else {
        # Provide very simple feedback to the user
        $c->response->body('Unauthorized!');
    }
}



=head2 form_create

Display form to collect information for book to create

=cut

sub form_create : Local {
    my ($self, $c) = @_;

    # Set the TT template to use
    $c->stash->{template} = 'books/form_create.tt2';
}


=head2 form_create_do

Take information from form and add to database

=cut

sub form_create_do : Local {
    my ($self, $c) = @_;

    # Retrieve the values from the form
    my $title     = $c->request->params->{title}     || 'N/A';
    my $rating    = $c->request->params->{rating}    || 'N/A';
    my $author_id = $c->request->params->{author_id} || '1';

    # Create the book
    my $book = $c->model('MyAppDB::Book')->create({
            title   => $title,
            rating  => $rating,
        });
    # Handle relationship with author
    $book->add_to_book_authors({author_id => $author_id});

    # Store new model object in stash
    $c->stash->{book} = $book;

    # Avoid Data::Dumper issue mentioned earlier
    # You can probably omit this    
    $Data::Dumper::Useperl = 1;

    # Set the TT template to use
    $c->stash->{template} = 'books/create_done.tt2';
}



=head2 delete 

Delete a book
    
=cut

sub delete : Local {
    # $id = primary key of book to delete
    my ($self, $c, $id) = @_;

    # Search for the book and then delete it
    $c->model('MyAppDB::Book')->search({id => $id})->delete_all;

    # Use 'flash' to save information across requests util it's read
    $c->flash->{status_msg} = "Book deleted";
        
    # Redirect the user back to the list page with status msg as an arg
    $c->response->redirect($c->uri_for('/books/list'));
}



=head2 access_denied

Handle Catalyst::Plugin::Authorization::ACL access denied exceptions

=cut

sub access_denied : Private {
    my ($self, $c) = @_;

    # Set the error message
    $c->stash->{error_msg} = 'Unauthorized!';

    # Display the list
    $c->forward('list');
}


=head2 make_book_widget

Build an HTML::Widget form for book creation and updates

=cut


sub make_book_widget {
    my ($self, $c) = @_;

    # Create an HTML::Widget to build the form
    my $w = $c->widget('book_form')->method('post');

    # ***New: Use custom class to render each element in the form    
    $w->element_container_class('FormElementContainer');
    
    # Get authors
    my @authorObjs = $c->model("MyAppDB::Author")->all();
    my @authors = map {$_->id => $_->last_name }
                       sort {$a->last_name cmp $b->last_name} @authorObjs;

    # Create the form feilds
    $w->element('Textfield', 'title'  )->label('Title')->size(60);
    $w->element('Textfield', 'rating' )->label('Rating')->size(1);
    # Convert to multi-select list
    $w->element('Select',    'authors')->label('Authors')
        ->options(@authors)->multiple(1)->size(3);
    $w->element('Submit',    'submit' )->value('submit');

    # Set constraints
    $w->constraint(All     => qw/title rating authors/)
        ->message('Required. ');
    $w->constraint(Integer => qw/rating/)
        ->message('Must be an integer. ');
    $w->constraint(Range   => qw/rating/)->min(1)->max(5)
        ->message('Must be a number between 1 and 5. ');
    $w->constraint(Length  => qw/title/)->min(5)->max(50)
        ->message('Must be between 5 and 50 characters. ');

    # Set filters
    for my $column (qw/title rating authors/) {
        $w->filter( HTMLEscape => $column );
        $w->filter( TrimEdges  => $column );
    }

    # Return the widget    
    return $w;
}


=head2 hw_create

Build an HTML::Widget form for book creation and updates

=cut

sub hw_create : Local {
    my ($self, $c) = @_;

    # Create the widget and set the action for the form
    my $w = $self->make_book_widget($c);
    $w->action($c->uri_for('hw_create_do'));

    # Write form to stash variable for use in template
    $c->stash->{widget_result} = $w->result;

    # Set the template
    $c->stash->{template} = 'books/hw_form.tt2';
}


=head2 hw_create_do

Build an HTML::Widget form for book creation and updates

=cut

=head2 hw_create_do

Build an HTML::Widget form for book creation and updates

=cut

sub hw_create_do : Local {
    my ($self, $c) = @_;

    # Create the widget and set the action for the form
    my $w = $self->make_book_widget($c);
    $w->action($c->uri_for('hw_create_do'));

    # Validate the form parameters
    my $result = $w->process($c->req);

    # Write form (including validation error messages) to
    # stash variable for use in template
    $c->stash->{widget_result} = $result;

    # Were their validation errors?
    if ($result->has_errors) {
        # Warn the user at the top of the form that there were errors.
        # Note that there will also be per-field feedback on
        # validation errors because of '$w->process($c->req)' above.
        $c->stash->{error_msg} = 'Validation errors!';
    } else {
        my $book = $c->model('MyAppDB::Book')->new({});
        $book->populate_from_widget($result);

        # Add a record to the join table for this book, mapping to
        # appropriate author.  Note that $authors will be 1 author as
        # a scalar or ref to list of authors depending on how many the
        # user selected; the 'ref $authors ?...' handles both cases
        my $authors = $c->request->params->{authors};
        foreach my $author (ref $authors ? @$authors : $authors) {
            $book->add_to_book_authors({author_id => $author});
        }

        # Set a status message for the user
        $c->stash->{status_msg} = 'Book created';
        
        # Redisplay an empty form for another
        $c->stash->{widget_result} = $w->result;
    }

    # Set the template
    $c->stash->{template} = 'books/hw_form.tt2';
}


=head1 AUTHOR

root

=head1 LICENSE

This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

1;