Commit | Line | Data |
b80f58e5 |
1 | =head1 NAME |
2 | |
f279297a |
3 | |
4 | |
b80f58e5 |
5 | Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Part 9: Advanced CRUD - FormFu |
6 | |
7 | |
8 | =head1 OVERVIEW |
9 | |
10 | This is B<Part 9 of 10> for the Catalyst tutorial. |
11 | |
12 | L<Tutorial Overview|Catalyst::Manual::Tutorial> |
13 | |
14 | =over 4 |
15 | |
16 | =item 1 |
17 | |
18 | L<Introduction|Catalyst::Manual::Tutorial::Intro> |
19 | |
20 | =item 2 |
21 | |
22 | L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics> |
23 | |
24 | =item 3 |
25 | |
26 | L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics> |
27 | |
28 | =item 4 |
29 | |
30 | L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> |
31 | |
32 | =item 5 |
33 | |
34 | L<Authentication|Catalyst::Manual::Tutorial::Authentication> |
35 | |
36 | =item 6 |
37 | |
38 | L<Authorization|Catalyst::Manual::Tutorial::Authorization> |
39 | |
40 | =item 7 |
41 | |
42 | L<Debugging|Catalyst::Manual::Tutorial::Debugging> |
43 | |
44 | =item 8 |
45 | |
46 | L<Testing|Catalyst::Manual::Tutorial::Testing> |
47 | |
48 | =item 9 |
49 | |
d682d02d |
50 | B<Advanced CRUD::FormFu> |
b80f58e5 |
51 | |
52 | =item 10 |
53 | |
54 | L<Appendices|Catalyst::Manual::Tutorial::Appendices> |
55 | |
56 | =back |
57 | |
58 | |
59 | =head1 DESCRIPTION |
60 | |
cca5cd98 |
61 | This portion of the tutorial explores L<HTML::FormFu|HTML::FormFu> and |
62 | how it can be used to manage forms, perform validation of form input, |
ab0db558 |
63 | as well as save and restore data to/from the database. This was written |
cf582e91 |
64 | using HTML::FormFu version 0.03007. |
cca5cd98 |
65 | |
66 | See |
67 | L<Catalyst::Manual::Tutorial::AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD> |
68 | for additional form management options other than |
69 | L<HTML::FormFu|HTML::FormFu>. |
70 | |
71 | |
72 | =head1 Install C<HTML::FormFu> |
73 | |
acbd7bdd |
74 | If you are following along in Debian 5, it turns out that some of the |
75 | modules we need are not yet available as Debian packages at the time |
76 | this was written. To install it with a combination of Debian packages |
77 | and traditional CPAN modules, first use C<aptitude> to install most of |
78 | the modules: |
cca5cd98 |
79 | |
acbd7bdd |
80 | we need to install the |
81 | L<HTML::FormFu|HTML::FormFu> package: |
cca5cd98 |
82 | |
acbd7bdd |
83 | sudo aptitude -y install libhtml-formfu-perl libmoose-perl \ |
84 | libregexp-assemble-perl libhtml-formfu-model-dbic-perl |
85 | |
028b4e1a |
86 | ... |
87 | |
acbd7bdd |
88 | sudo aptitude clean |
89 | |
90 | Then use the following command to install directly from CPAN the modules |
91 | that aren't available as Debian packages: |
ab0db558 |
92 | |
acbd7bdd |
93 | sudo cpan Catalyst::Component::InstancePerContext Catalyst::Controller::HTML::FormFu |
cca5cd98 |
94 | |
95 | |
96 | =head1 C<HTML::FormFu> FORM CREATION |
97 | |
98 | This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to |
99 | add additional functionality to the manually created form from Part 4. |
100 | |
101 | |
102 | =head2 Inherit From C<Catalyst::Controller::HTML::FormFu> |
103 | |
104 | First, change your C<lib/MyApp/Controller/Books.pm> to inherit from |
105 | L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu> |
8a7951ae |
106 | by changing the C<use parent> line from the default of: |
cca5cd98 |
107 | |
ab0db558 |
108 | use parent 'Catalyst::Controller'; |
cca5cd98 |
109 | |
110 | to use the FormFu base controller class: |
111 | |
ab0db558 |
112 | use parent 'Catalyst::Controller::HTML::FormFu'; |
cca5cd98 |
113 | |
114 | |
115 | =head2 Add Action to Display and Save the Form |
116 | |
117 | Open C<lib/MyApp/Controller/Books.pm> in your editor and add the |
118 | following method: |
119 | |
d682d02d |
120 | =head2 formfu_create |
cca5cd98 |
121 | |
59d6a035 |
122 | Use HTML::FormFu to create a new book |
cca5cd98 |
123 | |
124 | =cut |
125 | |
acbd7bdd |
126 | sub formfu_create :Chained('base) :PathPart('formfu_create') :Args(0) :FormConfig { |
cca5cd98 |
127 | my ($self, $c) = @_; |
128 | |
129 | # Get the form that the :FormConfig attribute saved in the stash |
130 | my $form = $c->stash->{form}; |
131 | |
166c90a0 |
132 | # Check if the form has been submitted (vs. displaying the initial |
133 | # form) and if the data passed validation. "submitted_and_valid" |
cca5cd98 |
134 | # is shorthand for "$form->submitted && !$form->has_errors" |
135 | if ($form->submitted_and_valid) { |
136 | # Create a new book |
137 | my $book = $c->model('DB::Books')->new_result({}); |
138 | # Save the form data for the book |
aee27483 |
139 | $form->model->update($book); |
cca5cd98 |
140 | # Set a status message for the user |
141 | $c->flash->{status_msg} = 'Book created'; |
142 | # Return to the books list |
e075db0c |
143 | $c->response->redirect($c->uri_for($self->action_for('list'))); |
cca5cd98 |
144 | $c->detach; |
9cb64a37 |
145 | } else { |
146 | # Get the authors from the DB |
acbd7bdd |
147 | my @author_objs = $c->model("DB::Authors")->all(); |
9cb64a37 |
148 | # Create an array of arrayrefs where each arrayref is an author |
149 | my @authors; |
acbd7bdd |
150 | foreach (sort {$a->last_name cmp $b->last_name} @author_objs) { |
9cb64a37 |
151 | push(@authors, [$_->id, $_->last_name]); |
152 | } |
153 | # Get the select added by the config file |
154 | my $select = $form->get_element({type => 'Select'}); |
155 | # Add the authors to it |
156 | $select->options(\@authors); |
157 | } |
158 | |
cca5cd98 |
159 | # Set the template |
d682d02d |
160 | $c->stash->{template} = 'books/formfu_create.tt2'; |
cca5cd98 |
161 | } |
162 | |
163 | |
164 | =head2 Create a Form Config File |
165 | |
166 | Although C<HTML::FormFu> supports any configuration file handled by |
167 | L<Config::Any|Config::Any>, most people tend to use YAML. First |
168 | create a directory to hold your form configuration files: |
169 | |
170 | mkdir -p root/forms/books |
171 | |
d682d02d |
172 | Then create the file C<root/forms/books/formfu_create.yml> and enter the |
cca5cd98 |
173 | following text: |
174 | |
175 | --- |
9cb64a37 |
176 | # indicator is the field that is used to test for form submission |
cca5cd98 |
177 | indicator: submit |
9cb64a37 |
178 | # Start listing the form elements |
cca5cd98 |
179 | elements: |
9cb64a37 |
180 | # The first element will be a text field for the title |
cca5cd98 |
181 | - type: Text |
182 | name: title |
183 | label: Title |
9cb64a37 |
184 | # This is an optional 'mouse over' title pop-up |
cca5cd98 |
185 | attributes: |
186 | title: Enter a book title here |
d682d02d |
187 | |
188 | # Another text field for the numeric rating |
cca5cd98 |
189 | - type: Text |
190 | name: rating |
191 | label: Rating |
192 | attributes: |
193 | title: Enter a rating between 1 and 5 here |
d682d02d |
194 | |
195 | # Add a drop-down list for the author selection. Note that we will |
196 | # dynamically fill in all the authors from the controller but we |
197 | # could manually set items in the drop-list by adding this YAML code: |
198 | # options: |
199 | # - [ '1', 'Bastien' ] |
200 | # - [ '2', 'Nasseh' ] |
201 | - type: Select |
202 | name: authors |
203 | label: Author |
204 | |
9cb64a37 |
205 | # The submit button |
cca5cd98 |
206 | - type: Submit |
207 | name: submit |
208 | value: Submit |
209 | |
0171092f |
210 | B<NOTE:> Copying and pasting YAML from perl documentation is sometimes |
1fba2369 |
211 | tricky. See the L<Config::General Config for this tutorial> section of |
212 | this document for a more foolproof config format. |
0171092f |
213 | |
cca5cd98 |
214 | |
215 | =head2 Update the CSS |
216 | |
ab0db558 |
217 | Edit C<root/static/css/main.css> and add the following lines to the bottom of |
cca5cd98 |
218 | the file: |
219 | |
acbd7bdd |
220 | ... |
d682d02d |
221 | input { |
cca5cd98 |
222 | display: block; |
223 | } |
d682d02d |
224 | select { |
cca5cd98 |
225 | display: block; |
226 | } |
d682d02d |
227 | .submit { |
228 | padding-top: .5em; |
229 | display: block; |
cca5cd98 |
230 | } |
231 | |
d682d02d |
232 | These changes will display form elements vertically. Note that the |
233 | existing definition of the C<.error> class is pulling the color scheme |
234 | settings from the C<root/lib/config/col> file that was created by the |
235 | TTSite helper. This allows control over the CSS color settings from a |
236 | single location. |
cca5cd98 |
237 | |
238 | |
239 | =head2 Create a Template Page To Display The Form |
240 | |
d682d02d |
241 | Open C<root/src/books/formfu_create.tt2> in your editor and enter the following: |
cca5cd98 |
242 | |
243 | [% META title = 'Create/Update Book' %] |
244 | |
245 | [%# Render the HTML::FormFu Form %] |
246 | [% form %] |
247 | |
e075db0c |
248 | <p><a href="[% c.uri_for(c.controller.action_for('list')) %]">Return to book list</a></p> |
cca5cd98 |
249 | |
250 | |
251 | =head2 Add Links for Create and Update via C<HTML::FormFu> |
252 | |
253 | Open C<root/src/books/list.tt2> in your editor and add the following to |
254 | the bottom of the existing file: |
255 | |
acbd7bdd |
256 | ... |
cca5cd98 |
257 | <p> |
258 | HTML::FormFu: |
e075db0c |
259 | <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a> |
cca5cd98 |
260 | </p> |
261 | |
d682d02d |
262 | This adds a new link to the bottom of the book list page that we can |
263 | use to easily launch our HTML::FormFu-based form. |
264 | |
cca5cd98 |
265 | |
d682d02d |
266 | =head2 Test The <HTML::FormFu> Create Form |
cca5cd98 |
267 | |
268 | Press C<Ctrl-C> to kill the previous server instance (if it's still |
269 | running) and restart it: |
270 | |
271 | $ script/myapp_server.pl |
272 | |
0909d46f |
273 | Login as C<test01> (password: mypass). Once at the Book List page, |
274 | click the new HTML::FormFu "Create" link at the bottom to display the |
275 | form. Fill in the following values: Title = "Internetworking with |
276 | TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit, |
277 | and you will be returned to the Book List page with a "Book created" |
278 | status message displayed. |
d682d02d |
279 | |
280 | Also note that this implementation allows you to can create books with |
281 | bogus information. Although we have constrained the authors with the |
59d6a035 |
282 | drop-down list (note that this isn't bulletproof because we still have |
283 | not prevented a user from "hacking" the form to specify other values), |
284 | there are no restrictions on items such as the length of the title (for |
285 | example, you can create a one-letter title) and value for the rating |
286 | (you can use any number you want, and even non-numeric values with |
287 | SQLite). The next section will address this concern. |
cca5cd98 |
288 | |
289 | B<Note:> Depending on the database you are using and how you established |
290 | the columns in your tables, the database could obviously provide various |
291 | levels of "type enforcement" on your data. The key point being made in |
292 | the previous paragraph is that the I<web application> itself is not |
293 | performing any validation. |
294 | |
295 | |
296 | =head1 C<HTML::FormFu> VALIDATION AND FILTERING |
297 | |
d682d02d |
298 | Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section |
299 | did provide an automated mechanism to build the form, the real power of |
300 | this module stems from functionality that can automatically validate and |
301 | filter the user input. Validation uses constraints to be sure that |
302 | users input appropriate data (for example, that the email field of a |
303 | form contains a valid email address). Filtering can also be used to |
304 | remove extraneous whitespace from fields or to escape meta-characters in |
305 | user input. |
cca5cd98 |
306 | |
cca5cd98 |
307 | |
d682d02d |
308 | =head2 Add Constraints |
cca5cd98 |
309 | |
d682d02d |
310 | Open C<root/forms/books/formfu_create.yml> in your editor and update it |
cca5cd98 |
311 | to match: |
312 | |
313 | --- |
9cb64a37 |
314 | # indicator is the field that is used to test for form submission |
cca5cd98 |
315 | indicator: submit |
9cb64a37 |
316 | # Start listing the form elements |
cca5cd98 |
317 | elements: |
9cb64a37 |
318 | # The first element will be a text field for the title |
cca5cd98 |
319 | - type: Text |
320 | name: title |
321 | label: Title |
9cb64a37 |
322 | # This is an optional 'mouse over' title pop-up |
cca5cd98 |
323 | attributes: |
324 | title: Enter a book title here |
9cb64a37 |
325 | # Add constraints for the field |
cca5cd98 |
326 | constraints: |
59d6a035 |
327 | # Force the length to be between 5 and 40 chars |
cca5cd98 |
328 | - type: Length |
d682d02d |
329 | min: 5 |
59d6a035 |
330 | max: 40 |
9cb64a37 |
331 | # Override the default of 'Invalid input' |
59d6a035 |
332 | message: Length must be between 5 and 40 characters |
ab0db558 |
333 | |
d682d02d |
334 | # Another text field for the numeric rating |
cca5cd98 |
335 | - type: Text |
336 | name: rating |
337 | label: Rating |
338 | attributes: |
339 | title: Enter a rating between 1 and 5 here |
d682d02d |
340 | # Use Filter to clean up the input data |
ab0db558 |
341 | # Could use 'NonNumeric' below, but since Filters apply *before* |
342 | # constraints, it would conflict with the 'Integer' constraint below. |
343 | # So let's skip this and just use the constraint. |
344 | #filter: |
d682d02d |
345 | # Remove everything except digits |
ab0db558 |
346 | #- NonNumeric |
d682d02d |
347 | # Add constraints to the field |
cca5cd98 |
348 | constraints: |
9cb64a37 |
349 | # Make sure it's a number |
ab0db558 |
350 | - type: Integer |
351 | message: "Required. Digits only, please." |
352 | # Check the min & max values |
353 | - type: Range |
354 | min: 1 |
355 | max: 5 |
356 | message: "Must be between 1 and 5." |
d682d02d |
357 | |
358 | # Add a select list for the author selection. Note that we will |
359 | # dynamically fill in all the authors from the controller but we |
360 | # could manually set items in the select by adding this YAML code: |
361 | # options: |
362 | # - [ '1', 'Bastien' ] |
363 | # - [ '2', 'Nasseh' ] |
364 | - type: Select |
365 | name: authors |
366 | label: Author |
367 | # Convert the drop-down to a multi-select list |
368 | multiple: 1 |
369 | # Display 3 entries (user can scroll to see others) |
370 | size: 3 |
371 | # One could argue we don't need to do filters or constraints for |
372 | # a select list, but it's smart to do validation and sanity |
373 | # checks on this data in case a user "hacks" the input |
d682d02d |
374 | # Add constraints to the field |
375 | constraints: |
d682d02d |
376 | # Make sure it's a number |
377 | - Integer |
378 | |
9cb64a37 |
379 | # The submit button |
cca5cd98 |
380 | - type: Submit |
381 | name: submit |
382 | value: Submit |
d682d02d |
383 | |
f279297a |
384 | # Global filters and constraints. |
cca5cd98 |
385 | constraints: |
f279297a |
386 | # The user cannot leave any fields blank |
387 | - Required |
ab0db558 |
388 | # If not all fields are required, move the Required constraint to the |
389 | # fields that are |
f279297a |
390 | filter: |
391 | # Remove whitespace at both ends |
392 | - TrimEdges |
393 | # Escape HTML characters for safety |
394 | - HTMLEscape |
cca5cd98 |
395 | |
0171092f |
396 | B<NOTE:> Copying and pasting YAML from perl documentation is sometimes |
1fba2369 |
397 | tricky. See the L<Config::General Config for this tutorial> section of |
398 | this document for a more foolproof config format. |
0171092f |
399 | |
d682d02d |
400 | The main changes are: |
401 | |
402 | =over 4 |
403 | |
404 | =item * |
405 | |
406 | The C<Select> element for C<authors> is changed from a single-select |
407 | drop-down to a multi-select list by adding configuration for the |
408 | C<multiple> and C<size> options in C<formfu_create.yml>. |
409 | |
410 | =item * |
411 | |
412 | Constraints are added to provide validation of the user input. See |
413 | L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other |
414 | constraints that are available. |
415 | |
416 | =item * |
417 | |
418 | A variety of filters are run on every field to remove and escape |
419 | unwanted input. See L<HTML::FormFu::Filter|HTML::FormFu::Filter> |
420 | for more filter options. |
421 | |
422 | =back |
423 | |
424 | |
425 | =head2 Try Out the Updated Form |
426 | |
427 | Press C<Ctrl-C> to kill the previous server instance (if it's still |
428 | running) and restart it: |
429 | |
430 | $ script/myapp_server.pl |
431 | |
59d6a035 |
432 | Make sure you are still logged in as C<test01> and try adding a book |
433 | with various errors: title less than 5 characters, non-numeric rating, a |
434 | rating of 0 or 6, etc. Also try selecting one, two, and zero authors. |
435 | When you click Submit, the HTML::FormFu C<constraint> items will |
436 | validate the logic and insert feedback as appropriate. Try adding blank |
437 | spaces at the front or the back of the title and note that it will be |
438 | removed. |
439 | |
440 | |
441 | =head1 CREATE AND UPDATE/EDIT ACTION |
442 | |
443 | Let's expand the work done above to add an edit action. First, open |
444 | C<lib/MyApp/Controller/Books.pm> and add the following method to the |
445 | bottom: |
446 | |
447 | =head2 formfu_edit |
448 | |
449 | Use HTML::FormFu to update an existing book |
450 | |
451 | =cut |
452 | |
acbd7bdd |
453 | sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0) |
454 | :FormConfig('books/formfu_create.yml') { |
59d6a035 |
455 | my ($self, $c, $id) = @_; |
456 | |
457 | # Get the specified book |
458 | my $book = $c->model('DB::Books')->find($id); |
459 | |
460 | # Make sure we were able to get a book |
461 | unless ($book) { |
462 | $c->flash->{error_msg} = "Invalid book -- Cannot edit"; |
e075db0c |
463 | $c->response->redirect($c->uri_for($self->action_for('list'))); |
59d6a035 |
464 | $c->detach; |
465 | } |
466 | |
467 | # Get the form that the :FormConfig attribute saved in the stash |
468 | my $form = $c->stash->{form}; |
469 | |
166c90a0 |
470 | # Check if the form has been submitted (vs. displaying the initial |
471 | # form) and if the data passed validation. "submitted_and_valid" |
59d6a035 |
472 | # is shorthand for "$form->submitted && !$form->has_errors" |
473 | if ($form->submitted_and_valid) { |
474 | # Save the form data for the book |
aee27483 |
475 | $form->model->update($book); |
59d6a035 |
476 | # Set a status message for the user |
477 | $c->flash->{status_msg} = 'Book edited'; |
478 | # Return to the books list |
e075db0c |
479 | $c->response->redirect($c->uri_for($self->action_for('list'))); |
59d6a035 |
480 | $c->detach; |
481 | } else { |
482 | # Get the authors from the DB |
acbd7bdd |
483 | my @author_objs = $c->model("DB::Authors")->all(); |
59d6a035 |
484 | # Create an array of arrayrefs where each arrayref is an author |
485 | my @authors; |
acbd7bdd |
486 | foreach (sort {$a->last_name cmp $b->last_name} @author_bjs) { |
59d6a035 |
487 | push(@authors, [$_->id, $_->last_name]); |
488 | } |
489 | # Get the select added by the config file |
490 | my $select = $form->get_element({type => 'Select'}); |
491 | # Add the authors to it |
492 | $select->options(\@authors); |
493 | # Populate the form with existing values from DB |
aee27483 |
494 | $form->model->default_values($book); |
59d6a035 |
495 | } |
496 | |
497 | # Set the template |
498 | $c->stash->{template} = 'books/formfu_create.tt2'; |
499 | } |
500 | |
501 | Most of this code should look familiar to what we used in the |
502 | C<formfu_create> method (in fact, we should probably centralize some of |
503 | the common code in separate methods). The main differences are: |
504 | |
505 | =over 4 |
506 | |
507 | =item * |
508 | |
509 | We accept C<$id> as an argument via the URL. |
510 | |
511 | =item * |
512 | |
513 | We use C<$id> to look up the existing book from the database. |
514 | |
515 | =item * |
516 | |
517 | We make sure the C<$id> and book lookup returned a valid book. If not, |
518 | we set the error message and return to the book list. |
519 | |
520 | =item * |
521 | |
522 | If the form has been submitted and passes validation, we skip creating a |
aee27483 |
523 | new book and just use C<$form-E<gt>model-E<gt>update> to update the existing |
59d6a035 |
524 | book. |
525 | |
526 | =item * |
527 | |
528 | If the form is being displayed for the first time (or has failed |
529 | validation and it being redisplayed), we use |
aee27483 |
530 | C<$form-E<gt>model-E<gt>default_values> to populate the form with data from the |
59d6a035 |
531 | database. |
532 | |
533 | =back |
534 | |
535 | Then, edit C<root/src/books/list.tt2> and add a new link below the |
536 | existing "Delete" link that allows us to edit/update each existing book. |
537 | The last E<lt>tdE<gt> cell in the book list table should look like the |
538 | following: |
539 | |
ab0db558 |
540 | ... |
59d6a035 |
541 | <td> |
542 | [% # Add a link to delete a book %] |
acbd7bdd |
543 | <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a> |
59d6a035 |
544 | [% # Add a link to edit a book %] |
acbd7bdd |
545 | <a href="[% c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a> |
59d6a035 |
546 | </td> |
ab0db558 |
547 | ... |
548 | |
549 | B<Note:> Only add two lines (the "Add a link to edit a book" comment |
550 | and the href for C<formfu_edit>). Make sure you add it below the |
551 | existing C<delete> link. |
59d6a035 |
552 | |
553 | |
554 | =head2 Try Out the Edit/Update Feature |
555 | |
556 | Press C<Ctrl-C> to kill the previous server instance (if it's still |
557 | running) and restart it: |
558 | |
559 | $ script/myapp_server.pl |
560 | |
561 | Make sure you are still logged in as C<test01> and go to the |
562 | L<http://localhost:3000/books/list> URL in your browser. Click the |
563 | "Edit" link next to "Internetworking with TCP/IP Vol. II", change the |
564 | rating to a 3, the "II" at end of the title to the number "2", add |
565 | Stevens as a co-author (control-click), and click Submit. You will then |
566 | be returned to the book list with a "Book edited" message at the top in |
567 | green. Experiment with other edits to various books. |
d682d02d |
568 | |
5fe0e6dd |
569 | =head2 More Things to Try |
570 | |
571 | You are now armed with enough knowledge to be dangerous. You can keep |
572 | tweaking the example application; some things you might want to do: |
573 | |
574 | =over 4 |
575 | |
576 | =item * |
577 | |
acbd7bdd |
578 | Add an appropriate authorization check to the new Edit function. |
5fe0e6dd |
579 | |
580 | =item * |
581 | |
582 | Cleanup the List page so that the Login link only displays when the user |
583 | isn't logged in and the Logout link only displays when a user is logged |
584 | in. |
585 | |
586 | =item * |
587 | |
588 | Add a more sensible policy for when and how users and admins can do |
589 | things in the CRUD cycle. |
590 | |
591 | =item * |
592 | |
593 | Support the CRUD cycle for authors. |
594 | |
595 | =back |
596 | |
597 | Or you can proceed to write your own application, which is probably the |
598 | real reason you worked through this Tutorial in the first place. |
cca5cd98 |
599 | |
1fba2369 |
600 | =head2 Config::General Config for this tutorial |
601 | |
602 | If you are having difficulty with YAML config above, please save the |
603 | below into the file C<formfu_create.conf> and delete the |
604 | C<formfu_create.yml> file. The below is in |
605 | L<Config::General|Config::General> format which follows the syntax of |
606 | Apache config files. |
607 | |
608 | constraints Required |
609 | <elements> |
1fba2369 |
610 | <constraints> |
611 | min 5 |
612 | max 40 |
613 | type Length |
614 | message Length must be between 5 and 40 characters |
615 | </constraints> |
616 | filter TrimEdges |
617 | filter HTMLEscape |
618 | name title |
619 | type Text |
620 | label Title |
621 | <attributes> |
622 | title Enter a book title here |
623 | </attributes> |
624 | </elements> |
625 | <elements> |
1fba2369 |
626 | constraints Integer |
627 | filter TrimEdges |
628 | filter NonNumeric |
629 | name rating |
630 | type Text |
631 | label Rating |
632 | <attributes> |
633 | title Enter a rating between 1 and 5 here |
634 | </attributes> |
635 | </elements> |
636 | <elements> |
637 | constraints Integer |
638 | filter TrimEdges |
639 | filter HTMLEscape |
640 | name authors |
641 | type Select |
642 | label Author |
643 | multiple 1 |
644 | size 3 |
645 | </elements> |
646 | <elements> |
647 | value Submit |
648 | name submit |
649 | type Submit |
650 | </elements> |
651 | indicator submit |
652 | |
653 | |
0171092f |
654 | |
cca5cd98 |
655 | =head1 AUTHOR |
656 | |
657 | Kennedy Clark, C<hkclark@gmail.com> |
658 | |
659 | Please report any errors, issues or suggestions to the author. The |
660 | most recent version of the Catalyst Tutorial can be found at |
82ab4bbf |
661 | L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>. |
cca5cd98 |
662 | |
51e85db7 |
663 | Copyright 2006-2008, Kennedy Clark, under Creative Commons License |
664 | (L<http://creativecommons.org/licenses/by-sa/3.0/us/>). |