view release on metacpan or search on metacpan
- replace unchecked evals around plugin callback execution (so that errors
aren't thrown away) with a conditional to see if the arrayref is even
defined.
- moved engine_init() and post_engine_init plugin callback execution out
of init() and into handler() above the init() call so that $r or CGI
is available to any pre_init callback methods, and so that any errors
that occur within those callbacks are caught correctly with
cast_custom_error().
3.52
- add relocate_permanently() to support 301 redirects (Stas Bekman)
- patch Gantry.pm - add a simple sort to the plugin calls
- add patches for Cache plugins
- add Gantry::Utils::CRON
- add setter/getter for Gantry::Utils::Crypt errors
- explicitly call the MP20 engine import for the Auth modules
- add handle_request_test_post for testing
3.51 Tue Aug 28 11:42:48 CDT 2007
- fix bug in Gantry::Server::handle_request_test
- fix some warning bugs in form.tt
3.0 Thu Dec 1 13:49:28 CST 2005
- Corrected templates so their html is more standards compliant.
- Increased version to indicate that we are using this third generation
of our framework in production for multiple apps.
0.26 Wed Nov 16 16:30:39 CST 2005
- Changed Gantry::user_row to return nothing when it can't find a user.
0.25 Wed Nov 16 11:46:53 CST 2005
- Added Gantry::Plugins::CRUD for more control.
- Corrected relocate so you have more control of where it takes you
from successful AutoCRUD actions.
- Corrected error which had boolean select lists always showing
their default values instead of showing those only when the
database didn't have a value.
0.24 Thu Nov 3 11:21:28 CST 2005
- Fixed Gantry::Utils::CDBI module to work with mp1 and 2.
- Modified Build.PL
0.23 Mon Oct 24 15:46:09 CDT 2005
lib/Gantry.pm view on Meta::CPAN
The init is called at the begining of each request and sets values such as,
app_rootp, img_rootp, and other application set vars.
=item declined
$self->declined( 1 );
Set and unset the declined flag
=item relocate
$self->relocate( location );
This method can be called from any controller will relocated
the user to the given location.
This method has been moved to Gantry::State::Default.
=item relocate_permanently
$self->relocate_permanently( location );
This method can be called from any controller will relocated the user
to the given location using HTTP_MOVED_PERMANENTLY 301.
This method has been moved to Gantry::State::Default.
=item redirect
$self->redirect( 1 );
Set and unset the redirect flag
lib/Gantry/Docs/FAQ.pod view on Meta::CPAN
is removed from the database. It receives the row object which is
about to meet its maker. This is a good place to log its demise
in some other table or die if something has gone awry.
=item delete_post_action
Called immediately after the row has been removed from the database
(commit has already been called). It receives the id number of the
deceased. This might be a good place to log the event.
=item get_relocation
Called with the current action (add, edit, or delete) and either submit
or cancel (depending on which button the user pressed). Return the
url where you want the user to be redirected.
=item get_submit_loc
Ignored if get_relocation is defined.
Called with the current action (add, edit, or delete) and the string 'submit'.
Return the url where you want the user to be redirected.
=item get_cancel_loc
Ignored if get_relocation is defined.
Called with the current action (add, edit, or delete) and the string 'cancel'.
Return the url where you want the user to be redirected.
=back
You may implement as many of these methods as you like in your controller.
Any that are not implemented are simply not called.
=head2 What if AutoCRUD really won't work for me?
lib/Gantry/Exception.pm view on Meta::CPAN
Accessor method for status attribute.
= item status_line()
Accessor method for status_line attribute.
=item Gantry::Exception::Redirect
You can use this to force a HTTP "Found" (302) to the browser. As an alternative
you can use the relocate() method for existing code.
=over 4
Example:
Gantry::Exception::Redirect->throw('/login');
$self->relocate('/login');
=back
=item Gantry::Exception::RedirectPermanently
You can use this to force a HTTP "Moved Permanently" (301) to the browser. As
an alternative you can use the relocate_permanently() method for existing code.
=over 4
Example:
Gantry::Exception::RedirectPermanently->throw('/somewhere');
$self->relocate_permanently('/somewhere');
=back
=item Gantry::Exception::Declined
Primiarily used internally by Gantry. It will produce a status page when a url
is not defined within your controllers.
=back
lib/Gantry/Plugins/AjaxFORM.pm view on Meta::CPAN
type => 'text',
is => 'varchar'}]
};
}
=head1 DESCRIPTION
This module is used for basic form processing. Instead of writing the
same form processing code over and over again. You can use this module
instead. This module is sensitive to server side relocations so it will
work with AJAX based systems.
Notice: most plugins export methods into your package, this one does NOT.
This module does the following basic form handling:
redispatch to listing page if user presses cancel
if form parameters are valid:
callback to action method
else:
lib/Gantry/Plugins/AuthCookie.pm view on Meta::CPAN
# Change the first & to a ? and add query string to goto.
$qstring =~ s/^&/?/o;
$goto .= $qstring;
}
# Encrypt goto
$goto = $gobj->url_encode( $crypt->encrypt( $goto ) );
$loc =~ s!^/$!!; # fix for root page login redirection
$gobj->relocate( $loc . "/login?url=${goto}" );
}
}
}
#-----------------------------------------------------------
# validate_user
#-----------------------------------------------------------
sub validate_user {
my $gobj = shift;
lib/Gantry/Plugins/AuthCookie.pm view on Meta::CPAN
my $cookie_name = 'auth_cookie';
my $domain;
eval { $cookie_name = $self->auth_cookie_name(); };
eval { $domain = $self->auth_cookie_domain(); };
if ( defined $param{logout} ) {
$self->auth_execute_logout();
my $relocation;
eval {
$relocation = $self->auth_logout_url;
};
if ( $@ ) {
$relocation = auth_logout_url( $self );
}
$self->relocate( $relocation );
return();
}
$page ||= $param{page};
$self->stash->view->template( 'login.tt' );
$self->stash->view->title( 'Login' );
my @errors;
if ( ! ( @errors = checkvals( $self ) ) ) {
lib/Gantry/Plugins/AuthCookie.pm view on Meta::CPAN
user => $param{username},
password => $param{password}
} );
# check for url param then redirect
if ( $param{url} ) {
my $crypt = Gantry::Utils::Crypt->new(
{ 'secret' => $self->auth_secret() }
);
$self->relocate( $self->location . $crypt->decrypt( $param{url} ) );
}
# check for ":" separated paths then redirect
elsif ( $page ) {
$page =~ s/\:/\//g;
$self->relocate( $page );
}
# else send them to the application root
else {
$self->relocate( $self->auth_login_url );
}
return();
}
my $retval = {};
my $url = $param{url} || '';
$retval->{page} = $page;
$retval->{url} = $url;
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
my ( $self, $pid ) = @_;
$self->{__PID__} = $pid;
$self->stash->view->template( $self->form_name( 'add' ) );
$self->stash->view->title( 'Add ' . $self->text_descr( 'add' ) );
my $params = $self->get_param_hash();
# Redirect if user pressed 'Cancel'
if ( $params->{cancel} ) {
return $self->relocate( find_cancel_loc( $self, 'add' ) );
}
# get and hold the form description
my $form;
eval {
$form = $self->form();
};
my $full_error = $@;
unless ( $form ) {
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
# move along, we're all done here
my $redirect;
if ( $submit_add_another ) {
$redirect = find_submit_loc( $self, 'add_another' );
}
else {
$redirect = find_submit_loc( $self, 'add' )
}
return $self->relocate( $redirect );
}
} # END: do_add
#-------------------------------------------------
# $self->do_edit( $id )
#-------------------------------------------------
sub do_edit {
my ( $self, $id, $pid ) = @_;
$self->{__PID__} = $pid;
$self->stash->view->template( $self->form_name( 'edit' ) );
my %params = $self->get_param_hash();
# Redirect if 'Cancel'
if ( $params{cancel} ) {
return $self->relocate( find_cancel_loc( $self, 'edit' ) );
}
$orm_helper ||= find_orm_helper( $self );
# Load data from database
my $row = $orm_helper->retrieve( $self, $id );
$self->stash->view->title( 'Edit ' . $self->text_descr( 'edit', $row ) );
my $show_form = 0;
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
# all done, move along
my $redirect;
if ( $submit_add_another ) {
$redirect = find_submit_loc( $self, 'add_another' )
}
else {
$redirect = find_submit_loc( $self, 'edit' );
}
return $self->relocate( $redirect );
}
} # END: do_edit
#-------------------------------------------------
# $self->do_delete( $id, $yes )
#-------------------------------------------------
sub do_delete {
my ( $self, $id, $pid, $yes ) = @_;
# look for a parent id and set the proper variables
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
}
else {
$self->{__PID__} = $pid;
}
$self->stash->view->template( 'delete.tt' );
$self->stash->view->title( 'Delete' );
# go back if user cancelled
if ( $self->params->{cancel} ) {
return $self->relocate( find_cancel_loc( $self, 'delete' ) );
}
$orm_helper ||= find_orm_helper( $self );
# Get the doomed row
my $row = $orm_helper->retrieve( $self, $id );
# do row auth check to see if they are allowed to add
my $permissions = $self->controller_config->{ permissions };
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
# dum dum da dum...
$orm_helper->delete( $self, $row );
# allow subclasses to do things after the delete
if ( $self->can( 'delete_post_action' ) ) {
$self->delete_post_action( $id );
}
# Move along, it's already dead
return $self->relocate( find_submit_loc( $self, 'delete' ) );
}
else {
$self->stash->view->form->message (
'Delete ' . $self->text_descr( 'delete', $row ) . '?'
);
}
}
#-------------------------------------------------
# The following routines look for user supplied
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
require $orm_helper_file;
return $orm_helper;
}
sub find_submit_loc {
my ( $self, $action ) = @_;
my $submit_loc;
if ( $self->can( 'get_relocation' ) ) {
$submit_loc = $self->get_relocation( $action, 'submit' );
}
else {
# see if caller has submit loc sub...
if ( $self->can( 'get_submit_loc' ) ) {
$submit_loc = $self->get_submit_loc( $action, 'submit' );
}
# ...or use ours
else {
$submit_loc = get_submit_loc( $self, $action );
}
}
return $submit_loc;
}
sub find_cancel_loc {
my ( $self, $action ) = @_;
my $cancel_loc;
if ( $self->can( 'get_relocation' ) ) {
$cancel_loc = $self->get_relocation( $action, 'cancel' );
}
else {
# see if caller has cancel loc sub...
if ( $self->can( 'get_cancel_loc' ) ) {
$cancel_loc = $self->get_cancel_loc( $action, 'cancel' );
}
# ...or use ours
else {
$cancel_loc = get_cancel_loc( $self );
}
lib/Gantry/Plugins/AutoCRUD.pm view on Meta::CPAN
name of your ORM helper. For instance, if you use DBIx::Class implement
this in your controller (or in something your controller inherits from):
sub get_orm_helper {
return 'Gantry::Plugins::AutoCRUDHelper::DBIxClass';
}
If you need to implement your own helper, see L<AutoCRUDHelpers> below
and/or look at any module in Gantry::Plugins::AutoCRUDHelper::* for advice.
=item get_relocation
Optional.
Called with the name of the current action and whether the user clicked
submit or cancel like this:
$self->get_relocation( 'add', 'cancel' );
Possible actions are add, edit, or delete. Clicks are either cancel
or submit.
Returns the url where users should go if they submit or cancel a form.
If defined, this method is used for both submit and cancel actions.
This means that get_submit_loc and get_cancel_loc are ignored.
=item get_cancel_loc
Optional.
Called with the action the user is cancelling (add, edit, or delete).
Returns the url where users should go if they cancel form submission.
Ignored if get_relocation is defined, otherwise defaults to
$self->location.
=item get_submit_loc
Optional.
Called with the action the user is submitting (add, edit, or delete).
Returns the url where users should go after they successfully submit a form.
Ignored if get_relocation is defined, otherwise defaults to
$self->location.
Instead of implementing get_relocation or get_submit_loc,
you could implement one or more *_post_action method which alter the location
attribute of the self object. Then the default behavior of get_submit_loc
would guide you to that location. In this case, you could still implement
get_cancel_loc to control where bailing out takes the user.
=item get_model_name
Return the name of your data model package. If your base class knows
this name you might want to do something like this:
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
if ( $params->{cancel} ) {
my $redirect = $self->_find_redirect(
{
gantry_site => $your_self,
data => $data,
user_req => 'add',
action => 'cancel',
}
);
return $your_self->relocate( $redirect );
}
# get and hold the form description
my $form = $self->form->( $your_self, $data );
# Check form data
my $show_form = 0;
my $results;
if ( $your_self->is_post ) {
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
# Find redirect.
$redirect = $self->_find_redirect(
{
gantry_site => $your_self,
data => $data,
action => $action,
user_req => 'add'
}
);
return $your_self->relocate( $redirect );
}
} # END: add
#-------------------------------------------------
# $self->edit( $your_self, { put => 'your', data => 'here' } );
#-------------------------------------------------
sub edit {
my ( $self, $your_self, $data ) = @_;
# Use the specified default template in the config if no
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
# Redirect if 'Cancel'
if ( $params{cancel} ) {
my $redirect = $self->_find_redirect(
{
gantry_site => $your_self,
data => $data,
action => 'cancel',
user_req => 'edit',
}
);
return $your_self->relocate( $redirect );
}
# get and hold the form description
my $form = $self->form->( $your_self, $data );
croak 'Your form callback gave me nothing' unless defined $form and $form;
my $show_form = 0;
my $results;
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
# Find redirect.
$redirect = $self->_find_redirect(
{
gantry_site => $your_self,
data => $data,
action => $action,
user_req => 'edit'
}
);
return $your_self->relocate( $redirect );
}
} # END: edit
#-------------------------------------------------
# $self->delete( $your_self, $confirm, { other => 'data' } )
#-------------------------------------------------
sub delete {
my ( $self, $your_self, $yes, $data ) = @_;
$your_self->stash->view->template( 'delete.tt' )
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
if ( $your_self->params->{cancel} ) {
my $redirect = $self->_find_redirect(
{
gantry_site => $your_self,
data => $data,
action => 'cancel',
user_req => 'delete'
}
);
return $your_self->relocate( $redirect );
}
if ( ( defined $yes ) and ( $yes eq 'yes' ) ) {
$self->delete_action->( $your_self, $data );
# Move along, it's already dead
my $redirect = $self->_find_redirect(
{
gantry_site => $your_self,
data => $data,
action => 'submit',
user_req => 'delete'
}
);
return $your_self->relocate( $redirect );
}
else {
$your_self->stash->view->form->message (
'Delete ' . $self->text_descr() . '?'
);
}
}
#-----------------------------------------------------------
# Helpers
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
my $self = shift;
$crud_obj->add( $self, { data => \@_ } );
}
It will die unless you passed the following to the constructor:
add_action
form
You may also pass C<redirect> which must return a location suitable for passing
to $your_self->relocate.
=item edit
Call this in your do_edit on a C<Gantry::Plugins::CRUD> instance:
sub do_special_edit {
my $self = shift;
my $id = shift;
my $row = Data::Model->retrieve( $id );
$crud_obj->edit( $self, { id => $id, row => $row } );
}
It will die unless you passed the following to the constructor:
edit_action
form
You may also pass C<redirect> which must return a location suitable for passing
to $your_self->relocate.
=item delete
Call this in your do_delete on a C<Gantry::Plugins::CRUD> instance:
sub do_special_delete {
my $self = shift;
my $id = shift;
my $confirm = shift;
$crud_obj->delete( $self, $confirm, { id => $id } );
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
http://somesite.example.com/item/delete/4/yes
which is taken as confirmation.
It will die unless you passed the following to the constructor:
delete_action
You may also pass C<redirect> which must return a location suitable for passing
to $your_self->relocate.
=back
You can pick and choose which CRUD help you want from this module. It is
designed to give you maximum flexibility, while doing the most repetative
things in a reasonable way. It is perfectly good use of this module to
have only one method which calls edit. On the other hand, you might have
two methods that call edit on two different instances, two methods
that call add on those same instances and a method that calls delete on
one of the instances. Mix and match.
lib/Gantry/Plugins/CRUD.pm view on Meta::CPAN
Gantry::Plugins::AutoCRUD (for simpler situations)
Gantry and the other Gantry::Plugins
=head1 LIMITATIONS
Currently only one redirection can be defined. You can get more control
by ending your action callback like this:
return $self->relocate( 'http://location.of/your/choice' );
=head1 AUTHOR
Phil Crow <philcrow2000@yahoo.com>
=head1 COPYRIGHT and LICENSE
Copyright (c) 2005, Phil Crow
This library is free software; you can redistribute it and/or modify
lib/Gantry/State/Default.pm view on Meta::CPAN
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
############################################################
# Variables #
############################################################
@ISA = qw( Exporter );
@EXPORT = qw(
state_run
state_engine
relocate
relocate_permanently
);
@EXPORT_OK = qw( );
############################################################
# Functions #
############################################################
#-------------------------------------------------
# $self->state_run( r_or_cgi, plugin_callbacks )
lib/Gantry/State/Default.pm view on Meta::CPAN
return( $self->cast_custom_error( $self->custom_error( $e ), $e ) );
}
my $status = $self->status() ? $self->status() : $self->success_code;
return $status;
}
#-------------------------------------------------
# $self->relocate( $location )
#-------------------------------------------------
sub relocate {
my ( $self, $location ) = ( shift, shift );
$location = $self->location if ( ! defined $location );
$self->redirect( 1 ); # Tag it for the handler to handle nice.
$self->header_out( 'location', $location );
$self->status( $self->status_const( 'REDIRECT' ) );
return( $self->status_const( 'REDIRECT' ) );
} # end relocate
#-------------------------------------------------
# $self->relocate_permanently( $location )
#-------------------------------------------------
sub relocate_permanently {
my ( $self, $location ) = ( shift, shift );
$location = $self->location if ( ! defined $location );
$self->header_out( 'location', $location );
$self->status( $self->status_const( 'MOVED_PERMANENTLY' ) );
return( $self->status_const( 'MOVED_PERMANENTLY' ) );
} # end relocate_permanently
#-------------------------------------------------
# $self->state_engine
#-------------------------------------------------
sub state_engine {
return __PACKAGE__;
} # end state_engine
1;
lib/Gantry/State/Default.pm view on Meta::CPAN
so this document will not get into the semantics of the term.
What this module does, is take Gantry's default handling of a request,
and places it into a loadable module. With these "states" now in a
loadable module, you can change the execution order to suit your
applications needs.
Why is this desirable?
Letâs say you have an application the loads some plugins, those plugins
must do a relocation for proper initialization and this relocation must
be done before the controllers can execute properly. Or you want "hook"
processing, using per and post "hook" plugins. With the default handler,
this is not possible, with a loadable "state machine" this is now
quite easy to do.
This module also moves the methods relocate() and relocate_permanently()
from Gantry.pm into this module.
=head1 CONFIGURATION
To load a differant state machine you need to do the following:
=over 4
use MyApp qw{ âStateMachine=Machine };
lib/Gantry/State/Default.pm view on Meta::CPAN
}
=back
=item state_engine
This method returns the name of the state machine.
$name = $selfâ>state_engine;
=item relocate
This method can be called from any controller and will relocate the
user to the given location.
$selfâ>relocate( location );
=item relocate_permanently
This method can be called from any controller and will relocate the
user to the given location using HTTP_MOVED_PERMANENTLY 301.
$selfâ>relocate_permanently( location );
=back
=head1 SEE ALSO
Gantry
=head1 AUTHOR
Kevin L. Esteb <kesteb@wsipc.org>
lib/Gantry/State/Exceptions.pm view on Meta::CPAN
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
############################################################
# Variables #
############################################################
@ISA = qw( Exporter );
@EXPORT = qw(
state_run
state_engine
relocate
relocate_permanently
);
@EXPORT_OK = qw( );
my ( @p, $p1 );
############################################################
# Functions #
############################################################
lib/Gantry/State/Exceptions.pm view on Meta::CPAN
}
}
return $status;
}
#-------------------------------------------------
# $self->relocate( $location )
#-------------------------------------------------
sub relocate {
my ( $self, $location ) = ( shift, shift );
$location = $self->location if ( ! defined $location );
Gantry::Exception::Redirect->throw($location);
} # end relocate
#-------------------------------------------------
# $self->relocate_permanently( $location )
#-------------------------------------------------
sub relocate_permanently {
my ( $self, $location ) = ( shift, shift );
$location = $self->location if ( ! defined $location );
Gantry::Exception::RedirectPermanently->throw($location);
} # end relocate_permanently
#-------------------------------------------------
# $self->state_engine
#-------------------------------------------------
sub state_engine {
return __PACKAGE__;
} # end state_engine
#-------------------------------------------------
lib/Gantry/State/Exceptions.pm view on Meta::CPAN
The currently implemented state machines use flag variables. At specific
steps, these variables are checked, the flow is then altered based on the
results of those tests. Gantry currently handles the following status
codes: 200, 301, 302, 400 and 500. There are flag variables for these codes and
handlers to be executed, when they are set. But what if you wanted to use a
402 code. Well, you would need to create a flag variable, code a state
machine to check for that variable and create a handler for that condition.
Not a very scalable solution.
This module introduces the concept of using structured exceptions to
change the flow of execution. If a redirect is issued using the relocate()
method, an exception is raised instead of a flag variable being set. The
exception takes effect immediately and is caught by the state machines handler.
Which then goes thru the redirect process.
This is not too differant from how Gantry::State::Simple currently works.
The advantage is that if I wanted to use that 402 code, all I would have to
do is the following:
Gantry::Exception->throw(
status => 402,
lib/Gantry/State/Simple.pm view on Meta::CPAN
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
############################################################
# Variables #
############################################################
@ISA = qw( Exporter );
@EXPORT = qw(
state_run
state_engine
relocate
relocate_permanently
);
@EXPORT_OK = qw( );
my ( @p, $p1 );
############################################################
# Functions #
############################################################
lib/Gantry/State/Simple.pm view on Meta::CPAN
my $e = $@;
$self->do_error($e);
return($self->cast_custom_error($self->custom_error($e), $e));
}
return $status;
}
#-------------------------------------------------
# $self->relocate( $location )
#-------------------------------------------------
sub relocate {
my ( $self, $location ) = ( shift, shift );
$location = $self->location if ( ! defined $location );
$self->redirect( 1 ); # Tag it for the handler to handle nice.
$self->header_out( 'location', $location );
$self->status( $self->status_const( 'REDIRECT' ) );
} # end relocate
#-------------------------------------------------
# $self->relocate_permanently( $location )
#-------------------------------------------------
sub relocate_permanently {
my ( $self, $location ) = ( shift, shift );
$location = $self->location if ( ! defined $location );
$self->redirect( 1 ); # Tag it for the handler to handle nice.
$self->header_out( 'location', $location );
$self->status( $self->status_const( 'MOVED_PERMANENTLY' ) );
} # end relocate_permanently
#-------------------------------------------------
# $self->state_engine
#-------------------------------------------------
sub state_engine {
return __PACKAGE__;
} # end state_engine
#-------------------------------------------------
lib/Gantry/Utils/Threeway.pm view on Meta::CPAN
);
my %selected;
while ( my $r = $selected->next ) {
$selected{$r->$secondary_table . ''} = $r->id;
}
if ( $gself->is_post() ) {
if ( $param{cancel} ) {
$gself->relocate( $redirect_loc );
return;
}
my $available = $sch->resultset( $secondary_table )->search(
{}, { order_by => $order_by }
);
my %available;
while ( my $r = $available->next ) {
++$available{$r->id . ''};
lib/Gantry/Utils/Threeway.pm view on Meta::CPAN
foreach my $k ( keys %selected ) {
push( @remove, $selected{$k} );
}
if ( scalar( @remove ) > 0 ) {
$sch->resultset( $join_table )->search(
id => \@remove
)->delete;
}
$gself->relocate(
ref( $redirect_loc ) eq 'CODE'
? $redirect_loc->( $gself ) : $redirect_loc
);
return;
}
my $available = $sch->resultset( $secondary_table )->search( {}, {
order_by => $order_by,