App-Manoc/lib/App/Manoc/ControllerRole/ObjectForm.pm
package App::Manoc::ControllerRole::ObjectForm;
#ABSTRACT: Role for editing objects with a form
use Moose::Role;
use Try::Tiny;
our $VERSION = '2.99.4'; ##TRIAL VERSION
use namespace::autoclean;
use MooseX::MethodAttributes::Role;
with 'App::Manoc::ControllerRole::Object';
has 'form_class' => (
is => 'rw',
isa => 'ClassName'
);
# can override form_class during object creation
has 'create_form_class' => (
is => 'rw',
isa => 'ClassName'
);
# can override form_class during object editing
has 'edit_form_class' => (
is => 'rw',
isa => 'ClassName'
);
has 'form_success_url' => (
is => 'rw',
isa => 'Str'
);
has 'object_updated_message' => (
is => 'rw',
isa => 'Str',
default => 'Updated',
);
has 'create_object_perm' => (
is => 'rw',
isa => 'Maybe[Str]',
default => 'create',
);
has 'edit_object_perm' => (
is => 'rw',
isa => 'Maybe[Str]',
default => 'edit',
);
has 'delete_object_perm' => (
is => 'rw',
isa => 'Maybe[Str]',
default => 'delete',
);
sub object_form_create : Private {
my ( $self, $c ) = @_;
my $object = $c->stash->{resultset}->new_result( {} );
$self->create_form_class and
$c->stash( form_class => $self->create_form_class );
$c->stash( object => $object, );
$c->forward('object_form');
}
sub object_form_edit : Private {
my ( $self, $c ) = @_;
my $object = $c->stash->{object};
$self->edit_object_perm and
$c->require_permission( $object, $self->edit_object_perm );
$self->edit_form_class and
$c->stash( form_class => $self->edit_form_class );
$c->forward('object_form');
}
sub object_form : Private {
my ( $self, $c ) = @_;
my $item = $c->stash->{object};
my $form = $c->stash->{form};
if ( !$form ) {
$form = $self->get_form($c);
$c->stash( form => $form );
}
my $is_api = $c->stash->{is_api};
my $is_xhr = $c->stash->{is_xhr};
$c->stash(
form => $form,
action => $c->uri_for( $c->action, $c->req->captures ),
);
my %process_params;
$process_params{item} = $c->stash->{object};
# prepare input, using body_parameters for forms or
# api_request_data for api calls
my $data = $is_api ? $c->stash->{api_request_data} : $c->req->body_parameters;
$process_params{params} = $data;
# allow partial updates
if ( $item && $c->req->method eq 'POST' && ( $is_api || $is_xhr ) ) {
my @inactive_fields;
foreach my $field ( $form->all_fields ) {
my $name = $field->name;
next if exists $data->{$name};
next if $field->type eq 'Button';
next if $field->type eq 'ButtonTag';
next if $field->type eq 'Submit';
$c->debug and
$c->log->debug( "Removing $name" . $field->type . " from active fields" );
push @inactive_fields, $name;
}
@inactive_fields and
$process_params{inactive} = \@inactive_fields;
}
if ( $c->stash->{form_defaults} ) {
$process_params{defaults} = $c->stash->{form_defaults};
$process_params{use_defaults_over_obj} = 1;
}
if ( $self->can("get_form_process_params") ) {
%process_params = $self->get_form_process_params( $c, %process_params );
}
if ( $c->stash->{form_require_post} ) {
$process_params{posted} = $c->req->method eq 'POST';
}
# the "process" call has all the saving logic,
# if it returns False, then a validation error happened
my $process_status = $form->process(%process_params);
$c->debug and $c->log->debug("Form process status = $process_status");
return unless $process_status;
$c->stash->{object_id} = $form->item_id;
$c->stash->{message} = $self->object_updated_message;
}
sub object_form_ajax_response : Private {
my ( $self, $c ) = @_;
my $form = $c->stash->{form};
my $process_status = $form->is_valid;
my $json_data = {};
$json_data->{form_ok} = $process_status ? 1 : 0;
$json_data->{status} = $process_status ? 'success' : 'error';
if ($process_status) {
$c->stash->{object_id} = $form->item_id;
$json_data->{message} = $self->object_updated_message;
$json_data->{redirect} = $self->get_form_success_url($c)->as_string;
$json_data->{object_id} = $form->item_id;
$form->item->can('label') and
$json_data->{object_label} = $form->item->label;
}
elsif ( $c->stash->{object_form_ajax_add_html} ) {
my $template_name = $c->stash->{ajax_form_template};
$template_name ||= $c->namespace . "/form.tt";
# render as a fragment{
$c->stash->{no_wrapper} = 1;
my $html = $c->forward( "View::TT", "render", [ $template_name, $c->stash ] );
$json_data->{html} = $html;
}
$c->stash->{json_data} = $json_data;
$c->stash->{current_view} = 'JSON';
}
sub get_form {
my ( $self, $c ) = @_;
my $class = $c->stash->{form_class} || $self->form_class;
$class or die "Form class not set (use form_class)";
my $parameters = $c->stash->{form_parameters} || {};
return $class->new( ctx => $c, %$parameters );
}
sub get_form_success_url {
my ( $self, $c ) = @_;
my $form_success_url = $c->stash->{form_success_url} ||
$self->form_success_url ||
$c->uri_for_action( $c->namespace . "/view", [ $c->stash->{object_id} ] );
return $form_success_url;
}
sub object_form_delete : Private {
my ( $self, $c ) = @_;
my $object = $c->stash->{object};
$self->delete_object_perm and
$c->require_permission( $object, $self->delete_object_perm );
if ( $c->req->method eq 'POST' ) {
my $success = $self->delete_object($c) || 0;
$c->stash(
{
form_delete_posted => 1,
form_delete_success => $success
}
);
}
else {
$c->stash( form_delete_posted => 0 );
}
}
sub object_form_delete_ajax_response : Private {
my ( $self, $c ) = @_;
my $process_status = $c->stask->{form_delete_posted} && $c->stash->{form_delete_success};
my $json_data = {};
$json_data->{form_ok} = $process_status ? 1 : 0;
$json_data->{status} = $process_status ? 'success' : 'error';
if ($process_status) {
$json_data->{message} = $self->object_deleted_message;
$json_data->{redirect} = $self->get_delete_success_url($c)->as_string;
}
else {
$json_data->{object_id} = $c->stash->{object_id};
if ( $c->stash->{object_form_ajax_add_html} ) {
my $template_name = $c->stash->{ajax_delete_form_template};
$template_name ||= "generic_delete.tt";
# render as a fragment{
$c->stash->{no_wrapper} = 1;
my $html = $c->forward( "View::TT", "render", [ $template_name, $c->stash ] );
$json_data->{html} = $html;
}
}
$c->stash->{json_data} = $json_data;
$c->stash->{current_view} = 'JSON';
}
sub delete_object {
my ( $self, $c ) = @_;
my $success = 0;
try {
$c->stash->{object}->delete;
$success = 1;
};
return $success;
}
sub get_delete_failure_url {
my ( $self, $c ) = @_;
my $action = $c->namespace . "/view";
return $c->uri_for_action( $action, [ $c->stash->{object_pk} ] );
}
sub get_delete_success_url {
my ( $self, $c ) = @_;
return $c->uri_for_action( $c->namespace . "/list" );
}
1;
# Local Variables:
# mode: cperl
# indent-tabs-mode: nil
# cperl-indent-level: 4
# cperl-indent-parens-as-block: t
# End:
__END__
=pod
=head1 NAME
App::Manoc::ControllerRole::ObjectForm - Role for editing objects with a form
=head1 VERSION
version 2.99.4
=head1 ACTIONS
=head2 object_form_create
=head2 object_form_edit
=head2 object_form
Private action which handle creation and editing of resources. Form
defaults can be injected using form_defaults in stash. Form is created
by get_form method unless already set in stash->{form}.
=head2 object_form_ajax_response
=head2 object_form_delete
Private action which handle deleting of resources.
=head2 object_form_delete_ajax_response
=head1 METHODS
=head2 get_form
Create a new form using form_class configuration parameter.
=head2 get_form_success_url
Get the URL to redirect after successful editing. Use
form_success_url or try to use the view action in the current
namespace.
=head2 delete_object
Delete the object using its C<delete> method.
=head2 get_delete_failure_url
Default is the view action in current namespace.
=head2 get_delete_success_url
Default is the list action in current namespace.
=head1 AUTHORS
=over 4
=item *
Gabriele Mambrini <gmambro@cpan.org>
=item *
Enrico Liguori
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Gabriele Mambrini.
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