.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Gantry::Plugins::AjaxCRUD 3" .TH Gantry::Plugins::AjaxCRUD 3 "2008-01-18" "perl v5.8.8" "User Contributed Perl Documentation" .SH "NAME" Gantry::Plugins::AjaxCRUD \- helper for AJAX based CRUD work .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Gantry::Plugins::AjaxCRUD; .Ve .PP .Vb 12 \& my $user_crud = Gantry::Plugins::AjaxCRUD->new( \& add_action => \e&user_insert, \& edit_action => \e&user_update, \& delete_action => \e&user_delete, \& form => \e&user_form, \& setup_action => \e&user_setup, \& cancel_action => \e&user_cancel, \& success_action => \e&user_success, \& text_descr => 'database row description', \& use_clean_dates => 1, \& turn_off_clean_params => 1, \& ); .Ve .PP .Vb 4 \& sub do_add { \& my ( $self ) = @_; \& $user_crud->add( $self, { data => \e@_ } ); \& } .Ve .PP .Vb 3 \& sub user_insert { \& my ( $self, $form_params, $data ) = @_; \& # $data is the value of data from do_add .Ve .PP .Vb 3 \& my $row = My::Model->create( $params ); \& $row->dbi_commit(); \& } .Ve .PP .Vb 1 \& # Similarly for do_delete .Ve .PP .Vb 4 \& sub do_delete { \& my ( $self, $doomed_id, $confirm ) = @_; \& $user_crud->delete( $self, $confirm, { id => $doomed_id } ); \& } .Ve .PP .Vb 2 \& sub user_delete { \& my ( $self, $data ) = @_; .Ve .PP .Vb 1 \& my $doomed = My::Model->retrieve( $data->{id} ); .Ve .PP .Vb 3 \& $doomed->delete; \& My::Model->dbi_commit; \& } .Ve .PP .Vb 2 \& sub user_success { \& my $self = shift; .Ve .PP .Vb 2 \& $self->do_main( @_ ); \& } .Ve .PP .Vb 2 \& sub user_cancel { \& my $self = shift; .Ve .PP .Vb 2 \& $self->do_main( @_ ); \& } .Ve .PP .Vb 2 \& sub user_setup { \& my ( $self, $data, $action, $text_descr ) = @_; .Ve .PP .Vb 2 \& $self->template_wrapper('nowrapper.tt'); \& $self->stash->view->template('form.tt'); .Ve .PP .Vb 6 \& $self->stash->view->title('Add' . $text_descr) \& if ($action eq 'add'); \& $self->stash->view->title('Edit' . $text_descr) \& if ($action eq 'edit'); \& $self->stash->view->title('Delete' . $text_descr) \& if ($action eq 'delete'); .Ve .PP .Vb 1 \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module is very similar to \f(CW\*(C`Gantry::Plugins::CRUD\*(C'\fR, but it is aimed at \s-1AJAX\s0 based systems. Therefore, it resists all urges to refresh the page. This leads to three extra callbacks as shown in the summary above and discussed below. .PP For those who don't know, \s-1CRUD\s0 is short for CReate, Update, and Delete. (Some people include retrieve in this list, but users of Perl ORMs can use those for retrievals.) While \s-1AJAX\s0 stands for Asynchronous JavaScript and \s-1XML\s0. With varying emphasis on the \s-1XML\s0 part. .PP What this all means, is that your application is now being driven from the browser and not from the server. So a differant style of \s-1CRUD\s0 needs to be used. .PP Notice: most plugins export methods into your package, this one does \s-1NOT\s0. .PP This module differs from \f(CW\*(C`Gantry::Plugins::AutoCRUD\*(C'\fR in the same ways that \f(CW\*(C`Gantry::Plugins::CRUD\*(C'\fR does. It differs from \f(CW\*(C`Gantry::Plugins::CRUD\*(C'\fR in how it responds to requests. This module exists to support \s-1AJAX\s0 forms. As such, it does not do anything which might cause a page refresh by the browser. .PP This module still does basically the same things that \s-1CRUD\s0 does: .PP .Vb 7 \& redispatch to listing page if user presses cancel \& if form parameters are valid: \& callback to action method \& else: \& if method is POST: \& add form validation errors \& (re)display form .Ve .PP And as such is an almost drop in replace for \s-1CRUD\s0. .SH "METHODS" .IX Header "METHODS" This is an object oriented only module (it doesn't export like the other plugins). It has many of the same methods as \f(CW\*(C`Gantry::Plugins::CRUD\*(C'\fR plus three extras. .IP "new" 4 .IX Item "new" Constructs a new AjaxCRUD helper. Pass in a list of the following callbacks and config parameters (similar, but not the same as in \s-1CRUD\s0): .RS 4 .IP "add_action (a code ref)" 4 .IX Item "add_action (a code ref)" Same as in \s-1CRUD\s0. .Sp Called with: .Sp .Vb 3 \& your self object \& hash of form parameters \& the data you passed to add .Ve .Sp Called only when the form parameters are valid. You should insert into the database and not die (unless the insert fails, then feel free to die). You don't need to change your location, but you may. .IP "edit_action (a code ref)" 4 .IX Item "edit_action (a code ref)" Same as in \s-1CRUD\s0. .Sp Called with: .Sp .Vb 3 \& your self object \& hash of form parameters \& the data you passed to edit .Ve .Sp Called only when form parameters are valid. You should update and not die (unless the update fails, then feel free to die). You don't need to change your location, but you may. .IP "delete_action (a code ref)" 4 .IX Item "delete_action (a code ref)" Same as in \s-1CRUD\s0. .Sp Called with: .Sp .Vb 2 \& your self object \& the data you passed to delete .Ve .Sp Called only when the user has confirmed that a row should be deleted. You should delete the corresponding row and not die (unless the delete fails, then feel free to die). You don't need to change your location, but you may. .IP "form (a code ref)" 4 .IX Item "form (a code ref)" Same as in \s-1CRUD\s0. .Sp Called with: .Sp .Vb 2 \& your self object \& the data you passed to add or edit .Ve .Sp This needs to return just like the _form method required by \&\f(CW\*(C`Gantry::Plugins::AutoCRUD\*(C'\fR. See its docs for details. The only difference between these is that the AutoCRUD calls _form with your self object and the row being edited (during editing) whereas this method \s-1ALWAYS\s0 receives both your self object and the data you supplied. .IP "setup_action (a code ref)" 4 .IX Item "setup_action (a code ref)" Called with: .Sp .Vb 4 \& your self object \& the data you passed to add, edit or deltet \& the desired action (add, edit or delete) \& the text description .Ve .Sp This method is called immediately by \f(CW\*(C`add_action\*(C'\fR, \f(CW\*(C`edit_action\*(C'\fR, and \&\f(CW\*(C`delete_action\*(C'\fR to set the forms title and template. The default action for \s-1CRUD\s0 is to use form.tt as the template and to wrap your form with the site template. Using the site wrapper will cause a page reload. By exposing this default, you can change how this is handled. .Sp In the above example this is done by calling \f(CW$self\fR\->\fItemplate_wrapper()\fR with the template nowrapper.tt. What nowrapper.tt needs to do, depends on which \s-1AJAX\s0 toolkit is being used on the browser. But it could be just as simple as the following: .Sp .Vb 1 \& [% content %] .Ve .Sp At this point your form is now just a \s-1HTML\s0 fragment. .Sp Another example, lets say that your boss has just returned from the latest Web Developer conference and is all aglow with the possibilites of an \s-1AJAX\s0 front end. He has deemed that all forms should be rendered on the client side and \s-1JSON\s0 will be used to send the form parameters. What to do? Well \s-1CPAN\s0 to the rescue. Install the \s-1TT\s0 filter for \s-1JSON\s0, along with the \&\s-1JSON\s0.pm module. Now create a template named json.tt like this: .Sp .Vb 2 \& [% USE JSON %] \& [% view.data.json %] .Ve .Sp Change the froms template from form.tt to json.tt and add the following statement: .Sp .Vb 1 \& $self->content_type('application/json'); .Ve .Sp You are now sending your form as a \s-1JSON\s0 datastream. .IP "cancel_action (a code ref)" 4 .IX Item "cancel_action (a code ref)" Called with: .Sp .Vb 4 \& your self object \& the data you passed to add, edit or deltet \& the action (add, edit or delete) \& the user request .Ve .Sp Triggered by the user successfully submitting the form. This and \f(CW\*(C`success_action\*(C'\fR replaces the redirect callback used by \&\f(CW\*(C`Gantry::Plugins::CRUD\*(C'\fR. They should redispatch directly to a do_* method like this: .Sp .Vb 2 \& sub _my_cancel_action { \& my $self = shift; .Ve .Sp .Vb 2 \& $self->do_something( @_ ); \& } .Ve .IP "success_action (a code ref)" 4 .IX Item "success_action (a code ref)" Called with: .Sp .Vb 4 \& your self object \& the data you passed to add, edit or delete \& the action (add, edit or delete) \& the user request .Ve .Sp Just like the \f(CW\*(C`cancel_action\*(C'\fR, but triggered when the user presses the Cancel button. .IP "text_descr" 4 .IX Item "text_descr" Same as in \s-1CRUD\s0. .Sp The text string used in the page titles and in the delete confirmation message. .IP "use_clean_dates (optional, defaults to false)" 4 .IX Item "use_clean_dates (optional, defaults to false)" Same as in \s-1CRUD\s0. .Sp This is ignored unless you turn_off_clean_params, since it is redundant when clean_params is in use. .Sp Make this true if you want your dates cleaned immediately before your add and edit callbacks are invoked. .Sp Cleaning sets any false fields marked as dates in the form fields list to undef. This allows your \s-1ORM\s0 to correctly insert them as nulls instead of trying to insert them as blank strings (which is fatal, at least in PostgreSQL). .Sp For this to work your form fields must have this key: \f(CW\*(C`>. .IP "turn_off_clean_params (optional, defaults to false)" 4 .IX Item "turn_off_clean_params (optional, defaults to false)" Same as in \s-1CRUD\s0. .Sp By default, right before an \s-1SQL\s0 insert or update, the params hash from the form is passed through the clean_params routine which sets all non-boolean fields which are false to undef. This prevents \s-1SQL\s0 errors with ORMs that can correctly translate blank strings into nulls for non-string types. .Sp If you really don't want this routine, set turn_off_clean_params. If you turn it off, you can use_clean_dates, which only sets false dates to undef. .RE .RS 4 .Sp Note that in all cases the submit key is removed from the params hash by this module before any callback is made. .RE .IP "add" 4 .IX Item "add" Call this in your do_add on a \f(CW\*(C`Gantry::Plugins::AjaxCRUD\*(C'\fR instance: .Sp .Vb 4 \& sub do_special_add { \& my $self = shift; \& $crud_obj->add( $self, { data => \e@_ } ); \& } .Ve .Sp It will die unless you passed the following to the constructor: .Sp .Vb 2 \& add_action \& form .Ve .IP "edit" 4 .IX Item "edit" Call this in your do_edit on a \f(CW\*(C`Gantry::Plugins::AjaxCRUD\*(C'\fR instance: .Sp .Vb 6 \& sub do_special_edit { \& my $self = shift; \& my $id = shift; \& my $row = Data::Model->retrieve( $id ); \& $crud_obj->edit( $self, { id => $id, row => $row } ); \& } .Ve .Sp It will die unless you passed the following to the constructor: .Sp .Vb 2 \& edit_action \& form .Ve .IP "delete" 4 .IX Item "delete" Call this in your do_delete on a \f(CW\*(C`Gantry::Plugins::AjaxCRUD\*(C'\fR instance: .Sp .Vb 6 \& sub do_special_delete { \& my $self = shift; \& my $id = shift; \& my $confirm = shift; \& $crud_obj->delete( $self, $confirm, { id => $id } ); \& } .Ve .Sp The \f(CW$confirm\fR argument is yes if the delete should go ahead and anything else otherwise. This allows our standard practice of having delete urls like this: .Sp .Vb 1 \& http://somesite.example.com/item/delete/4 .Ve .Sp which leads to the confirmation form whose submit action is: .Sp .Vb 1 \& http://somesite.example.com/item/delete/4/yes .Ve .Sp which is taken as confirmation. .Sp It will die unless you passed the following to the constructor: .Sp .Vb 1 \& delete_action .Ve .PP You can pick and choose which \s-1CRUD\s0 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. .SH "HELPER FUNCTIONS" .IX Header "HELPER FUNCTIONS" .IP "select_multiple_closure" 4 .IX Item "select_multiple_closure" If you have a form field of type select_multiple, one of the form.tt keys is selected. It wants a sub ref so it can reselect items when the form fails to validate. This function will generate the proper sub ref (aka closure). .Sp Parameters: form field name hash reference of default selections (usually the ones in the database) .Sp Returns: a closure suitable for immediate use as the selected hash key value for a form field of type select_multiple. .SH "SEE ALSO" .IX Header "SEE ALSO" .Vb 1 \& Gantry::Plugins::CRUD (for the same approach with page refreshes) .Ve .PP .Vb 1 \& Gantry::Plugins::AutoCRUD (for simpler situations) .Ve .PP .Vb 1 \& Gantry and the other Gantry::Plugins .Ve .SH "AUTHOR" .IX Header "AUTHOR" Kevin Esteb .SH "COPYRIGHT and LICENSE" .IX Header "COPYRIGHT and LICENSE" Copyright (c) 2006, Kevin Esteb .PP This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available.