diff options
author | groditi <groditi@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7> | 2007-12-17 16:32:16 +0000 |
---|---|---|
committer | groditi <groditi@03d0b0b2-0e1a-0410-a411-fdb2f4bd65d7> | 2007-12-17 16:32:16 +0000 |
commit | ddccc6a29affc90888a59f14d698fd3afb2757dc (patch) | |
tree | f6dec89af81aeb84fb308c961d92d049a7147e93 /lib/Reaction/UI/ViewPort/Action.pm | |
parent | 27959b780ce142e88419e66dc8e6e7d571a41bb3 (diff) | |
download | reaction-ddccc6a29affc90888a59f14d698fd3afb2757dc.tar.gz reaction-ddccc6a29affc90888a59f14d698fd3afb2757dc.zip |
new renamed viewports
Diffstat (limited to 'lib/Reaction/UI/ViewPort/Action.pm')
-rw-r--r-- | lib/Reaction/UI/ViewPort/Action.pm | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/lib/Reaction/UI/ViewPort/Action.pm b/lib/Reaction/UI/ViewPort/Action.pm new file mode 100644 index 0000000..d02719d --- /dev/null +++ b/lib/Reaction/UI/ViewPort/Action.pm @@ -0,0 +1,277 @@ +package Reaction::UI::ViewPort::Action; + +use Reaction::Class; + +use aliased 'Reaction::UI::ViewPort::Field::Mutable::Text'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::Array'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::String'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::Number'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::Integer'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::Boolean'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::Password'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::DateTime'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::ChooseOne'; +use aliased 'Reaction::UI::ViewPort::Field::Mutable::ChooseMany'; + +#use aliased 'Reaction::UI::ViewPort::InterfaceModel::Field::Mutable::File'; +#use aliased 'Reaction::UI::ViewPort::InterfaceModel::Field::Mutable::TimeRange'; + +class ActionForm is 'Reaction::UI::ViewPort', which { + has '+model' => (isa => 'Reaction::InterfaceModel::Action'); + + has next_action => (is => 'rw', isa => 'ArrayRef'); + has on_apply_callback => (is => 'rw', isa => 'CodeRef'); + + has ok_label => (is => 'rw', isa => 'Str', lazy_build => 1); + has apply_label => (is => 'rw', isa => 'Str', lazy_build => 1); + has close_label => (is => 'rw', isa => 'Str', lazy_fail => 1); + has close_label_close => (is => 'rw', isa => 'Str', lazy_build => 1); + has close_label_cancel => (is => 'rw', isa => 'Str', lazy_build => 1); + + has changed => (is => 'rw', isa => 'Int', reader => 'is_changed', default => sub{0}); + + implements BUILD => as{ + my $self = shift; + $self->close_label($self->close_label_close); + }; + + implements _build_ok_label => as{ 'ok' }; + implements _build_apply_label_ => as{ 'apply' }; + implements _build_close_label_close => as{ 'close' }; + implements _build_close_label_cancel => as{ 'cancel' }; + + implements can_apply => as { + my ($self) = @_; + foreach my $field ( @{ $self->ordered_fields } ) { + return 0 if $field->needs_sync; + # if e.g. a datetime field has an invalid value that can't be re-assembled + # into a datetime object, the action may be in a consistent state but + # not synchronized from the fields; in this case, we must not apply + } + return $self->model->can_apply; + }; + + implements do_apply => as { + shift->model->do_apply; + }; + + implements ok => as { + my $self = shift; + $self->close(@_) if $self->apply(@_); + }; + + implements apply => as { + my $self = shift; + if ($self->can_apply && (my $result = $self->do_apply)) { + $self->changed(0); + $self->close_label($self->close_label_close); + $self->on_apply_callback->($self => $result) if $self->has_on_apply_callback; + return 1; + } else { + $self->changed(1); + $self->close_label($self->close_label_cancel); + return 0; + } + }; + + implements close => as { + my $self = shift; + my ($controller, $name, @args) = @{$self->next_action}; + $controller->pop_viewport; + $controller->$name($self->ctx, @args); + }; + + implements can_close => as { 1 }; + + override accept_events => sub { + (($_[0]->has_next_action ? ('ok', 'close') : ()), 'apply', super()); + }; # can't do a close-type operation if there's nowhere to go afterwards + + after apply_child_events => sub { + # interrupt here because fields will have been updated + my ($self) = @_; + $self->sync_action_from_fields; + }; + + implements sync_action_from_fields => as { + my ($self) = @_; + foreach my $field ($self->fields) { + $field->sync_to_action; # get the field to populate the $action if possible + } + $self->action->sync_all; + foreach my $field ($self->fields) { + $field->sync_from_action; # get errors from $action if applicable + } + }; + + + implements _build_fields_for_type_Num => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => Number, %$args); + }; + + implements _build_fields_for_type_Int => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => Integer, %$args); + }; + + implements _build_fields_for_type_Bool => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => Boolean, %$args); + }; + + implements _build_fields_for_type_SimpleStr => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => String, %$args); + }; + + #implements _build_fields_for_type_File => as { + # my ($self, $attr, $args) = @_; + # $self->_build_simple_field(attribute => $attr, class => File, %$args); + #}; + + implements _build_fields_for_type_Str => as { + my ($self, $attr, $args) = @_; + if ($attr->has_valid_values) { # There's probably a better way to do this + $self->_build_simple_field(attribute => $attr, class => ChooseOne, %$args); + } + $self->_build_simple_field(attribute => $attr, class => Text, %$args); + }; + + implements _build_fields_for_type_Password => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => Password, %$args); + }; + + implements _build_fields_for_type_DateTime => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => DateTime, %$args); + }; + + implements _build_fields_for_type_Enum => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => ChooseOne, %$args); + }; + + #this needs to be fixed. somehow. beats the shit our of me. really. + #implements build_fields_for_type_Reaction_InterfaceModel_Object => as { + implements _build_fields_for_type_DBIx_Class_Row => as { + my ($self, $attr, $args) = @_; + $self->_build_simple_field(attribute => $attr, class => ChooseOne, %$args); + }; + + implements _build_fields_for_type_ArrayRef => as { + my ($self, $attr, $args) = @_; + if ($attr->has_valid_values) { + $self->_build_simple_field(attribute => $attr, class => ChooseMany, %$args); + } else { + $self->_build_simple_field + ( + attribute => $attr, + class => Array, + layout => 'interface_model/field/mutable/array/hidden', + %$args); + } + }; + + #implements _build_fields_for_type_DateTime_Spanset => as { + # my ($self, $attr, $args) = @_; + # $self->_build_simple_field(attribute => $attr, class => TimeRange, %$args); + #}; + +}; + + 1; + +=head1 NAME + +Reaction::UI::ViewPort::InterfaceModel::Action + +=head1 SYNOPSIS + + use aliased 'Reaction::UI::ViewPort::ActionForm'; + + $self->push_viewport(Action, + layout => 'register', + model => $action, + next_action => [ $self, 'redirect_to', 'accounts', $c->req->captures ], + ctx => $c, + field_order => [ + qw / contact_title company_name email address1 address2 address3 + city country post_code telephone mobile fax/ ], + ); + +=head1 DESCRIPTION + +This subclass of viewport is used for rendering a collection of +L<Reaction::UI::ViewPort::Field> objects for user editing. + +=head1 ATTRIBUTES + +=head2 model + +L<Reaction::InterfaceModel::Action> + +=head2 ok_label + +Default: 'ok' + +=head2 apply_label + +Default: 'apply' + +=head2 close_label_close + +Default: 'close' + +=head2 close_label_cancel + +This label is only shown when C<changed> is true. + +Default: 'cancel' + +=head2 fields + +=head2 can_apply + +=head2 can_close + +=head2 changed + +Returns true if a field has been edited. + +=head2 next_action + +=head2 on_apply_callback + +CodeRef. + +=head1 METHODS + +=head2 ok + +Calls C<apply>, and then C<close> if successful. + +=head2 close + +Pop viewport and proceed to C<next_action>. + +=head2 apply + +Attempt to save changes and update C<changed> attribute if required. + +=head1 SEE ALSO + +L<Reaction::UI::ViewPort> + +L<Reaction::InterfaceModel::Action> + +=head1 AUTHORS + +See L<Reaction::Class> for authors. + +=head1 LICENSE + +See L<Reaction::Class> for the license. + +=cut |