HTML-FormHandler-0.40050/0000755000077000007700000000000012221042077014134 5ustar gshankgshankHTML-FormHandler-0.40050/Changes0000644000077000007700000007311312221042076015433 0ustar gshankgshank0.40050 Thu Sep 26, 2013 *** 'widget_tags' in a field have been deprecated for a long time; removing. There are still widget_tags in the form and compound fields. Use 'tags' in a field instead. *** Initial support of Bootstrap3 - still EXPERIMENTAL. Do not use in production yet. Changes in interface may occur over the next few weeks. Supporting Bootstrap 3.0 required a surprising amount of minor refactoring: Bootstrap3 checkboxes and radio elements now have an additional div wrapping them. This required setting flags in the wrapper that could be seen by the field widgets. Used 'wrapper_tags' attribute, which is not the most satisfying solution but does work. The former 'controls' div now doesn't have the 'controls' class, but is used for sizing. Added new attribute 'element_wrapper_class' to provide these classes. Split out addition of classes to the wrapper and element into 'add_standard_wrapper_classes' and 'add_standard_element_classes', because B3.0 now wants 'has-error' and 'has-warning' instead of the former 'error' class. 'control-group' was changed to 'form-group'. The 'form-control' class has been added to text, password, textarea, and select fields. Add 'preserve_case' attribute to Email field 0.40028 Sat Sep 21, 2013 Fixed bug when rendering blocks with 'run' (results) Sort the deflated values of SelectCSV field Allow passing Email::Valid params to Email field Typos fixed Add 'use_init_obj_when_no_accessor_in_item' flag for dual-purpose init_obj 0.40027 Thu Aug 8, 2013 Add 'options_ref' method for using options in TT templates Add unique messages to field messages hash 0.40026 Wed Jul 3, 2013 Add Italian message file Doc tweaks Add errors_by_id and errors_by_name convenience methods 0.40025 Thu May 9, 2013 Add skip in t/setup_form_config.t unless YAML::Syck (Config::Any) 0.40024 Tue May 7, 2013 Add 'no_option_validation' flag to select field. Remove HtmlArea field. (Has always been broken, and there were complaints about not prereqing HTML::Tidy, which I won't do due to difficulty of installing) Change 'use' of GD::SecurityImage to a 'require'. (Will not prereq this one either.) 0.40023 Tue Apr 30, 2013 Add Brazilian Portuguese translation file Fix bug in process of re-loading repeatables without primary keys after db update Add TextCSV field for multiple values in a text field (useful with js libraries) 0.40022 Mon Mar 18, 2013 Remove 'writeonly' flag from Display field, because fix to not pull values from an item/init_object with that flag meant that values were not being applied to Display fields, when people were relying on that. Switch to using github issues instead of RT 0.40021 Mon Mar 4, 2013 Don't validate disabled fields check for existence of field in match_when before getting $field->fif, improve error message add type_attr to Select field; update t/render/ff.t to use it cleanup select field options_method building 0.40020 Sun Feb 20, 2013 More support for repeatable javascript: Tweak Bootsrap wrapper to check do_wrapper instead of do_label when rendering 'controls' div Reminder: It's always a good idea to make your own set of of widgets so that updates don't throw off your rendering. This change was hardly noticeable in the FH testcases, but it's possible you were relying on the old behavior for CSS. Add 'controls_div' to Simple wrapper. Add RmElement example field. 0.40019 Fri Feb 8, 2013 Move back 'before_element' tag; breaks existing rendering. Add additional 'before_element_inside_div' tag instead. 0.40018 Thur Feb 7, 2013 Don't put 'control-group' on Bootstrap hidden field div because of spacing issues Support for repeatable add/remove javascript add 'setup_for_js' flag to Field::Repeatable add HTML::FormHandler::Render::RepeatableJs add HTML::FormHandler::Field::AddElement add before_wrapper and after_wrapper tags add 'id' to wrappers of compound fields update Display field to use 'render_method' allow applying wrapper widget even if field has render method Fix positioning of 'before_element' tag in Bootstrap wrapper 0.40017 Sat Dec 1, 2012 Fix bad html in Span widget Fix unitialized warning processing has_field with '+' Use get_default_value in Submit/Reset field Improve doc for bootstrap theme, use BootstrapFormMessages role Add lazy to render_filter for random failures in 5.17.6 0.40016 Mon Oct 15, 2012 Fix bug with DBIC model interface 0.40015 Sun Oct 14, 2012 Remove extraneous use of Data::Printer Correct spelling of PadWalker 0.40014 Sat Oct 13, 2012 Add useful message and die in field widgets with no result Use string instead of object in LANGUAGE_HANDLE Fix bug in required_when when value is 0 Allow using arrayref for sort_column. Select field as_label for multiple fields. Minor doc fixes Bug - option group label attributes Add info_message to form and rendering. Add 'use_fields_for_input_without_param' flag Call inflate_default_method on repeatable elements 0.40013 Sun Jun 24, 2012 Re-write elimination of PrimaryKey field from repeatable value, add 'no_value_if_empty' attribute Fix bug with labels '0' not being displayed Change 'missing' to an attribute. Submit field has 'submit' html5_type_attr Add html5_type_attr to Hidden, Reset, and Password fields Add new behavior for compound fields and 'not_nullable' flag, where compound field value is not set to undef when all subfields are empty. This is needed for some kinds of db relationships, to ensure that subfields are set to null. 0.40012 Fri Jun 15, 2012 Bug cloning merging repeatable instances; form reference garbage collected Doc typos 0.40011 Tue Jun 5, 2012 Remove automatic building of field results. If you have field tests, you need to add $field->build_result after creating field with 'new'. Possible memory cycle if result is accessed when not built. Add 'required_when' Add Bulgarian message file (dpetrov) Bootstrap input_append/prepend: no linefeeds between input Add input_append_button tag to Bootstrap wrapper Correct camelcase for widgets in two fields Add 'value_when_empty' for multiple select Add SelectCSV multiple field Change Select to use sort_options_method for sorting 0.40010 Sun May 20, 2012 Add 'when' clause to apply actions Fix memory leak on fields with defaults due to missing 'my' causing $self to be closed over so that RAM was leaked if forms were constructed but never processed. (in default_ & validate_ methods) 0.40009 Mon May 14, 2012 Re-implement improved version of 'reload_after_update'. 0.40008 Fri May 11, 2012 Add 'missing' method to Field Use result in Bootstrap render_form_messages Fix Render::Table Propagate errors when they're added, so $form->has_errors works in sub validate Use do_render_label in Bootstrap wrapper to allow setting label class/attributes Add subfield convenience method. Remove 'reload_after_update'. Didn't work anyway. Bug: duplicate results with repeatables. (avoid with reload_after_update => 0) Re-factor RadioGroup widget to allow individually rendered options Support option groups in 'Select', 'RadioGroup' & 'CheckboxGroup' widgets 0.40007 Tues Apr 24, 2012 Re-factor widget to provide 'render_element' method Various doc updates Move 'by_flag' processing into '_merge_updates' Handle disabled fields better; result_from_fields if no input Fix bug: html_attributes callback called with 'input' instead of 'element' 0.40006 Tues Apr 10, 2012 Render::Table incorrect table start Minor doc cleanup Add 'build_label_method' Re-do merging of widget_tags Implement experimental 'include' list for Form/Compound fields Refactor merge_updates and update_subfields to handle contains Add 'by_type' to update_subfields 0.40005 Mon Mar 26, 2012 prevent undef from being passed to maketext expand use of 'posted' flag to check false values add wrap_label method 0.40004 Fri Mar 23, 2012 Don't put element attributes on select options Make render_list lazy Better defaults for compound fields Provide package name for die when not extending Add block_list to provide blocks Defaults for repeatable fields 0.40003 Wed Mar 14, 2012 Move dfv test that fails prereqs 0.40002 Tue Mar 13, 2012 Put form wrappers that are fieldsets inside form tag; outside not legal HTML Doc updates Add 'NonEditable' field and 'Span' field widget Patch HTMLAttributes (compatibility for older style custom widgets) 0.40001 Wed Mar 7, 2012 Remove \K in regex for ucc_widget; doesn't work pre 5.10 Switch DateTime field to use inflate_default_method 0.40000 Tue Mar 6, 2012 **** There are many changes to rendering, many of them incompatible. These changes *will* break existing form rendering. You must check that your rendering works before upgrading. Making a copy of the old code (the widget and rendering roles) may be helpful (or use the compatibility libraries -- see below). I always prefer to maintain backward compatibility if possible, but a number of the improvements were not possible without breaking compatibility, so I did a lot of changes at once. Compatibility libraries are provided to help support rendering that relied on the earlier libriaries at: git://github.com/gshank/html-formhandler_pre-0.40_compat.git README at: https://github.com/gshank/html-formhandler_pre-0.40_compat/blob/master/README Add Twitter Bootstrap 2.0 widget wrapper Add 'no_update' flag to allow skipping model_update. Remove 'deflate_to' flag; provide new inflation/deflation methods. see HTML::FormHandler::Manual::InflationDeflation New 'build_id_method' to provide different builder method for field IDs. 'auto_fieldset' and 'no_auto_fieldset' no longer used. No automatic fieldsets. Can be added with do_wrapper => 1 and a tag of wrapper_tag => 'fieldset' Localize the value of the reset button. Tests and fix for form 'validate_' and 'default_' method for repeatables fields. Change default radiogroup rendering to not use
elements. Add back with tag radio_br_after => 1 Switch to using coderef for deflate_method; custom fields with deflate sub will need to be modified. Add block rendering (HTML::FormHandler::Blocks) Re-do code for default & validate method construction; now provides 'default_method' and 'validate_method' coderef setting Remove 'init_value_*' from Field (deprecated for years). Use Hash::Merge in merging update field info on creation Fix bug in copying tags to fields by cloning field definitions Switch to using name 'element_attr' in fields instead of 'html_attr' Put wrapper class 'hfh-repinst' on Repeatable Instances unless they already have a wrapper class Remove 'javascript' field attribute. Put into *_attr hashref. Automatically put 'error' on element and wrapper. Switch to having the 'class' as a separate attribute from the _attr collection. Use 'element_class', 'wrapper_class' & 'label_class' arrayrefs Switch to having widget names by default be camel case; provide convenience methods for templates - uwidget, uwrapper, twidget, twrapper. ** this change will affect existing template systems, if they use the $field->widget method to get the widget name. See example templates. and conversion methods 'ucc_widget' and 'cc_widget' in HTML::FormHandler::Render::Util Create t/share/templates/form/form_in_one.tt Switch default rendering of checkbox to have label wrap input. Checkboxes are complicated. See t/render/checkbox.t for various options. Add 'build_update_subfields' to 'update_fields' processing to allow moving more of rendering settings into a separate role Change form 'html_attr' to 'form_element_attr', and use builder Change interface of html_field_attributes to also return attr (instead of just in-place) Remove 'label_no_colon', make labels without colon the default. Add widget tag 'label_after'. Use "label_after => ': '" for old behavior Add widget tag 'label_tag'. Default 'label'. Widget_tags replaced with 'form_tags' in form and 'tags' in Field. takes builder 'build_form_tags' instead of default Repeatable elements get automatic 'div' wrapper Remove attribute 'auto_fieldset'; wrapping form is no longer a default; Add back with sub build_do_form_wrapper {1}, and form_tags => { wrapper_tag => 'fieldset' } Remove automatic wrapping of compounds. Enable wrapping with do_wrapper => 1 (there's also do_label => 1) Put form wrapper around form tag instead of inside wrapper_start and wrapper_end tags not used to skip wrapper; use do_wrapper => 0 The 'get_tag' method now returns '' instead of undef if tag doesn't exist. 0.36001 Tues Jan 24, 2012 Add two more widget tags: 'no_auto_fieldset' and 'no_compound_wrapper' Remove automatic addition of 'class="label"' to labels; if you want that behavior, add it in with form sub field_html_attributes. Add 'SKIP' to t/config.t test for Template. Update Captcha so it might actually work. 0.36000 Sun Jan 22, 2012 Switch to using 'process_attrs' function to process attributes in rendering; *** There were lots of updates to rendering. You should verify your custom rendering, to make sure that nothing has broken. The '_add_html_attributes' method is no longer used. Add shorthand method for setting defaults in fields Add widget_tags 'label_no_colon' & 'wrapper_tag' Update and reorganize TT templates Add flags 'use_defaults_over_obj' & 'use_init_obj_over_item' Add 'num_extra' to Repeatable Update Turkish message file; add Float field Add lazy to 'html' attribute in Display field Add 'label_attr' and 'wrapper_attr' to Field Add 'Array' trait to field_name_space and widget_name_space Bug with selected/checked hash key in Multiple; switch to creating default Bug with repeatable contains; not using full name for accessor. Die if using HTML::FormHandler::Moose without HTML::FormHandler Field::TextArea extends Field::Text to reuse its validations (min/max length) Add is_html5 attribute to forms which causes forms to have the additional HTML 5 attributes which can be used by HTML 5 capable clients for validation 0.35005 Sat Oct 8, 2011 Fix bug repeatable result not returned for num_when_empty Fix bug repeatable required flag not propagated Fix bug building nested compound fields Allow html attributes on radio group elements Undefined string warning in select rendering Add Japanese message file 0.35004 Wed Oct 5, 2011 Fix bug setting multiple selects with init_object Provide html_attr for form attributes Use Moose type for field_name_space and widget_name_space 0.35003 Wed Sep 7, 2011 Fix bug constructing classes for Class::Load, revealed by Class::Load 0.10 0.35002 Mon Aug 8, 2011 Change to use Class::Load due to speed. 0.35001 Mon Jul 25, 2011 Undid setting processed flag when building result in BUILD. Breaks existing apps. Re-thinking that part for now. 0.35000 Thu Jul 21, 2011 Add support for tabindex attribute. Generic html attribute setting (html_attr) Set 'processed' flag when building results in BUILD to fix problem with garbage collected results. *** it's possible that this may break code if field values were being set outside of FormHandler, or params were set on new. Pass params on process. Set fields inside FormHandler, or run clear first, then set values. In general, it works best to update fields inside a FormHandler class, in a method or method modifier. Building results in 'new' happened originally because people expected to be able to do $form->render after new, without process. But you're better off always running 'process'. Add flag 'no_preload' to skip building results in new (BUILD) if not needed Add flag 'no_widgets' to skip applying widgets to fields if not needed Fix for Date fields in compounds. Types Printable & SingleWord use class messages Add link to the bug tracker into the HELP section in the Pod. Change how field_traits work: apply traits to field objects, add new class method apply_traits (Stephen Thirlwall) 0.34001 Mon May 16, 2011 Fiz another memory cycle using Select field Tweak code creating results for Field testing 0.34000 Mon May 16, 2011 Fixed memory cycles; 1 in HFH code, others by requiring Moose 2.0007 Localize value of a button Allow limited use of has_many multiple select Add SimpleInline & TableInline widgets to not wrap compound fields 0.33002 Tues Feb 22, 2011 Accidentally left off compatibility for 'required_message' attribute messages => { required => '...' } is new style and worked 0.33001 Mon Feb 21, 2011 Remove unnecessary with of HFH::Validate::Actions 0.33000 Mon Feb 21, 2011 bug - empty_select check defined Add button field, widget, template Check html attributes for definedness not truth Add ability to set field inactive on new & process in addition to setting active Move 'no_render_label' into Field Use form's language_handle in fields Improve PrimaryKey doc Return empty hashref from $form->value instead of undef Merge experimental Wizard into master Render disabled select options Repeatable contains rendering incorrectly, skipping empty elements Add rendering of form_errors to widgets and Render::Simple *** If you were using form_errors (there are none by default) and were using HFH rendering, check for compatibility Allow specifying full class for widget with '+' Document removing wrapper div from Simple wrapper Re-do how field messages are stored and accessed. Use messages => {...} instead of various _message attributes Add utilities in util to pull out class messages and check I18N Update I18N messages files (those that were provided by translators) Change render_filter Coderef setting because of leak; *** Possible incompatibility: if you have a form render_filter, change to function instead of method Change _localize to a Coderef to allow easier changing. *** If you have a custom _localize method, check for compatibility 0.32005 Wed Oct 20, 2010 Removed '//'; incompatible with earlier versions of Perl 0.32004 Wed Oct 20, 2010 Minor doc cleanup Switch away from MooseX::Traits; memory leak because of non-cached composed classes 0.32003 Sun Oct 3, 2010 Fix syntax for 'with', excludes => -excludes Use labels in radio group widgets Add 'is_active' and 'is_inactive' convenience methods in Field Select options - check defined instead of truth Misc minor doc and test improvements Coderef allowed for messages in apply actions Limit removing of numbers when constructing method names Use html_filter when rendering labels Allow undefining min_size and max_size in upload field Return in render_filter if string is not defined Change rendering of repeatable subfields 0.32002 Thu July 29, 2010 Update to handle newer Moose (error msg with Moose::Util::MetaRole API) Swich to Dist::Zilla Add customization of form tag attributes Add test prereqs 0.32001 Fri June 25, 2010 Add prereqs for DateTime::Format::Strptime and Email::Valid 0.32000 Fri June 25, 2010 Accept arrayref messages in add_error Add initial fieldset wrapper Flag (localize_labels) in Select field for rendering; localize empty_select Add posted flag for forms containing only fields with no params when unselected Add 'update_fields' methods and 'update_field_list' for preference-type field updates Fix incorrect error message in duration field Use LANGUAGE_HANDLE instead of LANG in tests Add 'input_class' for class attribute on input fields Allow deflation in fif, flag 'deflate_to' => 'value'/'fif' Fix bug with unselected Select field (move input_without_param & not_nullable into field) Add resultset example to cookbook Doc to look at input for multiple submit fields Fix bug in _set_dependency; use 'has_some_value' to determine emptiness Add form_errors for non-field errors Remove deprecated 'min_length' attribute ('minlength' is supported) Allow upload field to be passed a file handle Pass values to Display field (for display-only db fields) Change I18N to allow duck_type classes; add test for Data::Localize Added 'peek' diagnostic function for viewing field & result trees Fix bug with extra results in repeatable elements Strip empty pks and empty elements from repeatable values (avoid DB errors) Localize value of submit button Make '+' unnecessary in front of field name space types 0.31003 Fri May 7, 2010 Change precedence of defaults over item/init_object; add 'default_over_obj' for cases where that behavior is desired. Fix errors in filtering HTML in rendering Call deflation in InitResult::_result_from_obj Split localization of labels into separate 'loc_label' method Call loc_label where label is used in error messages Enable empty strings for wrapper_start and wrapper_end Set locale to en_us where needed in test Fix widget_name_space use in fields 0.31002 Wed Apr 21, 2010 Remove unused HTML::Entities from Simple form widget Move locale test file into xt because of env variable issues in test 0.31001 Tues Apr 20, 2010 Use full length version number Updates to translated messages & messages in Validate::Actions 0.31 Fri Apr 16, 2010 Remove use of HTML::Entities for filtering. New render_filter coderef for filering. Minor doc fixes for typos Use _html_attributes in widgets (for disabled, readonly, javascript) Localize default required field message Add 'render_upload' to Render::Simple Fix allowing array for field_name_space Selected_option for select lists Add example to cookbook and tests for setting a coderef for validation Checkbox group use 'eq' instead of '==' Fixes to tutorial to match Catalyst tutorial Allow arrayref for 'has_field' (like Moose 'has') Die on maketext errors Move deflation from fif to get_value called by '_result_from_object'. Possible incompatibility, except it was probably not working to start with... 0.30003 Sun Feb 21, 2010 Partial fix for lack of defaults for compound fields Support for using model_fields roles (DBICFields) Use 'eq' instead of '==' when constructing html for multiple selects Remove deprecated 'auto' syntax 0.30002 Thu Feb 11, 2010 Don't convert widget names that contain uppercase Better error messages for missing field classes Field attribute 'input_param' to allow input names different than field names Make field 'default' rw Clean up doc on init_object 0.30001 Fri Feb 5, 2009 Remove unnecessary IO::All use causing dep problems Changes to Turkish messages Russian and Urkainian message files Use HTML::FormHandlerX namespace for fields and widgets Fix bug with defaults set to 0 0.30 Mon Feb 1, 2010 Improve Display Field, adding more ways to set html Add initial pass at more automatic TT rendering Change readonly, html attributes to 'rw' Set widget in Reset field Fix bugs and oddities in HFH::types Fix bug allowing hashref to be passed to constructor Improve doc on 'trim' Add more doc on dynamic form creation Allow 'options_' attributes in form Add Turkish message file Add 'empty_select' to Select field Fix bug displaying empty repeatable element if no values from object Improvements in I18N factoring 0.29002 Wed Dec 16, 2009 Remove locale.t from dist until issues solved 0.29001 Tues Dec 15, 2009 Fix bug with passing widget_wrapper to fields Fix bug with generated method names for form methods 0.29 Wed Dec 2, 2009 Add CheckboxGroup widget, add MooseX::Traits to Form & Field class Fix bug where defaults were not being used with an initial object Fix DateTime field to trap DateTime errors, pass hash in value Use build_label for field labels Remove use of Class::Load, instead use Class::MOP::load_class() Add set_active and make switching fields to active simpler Fix bug when options lists from db are empty Add encode_entities to rendering widgets Switch from init_value_ to default_ Change upload field. Improve setting of method defaults for set_default, set_validate, set_options 0.28 Switched to using native traits Add Widget roles Major refactor to support result classes Reformatting source to more Perl standard Fix bug generating CSS classes in Render::Simple (mazpe) Fix POD example in ::Intro (mazpe) 0.27006 Mon Aug 17, 2009 Add ability to set params class and arguments 0.27005 Wed Aug 12, 2009 DateTime::Format::Strptime dep again 0.27004 Tues Aug 11, 2009 Date inherits from Text. Fix loading compound fields from related. Call load_options for forms with no init_obj & params 0.27003 Sat Aug 2, 2009 Indexing failure missing version in Date 0.27002 Sat Aug 2, 2009 Fix missing dependency on DateTime::Format::Strptime Doc tweaks 0.27001 Fri July 31, 2009 Doc fixes, fix Date field. 0.27 Sat July 25, 2009 Split HTML::FormHandler::Model::DBIC into separate distribution Add 'inactive' flag. Cleanup Makefile.PL. 'size' split into 'size' and 'maxlength'. 'min_length' renamed to 'minlength'. Add Catalyst pod. 'html_name' used for field 'id'. Fix DateMDY field. 0.26 Tues June 23, 2009 Fix dependency test failures on UNIVERSAL::require and version 0.25 Sat June 20, 2009 Add dependency for DateTime::Format::SQLite 0.24 Sat June 20, 2009 Refactor validation processing for api consistency Skip empty undef array elements. Update Password and PrimaryKey fields. Fix bugs: calling validate_ method, recognizing errors in repeatable fields, handling empty repeatable elements, incorrect cloning in Repeatable, rendering fixes/updates. 0.23 Fri May 22, 2009 Refactor HTH to use only 'process'. Deprecate 'validate' and 'update' Add field_list array, deprecate other usages. Clean up documentation Add Repeatable field to support has_many relationships 0.22 Fri May 1, 2009, 17:00 Removed development only test from distribution Expanded apply documentation. 0.21 Thu Apr 30, 2009, 20:00 Removed podcoverage, added skip to generator.t test, added 'apply' sugar for adding actions, doc for compound field 0.20 Thu Apr 23, 2009, 17:00 Added apply constraints, transforms, checks. Refactored code for future use of nested fields. Improvements to compound fields. Bug fix for checkboxes. Added ability to redefine attributes of existing fields with '+fieldname'. 0.19 Thu Mar 05, 2009, 17:00 Fix problem with empty values from form. Add Compound field. 0.18 Sun Feb 22 2009, 15:00 Add missing test prereq DateTime::Format::MySQL. Add 'values' method to form. Add 'accessor' attribute to field. 0.17 Thurs Feb 19 2009, 17:30 Refactor validate, adding validate_form method 0.16 Thurs Feb 19 2009, 17:00 Add ability to use arrayrefs for primary key Clear 'fif' for non-db forms. Allow init_object for non-db forms. 0.15 Mon Feb 16 2009, 19:00 Fix inheritance of has_field. Add ability to use has_field in roles. Some refactoring of 'clear'. If a field is not in params, don't touch in db. 0.14 Fri Feb 06 2009, 18:00 Wrong version in META.yml. Fix fif for password fields. 0.13 Wed Feb 04 2009, 23:00 Fix validate to set params if hash 0.12 Wed Feb 04 2009, 18:00 Fix 'dump_fields'. Add more output for verbose. Change so that 'validate' doesn't require a separate 'clear' for persistent forms. The controller test will only execute with an environment variable. 0.11 Mon Feb 02 2009, 17:00 Change to use BEGIN block in controllers for Catalyst 5.80. 0.10 Thu Jan 29 2009, 07:00 Remove unnecessary 'use' from Render::Simple to eliminate install failures. Change handling of 'has_field'. 0.09 Sun Jan 25 2009, 17:00 Minor changes. 0.08 Sat Jan 24 2009, 11:00 Remove controller and role. Refactor to support persistent forms. Remove update_from_form method. Add 'process', and 'update' methods. Update documentation to match. Update tutorial. 0.07 Thurs Jan 22 2009, 04:00 Add prereq of DateTime. Minor doc changes. 0.06 Wed Jan 21 2009, 04:00 Add prereq skip tests to controller test. Clean up Makefile.PL. Convert test controller Book.pm to use chained. Support empty rows. 0.05 Mon Jan 19 2009, 15:00 Add skip test to htmlarea test. Add action, http_method, & submit to form. Add javascript to field. Create widget directory for templates. 0.04 Fri Jan 16 2009, 19:00 Move example to test directory. Change controller; add controller test. Add use for HashRefInflator. Add more documentation. 0.03 Tues Jan 12 2009, 16:00 Pod fix, remove failing test from htmlarea 0.02 Tues Jan 12 2009, 03:00 Fixed pod formatting, naming of files 0.01 Mon Jan 12 2009, 17:00 Released on an unsuspecting world Conversion of Form::Processor to Moose, including renaming many attributes and methods and refactoring HTML-FormHandler-0.40050/dist.ini0000644000077000007700000000313712221042076015603 0ustar gshankgshank; Everything starting with ';' is a comment name = HTML-FormHandler main_module = lib/HTML/FormHandler.pm author = FormHandler Contributors - see HTML::FormHandler license = Perl_5 copyright_holder = Gerda Shank copyright_year = 2013 version = 0.40050 [@Git] tag_format = %v [@Basic] [InstallGuide] [MetaJSON] [MetaResources] bugtracker.web = http://github.com/gshank/html-formhandler/issues ; If you have a repository... repository.url = git://github.com/gshank/html-formhandler.git repository.web = http://github.com/gshank/html-formhandler repository.type = git ; You have to have Dist::Zilla::Plugin:: for these to work [PodWeaver] [NoTabsTests] [EOLTests] [Signature] [CheckChangeLog] [Prereqs] Class::Load = 0.06 Carp = 0 Moose = 2.0007 Locale::Maketext = 1.09 DateTime = 0 DateTime::Format::Strptime = 0 MooseX::Getopt = 0.16 MooseX::Types = 0.20 MooseX::Types::Common = 0 MooseX::Types::LoadableClass = 0.006 aliased = 0 File::Spec = 0 File::ShareDir = 0 Try::Tiny = 0 namespace::autoclean = 0.09 Email::Valid = 0 Sub::Exporter = 0 HTML::TreeBuilder = 3.23 Sub::Name = 0 Data::Clone = 0 JSON = 0 [Prereqs / TestRequires] Test::More = 0.94 Test::Differences = 0 Test::Exception = 0 Test::Memory::Cycle = 1.04 PadWalker = 0 HTML-FormHandler-0.40050/INSTALL0000644000077000007700000000172612221042076015172 0ustar gshankgshank This is the Perl distribution HTML-FormHandler. Installing HTML-FormHandler is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm HTML::FormHandler If you are installing into a system-wide directory, you may need to pass the "-S" flag to cpanm, which uses sudo to install the module: % cpanm -S HTML::FormHandler ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan HTML::FormHandler ## Manual installation As a last resort, you can manually install it. Download the tarball, untar it, then build it: % perl Makefile.PL % make && make test Then install it: % make install If you are installing into a system-wide directory, you may need to run: % sudo make install ## Documentation HTML-FormHandler documentation is available as POD. You can run perldoc from a shell to read the documentation: % perldoc HTML::FormHandler HTML-FormHandler-0.40050/lib/0000755000077000007700000000000012221042077014702 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/0000755000077000007700000000000012221042077015446 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/0000755000077000007700000000000012221042077017647 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Base.pm0000644000077000007700000000132412221042077021057 0ustar gshankgshankpackage HTML::FormHandler::Base; # ABSTRACT: stub use Moose; with 'HTML::FormHandler::Widget::Form::Simple'; # here to make it possible to combine the Blocks role with a role # setting the render_list without an 'excludes' sub has_render_list { } sub build_render_list {[]} __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Base - stub =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Blocks.pm0000644000077000007700000001160112221042077021421 0ustar gshankgshankpackage HTML::FormHandler::Blocks; # ABSTRACT: arrange form layout using blocks use Moose::Role; use Try::Tiny; use Class::Load qw/ load_optional_class /; use namespace::autoclean; use Data::Clone; use HTML::FormHandler::Widget::Block; has 'blocks' => ( isa => 'HashRef[Object]', is => 'ro', lazy => 1, traits => ['Hash'], builder => 'build_blocks', handles => { has_blocks => 'count', add_block => 'set', block => 'get', block_exists => 'exists', }, ); sub build_blocks { {} } has 'block_list' => ( is => 'rw', isa => 'ArrayRef', lazy => 1, builder => 'build_block_list' ); sub build_block_list {[]} has 'render_list' => ( is => 'rw', isa => 'ArrayRef[Str]', traits => ['Array'], lazy => 1, builder => 'build_render_list', handles => { has_render_list => 'count', add_to_render_list => 'push', all_render_list => 'elements', get_render_list => 'get', } ); sub get_renderer { my ( $self, $name ) = @_; die "must provide a name to get_renderer" unless $name; my $obj = $self->block($name); return $obj if ref $obj; $obj = $self->field_from_index($name); return $obj if ref $obj; die "did not find a field or block with name $name\n"; } after '_build_fields' => sub { my $self = shift; my $meta_blist = $self->_build_meta_block_list; if( @$meta_blist ) { foreach my $block_attr (@$meta_blist) { $self->make_block($block_attr); } } my $blist = $self->block_list; if( @$blist ) { foreach my $block_attr (@$blist) { $self->make_block($block_attr); } } }; sub make_block { my ( $self, $block_attr ) = @_; my $type = $block_attr->{type} ||= ''; my $name = $block_attr->{name}; die "You must supply a name for a block" unless $name; my $do_update; if ( $name =~ /^\+(.*)/ ) { $block_attr->{name} = $name = $1; $do_update = 1; } my $class; if( $type ) { $class = $self->get_widget_role($type, 'Block'); } else { $class = 'HTML::FormHandler::Widget::Block'; } $block_attr->{form} = $self->form if $self->form; my $block = $self->form->block( $block_attr->{name} ); if ( defined $block && $do_update ) { delete $block_attr->{name}; foreach my $key ( keys %{$block_attr} ) { $block->$key( $block_attr->{$key} ) if $block->can($key); } } else # new block { $block = $class->new(%$block_attr); $self->add_block( $name, $block ); } } # loops through all inherited classes and composed roles # to find blocks specified with 'has_block' sub _build_meta_block_list { my $self = shift; my @block_list; foreach my $sc ( reverse $self->meta->linearized_isa ) { my $meta = $sc->meta; if ( $meta->can('calculate_all_roles') ) { foreach my $role ( reverse $meta->calculate_all_roles ) { if ( $role->can('block_list') && $role->has_block_list ) { foreach my $block_def ( @{ $role->block_list } ) { push @block_list, $block_def; } } } } if ( $meta->can('block_list') && $meta->has_block_list ) { foreach my $block_def ( @{ $meta->block_list } ) { push @block_list, $block_def; } } } return clone( \@block_list ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Blocks - arrange form layout using blocks =head1 VERSION version 0.40050 =head1 SYNOPSIS This is a role which provides the ability to render your form in arbitrary 'blocks', instead of by fields. This role is included by default in HTML::FormHandler. package MyApp::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; sub build_render_list {[ 'foo', 'fset' ]} has_field 'foo'; has_field 'bar'; has_field 'nox'; has_block 'fset' => ( tag => 'fieldset', render_list => ['bar', 'nox'] );; .... $form->render; Blocks live in the HTML::FormHandler::Widget::Block:: namespace. The default, non-typed block is L. Provide a type for custom blocks: has_block 'my_block' => ( type => 'CustomBlock', render_list => [...] ); You can also build blocks with a 'block_list' attribute, or the builder for it, 'build_block_list'. Rendering with blocks is supported by the rendering widgets. Render::Simple doesn't do it, though it would be possible to make your own custom renderer. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/BuildFields.pm0000644000077000007700000004013212221042077022373 0ustar gshankgshankpackage HTML::FormHandler::BuildFields; # ABSTRACT: role to build field array use Moose::Role; use Try::Tiny; use Class::Load qw/ load_optional_class /; use namespace::autoclean; use HTML::FormHandler::Merge ('merge'); use Data::Clone; has 'fields_from_model' => ( isa => 'Bool', is => 'rw' ); has 'field_list' => ( isa => 'HashRef|ArrayRef', is => 'rw', default => sub { {} } ); has 'build_include_method' => ( is => 'ro', isa => 'CodeRef', traits => ['Code'], default => sub { \&default_build_include }, handles => { build_include => 'execute_method' } ); has 'include' => ( is => 'rw', isa => 'ArrayRef', traits => ['Array'], builder => 'build_include', lazy => 1, handles => { has_include => 'count' } ); sub default_build_include { [] } sub has_field_list { my ( $self, $field_list ) = @_; $field_list ||= $self->field_list; if ( ref $field_list eq 'HASH' ) { return $field_list if ( scalar keys %{$field_list} ); } elsif ( ref $field_list eq 'ARRAY' ) { return $field_list if ( scalar @{$field_list} ); } return; } # This is the only entry point for this file. It processes the # various methods of field definition (has_field plus the attrs above), # creates objects for fields and writes them into the 'fields' attr # on the base object. # # calls routines to process various field lists # orders the fields after processing in order to skip # fields which have had the 'order' attribute set sub _build_fields { my $self = shift; my $meta_flist = $self->_build_meta_field_list; $self->_process_field_array( $meta_flist, 0 ) if $meta_flist; my $flist = $self->has_field_list; if( $flist ) { if( ref($flist) eq 'ARRAY' && ref( $flist->[0] ) eq 'HASH' ) { $self->_process_field_array( $flist ); } else { $self->_process_field_list( $flist ); } } my $mlist = $self->model_fields if $self->fields_from_model; $self->_process_field_list( $mlist ) if $mlist; return unless $self->has_fields; $self->_order_fields; } # loops through all inherited classes and composed roles # to find fields specified with 'has_field' sub _build_meta_field_list { my $self = shift; my $field_list = []; foreach my $sc ( reverse $self->meta->linearized_isa ) { my $meta = $sc->meta; if ( $meta->can('calculate_all_roles') ) { foreach my $role ( reverse $meta->calculate_all_roles ) { if ( $role->can('field_list') && $role->has_field_list ) { foreach my $fld_def ( @{ $role->field_list } ) { push @$field_list, $fld_def; } } } } if ( $meta->can('field_list') && $meta->has_field_list ) { foreach my $fld_def ( @{ $meta->field_list } ) { push @$field_list, $fld_def; } } } return $field_list if scalar @$field_list; } sub _process_field_list { my ( $self, $flist ) = @_; if ( ref $flist eq 'ARRAY' ) { $self->_process_field_array( $self->_array_fields( $flist ) ); } } # munges the field_list array into an array of field attributes sub _array_fields { my ( $self, $fields ) = @_; $fields = clone( $fields ); my @new_fields; while (@$fields) { my $name = shift @$fields; my $attr = shift @$fields; unless ( ref $attr eq 'HASH' ) { $attr = { type => $attr }; } push @new_fields, { name => $name, %$attr }; } return \@new_fields; } # loop through array of field hashrefs sub _process_field_array { my ( $self, $fields ) = @_; # clone and, optionally, filter fields $fields = $self->clean_fields( $fields ); # the point here is to process fields in the order parents # before children, so we process all fields with no dots # first, then one dot, then two dots... my $num_fields = scalar @$fields; my $num_dots = 0; my $count_fields = 0; while ( $count_fields < $num_fields ) { foreach my $field (@$fields) { my $count = ( $field->{name} =~ tr/\.// ); next unless $count == $num_dots; $self->_make_field($field); $count_fields++; } $num_dots++; } } sub clean_fields { my ( $self, $fields ) = @_; if( $self->has_include ) { my @fields; my %include = map { $_ => 1 } @{ $self->include }; foreach my $fld ( @$fields ) { push @fields, clone($fld) if exists $include{$fld->{name}}; } return \@fields; } return clone( $fields ); } # Maps the field type to a field class, finds the parent, # sets the 'form' attribute, calls update_or_create # The 'field_attr' hashref must have a 'name' key sub _make_field { my ( $self, $field_attr ) = @_; my $type = $field_attr->{type} ||= 'Text'; my $name = $field_attr->{name}; my $do_update; if ( $name =~ /^\+(.*)/ ) { $field_attr->{name} = $name = $1; $do_update = 1; } my $class = $self->_find_field_class( $type, $name ); my $parent = $self->_find_parent( $field_attr ); $field_attr = $self->_merge_updates( $field_attr, $class ) unless $do_update; my $field = $self->_update_or_create( $parent, $field_attr, $class, $do_update ); $self->form->add_to_index( $field->full_name => $field ) if $self->form; } sub _make_adhoc_field { my ( $self, $class, $field_attr ) = @_; # remove and save form & parent, because if the form class has a 'clone' # method, Data::Clone::clone will clone the form my $parent = delete $field_attr->{parent}; my $form = delete $field_attr->{form}; $field_attr = $self->_merge_updates( $field_attr, $class ); $field_attr->{parent} = $parent; $field_attr->{form} = $form; my $field = $self->new_field_with_traits( $class, $field_attr ); return $field; } sub _find_field_class { my ( $self, $type, $name ) = @_; my $field_ns = $self->field_name_space; my @classes; # '+'-prefixed fields could be full namespaces if ( $type =~ s/^\+// ) { push @classes, $type; } foreach my $ns ( @$field_ns, 'HTML::FormHandler::Field', 'HTML::FormHandlerX::Field' ) { push @classes, $ns . "::" . $type; } # look for Field in possible namespaces my $class; foreach my $try ( @classes ) { last if $class = load_optional_class($try) ? $try : undef; } die "Could not load field class '$type' for field '$name'" unless $class; return $class; } sub _find_parent { my ( $self, $field_attr ) = @_; # parent and name correction for names with dots my $parent; if ( $field_attr->{name} =~ /\./ ) { my @names = split /\./, $field_attr->{name}; my $simple_name = pop @names; my $parent_name = join '.', @names; # use special 'field' method call that starts from # $self, because names aren't always starting from # the form $parent = $self->field($parent_name, undef, $self); if ($parent) { die "The parent of field " . $field_attr->{name} . " is not a Compound Field" unless $parent->isa('HTML::FormHandler::Field::Compound'); $field_attr->{name} = $simple_name; } else { die "did not find parent for field " . $field_attr->{name}; } } elsif ( !( $self->form && $self == $self->form ) ) { # set parent $parent = $self; } # get full_name my $full_name = $field_attr->{name}; $full_name = $parent->full_name . "." . $field_attr->{name} if $parent; $field_attr->{full_name} = $full_name; return $parent; } sub _merge_updates { my ( $self, $field_attr, $class ) = @_; # If there are field_traits at the form level, prepend them my $field_updates; unshift @{$field_attr->{traits}}, @{$self->form->field_traits} if $self->form; # use full_name for updates from form, name for updates from compound field my $full_name = delete $field_attr->{full_name} || $field_attr->{name}; my $name = $field_attr->{name}; my $single_updates = {}; # updates that apply to a single field my $all_updates = {}; # updates that apply to all fields # get updates from form update_subfields and widget_tags if ( $self->form ) { $field_updates = $self->form->update_subfields; if ( keys %$field_updates ) { $all_updates = $field_updates->{all} || {}; $single_updates = $field_updates->{$full_name}; if ( exists $field_updates->{by_flag} ) { $all_updates = $self->by_flag_updates( $field_attr, $class, $field_updates, $all_updates ); } if ( exists $field_updates->{by_type} && exists $field_updates->{by_type}->{$field_attr->{type}} ) { $all_updates = merge( $field_updates->{by_type}->{$field_attr->{type}}, $all_updates ); } } # merge widget tags into 'all' updates if( $self->form->has_widget_tags ) { $all_updates = merge( $all_updates, { tags => $self->form->widget_tags } ); } } # get updates from compound field update_subfields and widget_tags if ( $self->has_flag('is_compound') ) { my $comp_field_updates = $self->update_subfields; my $comp_all_updates = {}; my $comp_single_updates = {}; # -- compound 'all' updates -- if ( keys %$comp_field_updates ) { $comp_all_updates = $comp_field_updates->{all} || {}; # don't use full_name. varies depending on parent field name $comp_single_updates = $comp_field_updates->{$name} || {}; if ( exists $field_updates->{by_flag} ) { $comp_all_updates = $self->by_flag_updates( $field_attr, $class, $comp_field_updates, $comp_all_updates ); } if ( exists $comp_field_updates->{by_type} && exists $comp_field_updates->{by_type}->{$field_attr->{type}} ) { $comp_all_updates = merge( $comp_field_updates->{by_type}->{$field_attr->{type}}, $comp_all_updates ); } } if( $self->has_widget_tags ) { $comp_all_updates = merge( $comp_all_updates, { tags => $self->widget_tags } ); } # merge form 'all' updates, compound field higher precedence $all_updates = merge( $comp_all_updates, $all_updates ) if keys %$comp_all_updates; # merge single field updates, compound field higher precedence $single_updates = merge( $comp_single_updates, $single_updates ) if keys %$comp_single_updates; } # attributes set on a specific field through update_subfields override has_fields # attributes set by 'all' only happen if no field attributes $field_attr = merge( $field_attr, $all_updates ) if keys %$all_updates; $field_attr = merge( $single_updates, $field_attr ) if keys %$single_updates; # get the widget and widget_wrapper from form unless( $self->form && $self->form->no_widgets ) { # widget my $widget = $field_attr->{widget}; unless( $widget ) { my $attr = $class->meta->find_attribute_by_name( 'widget' ); $widget = $attr->default if $attr; } $widget = '' if $widget eq 'None'; # widget wrapper my $widget_wrapper = $field_attr->{widget_wrapper}; unless( $widget_wrapper ) { my $attr = $class->meta->get_attribute('widget_wrapper'); $widget_wrapper = $attr->default if $attr; $widget_wrapper ||= $self->form->widget_wrapper if $self->form; $widget_wrapper ||= 'Simple'; $field_attr->{widget_wrapper} = $widget_wrapper; } # add widget and wrapper roles to field traits if ( $widget ) { my $widget_role = $self->get_widget_role( $widget, 'Field' ); push @{$field_attr->{traits}}, $widget_role; } if ( $widget_wrapper ) { my $wrapper_role = $self->get_widget_role( $widget_wrapper, 'Wrapper' ); push @{$field_attr->{traits}}, $wrapper_role; } } return $field_attr; } sub by_flag_updates { my ( $self, $field_attr, $class, $field_updates, $all_updates ) = @_; my $by_flag = $field_updates->{by_flag}; if ( exists $by_flag->{contains} && $field_attr->{is_contains} ) { $all_updates = merge( $field_updates->{by_flag}->{contains}, $all_updates ); } elsif ( exists $by_flag->{repeatable} && $class->meta->find_attribute_by_name('is_repeatable') ) { $all_updates = merge( $field_updates->{by_flag}->{repeatable}, $all_updates ); } elsif ( exists $by_flag->{compound} && $class->meta->find_attribute_by_name('is_compound') ) { $all_updates = merge( $field_updates->{by_flag}->{compound}, $all_updates ); } return $all_updates; } # update, replace, or create field # Create makes the field object and passes in the properties as constructor args. # Update changed properties on a previously created object. # Replace overwrites a field with a different configuration. # (The update/replace business is much the same as you'd see with inheritance.) # This function populates/updates the base object's 'field' array. sub _update_or_create { my ( $self, $parent, $field_attr, $class, $do_update ) = @_; $parent ||= $self->form; $field_attr->{parent} = $parent; $field_attr->{form} = $self->form if $self->form; my $index = $parent->field_index( $field_attr->{name} ); my $field; if ( defined $index ) { if ($do_update) # this field started with '+'. Update. { $field = $parent->field( $field_attr->{name} ); die "Field to update for " . $field_attr->{name} . " not found" unless $field; foreach my $key ( keys %{$field_attr} ) { next if $key eq 'name' || $key eq 'form' || $key eq 'parent' || $key eq 'full_name' || $key eq 'type'; $field->$key( $field_attr->{$key} ) if $field->can($key); } } else # replace existing field { $field = $self->new_field_with_traits( $class, $field_attr); $parent->set_field_at( $index, $field ); } } else # new field { $field = $self->new_field_with_traits( $class, $field_attr); $parent->add_field($field); } $field->form->add_repeatable_field($field) if ( $field->form && $field->has_flag('is_repeatable') ); return $field; } sub new_field_with_traits { my ( $self, $class, $field_attr ) = @_; my $traits = delete $field_attr->{traits} || []; if( @$traits ) { $class = $class->with_traits( @$traits ); } my $field = $class->new( %{$field_attr} ); return $field; } sub _order_fields { my $self = shift; # order the fields # There's a hole in this... if child fields are defined at # a level above the containing parent, then they won't # exist when this routine is called and won't be ordered. # This probably needs to be moved out of here into # a separate recursive step that's called after build_fields. # get highest order number my $order = 0; foreach my $field ( $self->all_fields ) { $order++ if $field->order > $order; } $order++; # number all unordered fields foreach my $field ( $self->all_fields ) { $field->order($order) unless $field->order; $order++; } } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::BuildFields - role to build field array =head1 VERSION version 0.40050 =head1 SYNOPSIS These are the methods that are necessary to build the fields arrays in a form. This is a role which is composed into L. Internal code only. This role has no user interfaces. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/BuildPages.pm0000644000077000007700000001512412221042077022227 0ustar gshankgshankpackage HTML::FormHandler::BuildPages; # ABSTRACT: used in Wizard use Moose::Role; use Try::Tiny; use Class::Load qw/ load_optional_class /; use namespace::autoclean; has 'page_list' => ( isa => 'ArrayRef', is => 'rw', traits => ['Array'], default => sub { [] }, ); sub has_page_list { my ( $self ) = @_; my $page_list = $self->page_list; return unless $page_list && ref $page_list eq 'ARRAY'; return $page_list if ( scalar @{$page_list} ); return; } after '_build_fields' => sub { my $self = shift; my $meta_plist = $self->_build_meta_page_list; $self->_process_page_array( $meta_plist, 0 ) if $meta_plist; my $plist = $self->has_page_list; $self->_process_page_list($plist) if $plist; return unless $self->has_pages; }; sub _process_page_list { my ( $self, $plist ) = @_; if ( ref $plist eq 'ARRAY' ) { my @plist_copy = @{$plist}; $self->_process_page_array( $self->_array_pages( \@plist_copy ) ); return; } my %plist_copy = %{$plist}; $plist = \%plist_copy; } sub _array_pages { my ( $self, $pages ) = @_; my @new_pages; while (@$pages) { my $name = shift @$pages; my $attr = shift @$pages; unless ( ref $attr eq 'HASH' ) { $attr = { type => $attr }; } push @new_pages, { name => $name, %$attr }; } return \@new_pages; } sub _process_page_array { my ( $self, $pages ) = @_; my $num_pages = scalar @$pages; my $num_dots = 0; my $count_pages = 0; while ( $count_pages < $num_pages ) { foreach my $page (@$pages) { my $count = ( $page->{name} =~ tr/\.// ); next unless $count == $num_dots; $self->_make_page($page); $count_pages++; } $num_dots++; } } sub _make_page { my ( $self, $page_attr ) = @_; $page_attr->{type} ||= 'Simple'; my $type = $page_attr->{type}; my $name = $page_attr->{name}; return unless $name; my $do_update; if ( $name =~ /^\+(.*)/ ) { $page_attr->{name} = $name = $1; $do_update = 1; } my @page_name_space; my $page_ns = $self->page_name_space; if( $page_ns ) { @page_name_space = ref $page_ns eq 'ARRAY' ? @$page_ns : $page_ns; } my @classes; # '+'-prefixed fields could be full namespaces if ( $type =~ s/^\+// ) { push @classes, $type; } foreach my $ns ( @page_name_space, 'HTML::FormHandler::Page', 'HTML::FormHandlerX::Page' ) { push @classes, $ns . "::" . $type; } # look for Page in possible namespaces my $class; foreach my $try ( @classes ) { last if $class = load_optional_class($try) ? $try : undef; } die "Could not load page class '$type' for field '$name'" unless $class; $page_attr->{form} = $self->form if $self->form; # parent and name correction for names with dots if ( $page_attr->{name} =~ /\./ ) { my @names = split /\./, $page_attr->{name}; my $simple_name = pop @names; my $parent_name = join '.', @names; my $parent = $self->page($parent_name); if ($parent) { $page_attr->{parent} = $parent; $page_attr->{name} = $simple_name; } } elsif ( !( $self->form && $self == $self->form ) ) { # set parent $page_attr->{parent} = $self; } $self->_update_or_create_page( $page_attr->{parent} || $self->form, $page_attr, $class, $do_update ); } sub _update_or_create_page { my ( $self, $parent, $page_attr, $class, $do_update ) = @_; my $index = $parent->page_index( $page_attr->{name} ); my $page; if ( defined $index ) { if ($do_update) # this page started with '+'. Update. { $page = $parent->page( $page_attr->{name} ); die "Page to update for " . $page_attr->{name} . " not found" unless $page; delete $page_attr->{name}; foreach my $key ( keys %{$page_attr} ) { $page->$key( $page_attr->{$key} ) if $page->can($key); } } else # replace existing page { $page = $self->new_page_with_traits( $class, $page_attr); $parent->set_page_at( $index, $page ); } } else # new page { $page = $self->new_page_with_traits( $class, $page_attr); $parent->push_page($page); } } sub new_page_with_traits { my ( $self, $class, $page_attr ) = @_; my $widget = $page_attr->{widget}; my $page; unless( $widget ) { my $attr = $class->meta->find_attribute_by_name( 'widget' ); if ( $attr ) { $widget = $attr->default; } } my @traits; if( $page_attr->{traits} ) { @traits = @{$page_attr->{traits}}; delete $page_attr->{traits}; } if( $widget ) { my $widget_role = $self->get_widget_role( $widget, 'Page' ); push @traits, $widget_role; } if( @traits ) { $page = $class->new_with_traits( traits => \@traits, %{$page_attr} ); } else { $page = $class->new( %{$page_attr} ); } return $page; } # loops through all inherited classes and composed roles # to find pages specified with 'has_page' sub _build_meta_page_list { my $self = shift; my @page_list; foreach my $sc ( reverse $self->meta->linearized_isa ) { my $meta = $sc->meta; if ( $meta->can('calculate_all_roles') ) { foreach my $role ( reverse $meta->calculate_all_roles ) { if ( $role->can('page_list') && $role->has_page_list ) { foreach my $page_def ( @{ $role->page_list } ) { my %new_page = %{$page_def}; # copy hashref push @page_list, \%new_page; } } } } if ( $meta->can('page_list') && $meta->has_page_list ) { foreach my $page_def ( @{ $meta->page_list } ) { my %new_page = %{$page_def}; # copy hashref push @page_list, \%new_page; } } } return \@page_list if scalar @page_list; } 1; __END__ =pod =head1 NAME HTML::FormHandler::BuildPages - used in Wizard =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/0000755000077000007700000000000012221042077020672 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/AddElement.pm0000644000077000007700000000441012221042077023231 0ustar gshankgshankpackage HTML::FormHandler::Field::AddElement; # ABSTRACT: Field to support repeatable javascript add use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Display'; use HTML::FormHandler::Render::Util ('process_attrs'); has 'repeatable' => ( is => 'rw', isa => 'Str', required => 1 ); has '+do_wrapper' => ( default => 1 ); has '+value' => ( default => 'Add Element' ); sub build_render_method { return sub { my ( $self, $result ) = @_; $result ||= $self->result; my $rep_field = $self->parent->field($self->repeatable); die "Invalid repeatable name in field " . $self->name unless $rep_field; my $value = $self->html_filter($self->_localize($self->value)); my $attrs = $self->element_attributes($result); push @{$attrs->{class}}, ( 'add_element', 'btn' ); $attrs->{'data-rep-id'} = $rep_field->id; $attrs->{id} = $self->id; my $attr_str = process_attrs($attrs); my $wrapper_tag = $self->get_tag('wrapper_tag') || 'div'; my $output = qq{<$wrapper_tag$attr_str>$value}; $output = $self->wrap_field($self->result, $output); return $output; }; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::AddElement - Field to support repeatable javascript add =head1 VERSION version 0.40050 =head1 SYNOPSIS EXAMPLE field for rendering an AddElement field for doing javascript additions of repeatable elements. You probably want to make your own. The main requirements are that the button have 1) the 'add_element' class, 2) a 'data-rep-id' attribute that contains the id of the repeatable to which you want to add an element. =head1 NAME HTML::FormHandler::Field::AddElement =head1 ATTRIBUTES has_field 'add_element' => ( type => 'AddElement', repeatable => 'foo', value => 'Add another foo', ); =head2 repeatable Requires the name of a Repeatable sibling field. =head2 value The value of the button that's rendered, 'Add Element' by default. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Boolean.pm0000644000077000007700000000156712221042077022620 0ustar gshankgshankpackage HTML::FormHandler::Field::Boolean; # ABSTRACT: a true or false field use Moose; extends 'HTML::FormHandler::Field::Checkbox'; our $VERSION = '0.03'; sub value { my $self = shift; my $v = $self->next::method(@_); return $v ? 1 : 0; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Boolean - a true or false field =head1 VERSION version 0.40050 =head1 DESCRIPTION This field returns 1 if true, 0 if false. The widget type is 'Checkbox'. Similar to Checkbox, except only returns values of 1 or 0. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/BoolSelect.pm0000644000077000007700000000154512221042077023270 0ustar gshankgshankpackage HTML::FormHandler::Field::BoolSelect; # ABSTRACT: Boolean select field use Moose; extends 'HTML::FormHandler::Field::Select'; has '+empty_select' => ( default => 'Select One' ); sub build_options { [ { value => 1, label => 'True'}, { value => 0, label => 'False' } ]}; __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::BoolSelect - Boolean select field =head1 VERSION version 0.40050 =head1 SYNOPSIS A Boolean select field with three states: null, 1, 0. Empty select is 'Select One'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Button.pm0000644000077000007700000000151512221042077022505 0ustar gshankgshankpackage HTML::FormHandler::Field::Button; # ABSTRACT: button field use Moose; extends 'HTML::FormHandler::Field::NoValue'; has '+widget' => ( default => 'Button' ); has '+value' => ( default => 'Button' ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Button - button field =head1 VERSION version 0.40050 =head1 SYNOPSIS Use this field to declare a button field in your form. has_field 'button' => ( type => 'Button', value => 'Press Me!' ); Uses the 'button' widget. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Captcha.pm0000644000077000007700000000661712221042077022605 0ustar gshankgshankpackage HTML::FormHandler::Field::Captcha; # ABSTRACT: captcha field with GD::SecurityImage use Moose; extends 'HTML::FormHandler::Field'; use HTTP::Date; has 'height' => ( isa => 'Int', is => 'rw', default => '20' ); has 'width' => ( isa => 'Int', is => 'rw', default => '80' ); has 'scramble' => ( isa => 'Int', is => 'rw', default => '0' ); has 'lines' => ( isa => 'Int', is => 'rw', default => '2' ); has 'gd_font' => ( isa => 'Str', is => 'rw', default => 'Large' ); has 'image' => ( is => 'rw' ); has '+css_class' => ( default => 'captcha' ); has '+widget' => ( default => 'Captcha' ); has '+noupdate' => ( default => 1 ); our $class_messages = { 'captcha_verify_failed' => 'Verification incorrect. Try again.', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub get_default_value { my $self = shift; my $captcha = $self->form->get_captcha; # setting the widget after the field is instantiated # doesn't actually work. The Captcha widget checks for # this setting though. if ($captcha) { if ( $captcha->{validated} ) { $self->required(0); $self->widget('NoRender'); } else { $self->required(1); $self->widget('Captcha'); $self->image( $captcha->{image} ); } } else { $self->required(1); $self->widget('Captcha'); $self->gen_captcha; } return; } sub validate { my $self = shift; my $captcha = $self->form->get_captcha; unless ( $captcha->{rnd} eq $self->value ) { $self->add_error($self->get_message('captcha_verify_failed')); $self->gen_captcha; } else { $captcha->{validated} = 1; } return !$self->has_errors; } sub fif { } sub gen_captcha { my $self = shift; require GD::SecurityImage; my ( $image, $type, $rnd ) = GD::SecurityImage->new( height => $self->height, width => $self->width, scramble => $self->scramble, lines => $self->lines, gd_font => $self->gd_font, )->random->create->out; my $captcha = { image => $image, type => $type, rnd => $rnd, validated => 0, }; $self->image($image); $self->form->set_captcha($captcha); } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Captcha - captcha field with GD::SecurityImage =head1 VERSION version 0.40050 =head1 SYNOPSIS A Captcha class using GD::SecurityImage. Requires that three methods be available from a form object: $self->form->get_captcha; $self->form->set_captcha; Using Catalyst and the Catalyst session plugin this field can be used in a form by using L. package MyApp::Form::Post; use HTML::FormHandler::Moose; with 'HTML::FormHandler::TraitFor::Captcha'; You can set the following attributes on the 'captcha' field: height, width, scramble, lines, gd_font Example: has 'captcha' => ( height => '24', width => '70' ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Checkbox.pm0000644000077000007700000000431412221042077022760 0ustar gshankgshankpackage HTML::FormHandler::Field::Checkbox; # ABSTRACT: a checkbox field type use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field'; our $VERSION = '0.02'; has '+widget' => ( default => 'Checkbox' ); has 'checkbox_value' => ( is => 'rw', default => 1 ); has '+input_without_param' => ( default => 0 ); has '+type_attr' => ( default => 'checkbox' ); has 'option_label' => ( is => 'rw' ); sub value { my $field = shift; return $field->next::method(@_) if @_; my $v = $field->next::method(); return defined $v ? $v : 0; } sub validate { my $self = shift; $self->add_error($self->get_message('required'), $self->loc_label) if( $self->required && !$self->value ); return; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Checkbox - a checkbox field type =head1 VERSION version 0.40050 =head1 DESCRIPTION This field is very similar to the Boolean Widget except that this field allows other positive values besides 1. Since unselected checkboxes do not return a parameter, fields with Checkbox type will always be set to the 'input_without_param' default if they do not appear in the form. =head2 widget checkbox =head2 checkbox_value In order to create the HTML for a checkbox, there must be a 'value="xx"'. This value is specified with the 'checkbox_value' attribute, which defaults to 1. =head2 input_without_param If the checkbox is not checked, it will be set to the value of this attribute (the unchecked value). Default = 0. Because unchecked checkboxes do not return anything in the HTTP parameters, the absence of a checkbox key in the parameters hash forces this field to this value. This means that Checkbox fields, unlike other fields, will not be ignored if there is no input. If a particular checkbox should not be processed for a particular form, you must set 'inactive' to 1 instead. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Compound.pm0000644000077000007700000000771012221042077023021 0ustar gshankgshankpackage HTML::FormHandler::Field::Compound; # ABSTRACT: field consisting of subfields use Moose; extends 'HTML::FormHandler::Field'; with 'HTML::FormHandler::Fields'; with 'HTML::FormHandler::BuildFields'; with 'HTML::FormHandler::InitResult'; has '+widget' => ( default => 'Compound' ); has 'is_compound' => ( is => 'ro', isa => 'Bool', default => 1 ); has 'item' => ( is => 'rw', clearer => 'clear_item' ); has '+do_wrapper' => ( default => 0 ); has '+do_label' => ( default => 0 ); has 'primary_key' => ( is => 'rw', isa => 'ArrayRef', predicate => 'has_primary_key', ); has '+field_name_space' => ( default => sub { my $self = shift; return $self->form->field_name_space if $self->form && $self->form->field_name_space; return []; }, ); sub BUILD { my $self = shift; $self->_build_fields; } # this is for testing compound fields outside # of a form sub test_validate_field { my $self = shift; unless( $self->form ) { if( $self->has_input ) { $self->_result_from_input( $self->result, $self->input );; } else { $self->_result_from_fields( $self->result ); } } $self->validate_field; unless( $self->form ) { foreach my $err_res (@{$self->result->error_results}) { $self->result->_push_errors($err_res->all_errors); } } } around '_result_from_object' => sub { my $orig = shift; my $self = shift; my ( $self_result, $item ) = @_; $self->item($item) if $item; $self->$orig(@_); }; after 'clear_data' => sub { my $self = shift; $self->clear_item; }; around '_result_from_input' => sub { my $orig = shift; my $self = shift; my ( $self_result, $input, $exists ) = @_; if ( !$input && !$exists ) { return $self->_result_from_fields($self_result); } else { return $self->$orig(@_); } }; __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Compound - field consisting of subfields =head1 VERSION version 0.40050 =head1 SYNOPSIS This field class is designed as the base (parent) class for fields with multiple subfields. Examples are L and L. A compound parent class requires the use of sub-fields prepended with the parent class name plus a dot has_field 'birthdate' => ( type => 'DateTime' ); has_field 'birthdate.year' => ( type => 'Year' ); has_field 'birthdate.month' => ( type => 'Month' ); has_field 'birthdate.day' => ( type => 'MonthDay'); If all validation is performed in the parent class so that no validation is necessary in the child classes, then the field class 'Nested' may be used. The array of subfields is available in the 'fields' array in the compound field: $form->field('birthdate')->fields Error messages will be available in the field on which the error occurred. You can access 'error_fields' on the form or on Compound fields (and subclasses, like Repeatable). The process method of this field runs the process methods on the child fields and then builds a hash of these fields values. This hash is available for further processing by L and the validate method. =head2 widget Widget type is 'compound' =head2 build_update_subfields You can set 'defaults' or other settings in a 'build_update_subfields' method, which contains attribute settings that will be merged with field definitions when the fields are built. Use the 'by_flag' key with 'repeatable', 'compound', and 'contains' subkeys, or use the 'all' key for settings which apply to all subfields in the compound field. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Date.pm0000644000077000007700000001253612221042077022114 0ustar gshankgshankpackage HTML::FormHandler::Field::Date; # ABSTRACT: a date field with formats use Moose; extends 'HTML::FormHandler::Field::Text'; use DateTime; use DateTime::Format::Strptime; our $VERSION = '0.03'; has '+html5_type_attr' => ( default => 'date' ); has 'format' => ( is => 'rw', isa => 'Str', default => "%Y-%m-%d" ); has 'locale' => ( is => 'rw', isa => 'Str' ); # TODO has 'time_zone' => ( is => 'rw', isa => 'Str' ); # TODO has 'date_start' => ( is => 'rw', isa => 'Str', clearer => 'clear_date_start' ); has 'date_end' => ( is => 'rw', isa => 'Str', clearer => 'clear_date_end' ); has '+size' => ( default => '10' ); has '+deflate_method' => ( default => sub { \&date_deflate } ); # translator for Datepicker formats to DateTime strftime formats my $dp_to_dt = { "d" => "\%e", # day of month (no leading zero) "dd" => "\%1", # day of month (2 digits) "%d" "o" => "\%4", # day of year (no leading zero) "%{day_of_year}" "oo" => "\%j", # day of year (3 digits) "D" => "\%a", # day name long "DD" => "\%A", # day name short "m" => "\%5", # month of year (no leading zero) "%{day_of_month}" "mm" => "\%3", # month of year (two digits) "%m" "M" => "\%b", # Month name short "MM" => "\%B", # Month name long "y" => "\%2", # year (2 digits) "%y" "yy" => "\%Y", # year (4 digits) "@" => "\%s", # epoch }; our $class_messages = { 'date_early' => 'Date is too early', 'date_late' => 'Date is too late', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub date_deflate { my ( $self, $value ) = @_; # if not a DateTime, assume correctly formatted string and return return $value unless ref $value eq 'DateTime'; my $format = $self->get_strf_format; my $string = $value->strftime($format); return $string; } sub validate { my $self = shift; my $format = $self->get_strf_format; my $strp = DateTime::Format::Strptime->new( pattern => $format ); my $dt = eval { $strp->parse_datetime( $self->value ) }; unless ($dt) { $self->add_error( $strp->errmsg || $@ ); return; } $self->_set_value($dt); my $val_strp = DateTime::Format::Strptime->new( pattern => "%Y-%m-%d" ); if ( $self->date_start ) { my $date_start = $val_strp->parse_datetime( $self->date_start ); die "date_start: " . $val_strp->errmsg unless $date_start; my $cmp = DateTime->compare( $date_start, $dt ); $self->add_error($self->get_message('date_early')) if $cmp eq 1; } if ( $self->date_end ) { my $date_end = $val_strp->parse_datetime( $self->date_end ); die "date_end: " . $val_strp->errmsg unless $date_end; my $cmp = DateTime->compare( $date_end, $dt ); $self->add_error($self->get_message('date_late')) if $cmp eq -1; } } sub get_strf_format { my $self = shift; # if contains %, then it's a strftime format return $self->format if $self->format =~ /\%/; my $format = $self->format; foreach my $dpf ( reverse sort keys %{$dp_to_dt} ) { my $strf = $dp_to_dt->{$dpf}; $format =~ s/$dpf/$strf/g; } $format =~ s/\%1/\%d/g, $format =~ s/\%2/\%y/g, $format =~ s/\%3/\%m/g, $format =~ s/\%4/\%{day_of_year}/g, $format =~ s/\%5/\%{day_of_month}/g, return $format; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Date - a date field with formats =head1 VERSION version 0.40050 =head1 SUMMARY This field may be used with the jQuery Datepicker plugin. You can specify the format for the date using jQuery formatDate strings or DateTime strftime formats. (Default format is format => '%Y-%m-%d'.) d - "%e" - day of month (no leading zero) dd - "%d" - day of month (two digit) o - "%{day_of_year}" - day of the year (no leading zeros) oo - "%j" - day of the year (three digit) D - "%a" - day name short DD - "%A" - day name long m - "%{day_of_month}" - month of year (no leading zero) mm - "%m" - month of year (two digit) "%m" M - "%b" - month name short MM - "%B" - month name long y - "%y" - year (two digit) yy - "%Y" - year (four digit) @ - "%s" - Unix timestamp (ms since 01/01/1970) For example: has_field 'start_date' => ( type => 'Date', format => "dd/mm/y" ); or has_field 'start_date' => ( type => 'Date', format => "%d/%m/%y" ); You can also set 'date_end' and 'date_start' attributes for validation of the date range. Use iso_8601 formats for these dates ("yyyy-mm-dd"); has_field 'start_date' => ( type => 'Date', date_start => "2009-12-25" ); Customize error messages 'date_early' and 'date_late': has_field 'start_date' => ( type => 'Date, messages => { date_early => 'Pick a later date', date_late => 'Pick an earlier date', } ); If form has 'is_html5' flag active it will render instead of type="text" =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/DateMDY.pm0000644000077000007700000000146012221042077022460 0ustar gshankgshankpackage HTML::FormHandler::Field::DateMDY; # ABSTRACT: m/d/y date field use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Date'; has '+format' => ( default => '%m/%d/%Y' ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::DateMDY - m/d/y date field =head1 VERSION version 0.40050 =head1 SYNOPSIS For date fields in the format nn/nn/nnnn. This simply inherits from L and sets the format to "%m/%d/%Y". =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/DateTime.pm0000644000077000007700000000525012221042077022726 0ustar gshankgshankpackage HTML::FormHandler::Field::DateTime; # ABSTRACT: compound DateTime field use Moose; extends 'HTML::FormHandler::Field::Compound'; use DateTime; use Try::Tiny; our $VERSION = '0.04'; has '+widget' => ( default => 'Compound' ); has '+inflate_default_method' => ( default => sub { \&datetime_inflate } ); our $class_messages = { 'datetime_invalid' => 'Not a valid DateTime', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub datetime_inflate { my ( $self, $value ) = @_; return $value unless ref $value eq 'DateTime'; my %hash; foreach my $field ( $self->all_fields ) { my $meth = $field->name; $hash{$meth} = $value->$meth; } return \%hash; } sub validate { my ($self) = @_; my @dt_parms; foreach my $child ( $self->all_fields ) { next unless $child->value; push @dt_parms, ( $child->accessor => $child->value ); } # set the value my $dt; try { $dt = DateTime->new(@dt_parms); } catch { $self->add_error( $self->get_message('datetime_invalid') ); }; if( $dt ) { $self->_set_value($dt); } else { $self->_set_value( {@dt_parms} ); } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::DateTime - compound DateTime field =head1 VERSION version 0.40050 =head1 DESCRIPTION This is a compound field that requires you to define the subfields for month/day/year/hour/minute. Widget type is 'compound'. If you want to use drop-down select boxes for your DateTime, you can select fields like: has_field 'my_date' => ( type => 'DateTime' ); has_field 'my_date.month' => ( type => 'Month' ); has_field 'my_date.day' => ( type => 'MonthDay' ); has_field 'my_date.year' => ( type => 'Year' ); has_field 'my_date.hour' => ( type => 'Hour' ); has_field 'my_date.minute' => ( type => 'Minute' ); If you want simple input fields: has_field 'my_date' => ( type => 'DateTime' ); has_field 'my_date.month' => ( type => 'Integer', range_start => 1, range_end => 12 ); has_field 'my_date.day' => ( type => 'Integer', range_start => 1, range_end => 31 ); Customizable error: 'datetime_invalid' (default = "Not a valid DateTime") See the 'Date' field for a single input date field. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Display.pm0000644000077000007700000000530712221042077022642 0ustar gshankgshankpackage HTML::FormHandler::Field::Display; # ABSTRACT: display only field use Moose; extends 'HTML::FormHandler::Field::NoValue'; use namespace::autoclean; has 'html' => ( is => 'rw', isa => 'Str', builder => 'build_html', lazy => 1 ); sub build_html {''} has 'set_html' => ( isa => 'Str', is => 'ro'); has '+do_label' => ( default => 0 ); has 'render_method' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', lazy => 1, predicate => 'does_render_method', handles => { 'render' => 'execute_method' }, builder => 'build_render_method', ); sub build_render_method { my $self = shift; my $set_html = $self->set_html; $set_html ||= "html_" . HTML::FormHandler::Field::convert_full_name($self->full_name); return sub { my $self = shift; $self->form->$set_html($self); } if ( $self->form && $self->form->can($set_html) ); return sub { my $self = shift; return $self->html; }; } sub _result_from_object { my ( $self, $result, $value ) = @_; $self->_set_result($result); $self->value($value); $result->_set_field_def($self); return $result; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Display - display only field =head1 VERSION version 0.40050 =head1 SYNOPSIS This class can be used for fields that are display only. It will render the value returned by a form's 'html_' method, or the field's 'html' attribute. has_field 'explanation' => ( type => 'Display', html => '

This is an explanation...

' ); or in a form: has_field 'explanation' => ( type => 'Display' ); sub html_explanation { my ( $self, $field ) = @_; if( $self->something ) { return '

This type of explanation...

'; } else { return '

Another type of explanation...

'; } } #---- has_field 'username' => ( type => 'Display' ); sub html_username { my ( $self, $field ) = @_; return '
User: ' . $field->value . '
'; } or set the name of the rendering method: has_field 'explanation' => ( type => 'Display', set_html => 'my_explanation' ); sub my_explanation { .... } or provide a 'render_method': has_field 'my_button' => ( type => 'Display', render_method => \&render_my_button ); sub render_my_button { my $self = shift; .... return '...'; } =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Duration.pm0000644000077000007700000000335312221042077023021 0ustar gshankgshankpackage HTML::FormHandler::Field::Duration; # ABSTRACT: DateTime::Duration from HTML form values use Moose; extends 'HTML::FormHandler::Field::Compound'; use DateTime; our $VERSION = '0.01'; our $class_messages = { 'duration_invalid' => 'Invalid value for [_1]: [_2]', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub validate { my ($self) = @_; my @dur_parms; foreach my $child ( $self->all_fields ) { unless ( $child->has_value && $child->value =~ /^\d+$/ ) { $self->add_error( $self->get_message('duration_invalid'), $self->loc_label, $child->loc_label ); next; } push @dur_parms, ( $child->accessor => $child->value ); } # set the value my $duration = DateTime::Duration->new(@dur_parms); $self->_set_value($duration); } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Duration - DateTime::Duration from HTML form values =head1 VERSION version 0.40050 =head1 SubFields Subfield names: years, months, weeks, days, hours, minutes, seconds, nanoseconds For example: has_field 'duration' => ( type => 'Duration' ); has_field 'duration.hours' => ( type => 'Hour' ); has_field 'duration.minutes' => ( type => 'Minute' ); Customize error message 'duration_invalid' (default 'Invalid value for [_1]: [_2]') =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Email.pm0000644000077000007700000000451012221042077022257 0ustar gshankgshankpackage HTML::FormHandler::Field::Email; # ABSTRACT: validates email using Email::Valid use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; use Email::Valid; our $VERSION = '0.02'; our $class_messages = { 'email_format' => 'Email should be of the format [_1]', }; has '+html5_type_attr' => ( default => 'email' ); has 'email_valid_params' => ( is => 'rw', isa => 'HashRef', ); has 'preserve_case' => ( is => 'rw', isa => 'Bool', ); sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } apply( [ { transform => sub { my ( $value, $field ) = @_; return $value if $field->preserve_case; return lc( $value ); } }, { check => sub { my ( $value, $field ) = @_; my $checked = Email::Valid->address( %{ $field->email_valid_params || {} }, -address => $value, ); $field->value($checked) if $checked; }, message => sub { my ( $value, $field ) = @_; return [$field->get_message('email_format'), 'someuser@example.com']; }, } ] ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Email - validates email using Email::Valid =head1 VERSION version 0.40050 =head1 DESCRIPTION Validates that the input looks like an email address using L. Widget type is 'text'. If form has 'is_html5' flag active it will render instead of type="text" This field has an 'email_valid_params' attribute that accepts a hash reference of extra values passed to L when validating email addresses. If you want to preserve the case of the email address, set the 'preserve_case' attribute. =head1 DEPENDENCIES L =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/File.pm0000644000077000007700000000162312221042077022111 0ustar gshankgshankpackage HTML::FormHandler::Field::File; # ABSTRACT: simple file field; does no processing use Moose; extends 'HTML::FormHandler::Field'; has '+widget' => ( default => 'Upload' ); has '+type_attr' => ( default => 'file' ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::File - simple file field; does no processing =head1 VERSION version 0.40050 =head1 SYNOPSIS This field does nothing and is here mainly for testing purposes. If you use this field you'll have to handle the actual uploaded file yourself. See L =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Float.pm0000644000077000007700000001013012221042077022270 0ustar gshankgshankpackage HTML::FormHandler::Field::Float; # ABSTRACT: validate a float value use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.02'; has '+size' => ( default => 8 ); has 'precision' => ( isa => 'Int|Undef', is => 'rw', default => 2 ); has 'decimal_symbol' => ( isa => 'Str', is => 'rw', default => '.'); has 'decimal_symbol_for_db' => ( isa => 'Str', is => 'rw', default => '.'); has '+inflate_method' => ( default => sub { \&inflate_float } ); has '+deflate_method' => ( default => sub { \&deflate_float } ); our $class_messages = { 'float_needed' => 'Must be a number. May contain numbers, +, - and decimal separator \'[_1]\'', 'float_size' => 'Total size of number must be less than or equal to [_1], but is [_2]', 'float_precision' => 'May have a maximum of [quant,_1,digit] after the decimal point, but has [_2]', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub inflate_float { my ( $self, $value ) = @_; return $value unless defined $value; $value =~ s/^\+//; return $value; } sub deflate_float { my ( $self, $value ) = @_; return $value unless defined $value; my $symbol = $self->decimal_symbol; my $symbol_db = $self->decimal_symbol_for_db; $value =~ s/\Q$symbol_db\E/$symbol/x; return $value; } sub validate { my $field = shift; #return unless $field->next::method; my ($integer_part, $decimal_part) = (); my $value = $field->value; my $symbol = $field->decimal_symbol; my $symbol_db = $field->decimal_symbol_for_db; if ($value =~ m/^-?([0-9]+)(\Q$symbol\E([0-9]+))?$/x) { # \Q ... \E - All the characters between the \Q and the \E are interpreted as literal characters. $integer_part = $1; $decimal_part = defined $3 ? $3 : ''; } else { return $field->add_error( $field->get_message('float_needed'), $symbol ); } if ( my $allowed_size = $field->size ) { my $total_size = length($integer_part) + length($decimal_part); return $field->add_error( $field->get_message('float_size'), $allowed_size, $total_size ) if $total_size > $allowed_size; } if ( my $allowed_precision = $field->precision ) { return $field->add_error( $field->get_message('float_precision'), $allowed_precision, length $decimal_part) if length $decimal_part > $allowed_precision; } # Inflate to database accepted format $value =~ s/\Q$symbol\E/$symbol_db/x; $field->_set_value($value); return 1; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; =pod =head1 NAME HTML::FormHandler::Field::Float - validate a float value =head1 VERSION version 0.40050 =head1 DESCRIPTION This accepts a positive or negative float/integer. Negative numbers may be prefixed with a dash. By default a max of eight digits including 2 precision are accepted. Default decimal symbol is ','. Widget type is 'text'. # For example 1234,12 has size of 6 and precision of 2 # and separator symbol of ',' has_field 'test_result' => ( type => 'Float', size => 8, # Total size of number including decimal part. precision => 2, # Size of the part after decimal symbol. decimal_symbol => '.', # Decimal symbol accepted in web page form decimal_symbol_for_db => '.', # For inflation. Decimal symbol accepted in DB, which automatically converted to. range_start => 0, range_end => 100 ); =head2 messages float_needed float_size float_precision =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 __END__ HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Hidden.pm0000644000077000007700000000151612221042077022426 0ustar gshankgshankpackage HTML::FormHandler::Field::Hidden; # ABSTRACT: hidden field use Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.01'; has '+widget' => ( default => 'Hidden' ); has '+do_label' => ( default => 0 ); has '+html5_type_attr' => ( default => 'hidden' ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Hidden - hidden field =head1 VERSION version 0.40050 =head1 DESCRIPTION This is a text field that uses the 'hidden' widget type, for HTML of type 'hidden'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Hour.pm0000644000077000007700000000141412221042077022145 0ustar gshankgshankpackage HTML::FormHandler::Field::Hour; # ABSTRACT: accept integer from 0 to 23 use Moose; extends 'HTML::FormHandler::Field::IntRange'; our $VERSION = '0.03'; has '+range_start' => ( default => 0 ); has '+range_end' => ( default => 23 ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Hour - accept integer from 0 to 23 =head1 VERSION version 0.40050 =head1 DESCRIPTION Enter an integer from 0 to 23 hours. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Integer.pm0000644000077000007700000000331112221042077022623 0ustar gshankgshankpackage HTML::FormHandler::Field::Integer; # ABSTRACT: validate an integer value use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.02'; has '+size' => ( default => 8 ); has '+html5_type_attr' => ( default => 'number' ); our $class_messages = { 'integer_needed' => 'Value must be an integer', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } apply( [ { transform => sub { my $value = shift; $value =~ s/^\+//; return $value; } }, { check => sub { $_[0] =~ /^-?[0-9]+$/ }, message => sub { my ( $value, $field ) = @_; return $field->get_message('integer_needed'); }, } ] ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Integer - validate an integer value =head1 VERSION version 0.40050 =head1 DESCRIPTION This accepts a positive or negative integer. Negative integers may be prefixed with a dash. By default a max of eight digits are accepted. Widget type is 'text'. If form has 'is_html5' flag active it will render instead of type="text" The 'range_start' and 'range_end' attributes may be used to limit valid numbers. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/IntRange.pm0000644000077000007700000000316312221042077022742 0ustar gshankgshankpackage HTML::FormHandler::Field::IntRange; # ABSTRACT: integer range in select list use Moose; extends 'HTML::FormHandler::Field::Select'; our $VERSION = '0.01'; has 'label_format' => ( isa => 'Str', is => 'rw', default => '%d' ); has '+range_start' => ( default => 1 ); has '+range_end' => ( default => 10 ); sub build_options { my $self = shift; my $start = $self->range_start; my $end = $self->range_end; for ( $start, $end ) { die "Both range_start and range_end must be defined" unless defined $_; die "Integer ranges must be integers" unless /^\d+$/; } die "range_start must be less than range_end" unless $start < $end; my $format = $self->label_format || die 'IntRange needs label_format'; return [ map { { value => $_, label => sprintf( $format, $_ ) } } $self->range_start .. $self->range_end ]; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::IntRange - integer range in select list =head1 VERSION version 0.40050 =head1 DESCRIPTION This field generates a select list of numbers from 1 to 10. Override the range_start and range_end for a select list with a different range. has_field 'age' => ( type => 'IntRange', range_start => 0, range_end => 100 ); Widget type is 'select'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Minute.pm0000644000077000007700000000154212221042077022473 0ustar gshankgshankpackage HTML::FormHandler::Field::Minute; # ABSTRACT: input range from 0 to 59 use Moose; extends 'HTML::FormHandler::Field::IntRange'; our $VERSION = '0.01'; has '+range_start' => ( default => 0 ); has '+range_end' => ( default => 59 ); has '+label_format' => ( default => '%02d' ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Minute - input range from 0 to 59 =head1 VERSION version 0.40050 =head1 DESCRIPTION Generate a select list for entering a minute value. Widget type is 'select'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Money.pm0000644000077000007700000000357312221042077022327 0ustar gshankgshankpackage HTML::FormHandler::Field::Money; # ABSTRACT: US currency-like values use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.01'; has '+html5_type_attr' => ( default => 'number' ); our $class_messages = { 'money_convert' => 'Value cannot be converted to money', 'money_real' => 'Value must be a real number', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } apply( [ { transform => sub { my $value = shift; $value =~ s/^\$//; return $value; } }, { transform => sub { sprintf '%.2f', $_[0] }, message => sub { my ( $value, $field ) = @_; return [$field->get_message('money_convert'), $value]; }, }, { check => sub { $_[0] =~ /^-?\d+\.?\d*$/ }, message => sub { my ( $value, $field ) = @_; return [$field->get_message('money_real'), $value]; }, } ] ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Money - US currency-like values =head1 VERSION version 0.40050 =head1 DESCRIPTION Validates that a positive or negative real value is entered. Formatted with two decimal places. Uses a period for the decimal point. Widget type is 'text'. If form has 'is_html5' flag active it will render instead of type="text" =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Month.pm0000644000077000007700000000142312221042077022315 0ustar gshankgshankpackage HTML::FormHandler::Field::Month; # ABSTRACT: select list 1 to 12 use Moose; extends 'HTML::FormHandler::Field::IntRange'; our $VERSION = '0.01'; has '+range_start' => ( default => 1 ); has '+range_end' => ( default => 12 ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Month - select list 1 to 12 =head1 VERSION version 0.40050 =head1 DESCRIPTION Select list for range of 1 to 12. Widget type is 'select' =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/MonthDay.pm0000644000077000007700000000141512221042077022754 0ustar gshankgshankpackage HTML::FormHandler::Field::MonthDay; # ABSTRACT: select list 1 to 31 use Moose; extends 'HTML::FormHandler::Field::IntRange'; our $VERSION = '0.01'; has '+range_start' => ( default => 1 ); has '+range_end' => ( default => 31 ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::MonthDay - select list 1 to 31 =head1 VERSION version 0.40050 =head1 DESCRIPTION Generates a select list for integers 1 to 31. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/MonthName.pm0000644000077000007700000000201012221042077023107 0ustar gshankgshankpackage HTML::FormHandler::Field::MonthName; # ABSTRACT: select list with month names use Moose; extends 'HTML::FormHandler::Field::Select'; our $VERSION = '0.01'; sub build_options { my $i = 1; my @months = qw/ January February March April May June July August September October November December /; return [ map { { value => $i++, label => $_ } } @months ]; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::MonthName - select list with month names =head1 VERSION version 0.40050 =head1 DESCRIPTION Generates a list of English month names. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Multiple.pm0000644000077000007700000000306112221042077023023 0ustar gshankgshankpackage HTML::FormHandler::Field::Multiple; # ABSTRACT: multiple select list use Moose; extends 'HTML::FormHandler::Field::Select'; our $VERSION = '0.01'; has '+multiple' => ( default => 1 ); has '+size' => ( default => 5 ); has '+sort_options_method' => ( default => sub { \&default_sort_options } ); sub default_sort_options { my ( $self, $options ) = @_; return $options unless scalar @$options && defined $self->value; my $value = $self->deflate($self->value); return $options unless scalar @$value; # This places the currently selected options at the top of the list # Makes the drop down lists a bit nicer my %selected = map { $_ => 1 } @$value; my @out = grep { $selected{ $_->{value} } } @$options; push @out, grep { !$selected{ $_->{value} } } @$options; return \@out; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Multiple - multiple select list =head1 VERSION version 0.40050 =head1 DESCRIPTION This is a convenience field that inherits from the Select field and pre-sets some attributes. It sets the 'multiple' flag, sets the 'size' attribute to 5, and sets the 'sort_options_method' to move the currently selected options to the top of the options list. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Nested.pm0000644000077000007700000000144712221042077022460 0ustar gshankgshankpackage HTML::FormHandler::Field::Nested; # ABSTRACT: for nested elements of compound fields use Moose; extends 'HTML::FormHandler::Field::Text'; __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Nested - for nested elements of compound fields =head1 VERSION version 0.40050 =head1 SYNOPSIS This field class is intended for nested elements of compound fields. It does no particular validation, since the compound field should handle that. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/NonEditable.pm0000644000077000007700000000170312221042077023415 0ustar gshankgshankpackage HTML::FormHandler::Field::NonEditable; # ABSTRACT: reset field use Moose; extends 'HTML::FormHandler::Field::NoValue'; has '+widget' => ( default => 'Span' ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::NonEditable - reset field =head1 VERSION version 0.40050 =head1 SYNOPSIS Another flavor of a display field, but unlike L it's intended to be rendered somewhat more like a "real" field, like the 'non-editable' "fields" in Bootstrap. has_field 'source' => ( type => 'NonEditable', value => 'Outsourced' ); By default uses the 'Span' widget. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/NoValue.pm0000644000077000007700000000333512221042077022605 0ustar gshankgshankpackage HTML::FormHandler::Field::NoValue; # ABSTRACT: base class for submit field use Moose; extends 'HTML::FormHandler::Field'; has 'html' => ( is => 'rw', isa => 'Str', default => '' ); has 'value' => ( is => 'rw', predicate => 'has_value', clearer => 'clear_value', ); sub _result_from_fields { my ( $self, $result ) = @_; my $value = $self->get_default_value; if ( $value ) { $self->value($value); } $self->_set_result($result); $result->_set_field_def($self); return $result; } sub _result_from_input { my ( $self, $result, $input, $exists ) = @_; $self->_set_result($result); $result->_set_field_def($self); return $result; } sub _result_from_object { my ( $self, $result, $value ) = @_; $self->_set_result($result); $result->_set_field_def($self); return $result; } sub fif { } has '+widget' => ( default => '' ); has '+noupdate' => ( default => 1 ); sub validate_field { } #sub clear_value { } sub render { my $self = shift; return $self->html; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::NoValue - base class for submit field =head1 VERSION version 0.40050 =head1 SYNOPSIS This is the base class for the Submit & Reset fields. It can be used for fields that do not produce valid 'values'. It should not be used for fields that produce a value or need validating. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Password.pm0000644000077000007700000000565712221042077023047 0ustar gshankgshankpackage HTML::FormHandler::Field::Password; # ABSTRACT: password field use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.04'; has '+widget' => ( default => 'Password' ); has '+password' => ( default => 1 ); has 'ne_username' => ( isa => 'Str', is => 'rw' ); has '+type_attr' => ( default => 'password' ); has '+html5_type_attr' => ( default => 'password' ); our $class_messages = { 'required' => 'Please enter a password in this field', 'password_ne_username' => 'Password must not match [_1]', }; sub get_class_messages { my $self = shift; my $messages = { %{ $self->next::method }, %$class_messages, }; $messages->{required} = $self->required_message if $self->required_message; return $messages; } after 'validate_field' => sub { my $self = shift; if ( !$self->required && !( defined( $self->value ) && length( $self->value ) ) ) { $self->noupdate(1); $self->clear_errors; } }; sub validate { my $self = shift; $self->noupdate(0); return unless $self->next::method; my $value = $self->value; if ( $self->form && $self->ne_username ) { my $username = $self->form->get_param( $self->ne_username ); return $self->add_error( $self->get_message('password_ne_username'), $self->ne_username ) if $username && $username eq $value; } return 1; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Password - password field =head1 VERSION version 0.40050 =head1 DESCRIPTION The password field has a default minimum length of 6, which can be easily changed: has_field 'password' => ( type => 'Password', minlength => 7 ); It does not come with additional default checks, since password requirements vary so widely. There are a few constraints in the L modules which could be used with this field: NoSpaces, WordChars, NotAllDigits. These constraints can be used in the field definitions 'apply': use HTML::FormHandler::Types ('NoSpaces', 'WordChars', 'NotAllDigits' ); ... has_field 'password' => ( type => 'Password', apply => [ NoSpaces, WordChars, NotAllDigits ], ); You can add your own constraints in addition, of course. If a password field is not required, then the field will be marked 'noupdate', to prevent a null from being saved into the database. =head2 ne_username Set this attribute to the name of your username field (default 'username') if you want to check that the password is not the same as the username. Does not check by default. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/PasswordConf.pm0000644000077000007700000000423712221042077023646 0ustar gshankgshankpackage HTML::FormHandler::Field::PasswordConf; # ABSTRACT: password confirmation use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.03'; has '+widget' => ( default => 'Password' ); has '+password' => ( default => 1 ); has '+required' => ( default => 1 ); has 'password_field' => ( isa => 'Str', is => 'rw', default => 'password' ); has 'pass_conf_message' => ( isa => 'Str', is => 'rw' ); our $class_messages = { required => 'Please enter a password confirmation', pass_conf_not_matched => 'The password confirmation does not match the password', }; sub get_class_messages { my $self = shift; my $messages = { %{ $self->next::method }, %$class_messages, }; $messages->{pass_conf_not_matched} = $self->pass_conf_message if $self->pass_conf_message; return $messages; } sub validate { my $self = shift; my $value = $self->value; my $password = $self->form->field( $self->password_field )->value || ''; if ( $password ne $self->value ) { $self->add_error( $self->get_message('pass_conf_not_matched') ); return; } return 1; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::PasswordConf - password confirmation =head1 VERSION version 0.40050 =head1 DESCRIPTION This field needs to be declared after the related Password field (or more precisely it needs to come after the Password field in the list returned by the L method). =head2 password_field Set this attribute to the name of your password field (default 'password') Customize error message 'pass_conf_not_matched' or 'required' has_field '_password' => ( type => 'PasswordConf', messages => { required => 'You must enter the password a second time' }, ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/PosInteger.pm0000644000077000007700000000233512221042077023312 0ustar gshankgshankpackage HTML::FormHandler::Field::PosInteger; # ABSTRACT: positive integer field use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Integer'; our $VERSION = '0.02'; our $class_messages = { 'integer_positive' => 'Value must be a positive integer', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } apply( [ { check => sub { $_[0] >= 0 }, message => sub { my ( $value, $field ) = @_; return $field->get_message('integer_positive'); }, } ] ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::PosInteger - positive integer field =head1 VERSION version 0.40050 =head1 DESCRIPTION Tests that the input is an integer and has a positive value. Customize error message 'integer_positive'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/PrimaryKey.pm0000644000077000007700000000257012221042077023330 0ustar gshankgshankpackage HTML::FormHandler::Field::PrimaryKey; # ABSTRACT: primary key field use Moose; extends 'HTML::FormHandler::Field'; has 'is_primary_key' => ( isa => 'Bool', is => 'ro', default => '1' ); has '+widget' => ( default => 'Hidden' ); has '+do_label' => ( default => 0 ); has '+no_value_if_empty' => ( default => 1 ); sub BUILD { my $self = shift; if ( $self->has_parent ) { if ( $self->parent->has_primary_key ) { push @{ $self->parent->primary_key }, $self; } else { $self->parent->primary_key( [ $self ] ); } } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::PrimaryKey - primary key field =head1 VERSION version 0.40050 =head1 SYNOPSIS This field is for providing the primary key for Repeatable fields: has_field 'addresses' => ( type => 'Repeatable' ); has_field 'addresses.address_id' => ( type => 'PrimaryKey' ); Do not use this field to hold the primary key of the form's main db object (item). That primary key is in the 'item_id' attribute. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Repeatable/0000755000077000007700000000000012221042077022736 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Repeatable/Instance.pm0000644000077000007700000000217012221042077025040 0ustar gshankgshankpackage # hide from Pause HTML::FormHandler::Field::Repeatable::Instance; # ABSTRACT: used internally by repeatable fields use Moose; extends 'HTML::FormHandler::Field::Compound'; sub BUILD { my $self = shift; $self->add_wrapper_class('hfh-repinst') unless $self->has_wrapper_class; } sub build_tags {{ wrapper => 1 }} has '+do_label' => ( default => 0 ); has '+do_wrapper' => ( default => 1 ); has '+no_value_if_empty' => ( default => 1 ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Repeatable::Instance - used internally by repeatable fields =head1 VERSION version 0.40050 =head1 SYNOPSIS This is a simple container class to hold an instance of a Repeatable field. It will have a name like '0', '1'... Users should not need to use this class. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Repeatable.pm0000644000077000007700000003341112221042077023276 0ustar gshankgshankpackage HTML::FormHandler::Field::Repeatable; # ABSTRACT: repeatable (array) field use Moose; extends 'HTML::FormHandler::Field::Compound'; use aliased 'HTML::FormHandler::Field::Repeatable::Instance'; use HTML::FormHandler::Field::PrimaryKey; use HTML::FormHandler::Merge ('merge'); use Data::Clone ('data_clone'); has 'contains' => ( isa => 'HTML::FormHandler::Field', is => 'rw', predicate => 'has_contains', ); has 'init_contains' => ( is => 'rw', isa => 'HashRef', traits => ['Hash'], default => sub {{}}, handles => { has_init_contains => 'count' }, ); has 'num_when_empty' => ( isa => 'Int', is => 'rw', default => 1 ); has 'num_extra' => ( isa => 'Int', is => 'rw', default => 0 ); has 'setup_for_js' => ( isa => 'Bool', is => 'rw' ); has 'index' => ( isa => 'Int', is => 'rw', default => 0 ); has 'auto_id' => ( isa => 'Bool', is => 'rw', default => 0 ); has 'is_repeatable' => ( isa => 'Bool', is => 'ro', default => 1 ); has '+widget' => ( default => 'Repeatable' ); sub _fields_validate { my $self = shift; # loop through array of fields and validate my @value_array; foreach my $field ( $self->all_fields ) { next if ( $field->is_inactive ); # Validate each field and "inflate" input -> value. $field->validate_field; # this calls the field's 'validate' routine push @value_array, $field->value if $field->has_value; } $self->_set_value( \@value_array ); } sub init_state { my $self = shift; # must clear out instances built last time unless ( $self->has_contains ) { if ( $self->num_fields == 1 && $self->field('contains') ) { $self->field('contains')->is_contains(1); $self->contains( $self->field('contains') ); } else { $self->contains( $self->create_element ); } } $self->clear_fields; } sub create_element { my ($self) = @_; my $instance; my $instance_attr = { name => 'contains', parent => $self, type => 'Repeatable::Instance', is_contains => 1, }; # primary_key array is used for reloading after database update $instance_attr->{primary_key} = $self->primary_key if $self->has_primary_key; if( $self->has_init_contains ) { $instance_attr = merge( $self->init_contains, $instance_attr ); } if( $self->form ) { $instance_attr->{form} = $self->form; $instance = $self->form->_make_adhoc_field( 'HTML::FormHandler::Field::Repeatable::Instance', $instance_attr ); } else { $instance = Instance->new( %$instance_attr ); } # copy the fields from this field into the instance $instance->add_field( $self->all_fields ); foreach my $fld ( $instance->all_fields ) { $fld->parent($instance); } # set required flag $instance->required( $self->required ); # auto_id has no way to change widgets...deprecate this? if ( $self->auto_id ) { unless ( grep $_->can('is_primary_key') && $_->is_primary_key, $instance->all_fields ) { my $field; my $field_attr = { name => 'id', parent => $instance }; if ( $self->form ) { # this will pull in the widget role $field_attr->{form} = $self->form; $field = $self->form->_make_adhoc_field( 'HTML::FormHandler::Field::PrimaryKey', $field_attr ); } else { # the following won't have a widget role applied $field = HTML::FormHandler::Field::PrimaryKey->new( %$field_attr ); } $instance->add_field($field); } } $_->parent($instance) for $instance->all_fields; return $instance; } sub clone_element { my ( $self, $index ) = @_; my $field = $self->contains->clone( errors => [], error_fields => [] ); $field->name($index); $field->parent($self); if ( $field->has_fields ) { $self->clone_fields( $field, [ $field->all_fields ] ); } return $field; } sub clone_fields { my ( $self, $parent, $fields ) = @_; my @field_array; $parent->fields( [] ); foreach my $field ( @{$fields} ) { my $new_field = $field->clone( errors => [], error_fields => [] ); if ( $new_field->has_fields ) { $self->clone_fields( $new_field, [ $new_field->all_fields ] ); } $new_field->parent($parent); $parent->add_field($new_field); } } # params exist and validation will be performed (later) sub _result_from_input { my ( $self, $result, $input ) = @_; $self->init_state; $result->_set_input($input); $self->_set_result($result); # if Repeatable has array input, need to build instances $self->fields( [] ); my $index = 0; if ( ref $input eq 'ARRAY' ) { # build appropriate instance array foreach my $element ( @{$input} ) { next if not defined $element; # skip empty slots my $field = $self->clone_element($index); my $result = HTML::FormHandler::Field::Result->new( name => $index, parent => $self->result ); $result = $field->_result_from_input( $result, $element, 1 ); $self->result->add_result($result); $self->add_field($field); $index++; } } $self->index($index); $self->_setup_for_js if $self->setup_for_js; $self->result->_set_field_def($self); return $self->result; } sub _setup_for_js { my $self = shift; return unless $self->form; my $full_name = $self->full_name; my $index_level =()= $full_name =~ /{index\d+}/g; $index_level++; my $field_name = "{index-$index_level}"; my $field = $self->_add_extra($field_name); my $rendered = $field->render; # remove extra result & field, now that it's rendered $self->result->_pop_result; $self->_pop_field; # set the information in the form # $self->index is the index of the next instance $self->form->set_for_js( $self->full_name, { index => $self->index, html => $rendered, level => $index_level } ); } # this is called when there is an init_object or a db item with values sub _result_from_object { my ( $self, $result, $values ) = @_; return $self->_result_from_fields($result) if ( $self->num_when_empty > 0 && !$values ); $self->item($values); $self->init_state; $self->_set_result($result); # Create field instances and fill with values my $index = 0; my @new_values; $self->fields( [] ); $values = [$values] if ( $values && ref $values ne 'ARRAY' ); foreach my $element ( @{$values} ) { next unless $element; my $field = $self->clone_element($index); my $result = HTML::FormHandler::Field::Result->new( name => $index, parent => $self->result ); if( $field->has_inflate_default_method ) { $element = $field->inflate_default($element); } $result = $field->_result_from_object( $result, $element ); push @new_values, $result->value; $self->add_field($field); $self->result->add_result( $field->result ); $index++; } if( my $num_extra = $self->num_extra ) { while ($num_extra ) { $self->_add_extra($index); $num_extra--; $index++; } } $self->index($index); $self->_setup_for_js if $self->setup_for_js; $values = \@new_values if scalar @new_values; $self->_set_value($values); $self->result->_set_field_def($self); return $self->result; } sub _add_extra { my ($self, $index) = @_; my $field = $self->clone_element($index); my $result = HTML::FormHandler::Field::Result->new( name => $index, parent => $self->result ); $result = $field->_result_from_fields($result); $self->result->add_result($result) if $result; $self->add_field($field); return $field; } sub add_extra { my ( $self, $count ) = @_; $count = 1 if not defined $count; my $index = $self->index; while ( $count ) { $self->_add_extra($index); $count--; $index++; } $self->index($index); } # create an empty field sub _result_from_fields { my ( $self, $result ) = @_; # check for defaults if ( my @values = $self->get_default_value ) { return $self->_result_from_object( $result, \@values ); } $self->init_state; $self->_set_result($result); my $count = $self->num_when_empty; my $index = 0; # build empty instance $self->fields( [] ); while ( $count > 0 ) { my $field = $self->clone_element($index); my $result = HTML::FormHandler::Field::Result->new( name => $index, parent => $self->result ); $result = $field->_result_from_fields($result); $self->result->add_result($result) if $result; $self->add_field($field); $index++; $count--; } $self->index($index); $self->_setup_for_js if $self->setup_for_js; $self->result->_set_field_def($self); return $result; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Repeatable - repeatable (array) field =head1 VERSION version 0.40050 =head1 SYNOPSIS In a form, for an array of hashrefs, equivalent to a 'has_many' database relationship. has_field 'addresses' => ( type => 'Repeatable' ); has_field 'addresses.address_id' => ( type => 'PrimaryKey' ); has_field 'addresses.street'; has_field 'addresses.city'; has_field 'addresses.state'; In a form, for an array of single fields (not directly equivalent to a database relationship) use the 'contains' pseudo field name: has_field 'tags' => ( type => 'Repeatable' ); has_field 'tags.contains' => ( type => 'Text', apply => [ { check => ['perl', 'programming', 'linux', 'internet'], message => 'Not a valid tag' } ] ); or use 'contains' with single fields which are compound fields: has_field 'addresses' => ( type => 'Repeatable' ); has_field 'addresses.contains' => ( type => '+MyAddress' ); If the MyAddress field contains fields 'address_id', 'street', 'city', and 'state', then this syntax is functionally equivalent to the first method where the fields are declared with dots ('addresses.city'); You can pass attributes to the 'contains' field by supplying an 'init_contains' hashref. has_field 'addresses' => ( type => 'Repeatable, init_contains => { wrapper_attr => { class => ['hfh', 'repinst'] } }, ); =head1 DESCRIPTION This class represents an array. It can either be an array of hashrefs (compound fields) or an array of single fields. The 'contains' keyword is used for elements that do not have names because they are not hash elements. This field node will build arrays of fields from the parameters or an initial object, or empty fields for an empty form. The name of the element fields will be an array index, starting with 0. Therefore the first array element can be accessed with: $form->field('tags')->field('0') $form->field('addresses')->field('0')->field('city') or using the shortcut form: $form->field('tags.0') $form->field('addresses.0.city') The array of elements will be in C<< $form->field('addresses')->fields >>. The subfields of the elements will be in a fields array in each element. foreach my $element ( $form->field('addresses')->fields ) { foreach my $field ( $element->fields ) { # do something } } Every field that has a 'fields' array will also have an 'error_fields' array containing references to the fields that contain errors. =head2 Complications When new elements are created by a Repeatable field in a database form an attempt is made to re-load the Repeatable field from the database, because otherwise the repeatable elements will not have primary keys. Although this works, if you have included other fields in your repeatable elements that do *not* come from the database, the defaults/values must be able to be loaded in a way that works when the form is initialized from the database item. This is only an issue if you re-present the form after the database update succeeds. =head1 ATTRIBUTES =over =item index This attribute contains the next index number available to create an additional array element. =item num_when_empty This attribute (default 1) indicates how many empty fields to present in an empty form which hasn't been filled from parameters or database rows. =item num_extra When the field results are built from an existing object (item or init_object) an additional number of repeatable elements will be created equal to this number. Default is 0. =item add_extra When a form is submitted and the field results are built from the input parameters, it's not clear when or if an additional repeatable element might be wanted. The method 'add_extra' will add an empty repeatable element. $form->process( params => {....} ); $form->field('my_repeatable')->add_extra(1); This might be useful if the form is being re-presented to the user. =item setup_for_js setup_for_js => 1 Saves information in the form for javascript to use when adding repeatable elements. If using the example javascript, you also must set 'do_wrapper' in the Repeatable field and use the Bootstrap widget wrapper (or wrap the repeatable elements in a 'controls' div by setting tags => { controls_div => 1 }. See t/repeatable/js.t for an example. See also L and L. =back =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Reset.pm0000644000077000007700000000165712221042077022323 0ustar gshankgshankpackage HTML::FormHandler::Field::Reset; # ABSTRACT: reset field use Moose; extends 'HTML::FormHandler::Field::NoValue'; has '+widget' => ( default => 'Reset' ); has '+value' => ( default => 'Reset' ); has '+type_attr' => ( default => 'reset' ); has '+html5_type_attr' => ( default => 'reset' ); sub do_label {0} __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Reset - reset field =head1 VERSION version 0.40050 =head1 SYNOPSIS Use this field to declare a reset field in your form. has_field 'reset' => ( type => 'Reset', value => 'Restore' ); Uses the 'reset' widget. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Result.pm0000644000077000007700000000341112221042077022505 0ustar gshankgshankpackage HTML::FormHandler::Field::Result; # ABSTRACT: result class for fields use Moose; with 'HTML::FormHandler::Result::Role'; has 'value' => ( is => 'ro', writer => '_set_value', clearer => '_clear_value', predicate => 'has_value', ); has 'field_def' => ( is => 'ro', isa => 'HTML::FormHandler::Field', writer => '_set_field_def', ); has 'missing' => ( is => 'rw', isa => 'Bool' ); sub fif { my $self = shift; return $self->field_def->fif($self); } sub fields_fif { my ( $self, $prefix ) = @_; return $self->field_def->fields_fif( $self, $prefix ); } sub render { my $self = shift; return $self->field_def->render($self); } sub peek { my ( $self, $indent ) = @_; $indent ||= ''; my $name = $self->field_def ? $self->field_def->full_name : $self->name; my $type = $self->field_def ? $self->field_def->type : 'unknown'; my $string = $indent . "result " . $name . " type: " . $type . "\n"; $string .= $indent . "....value => " . $self->value . "\n" if $self->has_value; if( $self->has_results ) { $indent .= ' '; foreach my $res ( $self->results ) { $string .= $res->peek( $indent ); } } return $string; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Result - result class for fields =head1 VERSION version 0.40050 =head1 SYNOPSIS Result class for L =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/RmElement.pm0000644000077000007700000000404212221042077023120 0ustar gshankgshankpackage HTML::FormHandler::Field::RmElement; # ABSTRACT: field to support repeatable javascript remove use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Display'; use HTML::FormHandler::Render::Util ('process_attrs'); has '+do_wrapper' => ( default => 1 ); has '+value' => ( default => 'Remove' ); sub build_render_method { return sub { my ( $self, $result ) = @_; $result ||= $self->result; my $value = $self->html || $self->html_filter($self->_localize($self->value)); my $attrs = $self->element_attributes($result); push @{$attrs->{class}}, ( 'rm_element', 'btn' ); $attrs->{'data-rep-elem-id'} = $self->parent->id; $attrs->{id} = $self->id; my $attr_str = process_attrs($attrs); my $wrapper_tag = $self->get_tag('wrapper_tag') || 'div'; my $output = qq{<$wrapper_tag$attr_str>$value}; $output = $self->wrap_field($self->result, $output); return $output; }; } __PACKAGE__->meta->make_immutable; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::RmElement - field to support repeatable javascript remove =head1 VERSION version 0.40050 =head1 SYNOPSIS EXAMPLE field for rendering an RmElement field for doing javascript removals of repeatable elements. You probably want to make your own. The main requirements are that the button have 1) the 'rm_element' class, 2) a 'data-rep-elem-id' attribute that contains the id of the repeatable instance that you want to remove (C<< $self->parent->id >>). This field should be a subfield of the Repeatable, probably either first or last. =head1 NAME HTML::FormHandler::Field::RmElement =head1 ATTRIBUTES has_field 'rm_element' => ( type => 'RmElement', value => 'Remove', ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Second.pm0000644000077000007700000000141712221042077022446 0ustar gshankgshankpackage HTML::FormHandler::Field::Second; # ABSTRACT: select list 0 to 59 use Moose; extends 'HTML::FormHandler::Field::IntRange'; our $VERSION = '0.01'; has '+range_start' => ( default => 0 ); has '+range_end' => ( default => 59 ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Second - select list 0 to 59 =head1 VERSION version 0.40050 =head1 DESCRIPTION A select field for seconds in the range of 0 to 59. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Select.pm0000644000077000007700000005025512221042077022456 0ustar gshankgshankpackage HTML::FormHandler::Field::Select; # ABSTRACT: select fields use Moose; extends 'HTML::FormHandler::Field'; use Carp; our $VERSION = '0.03'; has 'options' => ( isa => 'ArrayRef', is => 'rw', traits => ['Array'], auto_deref => 1, handles => { all_options => 'elements', reset_options => 'clear', clear_options => 'clear', has_options => 'count', num_options => 'count', }, lazy => 1, builder => 'build_options' ); sub options_ref { [shift->options] } # this is used for rendering has 'options_index' => ( traits => ['Counter'], isa => 'Num', is => 'rw', default => 0, handles => { inc_options_index => 'inc', dec_options_index => 'dec', reset_options_index => 'reset' }, ); sub clear_data { my $self = shift; $self->next::method(); $self->reset_options_index; } sub build_options { [] } has 'options_from' => ( isa => 'Str', is => 'rw', default => 'none' ); has 'do_not_reload' => ( isa => 'Bool', is => 'ro' ); has 'no_option_validation' => ( isa => 'Bool', is => 'rw' ); sub BUILD { my $self = shift; $self->build_options_method; if( $self->options && $self->has_options ) { $self->options_from('build'); $self->default_from_options([$self->options]); } $self->input_without_param; # vivify } has 'options_method' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', writer => '_set_options_method', predicate => 'has_options_method', handles => { 'get_options' => 'execute_method' }, ); sub build_options_method { my $self = shift; my $set_options = $self->set_options; $set_options ||= "options_" . HTML::FormHandler::Field::convert_full_name($self->full_name); if ( $self->form && $self->form->can($set_options) ) { my $attr = $self->form->meta->find_method_by_name( $set_options ); if ( $attr and $attr->isa('Moose::Meta::Method::Accessor') ) { $self->_set_options_method( sub { my $self = shift; $self->form->$set_options; } ); } else { $self->_set_options_method( sub { my $self = shift; $self->form->$set_options($self); } ); } } } has 'sort_options_method' => ( traits => ['Code'], is => 'rw', isa => 'CodeRef', predicate => 'has_sort_options_method', handles => { sort_options => 'execute_method', }, ); has 'set_options' => ( isa => 'Str', is => 'ro'); has 'multiple' => ( isa => 'Bool', is => 'rw', default => '0' ); # following is for unusual case where a multiple select is a has_many type relation has 'has_many' => ( isa => 'Str', is => 'rw' ); has 'size' => ( isa => 'Int|Undef', is => 'rw' ); has 'label_column' => ( isa => 'Str', is => 'rw', default => 'name' ); has 'localize_labels' => ( isa => 'Bool', is => 'rw' ); has 'active_column' => ( isa => 'Str', is => 'rw', default => 'active' ); has 'auto_widget_size' => ( isa => 'Int', is => 'rw', default => '0' ); has 'sort_column' => ( isa => 'Str|ArrayRef[Str]', is => 'rw' ); has '+widget' => ( default => 'Select' ); sub html_element { 'select' } has '+type_attr' => ( default => 'select' ); has 'empty_select' => ( isa => 'Str', is => 'rw' ); has '+deflate_method' => ( default => sub { \&select_deflate } ); has '+input_without_param' => ( lazy => 1, builder => 'build_input_without_param' ); sub build_input_without_param { my $self = shift; if( $self->multiple ) { $self->not_nullable(1); return []; } else { return ''; } } has 'value_when_empty' => ( is => 'ro', lazy => 1, builder => 'build_value_when_empty' ); sub build_value_when_empty { my $self = shift; return [] if $self->multiple; return undef; } our $class_messages = { 'select_not_multiple' => 'This field does not take multiple values', 'select_invalid_value' => '\'[_1]\' is not a valid value', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub select_widget { my $field = shift; my $size = $field->auto_widget_size; return $field->widget unless $field->widget eq 'Select' && $size; my $options = $field->options || []; return 'Select' if @$options > $size; return $field->multiple ? 'checkbox_group' : 'radio_group'; } sub as_label { my $self = shift; my $value = $self->value; return unless defined $value; if ( $self->multiple ) { my @labels; my %value_hash; @value_hash{@$value} = (); for ( $self->options ) { if ( exists $value_hash{$_->{value}} ) { push @labels, $_->{label}; delete $value_hash{$_->{value}}; last unless keys %value_hash; } } my $str = join(', ', @labels); return $str; } else { for ( $self->options ) { return $_->{label} if $_->{value} eq $value; } } return; } sub _inner_validate_field { my ($self) = @_; my $value = $self->value; return unless defined $value; # nothing to check if ( ref $value eq 'ARRAY' && !( $self->can('multiple') && $self->multiple ) ) { $self->add_error( $self->get_message('select_not_multiple') ); return; } elsif ( ref $value ne 'ARRAY' && $self->multiple ) { $value = [$value]; $self->_set_value($value); } return if $self->no_option_validation; # create a lookup hash my %options; foreach my $opt ( @{ $self->options } ) { if ( exists $opt->{group} ) { foreach my $group_opt ( @{ $opt->{options} } ) { $options{$group_opt->{value}} = 1; } } else { $options{$opt->{value}} = 1; } } if( $self->has_many ) { $value = [map { $_->{$self->has_many} } @$value]; } for my $value ( ref $value eq 'ARRAY' ? @$value : ($value) ) { unless ( $options{$value} ) { $self->add_error($self->get_message('select_invalid_value'), $value); return; } } return 1; } sub _result_from_object { my ( $self, $result, $item ) = @_; $result = $self->next::method( $result, $item ); $self->_load_options; $result->_set_value($self->default) if( defined $self->default && not $result->has_value ); return $result; } sub _result_from_fields { my ( $self, $result ) = @_; $result = $self->next::method($result); $self->_load_options; $result->_set_value($self->default) if( defined $self->default && not $result->has_value ); return $result; } sub _result_from_input { my ( $self, $result, $input, $exists ) = @_; $input = ref $input eq 'ARRAY' ? $input : [$input] if $self->multiple; $result = $self->next::method( $result, $input, $exists ); $self->_load_options; $result->_set_value($self->default) if( defined $self->default && not $result->has_value ); return $result; } sub _load_options { my $self = shift; return if ( $self->options_from eq 'build' || ( $self->has_options && $self->do_not_reload ) ); my @options; if( $self->has_options_method ) { @options = $self->get_options; $self->options_from('method'); } elsif ( $self->form ) { my $full_accessor; $full_accessor = $self->parent->full_accessor if $self->parent; @options = $self->form->lookup_options( $self, $full_accessor ); $self->options_from('model') if scalar @options; } return unless @options; # so if there isn't an options method and no options # from a table, already set options attributes stays put # allow returning arrayref if ( ref $options[0] eq 'ARRAY' ) { @options = @{ $options[0] }; } return unless @options; my $opts; # if options_ is returning an already constructed array of hashrefs if ( ref $options[0] eq 'HASH' ) { $opts = \@options; $self->default_from_options($opts); } else { croak "Options array must contain an even number of elements for field " . $self->name if @options % 2; push @{$opts}, { value => shift @options, label => shift @options } while @options; } if ($opts) { # sort options if sort method exists $opts = $self->sort_options($opts) if $self->has_sort_options_method; $self->options($opts); } } # This is because setting 'checked => 1' or 'selected => 1' in an options # hashref is the equivalent of setting a default on the field. Originally # that was handled only in rendering, but it moved knowledge about where # the 'fif' value came from into the renderer, which was bad. So instead # we're setting the defaults from the options. # It's probably better to use 'defaults' to start with, but since there are # people using this method, this at least normalizes it. sub default_from_options { my ( $self, $options ) = @_; my @defaults = map { $_->{value} } grep { $_->{checked} || $_->{selected} } @$options; if( scalar @defaults ) { if( $self->multiple ) { $self->default(\@defaults); } else { $self->default($defaults[0]); } } } before 'value' => sub { my $self = shift; return undef unless $self->has_result; my $value = $self->result->value; if( $self->multiple ) { if ( !defined $value || $value eq '' || ( ref $value eq 'ARRAY' && scalar @$value == 0 ) ) { $self->_set_value( $self->value_when_empty ); } elsif ( $self->has_many && scalar @$value && ref($value->[0]) ne 'HASH' ) { my @new_values; foreach my $ele (@$value) { push @new_values, { $self->has_many => $ele }; } $self->_set_value( \@new_values ); } } }; sub select_deflate { my ( $self, $value ) = @_; return $value unless ( $self->has_many && $self->multiple ); # the following is for the edge case of a has_many select return $value unless ref($value) eq 'ARRAY' && scalar @$value && ref($value->[0]) eq 'HASH'; return [map { $_->{$self->has_many} } @$value]; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Select - select fields =head1 VERSION version 0.40050 =head1 DESCRIPTION This is a field that includes a list of possible valid options. This can be used for select and multiple-select fields. Widget type is 'select'. Because select lists and checkbox_groups do not return an HTTP parameter when the entire list is unselected, the Select field must assume that the lack of a param means unselection. So to avoid setting a Select field, it must be set to inactive, not merely not included in the HTML for a form. This field type can also be used for fields that use the 'radio_group' widget, and the 'checkbox_group' widget (for selects with multiple flag turned on, or that use the Multiple field). =head2 options The 'options' array can come from a number of different places: =over 4 =item From a field declaration In a field declaration: has_field 'opt_in' => ( type => 'Select', widget => 'RadioGroup', options => [{ value => 0, label => 'No'}, { value => 1, label => 'Yes'} ] ); =item From a field class 'build_options' method In a custom field class: package MyApp::Field::WeekDay; use Moose; extends 'HTML::FormHandler::Field::Select'; .... sub build_options { my $i = 0; my @days = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ); return [ map { { value => $i++, label => $_ } } @days ]; } =item From a coderef supplied to the field definition has_field 'flim' => ( type => 'Select', options_method => \&flim_options ); sub flim_options { } =item From a form 'options_' method or attribute has_field 'fruit' => ( type => 'Select' ); sub options_fruit { return ( 1 => 'apples', 2 => 'oranges', 3 => 'kiwi', ); } -- or -- has 'options_fruit' => ( is => 'rw', traits => ['Array'], default => sub { [1 => 'apples', 2 => 'oranges', 3 => 'kiwi'] } ); Notice that, as a convenience, you can return a simple array (or arrayref) for the options array in the 'options_field_name' method. The hashrefs with 'value' and 'label' keys will be constructed for you by FormHandler. =item From the database The final source of the options array is a database when the name of the accessor is a relation to the table holding the information used to construct the select list. The primary key is used as the value. The other columns used are: label_column -- Used for the labels in the options (default 'name') active_column -- The name of the column to be used in the query (default 'active') that allows the rows retrieved to be restricted sort_column -- The name or arrayref of names of the column(s) used to sort the options See also L, the 'lookup_options' method. =back =head2 Customizing options Additional attributes can be added in the options array hashref, by using the 'attributes' key. If you have custom rendering code, you can add any additional key that you want, of course. Note that you should *not* set 'checked' or 'selected' attributes in options. That is handled by setting a field default. An options array with an extra 'note' key: sub options_license { my $self = shift; return unless $self->schema; my $licenses = $self->schema->resultset('License')->search({active => 1}, {order_by => 'sequence'}); my @selections; while ( my $license = $licenses->next ) { push @selections, { value => $license->id, label => $license->label, note => $license->note }; } return @selections; } Setting the select element to disabled: sub options_license { my $self = shift; return unless $self->schema; my $licenses = $self->schema->resultset('License')->search(undef, {order_by => 'sequence'}); my @selections; while ( my $license = $licenses->next ) { push @selections, { value => $license->id, label => $license->label, attributes => { disabled => ($license->active == 0) ? 1 : 0 } }; } return @selections; } You can also divide the options up into option groups. See the section on rendering. =head2 Reloading options If the options come from the options_ method or the database, they will be reloaded every time the form is reloaded because the available options may have changed. To prevent this from happening when the available options are known to be static, set the 'do_not_reload' flag, and the options will not be reloaded after the first time =head2 Sorting options The sorting of the options may be changed using a 'sort_options' method in a custom field class. The 'Multiple' field uses this method to put the already selected options at the top of the list. Note that this won't work with option groups. =head1 Attributes and Methods =head2 options This is an array of hashes for this field. Each has must have a label and value keys. =head2 options_method Coderef of method to return options =head2 multiple If true allows multiple input values =head2 size This can be used to store how many items should be offered in the UI at a given time. Defaults to 0. =head2 empty_select Set to the string value of the select label if you want the renderer to create an empty select value. This only affects rendering - it does not add an entry to the list of options. has_field 'fruit' => ( type => 'Select', empty_select => '---Choose a Fruit---' ); =head1 value_when_empty Usually the empty value is an empty arrayref. This attribute allows changing that. Used by SelectCSV field. =head2 label_column Sets or returns the name of the method to call on the foreign class to fetch the text to use for the select list. Refers to the method (or column) name to use in a related object class for the label for select lists. Defaults to "name". =head2 localize_labels For the renderers: whether or not to call the localize method on the select labels. Default is off. =head2 active_column Sets or returns the name of a boolean column that is used as a flag to indicate that a row is active or not. Rows that are not active are ignored. The default is "active". If this column exists on the class then the list of options will included only rows that are marked "active". The exception is any columns that are marked inactive, but are also part of the input data will be included with brackets around the label. This allows updating records that might have data that is now considered inactive. =head2 auto_widget_size This is a way to provide a hint as to when to automatically select the widget to display for fields with a small number of options. For example, this can be used to decided to display a radio select for select lists smaller than the size specified. See L below. =head2 sort_column Sets or returns the column or arrayref of columns used in the foreign class for sorting the options labels. Default is undefined. If not defined the label_column is used as the sort condition. =head2 select_widget If the widget is 'select' for the field then will look if the field also has a L. If the options list is less than or equal to the L then will return C if L is false, otherwise will return C. =head2 as_label Returns the option label for the option value that matches the field's current value. Can be helpful for displaying information about the field in a more friendly format. =head2 no_option_validation Set this flag to true if you don't want to validate the options that are submitted. This would generally only happen if the options are generated via javascript. =head2 error messages Customize 'select_invalid_value' and 'select_not_multiple'. Though neither of these messages should really be seen by users in a properly constructed select. =head1 Rendering The 'select' field can be rendered by the 'Select', 'RadioGroup', and 'CheckboxGroup' widgets. 'RadioGroup' is for a single select, and 'CheckboxGroup' is for a multiple select. Option groups can be rendered by providing an options arrays with 'group' elements containing options: sub options_testop { ( { group => 'First Group', options => [ { value => 1, label => 'One' }, { value => 2, label => 'Two' }, { value => 3, label => 'Three' }, ], }, { group => 'Second Group', options => [ { value => 4, label => 'Four' }, { value => 5, label => 'Five' }, { value => 6, label => 'Six' }, ], }, ) } The select rendering widgets all have a 'render_option' method, which may be useful for situations when you want to split up the rendering of a radio group or checkbox group. =head1 Database relations Also see L. The single select is for a DBIC 'belongs_to' relation. The multiple select is for a 'many_to_many' relation. There is very limited ability to do multiple select with 'has_many' relations. It will only work in very specific circumstances, and requires setting the 'has_many' attribute to the name of the primary key of the related table. This is a somewhat peculiar data structure for a relational database, and may not be what you really want. A 'has_many' is usually represented with a Repeatable field, and may require custom code if the form structure doesn't match the database structure. See L. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/SelectCSV.pm0000644000077000007700000000322512221042077023025 0ustar gshankgshankpackage HTML::FormHandler::Field::SelectCSV; # ABSTRACT: Multiple select field from CSV value use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Select'; has '+inflate_default_method' => ( default => sub { \&selectcsv_inflate_default } ); has '+deflate_value_method' => ( default => sub { \&selectcsv_deflate_value } ); has '+multiple' => ( default => 1 ); sub build_value_when_empty { undef } sub selectcsv_inflate_default { my ( $self, $value ) = @_; if( defined $value ) { my @values = split (/,/, $value); return @values; } return; } sub selectcsv_deflate_value { my ( $self, $value ) = @_; if ( defined $value ) { my $str = join( ',', sort @$value ); return $str; } return; } sub fif { my $self = shift; my $fif = $self->next::method; $fif = [] if $fif eq ''; return $fif; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::SelectCSV - Multiple select field from CSV value =head1 VERSION version 0.40050 =head1 SYNOPSIS A multiple select field for comma-separated values in the database. It expects database values like: '1,5,7'. The string will be inflated into an arrayref for validation and form filling, and will be deflated into a comma-separated string in the output value. This field is useful for MySQL 'set' columns. =head1 NAME HTML::FormHandler::Field::SelectCSV =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Submit.pm0000644000077000007700000000264412221042077022501 0ustar gshankgshankpackage HTML::FormHandler::Field::Submit; # ABSTRACT: submit field use Moose; extends 'HTML::FormHandler::Field::NoValue'; has '+value' => ( default => 'Save' ); has '+widget' => ( default => 'Submit' ); has '+type_attr' => ( default => 'submit' ); has '+html5_type_attr' => ( default => 'submit' ); sub do_label {0} sub _result_from_input { my ( $self, $result, $input, $exists ) = @_; $self->_set_result($result); $result->_set_input($input); $result->_set_field_def($self); return $result; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Submit - submit field =head1 VERSION version 0.40050 =head1 SYNOPSIS Use this field to declare a submit field in your form. has_field 'submit' => ( type => 'Submit', value => 'Save' ); It will be used by L to construct a form with C<< $form->render >>. Uses the 'submit' widget. If you have multiple submit buttons, currently the only way to test which one has been clicked is with C<< $field->input >>. The 'value' attribute is used for the HTML input field 'value'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Text.pm0000644000077000007700000000577312221042077022170 0ustar gshankgshankpackage HTML::FormHandler::Field::Text; # ABSTRACT: text field use Moose; extends 'HTML::FormHandler::Field'; our $VERSION = '0.01'; has 'size' => ( isa => 'Int|Undef', is => 'rw', default => '0' ); has 'maxlength' => ( isa => 'Int|Undef', is => 'rw' ); has 'maxlength_message' => ( isa => 'Str', is => 'rw', default => 'Field should not exceed [quant,_1,character]. You entered [_2]', ); has 'minlength' => ( isa => 'Int|Undef', is => 'rw', default => '0' ); has 'minlength_message' => ( isa => 'Str', is => 'rw', default => 'Field must be at least [quant,_1,character]. You entered [_2]' ); has '+widget' => ( default => 'Text' ); our $class_messages = { 'text_maxlength' => 'Field should not exceed [quant,_1,character]. You entered [_2]', 'text_minlength' => 'Field must be at least [quant,_1,character]. You entered [_2]', }; sub get_class_messages { my $self = shift; my $messages = { %{ $self->next::method }, %$class_messages, }; $messages->{text_minlength} = $self->minlength_message if $self->minlength_message; $messages->{text_maxlength} = $self->maxlength_message if $self->maxlength_message; return $messages; } sub validate { my $field = shift; return unless $field->next::method; my $value = $field->input; # Check for max length if ( my $maxlength = $field->maxlength ) { return $field->add_error( $field->get_message('text_maxlength'), $maxlength, length $value, $field->loc_label ) if length $value > $maxlength; } # Check for min length if ( my $minlength = $field->minlength ) { return $field->add_error( $field->get_message('text_minlength'), $minlength, length $value, $field->loc_label ) if length $value < $minlength; } return 1; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Text - text field =head1 VERSION version 0.40050 =head1 DESCRIPTION This is a simple text entry field. Widget type is 'text'. =head1 METHODS =head2 size [integer] This is used in constructing HTML. It determines the size of the input field. The 'maxlength' field should be used as a constraint on the size of the field, not this attribute. =head2 minlength [integer] This integer value, if non-zero, defines the minimum number of characters that must be entered. =head2 maxlength [integer] A constraint on the maximum length of the text. =head2 error messages Set error messages (text_minlength, text_maxlength): has_field 'my_text' => ( type => 'Text', messages => { 'text_minlength' => 'Field is too short', 'text_maxlength' => 'Field is too long', } ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/TextArea.pm0000644000077000007700000000157312221042077022753 0ustar gshankgshankpackage HTML::FormHandler::Field::TextArea; # ABSTRACT: textarea input use Moose; extends 'HTML::FormHandler::Field::Text'; our $VERSION = '0.02'; has '+widget' => ( default => 'Textarea' ); has 'cols' => ( isa => 'Int', is => 'rw' ); has 'rows' => ( isa => 'Int', is => 'rw' ); sub html_element { 'textarea' } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::TextArea - textarea input =head1 VERSION version 0.40050 =head1 Summary For HTML textarea. Uses 'textarea' widget. Set cols/row/minlength/maxlength. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/TextCSV.pm0000644000077000007700000000333212221042077022531 0ustar gshankgshankpackage HTML::FormHandler::Field::TextCSV; # ABSTRACT: CSV Text field from multiple use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; has '+deflate_method' => ( default => sub { \&textcsv_deflate } ); has '+inflate_method' => ( default => sub { \&textcsv_inflate } ); has 'multiple' => ( isa => 'Bool', is => 'rw', default => '0' ); sub build_value_when_empty { [] } sub _inner_validate_field { my $self = shift; my $value = $self->value; return unless $value; if ( ref $value ne 'ARRAY' ) { $value = [$value]; $self->_set_value($value); } } sub textcsv_deflate { my ( $self, $value ) = @_; if( defined $value && length $value ) { my $value = ref $value eq 'ARRAY' ? $value : [$value]; my $new_value = join(',', @$value); return $new_value; } return $value; } sub textcsv_inflate { my ( $self, $value ) = @_; if ( defined $value && length $value ) { my @values = split(/,/, $value); return \@values; } return $value; } __PACKAGE__->meta->make_immutable; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::TextCSV - CSV Text field from multiple =head1 VERSION version 0.40050 =head1 SYNOPSIS A text field that takes multiple values from a database and converts them to comma-separated values. This is intended for javascript fields that require that, such as 'select2'. =head1 NAME HTML::FormHandler::Field::TextCSV =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Upload.pm0000644000077000007700000000701412221042077022456 0ustar gshankgshankpackage HTML::FormHandler::Field::Upload; # ABSTRACT: file upload field use Moose; use Moose::Util::TypeConstraints; extends 'HTML::FormHandler::Field'; our $VERSION = '0.02'; has '+widget' => ( default => 'Upload', ); has min_size => ( is => 'rw', isa => 'Maybe[Int]', default => 1 ); has max_size => ( is => 'rw', isa => 'Maybe[Int]', default => 1048576 ); has '+type_attr' => ( default => 'file' ); our $class_messages = { 'upload_file_not_found' => 'File not found for upload field', 'upload_file_empty' => 'File uploaded is empty', 'upload_file_too_small' => 'File is too small (< [_1] bytes)', 'upload_file_too_big' => 'File is too big (> [_1] bytes)', }; sub get_class_messages { my $self = shift; return { %{ $self->next::method }, %$class_messages, } } sub validate { my $self = shift; my $upload = $self->value; my $size = 0; if( blessed $upload && $upload->can('size') ) { $size = $upload->size; } elsif( is_real_fh( $upload ) ) { $size = -s $upload; } else { return $self->add_error($self->get_message('upload_file_not_found')); } return $self->add_error($self->get_message('upload_file_empty')) unless $size > 0; if( defined $self->min_size && $size < $self->min_size ) { $self->add_error( $self->get_message('upload_file_too_small'), $self->min_size ); } if( defined $self->max_size && $size > $self->max_size ) { $self->add_error( $self->get_message('upload_file_too_big'), $self->max_size ); } return; } # stolen from Plack::Util::is_real_fh sub is_real_fh { my $fh = shift; my $reftype = Scalar::Util::reftype($fh) or return; if( $reftype eq 'IO' or $reftype eq 'GLOB' && *{$fh}{IO} ){ my $m_fileno = $fh->fileno; return unless defined $m_fileno; return unless $m_fileno >= 0; my $f_fileno = fileno($fh); return unless defined $f_fileno; return unless $f_fileno >= 0; return 1; } else { return; } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Upload - file upload field =head1 VERSION version 0.40050 =head1 DESCRIPTION This field is designed to be used with a blessed object with a 'size' method, such as L, or a filehandle. Validates that the file is not empty and is within the 'min_size' and 'max_size' limits (limits are in bytes). A form containing this field must have the enctype set. package My::Form::Upload; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+enctype' => ( default => 'multipart/form-data'); has_field 'file' => ( type => 'Upload', max_size => '2000000' ); has_field 'submit' => ( type => 'Submit', value => 'Upload' ); In your controller: my $form = My::Form::Upload->new; my @params = ( file => $c->req->upload('file') ) if $c->req->method eq 'POST'; $form->process( params => { @params } ); return unless ( $form->validated ); You can set the min_size and max_size limits to undef if you don't want them to be validated. =head1 DEPENDENCIES =head2 widget Widget type is 'upload' =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Weekday.pm0000644000077000007700000000170712221042077022626 0ustar gshankgshankpackage HTML::FormHandler::Field::Weekday; # ABSTRACT: select list day of week strings use Moose; extends 'HTML::FormHandler::Field::Select'; our $VERSION = '0.01'; sub build_options { my $i = 0; my @days = qw/ Sunday Monday Tuesday Wednesday Thursday Friday Saturday /; return [ map { { value => $i++, label => $_ } } @days ]; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Weekday - select list day of week strings =head1 VERSION version 0.40050 =head1 DESCRIPTION Creates an option list for the days of the week. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field/Year.pm0000644000077000007700000000172412221042077022134 0ustar gshankgshankpackage HTML::FormHandler::Field::Year; # ABSTRACT: year selection list use Moose; extends 'HTML::FormHandler::Field::IntRange'; our $VERSION = '0.01'; has '+range_start' => ( default => sub { my $year = (localtime)[5] + 1900 - 5; return $year; } ); has '+range_end' => ( default => sub { my $year = (localtime)[5] + 1900 + 10; return $year; } ); __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field::Year - year selection list =head1 VERSION version 0.40050 =head1 DESCRIPTION Provides a list of years starting five years back and extending 10 years into the future. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Field.pm0000644000077000007700000014015112221042077021232 0ustar gshankgshankpackage HTML::FormHandler::Field; # ABSTRACT: base class for fields use HTML::FormHandler::Moose; use HTML::FormHandler::Field::Result; use Try::Tiny; use Moose::Util::TypeConstraints; use HTML::FormHandler::Merge ('merge'); use HTML::FormHandler::Render::Util('cc_widget', 'ucc_widget'); use Sub::Name; with 'HTML::FormHandler::Traits'; with 'HTML::FormHandler::Validate'; with 'HTML::FormHandler::Widget::ApplyRole'; with 'HTML::FormHandler::TraitFor::Types'; our $VERSION = '0.02'; has 'name' => ( isa => 'Str', is => 'rw', required => 1 ); has 'type' => ( isa => 'Str', is => 'rw', default => sub { ref shift } ); has 'parent' => ( is => 'rw', predicate => 'has_parent', weak_ref => 1 ); sub has_fields { } has 'input_without_param' => ( is => 'rw', predicate => 'has_input_without_param' ); has 'not_nullable' => ( is => 'rw', isa => 'Bool' ); has 'no_value_if_empty' => ( is => 'rw', isa => 'Bool' ); has 'validate_when_empty' => ( is => 'rw', isa => 'Bool' ); has 'init_value' => ( is => 'rw', clearer => 'clear_init_value', predicate => 'has_init_value' ); has 'default' => ( is => 'rw' ); has 'default_over_obj' => ( is => 'rw', builder => 'build_default_over_obj' ); sub build_default_over_obj { } has 'result' => ( isa => 'HTML::FormHandler::Field::Result', is => 'ro', weak_ref => 1, clearer => 'clear_result', predicate => 'has_result', writer => '_set_result', handles => [ '_set_input', '_clear_input', '_set_value', '_clear_value', 'errors', 'all_errors', '_push_errors', 'num_errors', 'has_errors', 'clear_errors', 'validated', 'add_warning', 'all_warnings', 'num_warnings', 'has_warnings', 'warnings', 'missing', ], ); has '_pin_result' => ( is => 'ro', reader => '_get_pin_result', writer => '_set_pin_result' ); sub has_input { my $self = shift; return unless $self->has_result; return $self->result->has_input; } sub has_value { my $self = shift; return unless $self->has_result; return $self->result->has_value; } # these should normally only be called for field tests sub reset_result { my $self = shift; $self->clear_result; $self->build_result; } sub build_result { my $self = shift; my @parent = ( 'parent' => $self->parent->result ) if ( $self->parent && $self->parent->result ); my $result = HTML::FormHandler::Field::Result->new( name => $self->name, field_def => $self, @parent ); $self->_set_pin_result($result); # to prevent garbage collection of result $self->_set_result($result); } sub input { my $self = shift; # allow testing fields individually by creating result if no form return undef unless $self->has_result || !$self->form; my $result = $self->result; return $result->_set_input(@_) if @_; return $result->input; } sub value { my $self = shift; # allow testing fields individually by creating result if no form return undef unless $self->has_result || !$self->form; my $result = $self->result; return undef unless $result; return $result->_set_value(@_) if @_; return $result->value; } # for compatibility. deprecate and remove at some point sub clear_input { shift->_clear_input } sub clear_value { shift->_clear_value } sub clear_data { my $self = shift; $self->clear_result; $self->clear_active; } # this is a kludge to allow testing field deflation sub _deflate_and_set_value { my ( $self, $value ) = @_; if( $self->_can_deflate ) { $value = $self->_apply_deflation($value); } $self->_set_value($value); } sub is_repeatable { } has 'fif_from_value' => ( isa => 'Str', is => 'ro' ); sub fif { my ( $self, $result ) = @_; return if ( $self->inactive && !$self->_active ); return '' if $self->password; return unless $result || $self->has_result; my $lresult = $result || $self->result; if ( ( $self->has_result && $self->has_input && !$self->fif_from_value ) || ( $self->fif_from_value && !defined $lresult->value ) ) { return defined $lresult->input ? $lresult->input : ''; } if ( defined $lresult->value ) { if( $self->_can_deflate ) { return $self->_apply_deflation($lresult->value); } else { return $lresult->value; } } elsif ( defined $self->value ) { # this is because checkboxes and submit buttons have their own 'value' # needs to be fixed in some better way return $self->value; } return ''; } has 'accessor' => ( isa => 'Str', is => 'rw', lazy => 1, default => sub { my $self = shift; my $accessor = $self->name; $accessor =~ s/^(.*)\.//g if ( $accessor =~ /\./ ); return $accessor; } ); has 'is_contains' => ( is => 'rw', isa => 'Bool' ); has 'temp' => ( is => 'rw' ); sub has_flag { my ( $self, $flag_name ) = @_; return unless $self->can($flag_name); return $self->$flag_name; } has 'label' => ( isa => 'Maybe[Str]', is => 'rw', lazy => 1, builder => 'build_label', ); has 'do_label' => ( isa => 'Bool', is => 'rw', default => 1 ); has 'build_label_method' => ( is => 'rw', isa => 'CodeRef', traits => ['Code'], handles => { 'build_label' => 'execute_method' }, default => sub { \&default_build_label }, ); sub default_build_label { my $self = shift; my $label = $self->name; $label =~ s/_/ /g; $label = ucfirst($label); return $label; } sub loc_label { my $self = shift; return $self->_localize($self->label); } has 'wrap_label_method' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', predicate => 'does_wrap_label', handles => { 'wrap_label' => 'execute_method' }, ); has 'title' => ( isa => 'Str', is => 'rw' ); has 'style' => ( isa => 'Str', is => 'rw' ); # deprecated; remove in six months. has 'css_class' => ( isa => 'Str', is => 'rw', trigger => \&_css_class_set ); sub _css_class_set { my ( $self, $value ) = @_; $self->add_wrapper_class($value); } # deprecated; remove in six months; has 'input_class' => ( isa => 'Str', is => 'rw', trigger => \&_input_class_set ); sub _input_class_set { my ( $self, $value ) = @_; $self->add_element_class($value); } has 'form' => ( isa => 'HTML::FormHandler', is => 'rw', weak_ref => 1, predicate => 'has_form', ); sub is_form { 0 } has 'html_name' => ( isa => 'Str', is => 'rw', lazy => 1, builder => 'build_html_name' ); sub build_html_name { my $self = shift; my $prefix = ( $self->form && $self->form->html_prefix ) ? $self->form->name . "." : ''; return $prefix . $self->full_name; } has 'widget' => ( isa => 'Str', is => 'rw' ); has 'widget_wrapper' => ( isa => 'Str', is => 'rw' ); has 'do_wrapper' => ( is => 'rw', default => 1 ); sub wrapper { shift->widget_wrapper || '' } sub uwrapper { ucc_widget( shift->widget_wrapper || '' ) || 'simple' } sub twrapper { shift->uwrapper . ".tt" } sub uwidget { ucc_widget( shift->widget || '' ) || 'simple' } sub twidget { shift->uwidget . ".tt" } # for use of wrapper classes has 'wrapper_tags' => ( isa => 'HashRef', traits => ['Hash'], is => 'rw', builder => 'build_wrapper_tags', handles => { has_wrapper_tags => 'count' } ); sub build_wrapper_tags { {} } has 'tags' => ( traits => ['Hash'], isa => 'HashRef', is => 'rw', builder => 'build_tags', handles => { _get_tag => 'get', set_tag => 'set', has_tag => 'exists', tag_exists => 'exists', delete_tag => 'delete', }, ); sub build_tags {{}} sub merge_tags { my ( $self, $new ) = @_; my $old = $self->tags; $self->tags( merge($new, $old) ); } sub get_tag { my ( $self, $name ) = @_; return '' unless $self->tag_exists($name); my $tag = $self->_get_tag($name); return $self->$tag if ref $tag eq 'CODE'; return $tag unless $tag =~ /^%/; ( my $block_name = $tag ) =~ s/^%//; return $self->form->block($block_name)->render if ( $self->form && $self->form->block_exists($block_name) ); return ''; } has 'widget_name_space' => ( isa => 'HFH::ArrayRefStr', is => 'rw', traits => ['Array'], default => sub {[]}, coerce => 1, handles => { push_widget_name_space => 'push', }, ); sub add_widget_name_space { my ( $self, @ns ) = @_; @ns = @{$ns[0]}if( scalar @ns && ref $ns[0] eq 'ARRAY' ); $self->push_widget_name_space(@ns); } has 'order' => ( isa => 'Int', is => 'rw', default => 0 ); # 'inactive' is set in the field declaration, and is static. Default status. has 'inactive' => ( isa => 'Bool', is => 'rw', clearer => 'clear_inactive' ); # 'active' is cleared whenever the form is cleared. Ephemeral activation. has '_active' => ( isa => 'Bool', is => 'rw', clearer => 'clear_active', predicate => 'has__active' ); sub is_active { my $self = shift; return ! $self->is_inactive; } sub is_inactive { my $self = shift; return (($self->inactive && !$self->_active) || (!$self->inactive && $self->has__active && $self->_active == 0 ) ); } has 'id' => ( isa => 'Str', is => 'rw', lazy => 1, builder => 'build_id' ); has 'build_id_method' => ( is => 'rw', isa => 'CodeRef', traits => ['Code'], default => sub { sub { shift->html_name } }, handles => { build_id => 'execute_method' }, ); # html attributes has 'password' => ( isa => 'Bool', is => 'rw' ); has 'disabled' => ( isa => 'Bool', is => 'rw' ); has 'readonly' => ( isa => 'Bool', is => 'rw' ); has 'tabindex' => ( is => 'rw', isa => 'Int' ); sub html_element { 'input' } has 'type_attr' => ( is => 'rw', isa => 'Str', default => 'text' ); has 'html5_type_attr' => ( isa => 'Str', is => 'ro', default => 'text' ); sub input_type { my $self = shift; return $self->html5_type_attr if ( $self->form && $self->form->has_flag('is_html5') ); return $self->type_attr; } # temporary methods for compatibility after name change sub html_attr { shift->element_attr(@_) } sub has_html_attr { shift->has_element_attr(@_) } sub get_html_attr { shift->get_element_attr(@_) } sub set_html_attr { shift->set_element_attr(@_) } { # create the attributes and methods for # element_attr, build_element_attr, element_class, # label_attr, build_label_attr, label_class, # wrapper_attr, build_wrapper_atrr, wrapper_class no strict 'refs'; foreach my $attr ('wrapper', 'element', 'label' ) { # trigger to move 'class' set via _attr to the class slot my $add_meth = "add_${attr}_class"; my $trigger_sub = sub { my ( $self, $value ) = @_; if( my $class = delete $self->{"${attr}_attr"}->{class} ) { $self->$add_meth($class); } }; has "${attr}_attr" => ( is => 'rw', traits => ['Hash'], builder => "build_${attr}_attr", handles => { "has_${attr}_attr" => 'count', "get_${attr}_attr" => 'get', "set_${attr}_attr" => 'set', "delete_${attr}_attr" => 'delete', "exists_${attr}_attr" => 'exists', }, trigger => $trigger_sub, ); # create builders fo _attrs my $attr_builder = __PACKAGE__ . "::build_${attr}_attr"; *$attr_builder = subname $attr_builder, sub {{}}; # create the 'class' slots has "${attr}_class" => ( is => 'rw', isa => 'HFH::ArrayRefStr', traits => ['Array'], coerce => 1, builder => "build_${attr}_class", handles => { "has_${attr}_class" => 'count', "_add_${attr}_class" => 'push', }, ); # create builders for classes my $class_builder = __PACKAGE__ . "::build_${attr}_class"; *$class_builder = subname $class_builder, sub {[]}; # create wrapper for add_to_ to accept arrayref my $add_to_class = __PACKAGE__ . "::add_${attr}_class"; my $_add_meth = __PACKAGE__ . "::_add_${attr}_class"; *$add_to_class = subname $add_to_class, sub { shift->$_add_meth((ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_)); } } } # we're assuming that the only attribute we want in an element wrapper is a class has 'element_wrapper_class' => ( is => 'rw', isa => 'HFH::ArrayRefStr', traits => ['Array'], coerce => 1, builder => "build_element_wrapper_class", handles => { has_element_wrapper_class => 'count', _add_element_wrapper_class => 'push', }, ); sub add_element_wrapper_class { shift->_add_element_wrapper_class((ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_)); } sub build_element_wrapper_class { [] } sub element_wrapper_attributes { my ( $self, $result ) = @_; $result ||= $self->result; # local copy of label_attr my $attr = {}; my $class = [@{$self->element_wrapper_class}]; $attr->{class} = $class if @$class; # call form hook my $mod_attr = $self->form->html_attributes($self, 'element_wrapper', $attr, $result) if $self->form; return ref($mod_attr) eq 'HASH' ? $mod_attr : $attr; } sub attributes { shift->element_attributes(@_) } sub element_attributes { my ( $self, $result ) = @_; $result ||= $self->result; my $attr = {}; # handle html5 attributes if ($self->form && $self->form->has_flag('is_html5')) { $attr->{required} = 'required' if $self->required; $attr->{min} = $self->range_start if defined $self->range_start; $attr->{max} = $self->range_end if defined $self->range_end; } # pull in deprecated attributes for backward compatibility for my $dep_attr ( 'readonly', 'disabled' ) { $attr->{$dep_attr} = $dep_attr if $self->$dep_attr; } for my $dep_attr ( 'style', 'title', 'tabindex' ) { $attr->{$dep_attr} = $self->$dep_attr if defined $self->$dep_attr; } $attr = {%$attr, %{$self->element_attr}}; my $class = [@{$self->element_class}]; $self->add_standard_element_classes($result, $class); $attr->{class} = $class if @$class; # call form hook my $mod_attr = $self->form->html_attributes($self, 'element', $attr, $result) if $self->form; return ref($mod_attr) eq 'HASH' ? $mod_attr : $attr; } sub add_standard_element_classes { my ( $self, $result, $class ) = @_; push @$class, 'error' if $result->has_errors; push @$class, 'warning' if $result->has_warnings; push @$class, 'disabled' if $self->disabled; } sub label_attributes { my ( $self, $result ) = @_; $result ||= $self->result; # local copy of label_attr my $attr = {%{$self->label_attr}}; my $class = [@{$self->label_class}]; $attr->{class} = $class if @$class; # call form hook my $mod_attr = $self->form->html_attributes($self, 'label', $attr, $result) if $self->form; return ref($mod_attr) eq 'HASH' ? $mod_attr : $attr; } sub wrapper_attributes { my ( $self, $result ) = @_; $result ||= $self->result; # copy wrapper my $attr = {%{$self->wrapper_attr}}; my $class = [@{$self->wrapper_class}]; # add 'error' to class $self->add_standard_wrapper_classes($result, $class); $attr->{class} = $class if @$class; # add id if compound field and id doesn't exist unless 'no_wrapper_id' tag $attr->{id} = $self->id if ( $self->has_flag('is_compound') && not exists $attr->{id} && ! $self->get_tag('no_wrapper_id') ); # call form hook my $mod_attr = $self->form->html_attributes($self, 'wrapper', $attr, $result) if $self->form; return ref($mod_attr) eq 'HASH' ? $mod_attr : $attr; } sub add_standard_wrapper_classes { my ( $self, $result, $class ) = @_; push @$class, 'error' if ( $result->has_error_results || $result->has_errors ); push @$class, 'warning' if $result->has_warnings; } sub wrapper_tag { my $self = shift; return $self->get_tag('wrapper_tag') || 'div'; } #===================== # these may be temporary sub field_filename { my $self = shift; return 'checkbox_tag.tt' if $self->input_type eq 'checkbox'; return 'input_tag.tt'; } sub label_tag { my $self = shift; return $self->get_tag('label_tag') || 'label'; } #=================== has 'writeonly' => ( isa => 'Bool', is => 'rw' ); has 'noupdate' => ( isa => 'Bool', is => 'rw' ); #============== sub convert_full_name { my $full_name = shift; $full_name =~ s/\.\d+\./_/g; $full_name =~ s/\./_/g; return $full_name; } has 'validate_method' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', lazy => 1, builder => 'build_validate_method', handles => { '_validate' => 'execute_method' }, ); has 'set_validate' => ( isa => 'Str', is => 'ro',); sub build_validate_method { my $self = shift; my $set_validate = $self->set_validate; $set_validate ||= "validate_" . convert_full_name($self->full_name); return sub { my $self = shift; $self->form->$set_validate($self); } if ( $self->form && $self->form->can($set_validate) ); return sub { }; } has 'default_method' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', writer => '_set_default_method', predicate => 'has_default_method', handles => { '_default' => 'execute_method' }, ); has 'set_default' => ( isa => 'Str', is => 'ro', writer => '_set_default'); # this is not a "true" builder, because sometimes 'default_method' is not set sub build_default_method { my $self = shift; my $set_default = $self->set_default; $set_default ||= "default_" . convert_full_name($self->full_name); if ( $self->form && $self->form->can($set_default) ) { $self->_set_default_method( sub { my $self = shift; return $self->form->$set_default($self, $self->form->item); } ); } } sub get_default_value { my $self = shift; if ( $self->has_default_method ) { return $self->_default; } elsif ( defined $self->default ) { return $self->default; } return; } { # create inflation/deflation methods foreach my $type ( 'inflate_default', 'deflate_value', 'inflate', 'deflate' ) { has "${type}_method" => ( is => 'ro', traits => ['Code'], isa => 'CodeRef', writer => "_set_${type}_method", predicate => "has_${type}_method", handles => { $type => 'execute_method', }, ); } } has 'deflation' => ( is => 'rw', predicate => 'has_deflation', ); has 'trim' => ( is => 'rw', default => sub { { transform => \&default_trim } } ); sub default_trim { my $value = shift; return unless defined $value; my @values = ref $value eq 'ARRAY' ? @$value : ($value); for (@values) { next if ref $_ or !defined; s/^\s+//; s/\s+$//; } return ref $value eq 'ARRAY' ? \@values : $values[0]; } has 'render_filter' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', lazy => 1, builder => 'build_render_filter', handles => { html_filter => 'execute' }, ); sub build_render_filter { my $self = shift; if( $self->form && $self->form->can('render_filter') ) { my $coderef = $self->form->can('render_filter'); return $coderef; } else { return \&default_render_filter; } } sub default_render_filter { my $string = shift; return '' if (!defined $string); $string =~ s/&/&/g; $string =~ s//>/g; $string =~ s/"/"/g; return $string; } has 'input_param' => ( is => 'rw', isa => 'Str' ); has 'language_handle' => ( isa => duck_type( [ qw(maketext) ] ), is => 'rw', reader => 'get_language_handle', writer => 'set_language_handle', predicate => 'has_language_handle' ); sub language_handle { my ( $self, $value ) = @_; if( $value ) { $self->set_language_handle($value); return; } return $self->get_language_handle if( $self->has_language_handle ); # if language handle isn't set use form language handle if possible return $self->form->language_handle if ( $self->has_form ); # no form, no language handle. This should only happen when # testing fields. my $lh; if ( $ENV{LANGUAGE_HANDLE} ) { if ( blessed $ENV{LANGUAGE_HANDLE} ) { $lh = $ENV{LANGUAGE_HANDLE}; } else { $lh = HTML::FormHandler::I18N->get_handle( $ENV{LANGUAGE_HANDLE} ); } } else { require HTML::FormHandler::I18N; $lh = HTML::FormHandler::I18N->get_handle; } $self->set_language_handle($lh); return $lh; } has 'localize_meth' => ( traits => ['Code'], is => 'ro', isa => 'CodeRef', lazy => 1, builder => 'build_localize_meth', handles => { '_localize' => 'execute_method' }, ); sub build_localize_meth { my $self = shift; if( $self->form && $self->form->can('localize_meth') ) { my $coderef = $self->form->can('localize_meth'); return $coderef; } else { return \&default_localize; } } sub default_localize { my ($self, @message) = @_; my $message = $self->language_handle->maketext(@message); return $message; } has 'messages' => ( is => 'rw', isa => 'HashRef', traits => ['Hash'], default => sub {{}}, handles => { '_get_field_message' => 'get', '_has_field_message' => 'exists', 'set_message' => 'set', }, ); our $class_messages = { 'field_invalid' => 'field is invalid', 'range_too_low' => 'Value must be greater than or equal to [_1]', 'range_too_high' => 'Value must be less than or equal to [_1]', 'range_incorrect' => 'Value must be between [_1] and [_2]', 'wrong_value' => 'Wrong value', 'no_match' => '[_1] does not match', 'not_allowed' => '[_1] not allowed', 'error_occurred' => 'error occurred', 'required' => '[_1] field is required', 'unique' => 'Duplicate value for [_1]', }; sub get_class_messages { my $self = shift; my $messages = { %$class_messages }; $messages->{required} = $self->required_message if $self->required_message; $messages->{unique} = $self->unique_message if $self->unique_message; return $messages; } sub get_message { my ( $self, $msg ) = @_; # first look in messages set on individual field return $self->_get_field_message($msg) if $self->_has_field_message($msg); # then look at form messages return $self->form->_get_form_message($msg) if $self->has_form && $self->form->_has_form_message($msg); # then look for messages up through inherited field classes return $self->get_class_messages->{$msg}; } sub all_messages { my $self = shift; my $form_messages = $self->has_form ? $self->form->messages : {}; my $field_messages = $self->messages || {}; my $lclass_messages = $self->my_class_messages || {}; return {%{$lclass_messages}, %{$form_messages}, %{$field_messages}}; } sub BUILDARGS { my $class = shift; # for backwards compatibility; these will be removed eventually my @new; push @new, ('element_attr', {@_}->{html_attr} ) if( exists {@_}->{html_attr} ); push @new, ('do_label', !{@_}->{no_render_label} ) if( exists {@_}->{no_render_label} ); return $class->SUPER::BUILDARGS(@_, @new); } sub BUILD { my ( $self, $params ) = @_; # temporary, for compatibility. move widget_tags to tags $self->merge_tags($self->wrapper_tags) if $self->has_wrapper_tags; # run default method builder $self->build_default_method; # build validate_method; needs to happen before validation # in order to have the "real" repeatable field names, not the instances $self->validate_method; # merge form widget_name_space $self->add_widget_name_space( $self->form->widget_name_space ) if $self->form; # handle apply actions $self->add_action( $self->trim ) if $self->trim; $self->_build_apply_list; $self->add_action( @{ $params->{apply} } ) if $params->{apply}; } # this is the recursive routine that is used # to initialize field results if there is no initial object and no params sub _result_from_fields { my ( $self, $result ) = @_; if ( $self->disabled && $self->has_init_value ) { $result->_set_value($self->init_value); } elsif ( my @values = $self->get_default_value ) { if ( $self->has_inflate_default_method ) { @values = $self->inflate_default(@values); } my $value = @values > 1 ? \@values : shift @values; $self->init_value($value) if defined $value; $result->_set_value($value) if defined $value; } $self->_set_result($result); $result->_set_field_def($self); return $result; } sub _result_from_input { my ( $self, $result, $input, $exists ) = @_; if ($exists) { $result->_set_input($input); } elsif ( $self->disabled ) { # This maybe should come from _result_from_object, but there's # not a reliable way to get there from here. Field can handle... return $self->_result_from_fields( $result ); } elsif ( $self->has_input_without_param ) { $result->_set_input( $self->input_without_param ); } elsif ( $self->form && $self->form->use_fields_for_input_without_param ) { return $self->_result_from_fields( $result ); } $self->_set_result($result); $result->_set_field_def($self); return $result; } sub _result_from_object { my ( $self, $result, $value ) = @_; $self->_set_result($result); if ( $self->form ) { $self->form->init_value( $self, $value ); } else { $self->init_value($value); $result->_set_value($value); } $result->_set_value(undef) if $self->writeonly; $result->_set_field_def($self); return $result; } sub full_name { my $field = shift; my $name = $field->name; my $parent_name; # field should always have a parent unless it's a standalone field test if ( $field->parent ) { $parent_name = $field->parent->full_name; } return $name unless defined $parent_name && length $parent_name; return $parent_name . '.' . $name; } sub full_accessor { my $field = shift; my $parent = $field->parent; if( $field->is_contains ) { return '' unless $parent; return $parent->full_accessor; } my $accessor = $field->accessor; my $parent_accessor; if ( $parent ) { $parent_accessor = $parent->full_accessor; } return $accessor unless defined $parent_accessor && length $parent_accessor; return $parent_accessor . '.' . $accessor; } sub add_error { my ( $self, @message ) = @_; unless ( defined $message[0] ) { @message = ( $class_messages->{field_invalid}); } @message = @{$message[0]} if ref $message[0] eq 'ARRAY'; my $out; try { $out = $self->_localize(@message); } catch { die "Error occurred localizing error message for " . $self->label . ". Check brackets. $_"; }; return $self->push_errors($out);; } sub push_errors { my $self = shift; $self->_push_errors(@_); if ( $self->parent ) { $self->parent->propagate_error($self->result); } return; } sub _apply_deflation { my ( $self, $value ) = @_; if ( $self->has_deflation ) { $value = $self->deflation->($value); } elsif ( $self->has_deflate_method ) { $value = $self->deflate($value); } return $value; } sub _can_deflate { my $self = shift; return $self->has_deflation || $self->has_deflate_method; } # use Class::MOP to clone sub clone { my ( $self, %params ) = @_; $self->meta->clone_object( $self, %params ); } sub value_changed { my ($self) = @_; my @cmp; for ( 'init_value', 'value' ) { my $val = $self->$_; $val = '' unless defined $val; push @cmp, join '|', sort map { ref($_) && $_->isa('DateTime') ? $_->iso8601 : "$_" } ref($val) eq 'ARRAY' ? @$val : $val; } return $cmp[0] ne $cmp[1]; } sub required_text { shift->required ? 'required' : 'optional' } sub input_defined { my ($self) = @_; return unless $self->has_input; return has_some_value( $self->input ); } sub dump { my $self = shift; require Data::Dumper; warn "HFH: ----- ", $self->name, " -----\n"; warn "HFH: type: ", $self->type, "\n"; warn "HFH: required: ", ( $self->required || '0' ), "\n"; warn "HFH: label: ", $self->label, "\n"; warn "HFH: widget: ", $self->widget || '', "\n"; my $v = $self->value; warn "HFH: value: ", Data::Dumper::Dumper($v) if $v; my $iv = $self->init_value; warn "HFH: init_value: ", Data::Dumper::Dumper($iv) if $iv; my $i = $self->input; warn "HFH: input: ", Data::Dumper::Dumper($i) if $i; my $fif = $self->fif; warn "HFH: fif: ", Data::Dumper::Dumper($fif) if $fif; if ( $self->can('options') ) { my $o = $self->options; warn "HFH: options: " . Data::Dumper::Dumper($o); } } sub apply_rendering_widgets { my $self = shift; if ( $self->widget ) { warn "in apply_rendering_widgets " . $self->widget . " Field\n"; $self->apply_widget_role( $self, $self->widget, 'Field' ); } my $widget_wrapper = $self->widget_wrapper; $widget_wrapper ||= $self->form->widget_wrapper if $self->form; $widget_wrapper ||= 'Simple'; unless ( $widget_wrapper eq 'none' ) { $self->apply_widget_role( $self, $widget_wrapper, 'Wrapper' ); } return; } sub peek { my ( $self, $indent ) = @_; $indent ||= ''; my $string = $indent . 'field: "' . $self->name . '" type: ' . $self->type . "\n"; if( $self->has_flag('has_contains') ) { $string .= $indent . "contains: \n"; my $lindent = $indent . ' '; foreach my $field ( $self->contains->sorted_fields ) { $string .= $field->peek( $lindent ); } } if( $self->has_fields ) { $string .= $indent . 'subfields of "' . $self->name . '": ' . $self->num_fields . "\n"; my $lindent = $indent . ' '; foreach my $field ( $self->sorted_fields ) { $string .= $field->peek( $lindent ); } } return $string; } sub has_some_value { my $x = shift; return unless defined $x; return $x =~ /\S/ if !ref $x; if ( ref $x eq 'ARRAY' ) { for my $elem (@$x) { return 1 if has_some_value($elem); } return 0; } if ( ref $x eq 'HASH' ) { for my $key ( keys %$x ) { return 1 if has_some_value( $x->{$key} ); } return 0; } return 1 if blessed($x); # true if blessed, otherwise false return 1 if ref( $x ); return; } sub apply_traits { my ($class, @traits) = @_; $class->meta->make_mutable; Moose::Util::apply_all_roles($class->meta, @traits); $class->meta->make_immutable; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Field - base class for fields =head1 VERSION version 0.40050 =head1 SYNOPSIS Instances of Field subclasses are generally built by L from 'has_field' declarations or the field_list, but they can also be constructed using new for test purposes (since there's no standard way to add a field to a form after construction). use HTML::FormHandler::Field::Text; my $field = HTML::FormHandler::Field::Text->new( name => $name, ... ); In your custom field class: package MyApp::Field::MyText; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Text'; has 'my_attribute' => ( isa => 'Str', is => 'rw' ); apply [ { transform => sub { ... } }, { check => ['fighter', 'bard', 'mage' ], message => '....' } ]; 1; =head1 DESCRIPTION This is the base class for form fields. The 'type' of a field class is used in the FormHandler field_list or has_field to identify which field class to load from the 'field_name_space' (or directly, when prefixed with '+'). If the type is not specified, it defaults to Text. See L for a list of the fields and brief descriptions of their structure. =head1 ATTRIBUTES =head2 Names, types, accessor =over =item name The name of the field. Used in the HTML form. Often a db accessor. The only required attribute. =item type The class or type of the field. The 'type' of L is 'Money'. Classes that you define yourself are prefixed with '+'. =item accessor If the name of your field is different than your database accessor, use this attribute to provide the accessor. =item full_name The name of the field with all parents: 'event.start_date.month' =item full_accessor The field accessor with all parents. =item html_name The full_name plus the form name if 'html_prefix' is set. =item input_param By default we expect an input parameter based on the field name. This allows you to look for a different input parameter. =back =head2 Field data =over =item inactive, is_inactive, is_active Set the 'inactive' attribute to 1 if this field is inactive. The 'inactive' attribute that isn't set or is set to 0 will make a field 'active'. This provides a way to define fields in the form and selectively set them to inactive. There is also an '_active' attribute, for internal use to indicate that the field has been activated/inactivated on 'process' by the form's 'active'/'inactive' attributes. You can use the is_inactive and is_active methods to check whether this particular field is active. if( $form->field('foo')->is_active ) { ... } =item input The input string from the parameters passed in. =item value The value as it would come from or go into the database, after being acted on by inflations/deflations and transforms. Used to construct the C<< $form->values >> hash. Validation and constraints act on 'value'. See also L. =item fif Values used to fill in the form. Read only. Use a deflation to get from 'value' to 'fif' if an inflator was used. Use 'fif_from_value' attribute if you want to use the field 'value' to fill in the form. [% form.field('title').fif %] =item init_value Initial value populated by init_from_object. You can tell if a field has changed by comparing 'init_value' and 'value'. Read only. =item input_without_param Input for this field if there is no param. Set by default for Checkbox, and Select, since an unchecked checkbox or unselected pulldown does not return a parameter. =back =head2 Form, parent =over =item form A reference to the containing form. =item parent A reference to the parent of this field. Compound fields are the parents for the fields they contain. =back =head2 Errors =over =item errors Returns the error list for the field. Also provides 'num_errors', 'has_errors', 'push_errors' and 'clear_errors' from Array trait. Use 'add_error' to add an error to the array if you want to use a MakeText language handle. Default is an empty list. =item add_error Add an error to the list of errors. Error message will be localized using '_localize' method. See also L. return $field->add_error( 'bad data' ) if $bad; =item error_fields Compound fields will have an array of errors from the subfields. =item localize_meth Set the method used to localize. =back =head2 Attributes for creating HTML The 'element_attr' hashref attribute can be used to set arbitrary HTML attributes on a field's input tag. has_field 'foo' => ( element_attr => { readonly => 1, my_attr => 'abc' } ); Note that the 'id' and 'type' attributes are not set using element_attr. Use the field's 'id' attribute (or 'build_id_method') to set the id. The 'label_attr' hashref is for label attributes, and the 'wrapper_attr' is for attributes on the wrapping element (a 'div' for the standard 'simple' wrapper). A 'javascript' key in one of the '_attr' hashes will be inserted into the element as-is. The following are used in rendering HTML, but are handled specially. label - Text label for this field. Defaults to ucfirst field name. build_label_method - coderef for constructing the label wrap_label_method - coderef for constructing a wrapped label id - Useful for javascript (default is html_name. to prefix with form name, use 'html_prefix' in your form) build_id_method - coderef for constructing the id render_filter - Coderef for filtering fields before rendering. By default changes >, <, &, " to the html entities disabled - Boolean to set field disabled The order attribute may be used to set the order in which fields are rendered. order - Used for sorting errors and fields. Built automatically, but may also be explicitly set The following are discouraged. Use 'element_attr', 'label_attr', and 'wrapper_attr' instead. css_class - instead use wrapper_class => [ '...' ] input_class - instead use element_class => [ '...' ] title - instead use element_attr => { title => '...' } style - instead use element_attr => { style => '...' } tabindex - instead use element_attr => { tabindex => 1 } readonly - instead use element_attr => { readonly => 'readonly' } Rendering of the various HTML attributes is done by calling the 'process_attrs' function (from HTML::FormHandler::Render::Util) and passing in a method that adds in error classes, provides backward compatibility with the deprecated attributes, etc. attribute hashref class attribute wrapping method ================= ================= ================ element_attr element_class element_attributes label_attr label_class label_attributes wrapper_attr wrapper_class wrapper_attributes element_wrapper_class element_wrapper_attributes ('element_wrapper' is for an inner div around the input element, not including the label. Used for Bootstrap3 rendering, but also available in the Simple wrapper.) The slots for the class attributes are arrayrefs; they will coerce a string into an arrayref. In addition, these 'wrapping methods' call a hook method in the form class, 'html_attributes', which you can use to customize and localize the various attributes. (Field types: 'element', 'wrapper', 'label') sub html_attributes { my ( $self, $field, $type, $attr ) = @_; $attr->{class} = 'label' if $type eq 'label'; return $attr; } The 'process_attrs' function will also handle an array of strings, such as for the 'class' attribute. =head2 tags A hashref containing flags and strings for use in the rendering code. The value of a tag can be a string, a coderef (accessed as a method on the field) or a block specified with a percent followed by the blockname ('%blockname'). Retrieve a tag with 'get_tag'. It returns a '' if the tag doesn't exist. This attribute used to be named 'widget_tags', which is deprecated. =head2 html5_type_attr [string] This string is used when rendering an input element as the value for the type attribute. It is used when the form has the is_html5 flag on. =head2 widget The 'widget' attribute is used in rendering, so if you are not using FormHandler's rendering facility, you don't need this attribute. It is used in generating HTML, in templates and the rendering roles. Fields of different type can use the same widget. This attribute is set in the field classes, or in the fields defined in the form. If you want a new widget type, create a widget role, such as MyApp::Form::Widget::Field::MyWidget. Provide the name space in the 'widget_name_space' attribute, and set the 'widget' of your field to the package name after the Field/Form/Wrapper: has_field 'my_field' => ( widget => 'MyWidget' ); If you are using a template based rendering system you will want to create a widget template. (see L) Widget types for some of the provided field classes: Widget : Field classes -----------------------:--------------------------------- Text : Text, Integer Checkbox : Checkbox, Boolean RadioGroup : Select, Multiple, IntRange (etc) Select : Select, Multiple, IntRange (etc) CheckboxGroup : Multiple select TextArea : TextArea Compound : Compound, Repeatable, DateTime Password : Password Hidden : Hidden Submit : Submit Reset : Reset NoRender : Upload : Upload Widget roles are automatically applied to field classes unless they already have a 'render' method, and if the 'no_widgets' flag in the form is not set. You can create your own widget roles and specify the namespace in 'widget_name_space'. In the form: has '+widget_name_space' => ( default => sub { ['MyApp::Widget'] } ); If you want to use a fully specified role name for a widget, you can prefix it with a '+': widget => '+MyApp::Widget::SomeWidget' For more about widgets, see L. =head2 Flags password - prevents the entered value from being displayed in the form writeonly - The initial value is not taken from the database noupdate - Do not update this field in the database (does not appear in $form->value) =head2 Defaults See also the documentation on L. =over =item default_method, set_default Supply a coderef (which will be a method on the field) with 'default_method' or the name of a form method with 'set_default' (which will be a method on the form). If not specified and a form method with a name of C<< default_ >> exists, it will be used. =item default Provide an initial value just like the 'set_default' method, except in the field declaration: has_field 'bax' => ( default => 'Default bax' ); FormHandler has flipped back and forth a couple of times about whether a default specified in the has_field definition should override values provided in an initial item or init_object. Sometimes people want one behavior, and sometimes the other. Now 'default' does *not* override. If you pass in a model object with C<< item => $row >> or an initial object with C<< init_object => {....} >> the values in that object will be used instead of values provided in the field definition with 'default' or 'default_fieldname'. If you want defaults that override or supplement the item/init_object, you can use the form flags 'use_defaults_over_obj', 'use_init_obj_over_item', and 'use_init_obj_when_no_accessor_in_item'. You could also put your defaults into your row or init_object instead. =item default_over_obj This is deprecated; look into using 'use_defaults_over_obj' or 'use_init_obj_over_item' flags instead. They allow using the standard 'default' attribute. Allows setting defaults which will override values provided with an item/init_object. (And only those. Will not be used for defaults without an item/init_object.) has_field 'quux' => ( default_over_obj => 'default quux' ); At this time there is no equivalent of 'set_default', but the type of the attribute is not defined so you can provide default values in a variety of other ways, including providing a trait which does 'build_default_over_obj'. For examples, see tests in the distribution. =back =head1 Constraints and Validations See also L. =head2 Constraints set in attributes =over =item required Flag indicating whether this field must have a value =item unique For DB field - check for uniqueness. Action is performed by the DB model. =item messages messages => { required => '...', unique => '...' } Set messages created by FormHandler by setting in the 'messages' hashref. Some field subclasses have additional settable messages. required: Error message text added to errors if required field is not present. The default is "Field is required". =item range_start =item range_end Field values are validated against the specified range if one or both of range_start and range_end are set and the field does not have 'options'. The IntRange field uses this range to create a select list with a range of integers. In a FormHandler field_list: age => { type => 'Integer', range_start => 18, range_end => 120, } =item not_nullable Fields that contain 'empty' values such as '' are changed to undef in the validation process. If this flag is set, the value is not changed to undef. If your database column requires an empty string instead of a null value (such as a NOT NULL column), set this attribute. has_field 'description' => ( type => 'TextArea', not_nullable => 1, ); This attribute is also used when you want an empty array to stay an empty array and not be set to undef. It's also used when you have a compound field and you want the 'value' returned to contain subfields with undef, instead of the whole field to be undef. =back =head2 apply Use the 'apply' keyword to specify an ArrayRef of constraints and coercions to be executed on the field at validate_field time. has_field 'test' => ( apply => [ 'MooseType', { check => sub {...}, message => { } }, { transform => sub { ... lc(shift) ... } } ], ); See more documentation in L. =head2 trim An action to trim the field. By default this contains a transform to strip beginning and trailing spaces. Set this attribute to null to skip trimming, or supply a different transform. trim => { transform => sub { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } } trim => { type => MyTypeConstraint } Trimming is performed before any other defined actions. =head2 Inflation/deflation There are a number of methods to provide finely tuned inflation and deflation: =over 4 =item inflate_method Inflate to a data format desired for validation. =item deflate_method Deflate to a string format for presenting in HTML. =item inflate_default_method Modify the 'default' provided by an 'item' or 'init_object'. =item deflate_value_method Modify the value returned by C<< $form->value >>. =item deflation Another way of providing a deflation method. =item transform Another way of providing an inflation method. =back Normally if you have a deflation, you will need a matching inflation. There are two different flavors of inflation/deflation: one for inflating values to a format needed for validation and deflating for output, the other for inflating the initial provided values (usually from a database row) and deflating them for the 'values' returned. See L. =head1 Processing and validating the field =head2 validate_field This is the base class validation routine. Most users will not do anything with this. It might be useful for method modifiers, if you want code that executed before or after the validation process. =head2 validate This field method can be used in addition to or instead of 'apply' actions in custom field classes. It should validate the field data and set error messages on errors with C<< $field->add_error >>. sub validate { my $field = shift; my $value = $field->value; return $field->add_error( ... ) if ( ... ); } =head2 validate_method, set_validate Supply a coderef (which will be a method on the field) with 'validate_method' or the name of a form method with 'set_validate' (which will be a method on the form). If not specified and a form method with a name of C<< validate_ >> exists, it will be used. Periods in field names will be replaced by underscores, so that the field 'addresses.city' will use the 'validate_addresses_city' method for validation. has_field 'my_foo' => ( validate_method => \&my_foo_validation ); sub my_foo_validation { ... } has_field 'title' => ( isa => 'Str', set_validate => 'check_title' ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Fields.pm0000644000077000007700000001666712221042077021433 0ustar gshankgshankpackage HTML::FormHandler::Fields; # ABSTRACT: internal role for form and compound fields use Moose::Role; use HTML::FormHandler::TraitFor::Types; has 'fields' => ( traits => ['Array'], isa => 'ArrayRef[HTML::FormHandler::Field]', is => 'rw', default => sub { [] }, auto_deref => 1, handles => { all_fields => 'elements', clear_fields => 'clear', add_field => 'push', push_field => 'push', num_fields => 'count', has_fields => 'count', set_field_at => 'set', _pop_field => 'pop', } ); # This is for updates applied via roles or compound field classes; allows doing # both updates on the process call and updates from class applied roles has 'update_subfields' => ( is => 'rw', isa => 'HashRef', builder => 'build_update_subfields', traits => ['Hash'], handles => { clear_update_subfields => 'clear', has_update_subfields => 'count' } ); sub build_update_subfields {{}} # used to transfer tags to fields from form and compound fields has 'widget_tags' => ( isa => 'HashRef', traits => ['Hash'], is => 'rw', default => sub {{}}, handles => { has_widget_tags => 'count' } ); # compatibility wrappers for result errors sub error_fields { my $self = shift; return map { $_->field_def } @{ $self->result->error_results }; } sub has_error_fields { shift->result->has_error_results } sub add_error_field { my ( $self, $field ) = @_; $self->result->add_error_result( $field->result ); } sub num_error_fields { shift->result->num_error_results } has 'field_name_space' => ( isa => 'HFH::ArrayRefStr', is => 'rw', traits => ['Array'], lazy => 1, default => '', coerce => 1, handles => { add_field_name_space => 'push', }, ); sub field_index { my ( $self, $name ) = @_; my $index = 0; for my $field ( $self->all_fields ) { return $index if $field->name eq $name; $index++; } return; } sub subfield { my ( $self, $name ) = @_; return $self->field($name, undef, $self); } sub field { my ( $self, $name, $die, $f ) = @_; my $index; # if this is a full_name for a compound field # walk through the fields to get to it return undef unless ( defined $name ); if( $self->form && $self == $self->form && exists $self->index->{$name} ) { return $self->index->{$name}; } if ( $name =~ /\./ ) { my @names = split /\./, $name; $f ||= $self->form || $self; foreach my $fname (@names) { $f = $f->field($fname); return unless $f; } return $f; } else # not a compound name { for my $field ( $self->all_fields ) { return $field if ( $field->name eq $name ); } } return unless $die; die "Field '$name' not found in '$self'"; } sub sorted_fields { my $self = shift; my @fields = sort { $a->order <=> $b->order } grep { $_->is_active } $self->all_fields; return wantarray ? @fields : \@fields; } # the routine for looping through and processing each field sub _fields_validate { my $self = shift; return unless $self->has_fields; # validate all fields my %value_hash; foreach my $field ( $self->all_fields ) { next if ( $field->is_inactive || $field->disabled || !$field->has_result ); # Validate each field and "inflate" input -> value. $field->validate_field; # this calls the field's 'validate' routine $value_hash{ $field->accessor } = $field->value if ( $field->has_value && !$field->noupdate ); } $self->_set_value( \%value_hash ); } sub fields_set_value { my $self = shift; my %value_hash; foreach my $field ( $self->all_fields ) { next if ( $field->is_inactive || !$field->has_result ); $value_hash{ $field->accessor } = $field->value if ( $field->has_value && !$field->noupdate ); } $self->_set_value( \%value_hash ); } sub fields_fif { my ( $self, $result, $prefix ) = @_; $result ||= $self->result; return unless $result; $prefix ||= ''; if ( $self->isa('HTML::FormHandler') ) { $prefix = $self->name . "." if $self->html_prefix; } my %params; foreach my $fld_result ( $result->results ) { my $field = $fld_result->field_def; next if ( $field->is_inactive || $field->password ); my $fif = $fld_result->fif; next if ( !defined $fif || (ref $fif eq 'ARRAY' && ! scalar @{$fif} ) ); if ( $fld_result->has_results ) { my $next_params = $fld_result->fields_fif( $prefix . $field->name . '.' ); next unless $next_params; %params = ( %params, %{$next_params} ); } else { $params{ $prefix . $field->name } = $fif; } } return if !%params; return \%params; } sub clear_data { my $self = shift; $self->clear_result; $self->clear_active; $_->clear_data for $self->all_fields; } sub propagate_error { my ( $self, $result ) = @_; # References to fields with errors are propagated up the tree. # All fields with errors should end up being in the form's # error_results. Once. my ($found) = grep { $_ == $result } $self->result->all_error_results; unless ( $found ) { $self->result->add_error_result($result); if ( $self->parent ) { $self->parent->propagate_error( $result ); } } } sub dump_fields { shift->dump(@_) } sub dump { my $self = shift; warn "HFH: ------- fields for ", $self->name, "-------\n"; for my $field ( $self->sorted_fields ) { $field->dump; } warn "HFH: ------- end fields -------\n"; } sub dump_validated { my $self = shift; warn "HFH: fields validated:\n"; foreach my $field ( $self->all_fields ) { $field->dump_validated if $field->can('dump_validated'); my $message = $field->has_errors ? join( ' | ', $field->all_errors) : 'validated'; warn "HFH: ", $field->name, ": $message\n"; } } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Fields - internal role for form and compound fields =head1 VERSION version 0.40050 =head1 SYNOPSIS A role to implement field attributes, accessors, etc. To be applied to L and L. =head2 fields The field definitions as built from the field_list and the 'has_field' declarations. This provides clear_fields, add_field, remove_last_field, num_fields, has_fields, and set_field_at methods. =head2 field( $full_name ) Return the field object with the full_name passed. Will return undef if the field is not found, or will die if passed a second parameter. =head2 field_index Convenience function for use with 'set_field_at'. Pass in 'name' of field (not full_name) =head2 sorted_fields Calls fields and returns them in sorted order by their "order" value. Non-sorted fields are retrieved with 'fields'. =head2 clear methods clear_data clear_fields clear_error_fields =head2 Dump information dump - turn verbose flag on to get this output dump_validated - shorter version =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Foo.pm0000644000077000007700000000356412221042077020740 0ustar gshankgshankpackage HTML::FormHandler::Foo; # ABSTRACT: Experiment in loading form from config file use Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Render::WithTT'; use Config::Any; has 'form_error_message' => ( isa => 'Str', is => 'rw' ); has 'javascript_src' => ( isa => 'Str', is => 'rw' ); has 'javascript' => ( isa => 'Str', is => 'rw' ); sub before_build { my $self = shift; $self->add_tt_include_path('share/templates/foo'); $self->process_config_file; $self->process_config; } sub build_tt_template { 'form.tt' } has 'config_file' => ( isa => 'Str', is => 'rw' ); has 'config' => ( isa => 'HashRef', is => 'rw' ); sub submitted_and_valid { shift->validated } sub process_config_file { my $self = shift; return unless $self->config_file; unless ( -e $self->config_file ) { die "form config file " . $self->config_file . " . does not exist"; } my $config = Config::Any->load_files({ files => [$self->config_file], use_ext => 1, driver_args => { General => { -UTF8 => 1 }, }, }); $config = $config->[0]->{$self->config_file}; $self->config($config); } sub process_config { my $self = shift; my $config = $self->config; while ( my ( $key, $value ) = each %{$config} ) { confess "invalid attribute '$key' in form config" unless $self->can($key); $self->$key($value); } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Foo - Experiment in loading form from config file =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/0000755000077000007700000000000012221042077020326 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/bg_bg.pm0000644000077000007700000001723112221042077021730 0ustar gshankgshankpackage HTML::FormHandler::I18N::bg_bg; # ABSTRACT: Bulgarian message file use strict; use warnings; use utf8; use base 'HTML::FormHandler::I18N'; # translator: Dimitar Petrov # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F::Field 'field is invalid' => 'Полето не е валидно', 'Wrong value' => 'Грешна стойност', '[_1] does not match' => '[_1] не съвпада', '[_1] not allowed' => '[_1] не разрешено', 'Value must be between [_1] and [_2]' => 'стойността трябва да бъде между [_1] и [_2]', 'Value must be greater than or equal to [_1]' => 'стойността трябва да бъде по-голяма или равна на [_1]', 'Value must be less than or equal to [_1]' => 'стойността трябва да бъде по-малка или равна на [_1]', '[_1] field is required' => 'полето [_1] е задължително', 'error occurred' => 'възникна грешка', # H::F::Types 'Must be a positive number' => 'Трябва да бъде положително число', 'Must be a positive integer' => 'Трябва да бъде положително цяло число', 'Must be a negative number' => 'Трябва да бъде отрицателно число', 'Must be a negative integer' => 'Трябва да бъде отрицателно цяло число', 'Must be a single digit' => 'Трябва да бъде една цифра', 'Must be a single line of no more than 255 chars' => 'Трябва да бъде стойност с дължина не по-голяма от 255 символа', 'Must be a non-empty single line of no more than 255 chars' => 'Трябва да бъде непразна стойност с дължина не по-голяма от 255 символа', 'Must be between 4 and 255 chars' => 'Трябва да бъде между 4 и 255 символа', 'Not a valid state' => 'Невалидно състояние', 'Email is not valid' => 'Невалидна електронна поща', 'Zip is not valid' => 'Невалиден пощенски код', 'Not a valid IP address' => 'Невалиден IP адрес', 'Must not contain spaces' => 'Не трябва да съдържа интервал', 'Must be made up of letters, digits, and underscores' => 'Трябва да се състои от букви, цифри и подчертавки', 'Must not be all digits' => 'Не трябва да съдържа само цифри', 'Field contains non-printable characters' => 'Полето съдържа символи, които не могат да бъдат разпечатани', 'Field must contain a single word' => 'Полето трябва да съдържа една дума', 'Must not be empty' => 'Не трябва да бъде празно', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Трябва да бъде между 8 и 255 символа и да съдържа поне един не-буквен символ', # H::F::Field::Date 'Date is too early' => 'Датата е прекалено рано', 'Date is too late' => 'Датата е прекалено късно', # H::F::Field::DateTime 'Not a valid DateTime' => 'Невалидна дата/време', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Невалидна стойност за [_1]: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'Електронната поща трябва да бъде във формат [_1]', # H::F::Field::Integer 'Value must be an integer' => 'Стойността трябва да бъде цяло число', # H::F::Field::Money 'Value cannot be converted to money' => 'Стойността не може да бъде конвертирана към пари', 'Value must be a real number' => 'Стойнноста трябва да бъде естествено число', # H::F::Field::Password 'Please enter a password in this field' => 'Моля въведете парола', 'Password must not match [_1]' => 'Паролата не съвпада с [_1]', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Моля, въведете парола за потвърждение', 'The password confirmation does not match the password' => 'Въведената парола за потвърждение не съвпада с паролата', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Стойността трябва да бъде положително цяло число', # H::F::Field::Select 'This field does not take multiple values' => 'Това поле не приема няколко стойности', '\'[_1]\' is not a valid value' => '\'[_1]\' не е валидна стойност', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Стойността не трябва да надминава [_1]. Въвели сте: [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Стойността трябва да бъде поне [_1]. Въвели сте: [_2]', # H::F::Field::Upload 'File uploaded is empty' => 'Каченият файл е празен', 'File is too small (< [_1] bytes)' => 'Файла е прекалено малък (< [_1] байта)', 'File is too big (> [_1] bytes)' => 'Файла е прекалено голям (> [_1] байта)', 'File not found for upload field' => 'Не е намерен файл файл за качване', # H::F::Model 'Value must be unique in the database' => 'Стойността трябва да е уникална в базата от данни', # Other 'Your datetime does not match your pattern.' => 'Въведената дата/време не съвпада с вашия шаблон.', ); 1; =pod =head1 NAME HTML::FormHandler::I18N::bg_bg - Bulgarian message file =head1 VERSION version 0.40050 =head1 NAME HTML::FormHandler::I18N::bg_bg - Bulgarian message file =head1 VERSION version 0.40010 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2012 by Gerda Shank. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 __END__ HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/de_de.pm0000644000077000007700000001150312221042077021724 0ustar gshankgshankpackage HTML::FormHandler::I18N::de_de; # ABSTRACT: German message translations use strict; use warnings; use base 'HTML::FormHandler::I18N'; use utf8; # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F::Field 'field is invalid' => 'Feld ist ungültig', 'Wrong value' => 'Ungültiger Wert', '[_1] does not match' => '[_1] ist kein gültiger Wert', '[_1] not allowed' => '[_1] ist nicht erlaubt', '[_1] field is required' => 'Feld ist erforderlich', 'error occurred' => 'Fehler aufgetreten', 'Value must be between [_1] and [_2]' => 'Wert muss zwischen [_1] und [_2] liegen', 'Value must be greater than or equal to [_1]' => 'Wert muss größer oder gleich [_1] sein', 'Value must be less than or equal to [_1]' => 'Wert muss kleiner oder gleich [_1] sein', # H::F::Types 'Must be a positive number' => 'Muss eine positive Zahl sein', 'Must be a positive integer' => 'Muss eine positive ganze Zahl sein', 'Must be a negative number' => 'Muss eine negative Zahl sein', 'Must be a negative integer' => 'Muss eine negative ganze Zahl sein', 'Must be a single digit' => 'Muss eine einzelne Ziffer sein', 'Must be a non-empty single line of no more than 255 chars' => 'Muss eine nicht leere Zeile (max. 255 Zeichen) sein', 'Must be made up of letters, digits, and underscores' => 'Darf nur Buchstaben, Ziffern oder "_" enthalten', 'Not a valid IP address' => 'IP Adresse ungültig', 'Must not be all digits' => 'Darf nicht nur Ziffern enthalten', 'Not a valid state' => 'Kein gültiger Bundesstaat', 'Field contains non-printable characters' => 'Feld enthält nicht druckbare Zeichen', 'Must be between 4 and 255 chars' => '4 bis 255 Zeichen erforderlich', 'Zip is not valid' => 'PLZ ungültig', 'Must be a single line of no more than 255 chars' => 'Muss eine einzelne Zeile (max. 255 Zeichen) sein', 'Email is not valid' => 'E-Mail ist nicht gültig', 'Must not contain spaces' => 'Darf keine Leerzeichen enthalten', 'Field must contain a single word' => 'Feld muss ein einzelnes Wort enthalten', 'Must not be empty' => 'Feld darf nicht leer bleiben', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Wert muss 8 bis 255 Zeichen und ein nicht alpha-num Zeichen enthalten', # H::F::Field::Date 'Date is too early' => 'Datum ist zu früh', 'Date is too late' => 'Datum ist zu spät', # H::F::Field::DateTime 'Not a valid DateTime' => 'Ungültige Datums-/Zeitangabe', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Ungültiger Wert für [_1]: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'E-Mail sollte die Form [_1] haben', # H::F::Field::Integer 'Value must be an integer' => 'Muss eine positive ganze Zahl sein', # H::F::Field::Money 'Value cannot be converted to money' => 'Wert kann nicht in Betrag konvertiert werden', 'Value must be a real number' => 'Muss eine Dezimalzahl sein', # H::F::Field::Password 'Please enter a password in this field' => 'Bitte ein Passwort eingeben', 'Password must not match [_1]' => 'Passwort darf nicht mit \'[_1]\' übereinstimmen', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Bitte das Passwort bestätigen', 'The password confirmation does not match the password' => 'Passwort Bestätigung stimmt nicht überein', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Muss eine positive ganze Zahl sein', # H::F::Field::Select 'This field does not take multiple values' => 'Mehrfachauswahl nicht erlaubt', '\'[_1]\' is not a valid value' => '\'[_1]\' ist kein gültiger Wert', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Bitte auf [_1] Zeichen beschränken. Sie haben [_2] eingegeben', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Eingabe muss mindestens [_1] Zeichen lang sein. Sie haben nur [_2] eingegeben', # H::F::Field::Upload 'File uploaded is empty' => 'Hochgeladene Datei ist leer', 'File is too small (< [_1] bytes)' => 'Datei ist zu klein (< [_1] bytes)', 'File is too big (> [_1] bytes)' => 'Datei ist zu groß (> [_1] bytes)', 'File not found for upload field' => 'Datei für upload Feld nicht gefunden', # H::F::Model 'Value must be unique in the database' => 'Wert existiert bereits in der Datenbank', ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::de_de - German message translations =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/en_us.pm0000644000077000007700000000113712221042077021777 0ustar gshankgshankpackage HTML::FormHandler::I18N::en_us; # ABSTRACT: base message file use strict; use warnings; use base 'HTML::FormHandler::I18N'; # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::en_us - base message file =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/hu_hu.pm0000644000077000007700000001233112221042077021774 0ustar gshankgshankpackage HTML::FormHandler::I18N::hu_hu; # ABSTRACT: Hungarian message file use strict; use warnings; use utf8; use base 'HTML::FormHandler::I18N'; # translator: Csaba Hetényi # notify before release: cub@cpan.org # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H:: F:: Field 'field is invalid' => 'A mező érvénytelen', 'Wrong value' => 'Rossz érték', # '[_1] does not match' => '...', # '[_1] not allowed' => '...', 'Value must be between [_1] and [_2]' => 'az érték [_1] és [_2] között legyen', 'Value must be greater than or equal to [_1]' => 'az érték nagyobb vagy egyenlő legyen: [_1]', 'Value must be less than or equal to [_1]' => 'az érték kisebb vagy egyenlő legyen: [_1]', '[_1] field is required' => 'A [_1] mező szükséges', # 'error occurred' => '...', # H:: F:: Types 'Must be a positive number' => 'Pozitív szám szükséges', 'Must be a positive integer' => 'Pozitív egész szám szükséges', 'Must be a negative number' => 'Negatív szám szükséges', 'Must be a negative integer' => 'Negatív egész szám szükséges', 'Must be a single digit' => 'Egy számjegy szükséges', 'Must be a single line of no more than 255 chars' => 'Egy sor legyen és ne legyen több 255 karakternél', 'Must be a non-empty single line of no more than 255 chars' => 'Nem lehet üres sor és nem lehet több 255 karakternél', 'Must be between 4 and 255 chars' => '4 és 255 karakter közt legyen', 'Not a valid state' => 'Érvénytelen állapot', 'Email is not valid' => 'Az email cím nem megfelelő', 'Zip is not valid' => 'Az irányítószám nem megfelelő', 'Not a valid IP address' => 'Az IP cím nem megfelelő', 'Must contain spaces' => 'Nem tartalmazhat szóközt', 'Must be made up of letters, digits, and underscores' => 'Csak betűket, számokat és alulvonást tartalmazhat', 'Must not be all digits' => 'Nem csak számok szükségesek', 'Field contains non-printable characters' => 'A mező nem nyomtatható karaktert tartalmaz', 'Field must contain a single word' => 'A mező csak egy szót tartalmazhat', # 'Must not be empty' => '...', # 'Must be between 8 and 255 chars, and contain a non-alpha char' => '...', # H::F::Field::Date 'Date is too early' => 'A dátum túl korai', 'Date is too late' => 'A dátum túl késő', # H::F::Field::DateTime 'Not a valid DateTime' => 'Érvénytelen formátum', # H::F::Field::Duration # 'Invalid value for [_1]: [_2]' => '.....', # H::F::Field::Email 'Email should be of the format [_1]' => 'Az email [_1] formátumú legyen', # H::F::Field::Integer 'Value must be an integer' => 'Az érték egész szám legyen', # H::F::Field::Money 'Value cannot be converted to money' => 'Az érték nem alakítható pénz formátumra', 'Value must be a real number' => 'Az érték valós szám kell legyen', # H::F::Field::Password 'Please enter a password in this field' => 'Légyszíves adj meg jelszót ebben a mezőben', # 'Password must not match [_1]' => '....', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Jelszó megerősítése', # 'The password confirmation does not match the password' => '...', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Pozitív egész szám szükséges', # H::F::Field::Select 'This field does not take multiple values' => 'Ez a mező csak egy értéket kaphat', # '\'[_1]\' is not a valid value' => '...', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'A maximális hossz: [_1] karakter. A tiéd pedig: [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'A minimális hossz: [_1] karakter. A tiéd pedig: [_2]', # H:: F:: Field:: Upload 'File uploaded is empty' => 'A feltöltött fájl üres', 'File is too small (< [_1] bytes)' => 'A fájl túl kicsi (<[_1] byte)', 'File is too big (> [_1] bytes)' => 'A fájl túl nagy (>[_1] byte)', # 'File not found for upload field' => '...', # H:: F:: Model 'Value must be unique in the database' => 'Az érték egyedi kell legyen az adatbázisban', # Other 'Your datetime does not match your pattern.' => 'A datetime érték nem illeszkedik a mintára.', ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::hu_hu - Hungarian message file =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/it_it.pm0000644000077000007700000001276612221042077022010 0ustar gshankgshankpackage HTML::FormHandler::I18N::it_it; # ABSTRACT: Italian message translations - traduzione italiana dei messaggi use strict; use warnings; use base 'HTML::FormHandler::I18N'; use utf8; our $VERSION = '0.01'; # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F 'There were errors in your form'=> q(Alcuni dati sono sbagliati), # H::F::Field 'field is invalid' => 'Campo non valido', 'Wrong value' => 'Valore errato', '[_1] does not match' => '[_1] non combacia', '[_1] not allowed' => '[_1] non permesso', '[_1] field is required' => 'Il campo [_1] è obbligatorio', 'error occurred' => q(C'è un errore), 'Value must be between [_1] and [_2]' => 'Il valore deve essere compreso tra [_1] e [_2]', 'Value must be greater than or equal to [_1]' => 'Il valore deve essere maggiore o eguale a [_1]', 'Value must be less than or equal to [_1]' => 'Il valore deve essere minore o eguale a [_1]', # H::F::Types 'Must be a positive number' => 'Deve essere un numero positivo', 'Must be a positive integer' => 'Deve essere un numero intero positivo', 'Must be a negative number' => 'Deve essere un numero negativo', 'Must be a negative integer' => 'Deve essere un numero intero negativo', 'Must be a single digit' => 'Deve essere di una singola cifra', 'Must be a non-empty single line of no more than 255 chars' => 'Deve essere un testo di una riga, non vuoto e con un massimo di 255 caratteri', 'Must be made up of letters, digits, and underscores' => 'Può essere composto da lettere, cifre e "_"', 'Not a valid IP address' => q(L'indirizzo IP non è valido), 'Must not be all digits' => 'Non devono essere solo cifre', 'Not a valid state' => 'Non è uno stato valido', 'Field contains non-printable characters' => 'Il campo contiene caratteri non stampabili', 'Must be between 4 and 255 chars' => 'Deve essere tra 4 e 255 caratteri', 'Zip is not valid' => 'il CAP non è valido', 'Must be a single line of no more than 255 chars' => 'Deve essere una sola riga di testo con un massimo di 255 caratteri', 'Email is not valid' => 'Non è un indirizzo E-mail', 'Must not contain spaces' => 'Non deve contenere spazi', 'Field must contain a single word' => 'Il campo deve contenere una sola parola', 'Must not be empty' => 'Non può essere vuoto', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Deve essere tra 8 e 255 caratteri, e contenere non solo lettere', # H::F::Field::Date 'Date is too early' => 'La data è troppo remota', 'Date is too late' => 'La data è troppo avanti', # H::F::Field::DateTime 'Not a valid DateTime' => 'Non è un DateTime valido', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Durata non valida per [_1]: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'E-mail deve essere nel formato [_1]', # H::F::Field::Integer 'Value must be an integer' => 'Deve essere un numero intero', # H::F::Field::Money 'Value cannot be converted to money' => 'Il valore non può essere converito in moneta', 'Value must be a real number' => 'Deve essere un numero reale', # H::F::Field::Password 'Please enter a password in this field' => 'Inserisci la password in questo campo', 'Password must not match [_1]' => 'La password non deve coincidere con [_1]', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Ripeti la password quale verifica', 'The password confirmation does not match the password' => 'La password di verifica non coincide', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Deve essere un intero positivo', # H::F::Field::Select 'This field does not take multiple values' => 'Questo campo non accetta più di un valore', '\'[_1]\' is not a valid value' => '\'[_1]\' non è un valore valido', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Il campo non deve eccedere [quant,_1,carattere,caratteri]. Tu ne hai inseriti [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Il campo deve essere di almeno [quant,_1,carattere,caratteri]. Tu ne hai inseriti [_2]', # H::F::Field::Upload 'File uploaded is empty' => 'Il file inserito è vuoto', 'File is too small (< [_1] bytes)' => 'Il file è troppo piccolo (< [_1] bytes)', 'File is too big (> [_1] bytes)' => 'Il file è troppo grande (> [_1] bytes)', 'File not found for upload field' => q(Il file nel campo di upload non esiste), # H::F::Model 'Value must be unique in the database' => 'Il valore deve essere unico nella base dati', ); 1; =pod =head1 NAME HTML::FormHandler::I18N::it_it - Italian message translations - traduzione italiana dei messaggi =head1 VERSION version 0.40050 =head1 NAME HTML::FormHandler::I18N::it_it - Italian message translations =head1 VERSION version 0.40025 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 __END__ HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/ja_jp.pm0000644000077000007700000001303512221042077021751 0ustar gshankgshankpackage HTML::FormHandler::I18N::ja_jp; # ABSTRACT: Japanese message file use strict; use warnings; use utf8; use base 'HTML::FormHandler::I18N'; # translator: Tomohiro Hosaka # もっと良い訳に直してください!! # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F::Field 'field is invalid' => 'フィールドが無効です。', 'Wrong value' => '不正な値です。', '[_1] does not match' => '[_1]は一致しません。', '[_1] not allowed' => '[_1]は許可されません。', '[_1] field is required' => '[_1]を入力してください。', 'error occurred' => 'エラーが起こりました。', 'Value must be between [_1] and [_2]' => '値を[_1]から[_2]の間にしてください。', 'Value must be greater than or equal to [_1]' => '値を[_1]以上にしてください。', 'Value must be less than or equal to [_1]' => '値を[_1]以下にしてください。', # H::F::Types 'Must be a positive number' => '数字を正の数にしてください。', 'Must be a positive integer' => '数字を正の整数にしてください。', 'Must be a negative number' => '数字を負の数にしてください。', 'Must be a negative integer' => '数字を負の整数にしてください。', 'Must be a single digit' => '数字を一桁にしてください。', 'Must be a non-empty single line of no more than 255 chars' => '空でない255字以下の文字列にしてください。', 'Must be made up of letters, digits, and underscores' => '数字とハイフンとアンダースコアで構成してください。', 'Not a valid IP address' => 'IPアドレスとして正しくありません。', 'Must not be all digits' => '全て数字にすることはできません。', 'Not a valid state' => '州として正しくありません。', 'Field contains non-printable characters' => '表示できない文字を含んでいます。', 'Must be between 4 and 255 chars' => '4字以上255字以下にしてください。', 'Zip is not valid' => 'ZIP codeが正しくありません。', 'Must be a single line of no more than 255 chars' => '255字以下の文字列にしてください。改行を含めることはできません。', 'Email is not valid' => 'メールアドレスが正しくありません。', 'Must not contain spaces' => 'スペースを含めることはできません。', 'Field must contain a single word' => '単語を含めてください。', 'Must not be empty' => '空にすることはできません。', 'Must be between 8 and 255 chars, and contain a non-alpha char' => '8字以上255字以下の文字列で、アルファベット以外の文字を含めてください。', # H::F::Field::Date 'Date is too early' => '日付が早すぎます。', 'Date is too late' => '日付が遅すぎます。', # H::F::Field::DateTime 'Not a valid DateTime' => '日時が正しくありません。', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => '無効な値です。[_1]([_2])', # H::F::Field::Email 'Email should be of the format [_1]' => 'メールアドレスは次のようにしてください。[_1]', # H::F::Field::Integer 'Value must be an integer' => '整数にしてください。', # H::F::Field::Money 'Value cannot be converted to money' => '金額として認識できません。', 'Value must be a real number' => '実数にしてください。l', # H::F::Field::Password 'Please enter a password in this field' => 'パスワードを入力してください。', 'Password must not match [_1]' => 'パスワードが「[_1]」と一致しています。', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'パスワードの確認を入力してください。', 'The password confirmation does not match the password' => 'パスワードの確認が入力されたパスワードと一致しません。', # H::F::Field::PosInteger 'Value must be a positive integer' => '正の整数にしてください。', # H::F::Field::Select 'This field does not take multiple values' => '複数選択することはできません。', '\'[_1]\' is not a valid value' => '「[_1]」は正しくありません。', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => '[_1]字以下にしてください。[_2]字入力されています。', 'Field must be at least [quant,_1,character]. You entered [_2]' => '[_1]字以上にしてください。[_2]字入力されています。', # H::F::Field::Upload 'File uploaded is empty' => 'アップロードされたファイルは空でした。', 'File is too small (< [_1] bytes)' => 'ファイルが小さすぎます。(< [_1] bytes)', 'File is too big (> [_1] bytes)' => 'ファイルが大きすぎます。 (> [_1] bytes)', 'File not found for upload field' => 'ファイルが見付かりません。', # H::F::Model 'Value must be unique in the database' => 'データベース内でユニークな値にしてください。', ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::ja_jp - Japanese message file =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/pt_br.pm0000644000077000007700000001423312221042077021775 0ustar gshankgshankpackage HTML::FormHandler::I18N::pt_br; # ABSTRACT: Brazilian Portuguese message file use strict; use warnings; use utf8; use base 'HTML::FormHandler::I18N'; # translator: Daniel Nicoletti # notify before release: dantti12@gmail.com # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H:: F:: Field 'field is invalid' => 'campo esta inválido', 'Wrong value' => 'Valor errado', '[_1] does not match' => '[_1] não coincide', '[_1] not allowed' => '[_1] não permitido', 'Value must be between [_1] and [_2]' => 'Valor deve estar entre [_1] e [_2]', 'Value must be greater than or equal to [_1]' => 'Valor deve ser maior ou igual a [_1]', 'Value must be less than or equal to [_1]' => 'Valor deve ser menor ou igual a [_1]', '[_1] field is required' => '[_1] é obrigatório', 'error occurred' => 'ocorreu um erro', # H:: F:: Types 'Must be a positive number' => 'Deve ser um número positivo', 'Must be a positive integer' => 'Deve ser um número inteiro positivo', 'Must be a negative number' => 'Deve ser um número negativo', 'Must be a negative integer' => 'Deve ser um número inteiro negativo', 'Must be a single digit' => 'Deve ser um único digito', 'Must be a single line of no more than 255 chars' => 'Deve ser uma única linha com não mais do que 255 caracteres', 'Must be a non-empty single line of no more than 255 chars' => 'Deve ser uma única linha não nula com não mais do que 255 caracteres', 'Must be between 4 and 255 chars' => 'Deve ser entre 4 e 255 caracteres', 'Not a valid state' => 'Não é um estado válido', 'Email is not valid' => 'Email inválido', 'Zip is not valid' => 'CEP inválido', 'Not a valid IP address' => 'Endereço IP inválido', 'Must not contain spaces' => 'Não deve conter espaços', 'Must be made up of letters, digits, and underscores' => 'Deve conter letras, digitos e underscores', 'Must not be all digits' => 'Não pode ter todos os digitos', 'Field contains non-printable characters' => 'Campo contém caracteres inválidos', 'Field must contain a single word' => 'Campo deve conter uma única palavra', 'Must not be empty' => 'Não pode estar vazio', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Deve ser entre 8 e 255 caracteres, e conter um caractere não alfa numérico', # H::F::Field::Date 'Date is too early' => 'A data é muito cedo', 'Date is too late' => 'A data é muito tarde', # H::F::Field::DateTime 'Not a valid DateTime' => 'Data e hora inválidos', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Valor inválido para [_1]: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'Email deve estar no formato [_1]', # H::F::Field::Integer 'Value must be an integer' => 'Valor deve ser um inteiro', # H::F::Field::Money 'Value cannot be converted to money' => 'Valor não pode ser convertido a dinheiro', 'Value must be a real number' => 'Valor deve ser um número real', # H::F::Field::Password 'Please enter a password in this field' => 'Por favor coloque uma senha neste campo', 'Password must not match [_1]' => 'Senha não pode coincidir com [_1]', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Por favor confirme a senha', 'The password confirmation does not match the password' => 'A confirmação da senha não coincide', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Valor deve ser um inteiro positivo', # H::F::Field::Select 'This field does not take multiple values' => 'Este campo não recebe valores múltiplos', '\'[_1]\' is not a valid value' => '\'[_1]\' é um valor inválido', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Campo não deve exceder [_1]. Você colocou: [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Campo deve ser ao menos [_1]. Você colocou: [_2]', # H:: F:: Field:: Upload 'File uploaded is empty' => 'Arquivo enviado está vazio', 'File is too small (< [_1] bytes)' => 'Arquivo é muito pequeno (menor que [_1] bytes)', 'File is too big (> [_1] bytes)' => 'Arquivo é muito grande (maior que [_1] bytes)', 'File not found for upload field' => 'Arquivo não encontrado no campo de envio', # H:: F:: Model 'Value must be unique in the database' => 'Valor deve ser único no banco de dados', # Other 'Your datetime does not match your pattern.' => 'A sua data/hora náo coincide com o padrão.', ); 1; =pod =head1 NAME HTML::FormHandler::I18N::pt_br - Brazilian Portuguese message file =head1 VERSION version 0.40050 =head1 NAME HTML::FormHandler::I18N::pt_br - Brazilian Portuguese message file =head1 VERSION version 0.40017 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2012 by Gerda Shank. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 __END__ HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/ru_ru.pm0000644000077000007700000001620512221042077022024 0ustar gshankgshankpackage HTML::FormHandler::I18N::ru_ru; # ABSTRACT: Russian message file use strict; use warnings; use utf8; use base 'HTML::FormHandler::I18N'; # translator: Oleg Kostyuk # notify before release: cub@cpan.org # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F::Field 'field is invalid' => 'Поле неверно', 'Wrong value' => 'Неверное значение', '[_1] does not match' => 'не совпадает с [_1]', '[_1] not allowed' => '[_1] не разрешено', 'Value must be between [_1] and [_2]' => 'значение должно быть между [_1] и [_2]', 'Value must be greater than or equal to [_1]' => 'значение должно быть больше или равно [_1]', 'Value must be less than or equal to [_1]' => 'значение должно быть меньше или равно [_1]', '[_1] field is required' => 'поле [_1] является обязательным', 'error occurred' => 'произошла ошибка', # H::F::Types 'Must be a positive number' => 'Должно быть положительным числом', 'Must be a positive integer' => 'Должно быть положительным целым числом', 'Must be a negative number' => 'Должно быть отрицательным числом', 'Must be a negative integer' => 'Должно быть отрицательным целым числом', 'Must be a single digit' => 'Должно быть одной цифрой', 'Must be a single line of no more than 255 chars' => 'Должно быть одной строкой, не более 255 символов', 'Must be a non-empty single line of no more than 255 chars' => 'Должно быть не пустой строкой, не более 255 символов', 'Must be between 4 and 255 chars' => 'Должно быть от 4 до 255 символов', 'Not a valid state' => 'Не верное состояние', 'Email is not valid' => 'Адрес электронной почты не корректен', 'Zip is not valid' => 'Почтовый индекс не корректен', 'Not a valid IP address' => 'IP адрес не корректен', 'Must not contain spaces' => 'Не может содержать пробелы', 'Must be made up of letters, digits, and underscores' => 'Должно состоять из букв, цифр и подчёркиваний', 'Must not be all digits' => 'Должно состоять не только из цифр', 'Field contains non-printable characters' => 'Поле содержит непечатаемые символы', 'Field must contain a single word' => 'Поле должно содержать одно слово', 'Must not be empty' => 'Должно быть не пустым', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Должно быть от 8 до 255 символов, и содержать не-буквенный символ', # H::F::Field::Date 'Date is too early' => 'Слишком ранняя дата', 'Date is too late' => 'Слишком поздняя дата', # H::F::Field::DateTime 'Not a valid DateTime' => 'Неверная дата/время', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Неверное значение для [_1]: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'Адрес электронной почты должен быть в формате [_1]', # H::F::Field::Integer 'Value must be an integer' => 'Значение должно быть целым числом', # H::F::Field::Money 'Value cannot be converted to money' => 'Значение не может быть воспринято как денежное', 'Value must be a real number' => 'Значение должно быть вещественным числом', # H::F::Field::Password 'Please enter a password in this field' => 'Пожалуйста, введите пароль', 'Password must not match [_1]' => 'Пароль должен не совпадать с [_1]', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Пожалуйста, введите подтверждение пароля', 'The password confirmation does not match the password' => 'Подтверждение пароля не совпадает с паролем', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Значение должно быть положительным целым числом', # H::F::Field::Select 'This field does not take multiple values' => 'Это поле не принимает несколько значений', '\'[_1]\' is not a valid value' => '\'[_1]\' не корректное значение', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Символов должно быть не более [_1]. Вы ввели: [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Символов должно быть не менее [_1]. Вы ввели: [_2]', # H::F::Field::Upload 'File uploaded is empty' => 'Переданный файл пуст', 'File is too small (< [_1] bytes)' => 'Файл слишком мал (менее [_1] байт)', 'File is too big (> [_1] bytes)' => 'Файл слишком велик (более [_1] байт)', 'File not found for upload field' => 'Файл для загрузки не найден', # H::F::Model 'Value must be unique in the database' => 'Значение должно быть уникальным для базы данных', # Other 'Your datetime does not match your pattern.' => 'Введённые дата/время не совпадают с вашим шаблоном.', ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::ru_ru - Russian message file =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/sv_se.pm0000644000077000007700000001137412221042077022011 0ustar gshankgshankpackage HTML::FormHandler::I18N::sv_se; # ABSTRACT: Swedish message translations use strict; use warnings; use base 'HTML::FormHandler::I18N'; use utf8; # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F::Field 'field is invalid' => 'Fältet är ogiltigt.', 'Wrong value' => 'Ogiltigt värde.', '[_1] does not match' => '[_1] matchar inte.', '[_1] not allowed' => '[_1] är inte tillåtet.', '[_1] field is required' => 'Fältet får inte vara tomt.', 'error occurred' => 'Ett fel har uppstått.', 'Value must be between [_1] and [_2]' => 'Värdet ska ligga mellan [_1] och [_2].', 'Value must be greater than or equal to [_1]' => 'Värdet ska vara minst [_1].', 'Value must be less than or equal to [_1]' => 'Värdet får vara högst [_1].', # H::F::Types 'Must be a positive number' => 'Ska vara ett positivt tal.', 'Must be a positive integer' => 'Ska vara ett positivt heltal.', 'Must be a negative number' => 'Ska vara ett negativt tal.', 'Must be a negative integer' => 'Ska vara ett negativt heltal.', 'Must be a single digit' => 'Ska vara ett ensiffrigt tal.', 'Must be a non-empty single line of no more than 255 chars' => 'Ska vara en enda rad med minst ett och högst 255 tecken.', 'Must be made up of letters, digits, and underscores' => 'Får bara innehålla bokstäver, siffror och understreck.', 'Not a valid IP address' => 'Ogiltig IP-adress.', 'Must not be all digits' => 'Får inte vara enbart siffror.', 'Not a valid state' => 'Ogiltig delstat.', 'Field contains non-printable characters' => 'Fältet innehåller tecken som inte går att skriva ut.', 'Must be between 4 and 255 chars' => 'Ska vara mellan 4 och 255 tecken.', 'Zip is not valid' => 'Ogiltigt postnummer.', 'Must be a single line of no more than 255 chars' => 'Ska vara en enda rad med högst 255 tecken.', 'Email is not valid' => 'Ogiltig e-postadress.', 'Must not contain spaces' => 'Får inte innehålla mellanslag.', 'Field must contain a single word' => 'Ska vara ett enda ord.', 'Must not be empty' => 'Ska inte vara tom.', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Ska vara mellan 8 och 255 tecken, och innehålla ett tecken som inte är en bokstav.', # H::F::Field::Date 'Date is too early' => 'Datumet är för tidigt.', 'Date is too late' => 'Datumet är för sent.', # H::F::Field::DateTime 'Not a valid DateTime' => 'Ogiltig datum- eller tidsangivelse.', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Ogiltigt värde för [_1]: [_2].', # H::F::Field::Email 'Email should be of the format [_1]' => 'E-postadressen måste ha formatet [_1].', # H::F::Field::Integer 'Value must be an integer' => 'Ska vara ett heltal.', # H::F::Field::Money 'Value cannot be converted to money' => 'Kan inte läsas som en summa pengar.', 'Value must be a real number' => 'Ska vara ett decimaltal.', # H::F::Field::Password 'Please enter a password in this field' => 'Skriv ett lösenord i detta fält.', 'Password must not match [_1]' => 'Lösenordet stämmer inte med [_1].', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Skriv lösenordet en gång till.', 'The password confirmation does not match the password' => 'Lösenorden stämmer inte överens.', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Ska vara ett positivt heltal.', # H::F::Field::Select 'This field does not take multiple values' => 'Välj inte mer än ett värde här.', '\'[_1]\' is not a valid value' => '\'[_1]\' är inte ett giltigt värde.', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Ska inte vara längre än [_1] tecken. Du har skrivit [_2].', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Ska vara minst [_1] tecken. Du har skrivit [_2].', # H::F::Field::Upload 'File uploaded is empty' => 'Fick ingen fil.', 'File is too small (< [_1] bytes)' => 'Filen är för liten. (Mindre än [_1] bytes).', 'File is too big (> [_1] bytes)' => 'Filen är för stor (Större än [_1] bytes).', 'File not found for upload field' => 'Filen hittades inte.', # H::F::Model 'Value must be unique in the database' => 'Värdet finns redan registrerat.', ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::sv_se - Swedish message translations =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/tr_tr.pm0000644000077000007700000001575112221042077022027 0ustar gshankgshankpackage HTML::FormHandler::I18N::tr_tr; # ABSTRACT: Turkish message file use strict; use warnings; use base 'HTML::FormHandler::I18N'; # Translated by Ozum Eldogan use utf8; # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H::F::Field 'field is invalid' => 'Geçersiz değer', 'Wrong value' => 'Hatalı değer', '[_1] does not match' => '[_1] formatı uymuyor', '[_1] not allowed' => '[_1] izinli değil', 'Value must be between [_1] and [_2]' => 'Değer [_1] ile [_2] arasında olmalı', 'Value must be greater than or equal to [_1]' => 'Değer [_1] veya daha yüksek olmalı', 'Value must be less than or equal to [_1]' => 'Değer [_1] veya daha düşük olmalı', '[_1] field is required' => '[_1] alanı boş bırakılamaz', 'error occurred' => 'Hata oluştu', # H::F::Types 'Must be a positive number' => 'Pozitif sayı olmalı', 'Must be a positive integer' => 'Pozitif tam sayı olmalı', 'Must be a negative number' => 'Negatif sayı olmalı', 'Must be a negative integer' => 'Negatif tam sayı olmalı', 'Must be a single digit' => 'Tek haneli bir sayı olmalı', 'Must be a single line of no more than 255 chars' => '255 karakterden kısa ve tek bir satır olmalı', 'Must be a non-empty single line of no more than 255 chars' => 'Boş bırakılmamalı, 255 karakterden kısa ve tek bir satır olmalı', 'Must be between 4 and 255 chars' => '4 ile 255 karakter arasında olmalı', 'Not a valid state' => 'Geçerli bir eyalet değil', 'Email is not valid' => 'Geçersiz E-Posta', 'Zip is not valid' => 'Geçersiz posta kodu', 'Not a valid IP address' => 'Geçersiz IP adresi', 'Must not contain spaces' => 'Boşluk içeremez', 'Must be made up of letters, digits, and underscores' => 'Sadece harf, rakam ya da "_" içerebilir', 'Must not be all digits' => 'Sadece rakamlardan oluşamaz', 'Field contains non-printable characters' => 'Basılamayan karakterler içeriyor', 'Field must contain a single word' => 'Tek bir kelime olmalı', 'Must not be empty' => 'Boş olmamalı', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Harf olmayan karakter içermeli ve 8-255 karakter arasında olmalı', # H::F::Field::Date 'Date is too early' => 'Bu tarih izin verilen en küçük tarihten daha önce', 'Date is too late' => 'Bu tarih izin verilen en büyük tarihten daha sonra', # H::F::Field::DateTime 'Not a valid DateTime' => 'Geçersiz tarih/zaman', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => '[_1] için geçersiz değer: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'E-Posta [_1] formatında olmalı', # H::F::Field::FloatNumber 'Must be a number. May contain numbers, +, - and decimal separator \'[_1]\'', => 'Bir sayı olmalı. Rakamlar, +, -, ve ondalık ayırıcı \'[_1]\' içerebilir', 'Total size of number must be less than or equal to [_1], but is [_2]', => 'Maksimum [_1] rakam içerebilir ama [_2] rakam içeriyor', 'May have a maximum of [quant,_1,digit] after the decimal point, but has [_2]', => 'Ayraçtan sonra maksimum [_1] rakam içerebilir ama [_2] rakam içeriyor', # H::F::Field::Integer 'Value must be an integer' => 'Tam sayı olmalı', # H::F::Field::Money 'Value cannot be converted to money' => 'Değer para birimine çevrilemedi', 'Value must be a real number' => 'Ondalık sayı olmalı', # H::F::Field::Password 'Please enter a password in this field' => 'Lütfen bir şifre girin', 'Password must not match [_1]' => 'Şifre [_1] ile aynı olmamalı', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Lütfen şifre onayı girin', 'The password confirmation does not match the password' => 'Şifre onayı ile şifre aynı değil', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Pozitif tam sayı olmalı', # H::F::Field::Select 'This field does not take multiple values' => 'Birden fazla değer seçilemez', '\'[_1]\' is not a valid value' => '\'[_1]\' geçerli bir değer değil', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Girilen verinin uzunluğu en fazla [_1] olabilir. Gönderilen: [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Girilen verinin uzunluğu en az [_1] olabilir. Gönderilen: [_2]', # H::F::Field::Upload 'File uploaded is empty' => 'Gönderilen dosya boş', 'File is too small (< [_1] bytes)' => 'Dosya çok küçük. (< [_1] bytes)', 'File is too big (> [_1] bytes)' => 'Dosya çok büyük. (> [_1] bytes)', 'File not found for upload field' => 'Dosya bulunamadı', # H::F::Model 'Value must be unique in the database' => 'Daha önceden kullanımda', # Other 'Your datetime does not match your pattern.' => 'Tarih formatı hatalı.', ); 1; =pod =head1 NAME HTML::FormHandler::I18N::tr_tr - Turkish message file =head1 VERSION version 0.40050 =head1 NAME HTML::FormHandler::I18N::tr_tr - Turkish message file =head1 VERSION version 0.35005 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2011 by Gerda Shank. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 __END__ HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N/ua_ua.pm0000644000077000007700000001571112221042077021763 0ustar gshankgshankpackage HTML::FormHandler::I18N::ua_ua; # ABSTRACT: Ukrainian message file use strict; use warnings; use utf8; use base 'HTML::FormHandler::I18N'; # translator: Oleg Kostyuk # notify before release: cub@cpan.org # Auto define lexicon our %Lexicon = ( '_AUTO' => 1, # H:: F:: Field 'field is invalid' => 'Поле невірне', 'Wrong value' => 'Неправильне значення', '[_1] does not match' => 'не співпадає з [_1]', '[_1] not allowed' => '[_1] не дозволяється', 'Value must be between [_1] and [_2]' => 'значення повинне бути між [_1] та [_2]', 'Value must be greater than or equal to [_1]' => 'значення повинне бути більше або дорівнювати [_1]', 'Value must be less than or equal to [_1]' => 'значення повинне бути менше або дорівнювати [_1]', '[_1] field is required' => 'поле [_1] є обов\x{02BC}язковим', 'error occurred' => 'трапилась помилка', # H:: F:: Types 'Must be a positive number' => 'Має бути позитивним числом', 'Must be a positive integer' => 'Має бути позитивним цілим числом', 'Must be a negative number' => 'Має бути негативним числом', 'Must be a negative integer' => 'Має бути негативним цілим числом', 'Must be a single digit' => 'Має бути однією цифрою', 'Must be a single line of no more than 255 chars' => 'Має бути одним рядком, не більше 255 символів', 'Must be a non-empty single line of no more than 255 chars' => 'Має бути не пустим рядком, не більше 255 символів', 'Must be between 4 and 255 chars' => 'Має бути від 4 до 255 символів', 'Not a valid state' => 'Не вірний стан', 'Email is not valid' => 'Адреса електронної пошти не коректна', 'Zip is not valid' => 'Поштовий індекс не коректний', 'Not a valid IP address' => 'IP адреса не коректна', 'Must not contain spaces' => 'Не може мати пробіли', 'Must be made up of letters, digits, and underscores' => 'Має складатися з букв, цифр та підкреслень', 'Must not be all digits' => 'Має бути не тільки з цифр', 'Field contains non-printable characters' => 'Поле містить недруковані символи', 'Field must contain a single word' => 'Поле має містити одне слово', 'Must not be empty' => 'Має бути не пустим', 'Must be between 8 and 255 chars, and contain a non-alpha char' => 'Має бути від 8 до 255 символів, та мати не-буквений символ', # H::F::Field::Date 'Date is too early' => 'Дата занадто рання', 'Date is too late' => 'Дата занадто піздня', # H::F::Field::DateTime 'Not a valid DateTime' => 'Невірна дата/час', # H::F::Field::Duration 'Invalid value for [_1]: [_2]' => 'Невірне значення для [_1]: [_2]', # H::F::Field::Email 'Email should be of the format [_1]' => 'Адреса электроної пошти має бути у форматі [_1]', # H::F::Field::Integer 'Value must be an integer' => 'Значення має бути цілим числом', # H::F::Field::Money 'Value cannot be converted to money' => 'Значення не може бути сприйнято як грошове', 'Value must be a real number' => 'Значення має бути речовим числом', # H::F::Field::Password 'Please enter a password in this field' => 'Будь ласка, введіть пароль', 'Password must not match [_1]' => 'Пароль має не співпадати з [_1]', # H::F::Field::PasswordConf 'Please enter a password confirmation' => 'Будь ласка, введіть підтвердження паролю', 'The password confirmation does not match the password' => 'Підтверження паролю не співпадає з паролем', # H::F::Field::PosInteger 'Value must be a positive integer' => 'Значення має бути позитивним цілим числом', # H::F::Field::Select 'This field does not take multiple values' => 'Це поле не приймає кілька значень', '\'[_1]\' is not a valid value' => '\'[_1]\' не є вірним значенням', # H::F::Field::Text 'Field should not exceed [quant,_1,character]. You entered [_2]' => 'Символів має бути не більше [_1]. Ви ввели: [_2]', 'Field must be at least [quant,_1,character]. You entered [_2]' => 'Символів має бути не менше [_1]. Ви ввели: [_2]', # H:: F:: Field:: Upload 'File uploaded is empty' => 'Переданий файл порожній', 'File is too small (< [_1] bytes)' => 'Файл занадто малий (менше [_1] байт)', 'File is too big (> [_1] bytes)' => 'Файл занадто великий (більше [_1] байт)', 'File not found for upload field' => 'Файл для загрузки не знайдено', # H:: F:: Model 'Value must be unique in the database' => 'Значення має бути унікальним для бази даних', # Other 'Your datetime does not match your pattern.' => 'Введені дата/час не співпадають з вашим шаблоном.', ); 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N::ua_ua - Ukrainian message file =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/I18N.pm0000644000077000007700000000164512221042077020672 0ustar gshankgshankpackage HTML::FormHandler::I18N; # ABSTRACT: internationalization use strict; use warnings; use base ('Locale::Maketext'); use Try::Tiny; sub maketext { my ( $lh, @message ) = @_; return '' unless scalar @message; return '' unless defined $message[0]; my $out; try { $out = $lh->SUPER::maketext(@message); } catch { die "Unable to do maketext on: " . $message[0] . "\nIf the message contains brackets you may need to escape them with a tilde."; }; return $out; } 1; __END__ =pod =head1 NAME HTML::FormHandler::I18N - internationalization =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/InitResult.pm0000644000077000007700000001542312221042077022314 0ustar gshankgshankpackage HTML::FormHandler::InitResult; # ABSTRACT: internal code use Moose::Role; # _init is for building fields when # there is no initial object and no params # formerly _init sub _result_from_fields { my ( $self, $self_result ) = @_; # defaults for compounds, etc. if ( my @values = $self->get_default_value ) { my $value = @values > 1 ? \@values : shift @values; if( ref $value eq 'HASH' || blessed $value ) { return $self->_result_from_object( $self_result, $value ); } $self->init_value($value) if defined $value; $self_result->_set_value($value) if defined $value; } my $my_value; for my $field ( $self->sorted_fields ) { next if ($field->inactive && !$field->_active); my $result = HTML::FormHandler::Field::Result->new( name => $field->name, parent => $self_result ); $result = $field->_result_from_fields($result); $my_value->{ $field->name } = $result->value if $result->has_value; $self_result->add_result($result) if $result; } # setting value here to handle disabled compound fields, where we want to # preserve the 'value' because the fields aren't submitted...except for the # form. Not sure it's the best idea to skip for form, but it maintains previous behavior $self_result->_set_value($my_value) if ( keys %$my_value ); $self->_set_result($self_result); $self_result->_set_field_def($self) if $self->DOES('HTML::FormHandler::Field'); return $self_result; } # building fields from input (params) # formerly done in validate_field sub _result_from_input { my ( $self, $self_result, $input, $exists ) = @_; # transfer the input values to the input attributes of the # subfields return unless ( defined $input || $exists || $self->has_fields ); $self_result->_set_input($input); if ( ref $input eq 'HASH' ) { foreach my $field ( $self->sorted_fields ) { next if ($field->inactive && !$field->_active); my $field_name = $field->name; my $result = HTML::FormHandler::Field::Result->new( name => $field_name, parent => $self_result ); $result = $field->_result_from_input( $result, $input->{$field->input_param || $field_name}, exists $input->{$field->input_param || $field_name} ); $self_result->add_result($result) if $result; } } $self->_set_result($self_result); $self_result->_set_field_def($self) if $self->DOES('HTML::FormHandler::Field'); return $self_result; } # building fields from model object or init_obj hash # formerly _init_from_object sub _result_from_object { my ( $self, $self_result, $item ) = @_; return unless ( $item || $self->has_fields ); # empty fields for compounds my $my_value; my $init_obj = $self->form->init_object; for my $field ( $self->sorted_fields ) { next if ( $field->inactive && !$field->_active ); my $result = HTML::FormHandler::Field::Result->new( name => $field->name, parent => $self_result ); if ( (ref $item eq 'HASH' && !exists $item->{ $field->accessor } ) || ( blessed($item) && !$item->can($field->accessor) ) ) { my $found = 0; if ($field->form->use_init_obj_when_no_accessor_in_item) { # if we're using an item, look for accessor not found in item # in the init_object my @names = split( /\./, $field->full_name ); my $init_obj_value = $self->find_sub_item( $init_obj, \@names ); if ( defined $init_obj_value ) { $found = 1; $result = $field->_result_from_object( $result, $init_obj_value ); } } $result = $field->_result_from_fields($result) unless $found; } else { my $value = $self->_get_value( $field, $item ) unless $field->writeonly; $result = $field->_result_from_object( $result, $value ); } $self_result->add_result($result) if $result; $my_value->{ $field->name } = $field->value; } $self_result->_set_value($my_value); $self->_set_result($self_result); $self_result->_set_field_def($self) if $self->DOES('HTML::FormHandler::Field'); return $self_result; } # this is used for reloading repeatables form the database if they've changed and # for finding field values in the init_object when we have an item and the # 'use_init_obj_when_no_accessor_in_item' flag is set sub find_sub_item { my ( $self, $item, $field_name_array ) = @_; my $this_fname = shift @$field_name_array;; my $field = $self->field($this_fname); my $new_item = $self->_get_value( $field, $item ); if ( scalar @$field_name_array ) { $new_item = $field->find_sub_item( $new_item, $field_name_array ); } return $new_item; } sub _get_value { my ( $self, $field, $item ) = @_; my $accessor = $field->accessor; my @values; if( defined $field->default_over_obj ) { @values = $field->default_over_obj; } elsif( $field->form && $field->form->use_defaults_over_obj && ( @values = $field->get_default_value ) ) { } elsif ( blessed($item) && $item->can($accessor) ) { # this must be an array, so that DBIx::Class relations are arrays not resultsets @values = $item->$accessor; # for non-DBIC blessed object where access returns arrayref if ( scalar @values == 1 && ref $values[0] eq 'ARRAY' && $field->has_flag('multiple') ) { @values = @{$values[0]}; } } elsif ( exists $item->{$accessor} ) { my $v = $item->{$accessor}; if($field->has_flag('multiple') && ref($v) eq 'ARRAY'){ @values = @$v; } else { @values = $v; } } elsif ( @values = $field->get_default_value ) { } else { return; } if( $field->has_inflate_default_method ) { @values = $field->inflate_default(@values); } my $value; if( $field->has_flag('multiple')) { $value = scalar @values == 1 && ! defined $values[0] ? [] : \@values; } else { $value = @values > 1 ? \@values : shift @values; } return $value; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::InitResult - internal code =head1 VERSION version 0.40050 =head1 SYNOPSIS Internal role for initializing the result objects. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/0000755000077000007700000000000012221042077021064 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Catalyst.pod0000644000077000007700000001435412221042077023363 0ustar gshankgshankpackage HTML::FormHandler::Manual::Catalyst; # ABSTRACT: using HFH forms in Catalyst __END__ =pod =head1 NAME HTML::FormHandler::Manual::Catalyst - using HFH forms in Catalyst =head1 VERSION version 0.40050 =head1 SYNOPSIS L This part of the FormHandler Manual describes the use of the L package in Catalyst controllers. See the other FormHandler documentation at L, or the base class at L. =head1 DESCRIPTION Although L can be used in any Perl web application, module, or script, one of its most common uses is in L applications. Using a form takes only a few lines of code, so it's not necessary to have a L base controller, although you could make a base controller for FormHandler if you're doing more than the basics. =head2 A Controller Example The following example uses chained dispatching. The 'form' method is called by both the create and edit actions. package BookDB::Controller::Borrower; use Moose; BEGIN { extends 'Catalyst::Controller' } use BookDB::Form::Borrower; sub borrower_base : Chained PathPart('borrower') CaptureArgs(0) { } sub list : Chained('borrower_base') PathPart('list') Args(0) { my ( $self, $c ) = @_; my $borrowers = [ $c->model('DB::Borrower')->all ]; my @columns = ( 'name', 'email' ); $c->stash( borrowers => $borrowers, columns => \@columns, template => 'borrower/list.tt' ); } sub add : Chained('borrower_base') PathPart('add') Args(0) { my ( $self, $c ) = @_; # Create the empty borrower row for the form $c->stash( borrower => $c->model('DB::Borrower')->new_result({}) ); return $self->form($c); } sub item : Chained('borrower_base') PathPart('') CaptureArgs(1) { my ( $self, $c, $borrower_id ) = @_; $c->stash( borrower => $c->model('DB::Borrower')->find($borrower_id) ); } sub edit : Chained('item') PathPart('edit') Args(0) { my ( $self, $c ) = @_; return $self->form($c); } sub form { my ( $self, $c ) = @_; my $form = BookDB::Form::Borrower->new; $c->stash( form => $form, template => 'borrower/form.tt' ); return unless $form->process( item => $c->stash->{borrower}, params => $c->req->parameters ); $c->res->redirect( $c->uri_for($self->action_for('list')) ); } sub delete : Chained('item') PathPart('delete') Args(0) { my ( $self, $c ) = @_; $c->stash->{borrower}->delete; $c->res->redirect( $c->uri_for($c->action_for('list')) ); } 1; =head2 Another way to set up your form If you are setting the schema or other form attributes (such as the user_id, or other attributes) on your form you could create a base controller that would set these in the form on each call using L, or set them in a base Chained method. sub book_base : Chained PathPart('book') CaptureArgs(0) { my ( $self, $c ) = @_; my $form = MyApp::Form->new; $form->schema( $c->model('DB')->schema ); $form->params( $c->req->parameters ); $form->user_id( $c->user->id ); $c->stash( form => $form ); } Then you could just pass in the item_id when the form is processed. return unless $c->stash->{form}->process( item_id => $id ); =head2 Putting a form in a Moose attribute You can also put your form in a Moose attribute in the controller. package MyApp::Controller::Book; use Moose; BEGIN { extends 'Catalyst::Controller'; } use MyApp::Form::Book; has 'edit_form' => ( isa => 'MyApp::Form::Book', is => 'rw', lazy => 1, default => sub { MyApp::Form::Book->new } ); Then you can process the form in your actions with C<< $self->edit_form->process( params => $c->req->body_parameters ); >> or C<< my $result = $self->edit_form->run( params => $c->req->body_parameters ); >>. =head2 Using HTML::FillInForm If you want to use L to fill in values instead of doing it in directly in a template using either the field or the form 'fif' methods, you can use L on your view class: package MyApp::View::TT; use Moose; with 'Catalyst::View::FillInForm'; .... 1; and set the 'fif' hash in the 'fillinform' stash variable: $self->form->process( ... ); $c->stash( fillinform => $self->form->fif ); return unless $form->validated; When the 'fillinform' stash variable is set, HTML::FillInForm will automatically be used by your view to fill in the form values. This can be very helpful when you want to build your forms by hand, or when you have legacy forms that you're just trying to hook up to FormHandler. =head2 The Catalyst context FormHandler has a 'ctx' attribute that can be used to set the Catalyst context (or anything you want, really). But if you can avoid passing in the context, you should do so, because you're mixing up your MVC and it makes it much more difficult to test your forms. But if you need to do it, you can: my $form = MyApp::Form->new( ctx => $c ); Usually you should prefer to add new attributes to your form: package MyApp::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has 'user_id' => ( is => 'rw' ); has 'hostname' => ( is => 'rw' ); has 'captcha_store' => ( is => 'rw' ); .... 1; Then just pass the attributes in on new: my $form => MyApp::Form->new( user_id => $c->user->id, hostname => $c->req->host, captcha_store => $c->{session}->{captcha} ); Or set them using accessors: $form->user_id( $c->user->id ); $form->hostname( $c->req->host ); $form->captcha_store( $c->{session}->{captcha} ); Then you can access these attributes in your form validation methods: sub validate_selection { my ( $self, $field ) = @_; if( $field->value eq 'something' && $self->hostname eq 'something_else' ) { $field->add_error("some error message" ); } } =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Cookbook.pod0000644000077000007700000006563112221042077023351 0ustar gshankgshankpackage HTML::FormHandler::Manual::Cookbook; # ABSTRACT: FormHandler use recipes __END__ =pod =head1 NAME HTML::FormHandler::Manual::Cookbook - FormHandler use recipes =head1 VERSION version 0.40050 =head1 SYNOPSIS L Collection of use recipes for L =head2 No form file, no template file... I had to create a tiny little form this week for admins to enter a comment, and it seemed silly to have to create a form file and a template file. I remembered that you can set the TT 'template' to a string reference and not use a template at all, which is nice when FormHandler will create the form HTML for you anyway. sub comment : Chained('base_sub') PathPart('comment') Args(0) { my ( $self, $c ) = @_; my $form = HTML::FormHandler->new( field_list => [ comment => { type => 'Text', size => 60 }, submit => {type => 'Submit'} ] ); $form->process($c->req->params); if ( $form->validated ) { $self->admin_log( $c, "Admin::Queue", "admin comment", $form->field('comment')->value ); $c->flash( message => 'Comment added' ); $c->res->redirect( $c->stash->{urilist}->{view} ); } my $rendered_form = $form->render; $c->stash( template => \$rendered_form ); } This creates the form on the fly with a comment field and a submit button, renders it using the default TT wrappers, then logs the comment. No other files at all.... FormHandler isn't really necessary for validation here, but it does make it possible to have a simple, standalone method. =head2 Dynamically change the active fields A common use case is for forms with some fields that should be displayed in some circumstances and not in others. There are a number of ways to do this. One way is to use the 'field_list' method: sub field_list { my $self = shift; my @fields; return \@fields; } This only happens at form construction time, however. Another method that works is to define all of the possible fields in your form, and mark some of them 'inactive'; package MyApp::Variable::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar' => ( inactive => 1 ); 1; Set to 'active' or 'inactive' on the 'process' call: $form->process( params => $params, active => ['foo', 'bar'] ); ... $form->process( params => $params, inactive => ['bar'] ); If you need to check some other state to determine whether or not a field should be active, you can do that using a Moose method modifier on 'set_active': before 'set_active' => sub { my $self = shift; $self->active(['foo', bar']) if ( ); }; Fields set to active/inactive on the 'process' call are automatically set back to inactive when the form is cleared, so there's no need to reset. If you want the fields activated for the life of an object, set active on new: my $form = MyApp::Form::User->new( active => ['opt_in', 'active']); =head2 Add custom attributes to FormHandler fields If you want to add custom attributes to the FormHandler fields but don't want to subclass all the fields, you can apply a role containing the new attributes to an L in your form. Use 'traits' on the individual fields to apply a role to field instances. Use the form attribute 'field_traits' to apply a role to all field instances in the form. package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( traits => ['MyApp::TraitFor::Test'] ); has '+field_traits' => ( default => sub { ['Some::Trait', 'Another::Trait'] } ); Or set the traits on new: my $form = MyApp::Form::User->new( field_traits => ['MyApp::TraitFor::Test'] ); my $form = MyApp::Form::User->new( field_list => [ '+foo' => { traits => [...] } ]); To apply the role to a field base class, use 'apply_traits' on that class: HTML::FormHandler::Field->apply_traits( 'Some::Test' ); HTML::FormHandler::Field::Text->apply_traits( 'Another::Trait' ); =head2 Select lists If you want to set the default value of a select field to 0, you can just use 'default' on the field: has_field 'license' => ( default => 0 ); If there is logic involved, you can use a 'default_' method: sub default_license { my ( $self, $field, $item ) = @_; return 0 unless $item && $item->license_id; return $item->license_id; } If the table defining the choices for a select list doesn't include a 'no choice' choice, you can set 'empty_select' in your field if you are using FormHandler rendering: has_field 'subject_class' => ( type => 'Select', empty_select => '--- Choose Subject Class ---' ); Or you can do in a template: [% f = form.field('subject_class') %] You can create a custom select list in an 'options_' method: sub options_country { my $self = shift; return unless $self->schema; my @rows = $self->schema->resultset( 'Country' )-> search( {}, { order_by => ['rank', 'country_name'] } )->all; return [ map { $_->digraph, $_->country_name } @rows ]; } =head2 The database and FormHandler forms If you have to process the input data before saving to the database, and this is something that would be useful in other places besides your form, you should do that processing in the DBIx::Class result class. If the pre-processing is only relevant to HTML form input, you might want to do it in the form by setting a flag to prevent database updates, performing the pre-processing, and then updating the database yourself. has_field 'my_complex_field' => ( type => 'Text', noupdate => 1 ); The 'noupdate' flag is set in order to skip an attempt to update the database for this field (it would not be necessary if the field doesn't actually exist in the database...). You can process the input for the non-updatable field field in a number of different places, depending on what is most logical. Some of the choices are: 1) validate (for the form or field) 2) validate_model 3) model_update When the field is flagged 'writeonly', the value from the database will not be used to fill in the form (put in the C<< $form->fif >> hash, or the field C<< $field->fif >>), but a value entered in the form WILL be used to update the database. If you want to enter fields from an additional table that is related to this one in a 'single' relationship, you can use the DBIx::Class 'proxy' feature to create accessors for those fields. =head2 Set up form base classes or roles for your application You can add whatever attributes you want to your form classes. Maybe you want to save a title, or a particular navigation widget. You could even save bits of text, or retrieve them from the database. package MyApp::Form::Base; use Moose; extends 'HTML::FormHandler::Model::DBIC'; has 'title' => ( isa => 'Str', is => 'rw' ); has 'nav_bar' => ( isa => 'Str', is => 'rw' ); has_block 'reg_header' => ( tag => 'fieldset', label => 'Registration form', content => 'We take your membership seriously...' ); sub summary { my $self = shift; my $schema = $self->schema; my $text = $schema->resultset('Summary')->find( ... )->text; return $text; } 1; Then: package MyApp::Form::Whatsup; use Moose; extends 'MyApp::Form::Base'; has '+title' => ( default => 'This page is an example of what to expect...' ); has '+nav_bar' => ( default => ... ); ... 1; And in the template:

[% form.title %]

[% form.nav_bar %] [% form.block('reg_header')->render %]

Summary: [% form.summary %]

Or you can make these customizations Moose roles. package MyApp::Form::Role::Base; use Moose::Role; ... package MyApp::Form::Whatsup; use Moose; with 'MyApp::Form::Role::Base'; ... =head2 Split up your forms into reusable pieces An address field: package Form::Field::Address; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'street'; has_field 'city'; has_field 'state' => ( type => 'Select', options_method => \&options_state ); has_field 'zip' => ( type => '+Zip' ); sub options_state { ... } no HTML::FormHandler::Moose; 1; A person form that includes an address field: package Form::Person; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+widget_name_space' => ( default => sub {['Form::Field']} ); has_field 'name'; has_field 'telephone'; has_field 'email' => ( type => 'Email' ); has_field 'address' => ( type => 'Address' ); sub validate_name { .... } no HTML::FormHandler::Moose; 1; Or you can use roles; package Form::Role::Address; use HTML::FormHandler::Moose::Role; has_field 'street'; has_field 'city'; has_field 'state' => ( type => 'Select' ); has_field 'zip' => ( type => '+Zip' ); sub options_state { ... } no HTML::FormHandler::Moose::Role; 1; You could make roles that are collections of validations: package Form::Role::Member; use Moose::Role; sub check_zip { ... } sub check_email { ... } 1; And if the validations apply to fields with different names, specify the 'validate_method' on the fields: with 'Form::Role::Member'; has_field 'zip' => ( type => 'Integer', validate_method => \&check_zip ); =head2 Access a user record in the form You might need the user_id to create specialized select lists, or do other form processing. Add a user_id attribute to your form: has 'user_id' => ( isa => 'Int', is => 'rw' ); Then pass it in when you process the form: $form->process( item => $item, params => $c->req->parameters, user_id => $c->user->user_id ); =head2 Handle extra database fields If there is another database field that needs to be updated when a row is created, add an attribute to the form, and then process it with C< before 'update_model' >. In the form: has 'hostname' => ( isa => 'Int', is => 'rw' ); before 'update_model' => sub { my $self = shift; $self->item->hostname( $self->hostname ); }; Then just use an additional parameter when you create/process your form: $form->process( item => $item, params => $params, hostname => $c->req->host ); Some kinds of DB relationships need to have primary keys which might be more easily set in the update_model method; sub update_model { my $self = shift; my $values = $self->values; $values->{some_field}->{some_key} = 'some_value'; $self->_set_value($values); $self->next::method; } If you need to access a database field in order to create the value for a form field you can use a C< default_* > method. sub default_myformfield { my ($self, $field, $item) = @_; return unless defined $item; my $databasefield = $item->databasefield; my $value = ... # do stuff return $value; } =head2 Additional changes to the database If you want to do additional database updates besides the ones that FormHandler does for you, the best solution would generally be to add the functionality to your result source or resultset classes, but if you want to do additional updates in a form you should use an 'around' method modifier and a transaction: around 'update_model' => sub { my $orig = shift; my $self = shift; my $item = $self->item; $self->schema->txn_do( sub { $self->$orig(@_); }); }; =head2 Doing cross validation in roles In a role that handles a number of different fields, you may want to perform cross validation after the individual fields are validated. In the form you could use the 'validate' method, but that doesn't help if you want to keep the functionality packaged in a role. Instead you can use the 'after' method modifier on the 'validate' method: package MyApp::Form::Roles::DateFromTo; use HTML::FormHandler::Moose::Role; has_field 'date_from' => ( type => 'Date' ); has_field 'date_to' => ( type => 'Date' ); after 'validate' => sub { my $self = shift; $self->field('date_from')->add_error('From date must be before To date') if $self->field('date_from')->value gt $self->field('date_to')->value; }; =head2 Changing required flag Sometimes a field is required in one situation and not required in another. You can use a method modifier before 'validate_form': before 'validate_form' => sub { my $self = shift; my $required = 0; $required = 1 if( $self->params->{field_name} eq 'something' ); $self->field('some_field')->required($required); }; This happens before the fields contain input or values, so you would need to look at the param value. If you need the validated value, it might be better to do these sort of checks in the form's 'validate' routine. sub validate { my $self = shift; $self->field('dependent_field')->add_error("Field is required") if( $self->field('some_field')->value eq 'something' && !$self->field('dependent_field')->has_value); } In a Moose role you would need to use a method modifier instead. after 'validate' => sub { ... }; Don't forget the dependency list, which is used for cases where if any of one of a group of fields has a value, all of the fields are required. =head2 Supply an external coderef for validation There are situations in which you need to use a subroutine for validation which is not logically part of the form. It's possible to pass in a context or other sort of pointer and call the routine in the form's validation routine, but that makes the architecture muddy and is not a clear separation of concerns. This is an example of how to supply a coderef when constructing the form that performs validation and can be used to set an appropriate error using L. (Thanks to Florian Ragwitz for this excellent idea...) Here's the form: package SignupForm; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has check_name_availability => ( traits => ['Code'], isa => 'CodeRef', required => 1, handles => { name_available => 'execute', }, ); has_field 'name'; has_field 'email'; sub validate { my $self = shift; my $name = $self->value->{name}; if ( defined $name && length $name && !$self->name_available($name) ) { $self->field('name')->add_error('That name is taken already'); } } 1; And here's where the coderef is passed in to the form. package MyApp::Signup; use Moose; has 'form' => ( is => 'ro', builder => 'build_form' ); sub build_form { my $self = shift; return SignupForm->new( { check_name_availability => sub { my $name = shift; return $self->username_available($name); }, } ); } sub username_available { my ( $self, $name ) = @_; # perform some sort of username availability checks } 1; =head2 Example of a form with custom database interface The default DBIC model requires that the form structure match the database structure. If that doesn't work - you need to present the form in a different way - you may need to fudge it by creating your own 'init_object' and doing the database updates in the 'update_model' method. Here is a working example for a 'family' object (equivalent to a 'user' record') that has a relationship to permission type roles in a relationship 'user_roles'. package My::Form::AdminRoles; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has 'schema' => ( is => 'ro', required => 1 ); # Note 1 has '+widget_wrapper' => ( default => 'None' ); # Note 2 has_field 'admin_roles' => ( type => 'Repeatable' ); # Note 3 has_field 'admin_roles.family' => ( type => 'Hidden' ); # Note 4 has_field 'admin_roles.family_id' => ( type => 'PrimaryKey' ); # Note 5 has_field 'admin_roles.admin_flag' => ( type => 'Boolean', label => 'Admin' ); # Note 6 sub init_object { my $self = shift; my @is_admin; my @is_not_admin; my $active_families = $self->schema->resultset('Family')->search( { active => 1 } ); while ( my $fam = $active_families->next ) { my $admin_flag = $fam->search_related('user_roles', { role_id => 2 } )->count > 0 ? 1 : 0; my $family_name = $fam->name1 . ", " . $fam->name2; my $elem = { family => $family_name, family_id => $fam->family_id, admin_flag => $admin_flag }; if( $admin_flag ) { push @is_admin, $elem; } else { push @is_not_admin, $elem; } } # Note 7 # sort into admin flag first, then family_name @is_admin = sort { $a->{family} cmp $b->{family} } @is_admin; @is_not_admin = sort { $a->{family} cmp $b->{family} } @is_not_admin; return { admin_roles => [@is_admin, @is_not_admin] }; } # Note 8 sub update_model { my $self = shift; my $families = $self->schema->resultset('Family'); my $family_roles = $self->value->{admin_roles}; foreach my $elem ( @{$family_roles} ) { my $fam = $families->find( $elem->{family_id} ); my $has_admin_flag = $fam->search_related('user_roles', { role_id => 2 } )->count > 0; if( $elem->{admin_flag} == 1 && !$has_admin_flag ) { $fam->create_related('user_roles', { role_id => 2 } ); } elsif( $elem->{admin_flag} == 0 && $has_admin_flag ) { $fam->delete_related('user_roles', { role_id => 2 } ); } } } Note 1: This form creates its own 'schema' attribute. You could inherit from L, but you won't be using its update code, so it wouldn't add much. Note 2: The form will be displayed with a template that uses 'bare' form input fields, so 'widget_wrapper' is set to 'None' to skip wrapping the form inputs with divs or table elements. Note 3: This form consists of an array of elements, so there will be a single Repeatable form field with subfields. If you wanted to use automatic rendering, you would also need to create a 'submit' field, but in this case it will just be done in the template. Note 4: This field is actually going to be used for display purposes only, but it's a hidden field because otherwise the information would be lost when displaying the form from parameters. For this case there is no real 'validation' so it might not be necessary, but it would be required if the form needed to be re-displayed with error messages. Note 5: The 'family_id' is the primary key field, necessary for updating the correct records. Note 6: 'init_object' method: This is where the initial object is created, which takes the place of a database row for form creation. Note 7: The entries with the admin flag turned on are sorted into the beginning of the list. This is entirely a user interface choice. Note 8: 'update_model' method: This is where the database updates are performed. The Template Toolkit template for this form:

Update admin status for members

[% FOREACH f IN form.field('admin_roles').sorted_fields %] [% END %]
FamilyAdmin
[% f.field('family').fif %][% f.field('family').render %] [% f.field('family_id').render %] [% f.field('admin_flag').render %]
'None'). There are two hidden fields here, so what is actually seen is two columns, one with the user (family) name, the other with a checkbox showing whether the user has admin status. Notice that the 'family' field information is rendered twice: once as a hidden field that will allow it to be preserved in params, once as a label. The Catalyst controller action to execute the form: sub admin_roles : Local { my ( $self, $c ) = @_; my $schema = $c->model('DB')->schema; my $form = My::Form::AdminRoles->new( schema => $schema ); $form->process( params => $c->req->params ); # re-process if form validated to reload from db and re-sort $form->process( params => {}) if $form->validated; $c->stash( form => $form, template => 'admin/admin_roles.tt' ); return; } Rather than redirect to some other page after saving the form, the form is redisplayed. If the form has been validated (i.e. the 'update_model' method has been run), the 'process' call is run again in order to re-sort the displayed list with admin users at the top. That could have also been done in the 'update_model' method. =head2 A form that takes a resultset, with custom update_model For updating a Repeatable field that is filled from a Resultset, and not a relationship on a single row. Creates a 'resultset' attribute to pass in a resultset. Massages the data into an array that's pointed to by an 'employers' hash key, and does the reverse in the 'update_model' method. Yes, it's a kludge, but it could be worse. If you want to implement a more general solution, patches welcome. package Test::Resultset; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Model::DBIC'; has '+item_class' => ( default => 'Employer' ); has 'resultset' => ( isa => 'DBIx::Class::ResultSet', is => 'rw', trigger => sub { shift->set_resultset(@_) } ); sub set_resultset { my ( $self, $resultset ) = @_; $self->schema( $resultset->result_source->schema ); } sub init_object { my $self = shift; my $rows = [$self->resultset->all]; return { employers => $rows }; } has_field 'employers' => ( type => 'Repeatable' ); has_field 'employers.employer_id' => ( type => 'PrimaryKey' ); has_field 'employers.name'; has_field 'employers.category'; has_field 'employers.country'; sub update_model { my $self = shift; my $values = $self->values->{employers}; foreach my $row (@$values) { delete $row->{employer_id} unless defined $row->{employer_id}; $self->resultset->update_or_create( $row ); } } =head2 Server-provided dynamic value for field There are many different ways to provide values for fields. Default values can be statically provided in the form with the 'default' attribute on the field, with a default_ method in the form, with an init_object/item, and with 'default_over_obj' if you have both an item/init_object and want to provide a default. has_field 'foo' => ( default => 'my_default' ); has_field 'foo' => ( default_over_obj => 'my_default' ); sub default_foo { 'my_default' } .. $form->process( init_object => { foo => 'my_default } ); $form->process( item => foo method to provide default> ); If you want to change the default for the field at run time, there are a number of options. You can set the value in the init_object or item before doing process: my $foo_value = 'some calculated value'; $form->process( init_object => { foo => $foo_value } ); You can use 'update_field_list' or 'defaults' on the 'process' call: $form->process( update_field_list => { foo => { default => $foo_value } } ); -- or -- $form->process( defaults => { foo => $foo_value } ); You can set a Moose attribute in the form class, and set the default in a default_ method: package My::Form; use HTML::FormHandler::Moose; extends 'HTML::Formhandler'; has 'form_id' => ( isa => 'Str', is => 'rw' ); has_field 'foo'; sub default_foo { my $self = shift; return $self->form_id; } .... $form->process( form_id => 'my_form', params => $params ); You can set a Moose attribute in the form class and set it in an update_fields method: sub update_fields { my $self = shift; $self->field('foo')->default('my_form'); } =head2 Static form, dynamic field IDs The problem: you have a form that will be used in multiple places on a page, but you want to use a static form instead of doing 'new' for each. You can pass a form name in on the process call and use 'html_prefix' in the form: $form->process( name => '...', params => {} ); But the field 'id' attribute has already been constructed and doesn't change. Solution: apply a role to the base field class to replace the 'id' getter for the 'id' attribute with a method which constructs the 'id' dynamically. Since the role is being applied to the base field class, you can't just use 'sub id', because the 'id' method defined by the 'id' attribute has precedence. So create an 'around' method modifier that replaces it in the role. package My::DynamicFieldId; use Moose::Role; around 'id' => sub { my $orig = shift; my $self = shift; my $form_name = $self->form->name; return $form_name . "." . $self->full_name; }; package My::CustomIdForm; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+html_prefix' => ( default => 1 ); has '+field_traits' => ( default => sub { ['My::DynamicFieldId'] } ); has_field 'foo'; has_field 'bar'; =head2 Create different field IDs Use 'build_id_method' to give your fields a different format 'id': package MyApp::CustomId; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+update_field_list' => ( default => sub { { all => { build_id_method => \&custom_id } } } ); has_field 'foo' => ( type => 'Compound' ); has_field 'foo.one'; has_field 'foo.two'; has_field 'foo.three'; sub custom_id { my $self = shift; my $full_name = $self->full_name; $full_name =~ s/\./_/g; return $full_name; } The above method provides IDs of "foo_two" and "foo_three" instead of "foo.two" and "foo.three". =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Database.pod0000644000077000007700000001223712221042077023301 0ustar gshankgshankpackage HTML::FormHandler::Manual::Database; # ABSTRACT: FormHandler use recipes __END__ =pod =head1 NAME HTML::FormHandler::Manual::Database - FormHandler use recipes =head1 VERSION version 0.40050 =head1 SYNOPSIS L Information on interfacing FormHandler forms and fields with a database. Also see L. =head1 Form Models For a database form, use a model base class that interfaces with the database, such as L, which needs to be installed as a separate package. There's also a sample 'object' model in L, which will update a simple object. When using a database model, form field values for the row are retrieved from the database using the field 'accessor' attributes (defaults to field name) as database class accessors. FormHandler will use relationships to populate single and multiple selection lists, and validate input. A 'single' relationship is processed by L. A 'has_many' relationship is processed by L. Do not use database row method names, such as 'delete', as field names in a database form. You can pass in either the primary key or a row object to the form. If a primary key (item_id) is passed in, you must also provide the schema. The model will use the item_class (DBIC source name) to fetch the row from the database. If you pass in a row object (item), the schema, item_class, and item_id will be set from the row. Executing C<< $form->process( item => $row, params => $params ); >> will validate the parameters and then update or create the database row object. =head1 Fields that map to database relationships =head2 Select A select field will automatically retrieve a select list from the database, if the proper column names are provided. Single selects handle 'belongs_to' relationships, where the related table is used to construct a selection list from the database. See also L and 'lookup_options' in L. =head2 Multiple Select A multiple select is either a 'Select' with multiple => 1 set, or a field of the 'Multiple' type. The name of a Multiple select which pulls options from the database automatically should be the name of the 'many_to_many' relationship. The 'value' of the field is derived from the 'has_many' part of the relationship. The primary key is used for the 'id' of the select. The 'label' column of the select is assumed to be 'name'. If the label column has a different name, it must be specified with 'label_column'. Pertinent attributes: label_column active_column sort_column See also L and L. =head2 Compound fields A compound field represents a single relationship to another table. Although most compound relations can be handled without providing a primary key, in some circumstances you may need to provide a PrimaryKey field, or add extra values in update_model. See also L. The default for compound fields is that if all subfields are empty, the value of the compound field is set to undef (null). For some types of relations, you may want to set the 'not_nullable' flag to force the field to contain all subfields anyway, such as when the related rows are not deleted when empty. See test t/compound/empty.t for a demonstration of the difference in output. =head2 Repeatable fields The 'Repeatable' field type allows you to update arrays of columns from related tables easily. You will need to provide a 'PrimaryKey' hidden field in the compound field contained in the Repeatable. has_field 'addresses' => ( type => 'Repeatable' ); has_field 'addresses.address_id' => ( type => 'PrimaryKey' ); has_field 'addresses.street'; has_field 'addresses.city'; has_field 'addresses.state'; There are some complications with creating Repeatable elements (with the PrimaryKey field set to undef) in a database and re-presenting the form. See L for more info. =head1 Flags =head2 writeonly Do not read the value from the 'item' when populating the form. =head2 noupdate Do not update the database with this field, i.e. do not include it in C<< $form->value >>. =head1 Form generator A DBIC form generator is installed with the L package. See L. There's also a role, L, that allows simple form fields to be auto-generated from a DBIC result class. my $form = HTML::FormHandler::Model::DBIC->new_with_traits( traits => ['HTML::FormHandler::TraitFor::DBICFields'], includes => ['title', 'author' ], field_list => [ 'submit' => { type => 'Submit', value => 'Save', order => 99 } ], item => $book ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Defaults.pod0000644000077000007700000001353012221042077023341 0ustar gshankgshankpackage HTML::FormHandler::Manual::Defaults; # ABSTRACT: form defaults documentation __END__ =pod =head1 NAME HTML::FormHandler::Manual::Defaults - form defaults documentation =head1 VERSION version 0.40050 =head1 SYNOPSIS L How to set defaults for your fields. =head1 Defaults Defaults for form fields come from a number of different places. The simplest way to set a field's default is on the field definition: has_field 'foo' => ( type => 'Text', default => 'my_foo' ); has_field 'select_many' => ( type => 'Multiple', default => [1, 2, 3] ); You can also set the default for a field with a method in the form with the name 'default_', where any periods in the field name are replaced with underscores. has_field 'foo'; sub default_foo { 'my_default' } Like other field attributes, the 'default' attribute can be modified on new with the 'field_list' attribute, or on 'process' with the 'update_field_list' parameter (or the shorthand form 'defaults'). my $form => MyApp::Form->new( field_list => { '+foo' => { default => 'my_foo' } } ); $form->process( update_field_list => { foo => { default => 'my_foo' } } ); $form->process( defaults => { foo => 'my_foo' }, params => $params ); For forms where you pass in an 'item' (usually a database row object), the values in that object will be used preferentially; if an accessor exists in the 'item' object, then the defaults won't be used. (If an accessor doesn't exist, the defaults *will* be used.) $form->process( item => $row, params => {} ); For the above call the 'default' on the field will not be used, which is usually what you want. When creating a new database record with your form, if you don't pass in an empty row, then the field defaults will be used, or you can provide defaults in an 'init_object'. note: the form class has 'item_class' set already. $form->process( schema => $schema, init_object => $obj ... ); If you provide an empty row object for 'create' type actions, however, you might want some defaults filled in. This can be done by filling the values into the row object or by turning on the form flag 'use_defaults_over_obj'. $form->process( item => $empty_row, use_defaults_over_obj => 1 ); You could also pass in another object or hashref in the 'init_object' attribute, and set the 'use_init_obj_over_item' flag: $form->process( item => $empty_row, init_object => $example, use_init_obj_over_item => 1 ); Note that the 'use_init_obj_over_item' and 'use_defaults_over_obj' flags are automatically cleared (if you're using persistent forms). For forms where some defaults come from a database row, and some defaults come from some other dynamic source (so that putting them into the field definitions doesn't make sense), you can use the 'use_init_obj_when_no_accessor_in_item' flag to provide two different sets of defaults, one set in the 'item' (usually a db row) and one set in the init_obj. If the 'item' is undefined, the values in the init_object are used. in form: has '+use_init_obj_when_no_accessor_in_item' => ( default => 1 ); $form->process( item => $item, init_object => { foo => '...' }, .. ); There is a convenience method for setting 'defaults' on a number of fields at once, the form's 'defaults' attribute, which uses the same mechanism as 'update_field_list' but only sets defaults. Note that this hashref is structured like the update_field_list with regard to field names, while the 'init_object' uses "structured" data: my $defaults = { model => 'standard', 'opts.color' => 'Red', 'opts.size' => 'Big', }; my $init_object => { model => 'standard', opts => { color => 'Red', size => 'Big' } }; $form->process( defaults => $defaults, ... ); $form->process( init_object => $init_object ... ); In addition, the 'defaults' actually changes the 'default' stored in the field definitions, while the init_object does not. There is also an alternative attribute in the fields, 'default_over_obj', but the new 'use_defaults_over_obj' and 'use_init_obj_over_item' flags, make it less necessary. Note that the 'default_over_obj' attribute only provides a default if an item/init_object and accessor exists. =head2 Defaults when processing params Normally when a form is posted, the params will contain all the values that are necessary to fill in the form. However, when a form is used in an API-like fashion, such as complex search forms, sometimes it is convenient to only provide particular params and let the others use defaults. However when the results are built from input, fields with no input are skipped unless the field has a value for 'input_without_param'. There is an additional form-level flag, 'use_fields_for_input_without_param' which will cause fields with no param entry to be built from the fields. This means that 'defaults' on the field will be used to provide a value and an input for the field. =head2 Query parameters for defaults You can use either the 'defaults' hashref or the 'init_object' to provide query parameter 'defaults'. They should not be provided in the 'params' hash, because then FormHandler will assume that the form has been posted and attempt to validate, which you don't want to do until the form has been submitted. Or you can use the 'posted' flag, to indicate whether or not to perform validation: $form->process( posted => ( $c->req->method eq 'POST' ), params => $c->req->params ); Note that in Catalyst, there are 'query_parameters' and 'body_parameters'. The 'parameters' contains both 'query_parameters' and 'body_parameters'. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Errors.pod0000644000077000007700000001032212221042077023042 0ustar gshankgshankpackage HTML::FormHandler::Manual::Errors; # ABSTRACT: FormHandler error methods __END__ =pod =head1 NAME HTML::FormHandler::Manual::Errors - FormHandler error methods =head1 VERSION version 0.40050 =head1 SYNOPSIS L Errors and error messages for L. =head1 DESCRIPTION Errors are added to field or form objects by the field 'add_error' method or the form 'add_form_error' method. FormHandler will perform the 'add_error' for you for built-in validation or 'apply' actions. When performing your own validation in a validation method, you must do the 'add_error' yourself. Errors, along with 'input' and 'value' attributes, are collected in the FormHandler 'result' objects. A number of error retrieving methods are delegated to the field and form classes. The existence (or not) of errors determines whether or not the form has been 'validated'. =head1 Form methods =over 4 =item errors Returns an array of localized error strings (both field and form errors): my @errors = $form->errors; =item has_errors Both 'form' errors and errors from the tree of subfields if( $form->has_errors ) { } =item form_errors, all_form_errors Returns an arrayref / array of error strings on the form (not including field errors). foreach my $err ( $self->all_form_errors ) { $output .= "$err"; } =item has_form_errors Does the form have form_errors? =item add_form_error Add an error to the form which is not associated with a specific field. sub validate { my $self = shift; unless( ) { $self->add_form_error('....'); } } =item push_form_errors Add a non-localized error to the form. =back =head1 Field methods The most common error method is probably 'add_error', which you use in the validation process. sub validate_foo { my ( $self, $field ) = @_; unless ( ) { $field->add_error('Error condition'); } } =over 4 =item errors Returns an array of error strings. =item has_errors Does the field have errors? Note that a compound field that contains subfields with errors will not return true for this method. If you want to know if there are errors in the subfields, do 'has_error_fields'. =item num_errors =item add_error Add an error to the field. Localization is performed. =item push_errors Add an error without localization. =item error_fields In a compound field (and its subclasses, like 'Repeatable'), the list of fields with errors. =back =head1 Result methods The input, value, and error attributes are actually stored in the result objects. Although most of the methods are delegated to the form and field classes, there are times, such as when rendering (because you might be rendering a result that's been peeled off of the form object), that you may need to use result methods. These are the main methods that you might need to use. =over 4 =item has_errors =item errors =item error_results The results with errors; 'error_fields' is a wrapper around this. =back =head1 Messages The base field class and the field subclasses have some 'built-in' error messages. These can be modified by setting the 'messages' hashref in the form or the individual fields. When a message is retrieved in a field with C<< $field->get_message('upload_file_') >> for example, the 'get_message' method will look first in user-set field specific messages, then in user-supplied form messages, finally in messages provided by the field classes. package MyApp::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; sub build_messages { return { required => '....', my_message => '....' }; } ... my $form = MyApp::Form->new( messages => { required => '...', ...} ); ... has_field 'my_field' => ( messages => { required => 'Please provide a my_field' }, required => 1 ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Fields.pod0000644000077000007700000003706712221042077023013 0ustar gshankgshankpackage HTML::FormHandler::Manual::Fields; # ABSTRACT: brief documentation of available fields __END__ =pod =head1 NAME HTML::FormHandler::Manual::Fields - brief documentation of available fields =head1 VERSION version 0.40050 =head1 SYNOPSIS L See also L for a description of the base field attributes. The inheritance hierarchy of HTML::FormHandler Fields Text Money Password Hidden Integer PosInteger Float Date DateMDY Email TextCSV TextArea Select Multiple SelectCSV BoolSelect IntRange Hour Minute MonthDay Month Second Year MonthName Weekday Checkbox Boolean Compound Repeatable Duration DateTime NoValue Submit Reset Button Display AddElement RmElement NonEditable PrimaryKey Upload File =head1 DESCRIPTION A form's fields are created from the 'has_field' and 'field_list' definitions. FormHandler processes the field lists and creates an array of L objects. The "type" of a field determines which field class to use. The field class determines which attributes are valid for a particular field. A number of field classes are provided by FormHandler. You can customize the validation in your form on a per field basis, but validation that will be used for more than one field might be more easily handled in a custom field class. Fields are accessed with C<< form->field('name') >>. Field errors are in C<< $field->errors >>. If the 'field_name_space' is not set, fields will be loaded from the HTML::FormHandler::Field name space. If you provide a 'field_name_space' it will be searched before FormHandler. If you want to explicitly list the field's package, prefix it with a plus sign. The field_name_space plus the default name spaces 'HTML::FormHandler::Field' and 'HTML::FormHandlerX::Field' will be searched for fields. has '+field_name_space' => ( default => 'MyApp::Form::Field' ); has_field 'name' => ( type => 'Text' ); # HTML::FormHandler::Field::Text has_field 'name' => ( type => '+My::FieldType' ); # My::Fieldtype has_field 'foo' => ( type => '+Foo' ); # MyApp::Form::Field::Foo or has_field 'foo' => ( type => 'Foo' ); # MyApp::Form::Field::Foo The most basic type is "Text", which is usually a 'text' HTML element and a string data type. (If the type of a field is not specified, it will be set to 'Text'.) A "Select" field type is a HTML select element, and validates against the list of values provided in the 'options'. A "Multiple" type is like "Select" but it allows selecting more than one value at a time. Many field classes contain only a list of constraints and transformations to apply. Some use the 'validate' method, which is called after the actions are applied. Some build a custom select list using 'build_options'. There are two rough categories of Field classes: those that do extra processing and those that are simple validators. The 'Compound', 'Repeatable', and 'Select' fields are fields that are functional. =head1 Field names The standard way to use FormHandler is with field names that match your database accessors. If you want to prepend the HTML field names with a name plus dot, you can set the form 'name' and use the 'html_prefix' flag. "$name." will be stripped from the beginning of the HTML fields before processing by HFH, and will be added back in 'fif'. The field's 'html_name' convenience attribute will return this name for use in templates. If you want the FormHandler field name to be different than the database accessor, set 'accessor' on your fields. (It defaults to the field name.) You could then use any name that you want for your field. There are a number of name-related field attributes. The 'name' is the name used to identify this particular field in this fields array. The 'full_name' includes the names of all parents of this field, like 'address.street.streetname'. The 'html_name' is the same as the 'full_name' unless you have set the 'html_prefix' flag, in which case it includes the form name: 'myform.address.street.streetname'. To retrieve a field by name, you can use either the full_name or a chain: C<< $form->field('address')->field('street')->field('streetname') >> or: C<< $form->field('address.street.streetname') >>. =head1 Creating custom fields Subclass a custom field from L, or one of the existing subclasses. Almost everything that is done in a custom field class can also be done in a form. The advantage of a field class is that it can simplify declaration of often-repeated sets of attributes. The simplest subclasses contain only a 'validate' routine or an 'apply' attribute, which is called by the base Field class from 'process'. Look at L, for example. If the field's value will be an object instead of a simple scalar, such as a DateTime, and you want to use the transformed value to fill in the form, then you will also need a deflation or field class 'deflate' method to reformat the object into a form suitable for an HTML form field. See L for more info. Some custom fields might only require setting certain attributes to defaults, such as the L field, which set 'range_start' to 0 and 'range_end' to 23. A 'select' field might override the 'build_options' builder for the 'options' array, like L. A field may add additional attributes, such as 'label_format' in L, or set the 'required' message. An alternative to new field classes for many field validations might be roles with collections of validations. =head1 Other field packages Some custom fields are supplied as CPAN packages, in the HTML::FormHandlerX name space. L L L =head1 Fields supplied by FormHandler =head2 Basic Fields Although there are a lot of fields provided (probably too many) a lot of them are "convenience" fields or "name" fields, where the main benefit is that the field type is a name that gives the main purpose of the field. Most of these fields could be replaced by a basic field with a bit of validation or some select options. A few of the fields are special purpose fields that won't be used very often. The fields in this section are the basic fields, the commonly used fields that will be most often used in a form. =head3 Text A string data type that will be formatted as an HTML text field. Has 'minlength' and 'maxlength' attributes. L =head3 Select A field formatted as a select element. L =head3 Checkbox A field formatted as a checkbox. If not in params, will be forced to 'false' value by 'input_without_param' attribute (0 by default). L =head3 Hidden A hidden field. L =head3 Password A password field. The value is not re-displayed. L =head3 TextArea A textarea field. Has 'cols' and 'rows' attributes. L =head3 Upload A file upload field that takes a filehandle or a Catalyst upload object (an object with a 'size' method). L =head3 Submit A submit field. L =head3 Reset A reset field. L =head2 Complex Fields (Compound and Repeatable) These fields are complex fields which contain a fair amount of special code. They do not map to a single HTML element; they contain multiple subfields. =head3 Compound A compound field is a field that has sub-fields. Compound fields can be created in two ways: 1) using a field class, 2) by declaration. To create a compound field class, you must extend L and use L to allow declaring fields: package MyApp::Field::Duration; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'month' => (type => 'Integer'); has_field 'day' => ( type => 'Integer' ); has_field 'minutes' => ( type => 'Integer' ); Then in the form: has_field 'my_duration' => ( type => '+Duration' ); To create a compound field by declaration, declare the containing compound field and subfields, prefixing the subfield names with the name of the containing compound field plus a dot: package MyApp::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'duration' => ( type => 'Compound' ); has_field 'duration.month' => ( type => 'Integer' ); has_field 'duration.day' => ( type => 'Integer' ); has_field 'duration.year' => ( type => 'Integer' ); In an HTML form the name of the field must be the complete name with dots. The 'html_name' field attribute can be used to get this name, C<< $field->html_name >>. A compound field can be used for a database relation that will have only one row (belongs_to or has_one). If the relation has a compound primary key, you may need to provide the primary key columns, either through hidden fields or by setting them in the C<< $form->value >> hash before 'update_model' is called. See also L. =head3 Repeatable Repeatable fields are used for arrays of compound fields. has_field 'addresses' => ( type => 'Repeatable' ); has_field 'addresses.address_id' => ( type => 'PrimaryKey' ); has_field 'addresses.street'; has_field 'addresses.city'; has_field 'addresses.country' => ( type => 'Select' ); The arrays will be built from arrays passed in the params, or from related ('has_many') rows in the database. It is also used for arrays of single fields using the 'contains' keyword: has_field 'tags' => ( type => 'Repeatable' ); has_field 'tags.contains' => ( type => '+Tag' ); See L for more information. =head2 Text Fields Fields subclassed from the Text field. =head3 Text Text field. L =head3 Money Positive or negative real value, formatted to two decimal places. L =head3 Date Date field that can be used by jQuery datepicker plugin. L =head3 DateMDY A subclass of 'Date' with the "%m/%d/%Y" format. L =head3 Email Uses Email::Valid for validation. L =head3 Integer Positive and negative integers. Can use range_start and range_end. L =head3 PosInteger A positive integer field. L =head3 Float Float field that allows you to set size, precision, decimal_symbol, and decimal_symbol_for_db. L =head3 TextCSV A text field that takes multiple values from a database and converts them to comma-separated values. This is intended for javascript fields that require that, such as 'select2'. This is the only 'multiple' text field. This text field would be a select-type field for the user. L =head2 Compound Fields Fields subclassed from 'Compound'. =head3 Compound L =head3 Repeatable L =head3 Duration Compound field with possible subfields: years, months, weeks, days, hours, minutes, seconds, nanoseconds. L =head3 DateTime A compound field that requires you to provide the subfields that you want. (month/day/year/hour/minutes) L =head2 Checkbox Fields Fields that inherit from 'Checkbox'. =head3 Checkbox L =head3 Boolean Checkbox that return 1 or 0. L =head2 Select Fields Fields that inherit from 'Select'. =head3 Select L =head3 Multiple Multiple select. Also sorts the selected options to the top of the select list. L =head2 SelectCSV A multiple select field for comma-separated values in the database. It expects database values like: '1,5,7'. The string will be inflated into an arrayref for validation and form filling, and will be deflated into a comma-separated string in the output value. L =head3 BoolSelect A field with three possible values: empty/0/1. L =head3 Hour Integer select range field from 0-23. L =head3 Second Select field with range from 0-59. L =head3 IntRange An integer select field. Can set label format with 'label_format'. L =head3 Month Select field with range from 1 - 12. L =head3 MonthDay Select field with range from 1 - 31. L =head3 MonthName Select field with month name labels, value 1-12. L =head3 Minute Select field with range from 0-59. L =head3 Weekday A select field where the labels are the names of the week, and the values are 0-6. L =head3 Year Select field providing year list 5 years back and 10 years forward. L =head2 NoValue fields Fields that inherit from 'NoValue'. None of these fields will provide a 'value' in the C<< $form->value >> hashref. =head3 NoValue Base class for fields that don't produce a 'value'. L =head3 Submit L =head3 Reset L =head3 Button Button field that is rendered by the Button widget. L =head3 Display Non-data field used for inserting HTML into the form. Probably now better handled by a Block or a rendering tag. L =head3 AddElement Example field for adding a repeatable element. L =head3 RmElement Example field for removing a repeatable element L =head3 NonEditable For Bootstrap-style non-editable fields. =head2 TextArea fields Fields that inherit from 'TextArea'. =head3 TextArea L =head2 Password fields =head3 Password Password field. Sets 'noupdate' flag if empty and not required. L =head3 PasswordConf Password confirmation field. L =head2 Other fields These fields inherit just from 'Field'. =head3 File A file field that does no processing. Most people probably want to use 'Upload' instead. L =head3 PrimaryKey Hidden field that provides the primary key for Repeatable fields. L =head3 Captcha A Captcha field using GD::SecurityImage. Requires the use of the L role, or similar code. L =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/FromDFV.pod0000644000077000007700000000540412221042077023036 0ustar gshankgshankpackage HTML::FormHandler::Manual::FromDFV; # ABSTRACT: converting from Data::FormValidator __END__ =pod =head1 NAME HTML::FormHandler::Manual::FromDFV - converting from Data::FormValidator =head1 VERSION version 0.40050 =head1 SYNOPSIS L Cheatsheet for converting from L. =head1 DESCRIPTION Information that's useful when switching from Data::FormValidator to HTML::FormHandler. There's not a lot here yet, so if you have something to add, patches are welcome. In a general way, FormHandler doesn't have nearly so many "special" checks as Data::FormValidator. It would be possible to implement many of them, but there hasn't been much demand for them. So far FormHandler users seem to be satisfied with the "do your own checks in a Perl method" solution. Because of the greater complexity of FormHandler's data model - with Repeatable arrays and nested compounds, etc - it's somewhat harder to do some of them automatically. =head1 Differences/conversions =over 4 =item dependencies In FormHandler, 'dependency' is the equivalent of 'dependency_group', without the key names. The other variations of dependencies in DFV are not implemented in FormHandler, and would normally be done in a form's 'validate' sub. =item trim, filters A 'trim' filter is installed by default in FormHandler; it's a special version of an apply action, and can be set to a transform or Moose type. See the documentation in L. Transforms and inflations/deflations do not change what is presented in the form unless you set the 'fif_from_value' flag on the field. =item FV_length_between, FV_max_length, FV_min_length Use text fields with 'minlength' and 'maxlength' attributes. =item FV_eq_with Perform your own checks in the form 'validate' sub. sub validate { my $self = shift; if( $self->field('one')->value eq $self->field('two')->value ) { } } =back =head1 Constraints The simple constraints from L can be used directly in a FormHandler form: use Data::FormValidator::Constraints ('match_state'); has_field 'my_state' => ( apply => [ { check => \&match_state, message => 'Invalid State' } ] ); =over 4 =item email Use the 'Email' field type, or use the FH Moose Type, 'email'. has_field 'email' => ( type => 'Email' ); -- or -- use HTML::FormHandler::Types ('Email'); has_field 'email' => ( apply => [ Email ] ); =back =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/FromFF.pod0000644000077000007700000001376112221042077022717 0ustar gshankgshankpackage HTML::FormHandler::Manual::FromFF; # ABSTRACT: converting from HTML::FormFu __END__ =pod =head1 NAME HTML::FormHandler::Manual::FromFF - converting from HTML::FormFu =head1 VERSION version 0.40050 =head1 SYNOPSIS L Cheatsheet for converting from L. =head1 DESCRIPTION Information that may be useful when converting to FormHandler from FormFu. =head2 Inside/Outside FormFu forms look to me like "inside-out" objects. The attributes and code are all set outside of the object. FormHandler forms are the opposite. Almost all attributes and settings are set *inside* the form class, although settings can be passed in on 'new' and 'process', of course. FormHandler fields are built as part of the object construction process, so you do not create or add new fields after the form instance has been constructed. There are many facilities for setting fields active/inactive or changing the various field attributes, so this is not limiting in what you can do. One of the big advantages of having form fields and validations, etc, inside the object is that it makes it a lot easier to test exactly what you're using in your controllers. =head2 Config files There are so many drawbacks to using config files to specify your forms that I don't see why anybody would voluntarily do it. However it takes all kinds, so if you really want to use config files, you can...mostly. There are a lot of things you can't do in config files, but FormHandler provides so many ways of doing things, that you can probably get it to work. And sometimes it's easier to update forms piecemeal, or there may be policies in place, so if you really want/need config files for building forms, see L and the test in t/form_setup/config.t. =head2 Rendering You should be able to make your FormHandler forms automatically render very close to FormFu's rendering. There's an example of simple FormFu-like rendering in t/render/ff.t. Set up a base class with the necessary code, and your forms could be practically drop-in replacements from a rendering perspective. =head1 Filters, Constraints, Inflators, Validators, Transformers FormHandler doesn't distinguish between these categories in the same way that FormFu does. FormHandler has inflation/deflation, validation methods, and apply actions. The distinguishing factor is mostly where it happens in the process. =head2 Filters A 'trim' filter is installed by default in FormHandler; it's a special version of an apply action, and can be set to a transform or Moose type. See the documentation in L. An HTML filter is applied by default in certain places in the rendering. You can change it with the 'render_filter' attribute. See L. You can change the form of the field's value using a number of inflation/deflation methods, or a transform, or a Moose type. See L and L. Transforms and inflations/deflations do not change what is presented in the form unless you set the 'fif_from_value' flag on the field (the rough equivalent of FormFu's 'render_processed_value'). =head3 FormatNumber Use an inflation: has_field 'foo' => ( type => 'PosInteger', inflate_method => \&format_number ); sub format_number { my ( $self, $value ) = @_; return unformat_number( $value ); } =head2 Constraints A lot of these are simple regexes or functions. If they're things you're going to use often, you probably want to put them in a type library or validator class. =over 4 =item AllOrNone Not implemented. Do this in a form 'validate' sub. =item ASCII A simple regex: has foo => ( apply => [ { check => qr/^\p{IsASCII}*\z/, message => 'Not a valid string' } ] ); =item AutoSet Not necessary. This is done automatically by FormHandler. You'd have to go to some work to avoid it. =item Bool A simple regex: qr/^[01]?\z/ Or you can use the Boolean field. =item Callback, CallbackOnce This is just validation done in code. Use one of the many places you can put validation in methods in FormHandler. See L. =item DateTime Use Date or DateTime field or make your own. =item DependOn Use 'dependency' attribute in the form. Or do more complicated things in the form's 'validate' sub. =item Email Use the 'Email' field type, or use the FH Moose Type, 'email'. has_field 'email' => ( type => 'Email' ); -- or -- use HTML::FormHandler::Types ('Email'); has_field 'email' => ( apply => [ Email ] ); =item Equal No equivalent. Perform this check in the form's 'validate' sub. =item File Use 'Upload' field. =item Integer Use 'Integer' field. =item Length, MaxLength, MinLength Use 'minlength' and 'maxlength' on the Text field and its subclasses. =item Range, MaxRange, MinRange Use 'range_start' and 'range_end'. =item MinMaxFields No equivalent. =item Number Use Moose type 'Num'. =item Printable Use FormHandler Moose type 'Printable'. =item reCAPTCHA Use Captcha field or L. =item Regex Use 'check' action with regex. =item Required Set 'required' flag on the field. =item Set Use 'check' action with arrayref of strings. =item SingleValue Not necessary. =item Word This is a simple regex: qr/^\w*\z/ Substitute FormHandler Moose type 'SingleWord'. =back =head2 Inflators Use one of the inflation/deflation methods. See L. =head2 Validators See L. =head2 Transformers See L and L. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/InflationDeflation.pod0000644000077000007700000001253712221042077025351 0ustar gshankgshankpackage HTML::FormHandler::Manual::InflationDeflation; # ABSTRACT: inflation and deflation of field values __END__ =pod =head1 NAME HTML::FormHandler::Manual::InflationDeflation - inflation and deflation of field values =head1 VERSION version 0.40050 =head1 SYNOPSIS L How to inflate and deflate field values. =head2 DESCRIPTION When working with the various ways that data can be transformed, in and out, the meaning of the terms 'inflate' and 'deflate' starts to feel kind of slippery. The one constant is that values presented in an HTML form must be in a string format, or presented with select elements or checkboxes. There are two general types of inflation/deflation provided by FormHandler. The first, 'standard' type inflates values in order to validate them, and deflates them in order to present them in string format via HTML. The other ('DB') type takes values provided by defaults (usually a DB row, or item, but could also be a field default or an init_object) and munges the values coming in and changes them back going out. =head2 Standard inflation/deflation The standard type of inflation/deflation is implemented by using some of the following options for inflation: inflate_method transform (using 'apply') ..and the following options for deflation: deflate_method deflation (field attribute) When validation starts, the param input will be inflated by the inflate method, allowing validation to be performed on the inflated object. When the 'fif' fill-in-form value is returned for HTML generation, the deflation is used to flatten the object, usually into a string format. =head2 DB inflation/deflation The 'DB' type of inflation/deflation uses 'inflate_default_method' for inflation, and 'deflate_value_method' for deflation. Deflation could also be handled by changing the value in one of the various validation methods. This type of inflation/deflation is, logically, just a different way of providing data munging around the defaults (item/init_object/default) and 'value' output. The same effect could be achieved by performing a transformation outside of FormHandler - if you were handling the database updates yourself. Since the DBIC model enables automatic database updates, this kind of inflation/deflation makes that easier. One circumstance in which this type of inflation/deflation is useful is when there's a single field in the database row object which you want to expand into a compound field in the form. =head2 Attributes used in deflation/inflation =head3 Inflation methods The field 'input' comes from the params that are passed in from the submission of the form, so the input will always be in string format if it comes from an HTTP request. It's also possible to pass in params in other formats, of course. Or the params could be pre-processed before passing in to FormHandler. You should not normally be changing the 'input' attribute of a field. If you want the changed field value to be used when re-presenting the form, such as when you're adopting a standard format for the field, you should set C<< fif_from_value => 1 >>. There are three options for standard inflation, or transforming the field's 'input' to the field's 'value': =over 4 =item inflate_method Provide a method on the field which inflates the field 'input' (from params): has_field 'futility' => ( inflate_method => \&inflate_field ); sub inflate_field { my ( $self, $value ) = @_; .... return $value; } =item transform In a sequence of 'apply' actions, changes the format of the 'value' that is being validated. This might be useful if there are some validations that work on one format of the value, and some that work on another. =item set the value in validation methods In a validate method, you can change the format of the value, with $field->value(...); =back =head3 Deflation methods =over 4 =item deflate_method Most general purpose deflation method. Provide a coderef which is a method on the field: has_field => 'foo' => ( deflate_method => \&deflate_foo ); sub deflate_foo { my ( $self, $value ) = @_; # $self is the 'foo' field return $value; } =item deflation This is a coderef that performs deflation. has_field => 'bar' => ( deflation => sub { shift $value; ... return $value } ); =item set the value in validation methods Just like for inflation, you can change the value in a validation method; however, it won't be used for fill-in-form unless you set the 'fif_from_value' flag to true. =back =head3 fif_from_value Normally the fill-in-form value will be the param value that was submitted. If you want to change the format of the input when re-presenting the form, you can set 'fif_from_value'. =head3 deflate_to Earlier versions of FormHandler provided a 'deflate_to' attribute which allowed the deflation methods to be used for multiple, confusing purposes. This flag has been removed, since it made the process hard to understand and was mixing apples and oranges. The new inflation/deflation methods can handle all of the previous situations. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Intro.pod0000644000077000007700000002531412221042077022670 0ustar gshankgshankpackage HTML::FormHandler::Manual::Intro; # ABSTRACT: introduction to using FormHandler __END__ =pod =head1 NAME HTML::FormHandler::Manual::Intro - introduction to using FormHandler =head1 VERSION version 0.40050 =head1 SYNOPSIS L HTML::FormHandler is a form handling package that validates HTML form data and, for database forms, saves it to the database on validation. It has field classes that match various data types and HTML form elements, and rendering roles that can be used to render forms in many different ways, from hand-built forms to totally automated rendering. It can, of course, be used to validate data even if you are not interested in the rendering capabilities. A FormHandler 'form' is a Perl subclass of L for non-database forms, or a subclass of L for database forms, and in it you define your fields and validation routines. Because it's a Perl class written in Moose, you have a lot of flexibility and control. You can validate with Perl methods or Moose type constraints; you can use your own validation libraries. You can define your own field classes that perform specialized validation. When the form is validated, you can get the validated values back with C<< $form->value >>. A working example of a Catalyst app using FormHandler forms is available on github at L. =head1 Basics =head2 Create a form class The most common way of using FormHandler is to create a form package. You must 'use' "HTML::FormHandler::Moose" and 'extend' FormHandler: package MyApp::Form::Sample; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; Then you add some fields with 'has_field', and a field 'type' (the short name of the field package). (Fields with no type have type 'Text'.) has_field 'foo'; has_field 'bar' => ( type => 'Select' ); Basic field types are Text, Select, Checkbox, Submit, Hidden, Reset, TextArea, Password, Upload. See L for more information. =head2 Or create a form class dynamically You can also create a form class 'dynamically', by creating a 'new' HTML::FormHandler object. Use a 'field_list' parameter to create the fields instead of 'has_field'. my $form = HTML::FormHandler->new( field_list => [ 'username' => { type => 'Text' }, 'selections' => { type => 'Select' }, ] ); Some features will not be available using this method (like the automatic use of 'validate_' methods) and it's not as easy to test, of course. =head2 Process the form The form's 'process' method should be run on each request, passing in the request parameters: $form->process( params => $c->request->body_parameters, action => $action, ); If the parameters are not empty, then validation will be performed. The corollary is that you should not pass in extra parameters when the form has not been posted. A special 'posted' flag can be used if the form consists entirely of fields like checkboxes that do not include names in params if unchecked, and also works to prevent validation from being performed if there are extra params: $form->process( posted => ( $c->req->method eq 'POST' ), params => $c->request->parameters, action => $action ); There is an alternative method for processing the form, which is sometimes preferred for persistent forms. It returns a 'result' object, and clears the form: my $result = $form->run( params => $c->request->body_parameters ); You can also set most other FormHandler attributes on the 'process' call., One useful feature is that you can activate or inactivate fields: $form->process( params => $params, active => ['field1', 'field2'] ); See also L. =head2 Or process a database form A database form inherits from L instead of L. You must either pass in the DBIC row object or give FormHandler information to retrieve the row object. $form->process( item => $row, params => $params ); -- or -- $form->process( item_id => $id, schema => $schema, item_class => 'MyRow', params => $params ); 'item_class' is often set in the form class. See also L and L. =head2 After processing the form A database form will have saved the data or created a new row, so often no more processing is necessary. You can get the structured field values from C<< $form->value >>, and do whatever you want with them. If the validation succeeded, you may want to redirect: $form->process( params => $params ); return unless $form->validated $c->res->redirect( .... ); -- or -- return unless $form->process( params => params ); $c->res->redirect; =head2 Rendering the form At its simplest, all you need to do is C<< $form->render >> in a template. [% form.render %] The automatic rendering is powerful and flexible -- you can do almost anything with the right settings. Or you can render the form with a template. The form object will give you a hashref of values suitable for filling in the form with C<< $form->fif >>. By default FormHandler structures fields (and renders them) in a way that matches the database. If you want to organize the rendering output in different ways, you can use blocks to organize your fields. has_block 'fieldset1' => ( render_list => ['foo', 'bar'] ); For more rendering info, see L. =head2 Defaults for form fields The simplest way to provide defaults is by setting the default attribute in a field definition: has_field 'my_foo' => ( default => 'my_foo' ); The database row ('item') that is passed in will provide initial values for the form, of course. You can also provide default values with an 'init_object', which acts kind of like a database object: $form->process( init_object => { foo => '...', bar => '...' } ); There are a number of other flags and methods for providing defaults. See L. =head2 Validation You can validate a field with a method in the form 'validate_': has_field 'foo'; sub validate_foo { my ( $self, $field ) = @_; # self is the form unless( $field->value == .... ) { $field->add_error( .... ); } } You can provide a validation coderef that will be a field method: has_field 'foo' => ( validate_method => \&check_foo ); sub check_foo { my $self = shift; # self is field unless( $self->value == ... ) { $self->add_error( ... ); } } You can use 'apply' to use Moose types for validation, from L or another Moose type collection: use HTML::FormHandler::Types ('NotAllDigits'); ... has_field 'my_field' => ( apply => [NotAllDigits] ); Or create validators with check: has_field 'quux' => ( apply => [ { check => qr/abc/, message => 'Not a valid quux' } ] ); Or use a validate coderef: has_field 'foo' => ( validate_method => \&check_foo ); sub check_foo { my $self = shift; if ( $self->value =~ s/..../ ) { $self->add_error('....'); } } You can also create custom fields with custom validation, or use an existing field that does the validation you need. See L for more information on validation or L for more information on fields. =head2 Organizing your form code You can use 'has_field' and 'has_block' in Moose roles: package MyApp::Form::Role::Address; use HTML::FormHandler::Moose::Role; has_field 'foo'; has_block 'bar'; Your forms can inherit from base classes that set common application defaults. You can override field definitions with '+'. You can create 'compound' fields and include them in a form: package MyApp::Form::Field::Complex; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'field1' => ( validate_method => \&validate_field1 ); has_field 'field2' => ( type => 'Select', options_method => \&options_field2 ); sub validate_field1 { ... } sub options_field2 { ... } ... package MyApp::Form::Complex; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+field_name_space' => ( sub => {['MyApp::Form::Field']} ); has_field 'compound1' => ( type => 'Complex' ); has_field 'compound2' => ( type => 'Complex' ); =head2 Testing It's much easier to write unit tests for FormHandler forms than for Catalyst controllers. The 't' directory of the downloaded distribution has lots of examples. See L for more information. =head1 Localization FormHandler's built-in errors are added to the form fields with C<< $field->add_error >>, and to the form with C<< $form->add_form_error >>. These methods call a C<< $self->_localize >> method which is a coderef set from the field's default_localize sub, the field's 'localize_meth' attribute with C<< localize_meth => sub {} >>, or a form's sub localize_meth. The default localize uses Locale::Maketext. You can also use duck_type classes for localization. See the documentation in L and the tests in xt/locale.t. If you wish to skip localization for a particular message (such as for system errors) you can use C<< $field->push_errors >> or C<< $form->push_form_errors >>. See also L. =head1 Performance FormHandler makes heavy use of Moose, so almost all of FormHandler's profiled time will actually be in Moose methods, mostly constructing form and field attributes. Some people prefer to use a persistent form class (in a Moose attribute) in order to skip the form building step on each call. Other people don't like that solution because state will remain in the form until the next process call. The 'clear' method is called at the beginning of each 'process', but additional Moose attributes in the form, etc, will have to cleared by the programmer. If you are loading options from the database and you don't need to have them refreshed each time, you can set the 'do_not_reload' flag in the Select/Multiple field. If you're not using the field widget roles, you can set the 'no_widgets' flag. If you always use 'process' on each call (recommended) then you can set the 'no_preload' flag in the form to skip building results in BUILD (new). =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Reference.pod0000644000077000007700000002223412221042077023471 0ustar gshankgshankpackage HTML::FormHandler::Manual::Reference; # ABSTRACT: concise reference __END__ =pod =head1 NAME HTML::FormHandler::Manual::Reference - concise reference =head1 VERSION version 0.40050 =head1 DESCRIPTION L This is a concise reference of HTML::FormHandler. HTML::FormHandler has a lot of options and many ways to customize your forms. More complete documentation can be found in the rest of the manual as L and in L, L, L, L, and in the individual field classes. =head1 Form =head2 Form Attributes params HTTP parameters; if present validation will be attempted name Form name. Used in 'id' of form element field_name_space Where to search for fields widget_name_space Where to search for widgets language handle For MakeText ctx Application context for your use init_object For default values instead of $item dependency Array of arrays of field names. If one name has a value, all fields in the list are set to 'required' fields Field array sorted_fields The sorted array of fields field( $name ) Returns a field object has_field Moose-y sugar for declaring fields field_list Non-moose-y way to define fields clear Resets state. Used in persistent forms. value Returns a hashref of values (with accessor keys) fif Returns a hashref for filling in form =head2 Form database attributes item DB row object item_class Class of db item item_id Primary key of db item schema Schema of item Also 'update_model' sub =head2 Form processing process Sets up form, validates, updates model run Returns a result object and clears form update_field_list Updates to fields on process posted Flag to say whether or not to validate, instead of depending on presence of params =head3 Validation validate Sub for validation after individual fields are validated validate_model Sub for additional database type validation validated Flag that form has validated is_valid Synonym of 'validated' ran_validation Flag that validation has already been run dependency Set groups of fields that are set to required if one is present validate_$fieldname Validation routine for field (also 'validate_method') =head3 Errors has_errors True if any field has errors num_errors The number of errors (field errors + form errors) error_fields An array of fields with errors errors Returns an array of all errors error_field_names Returns an array of field names with errors =head3 Form Methods and process hooks update_model To perform additional database actions on update update_fields Sub providing convenient place to update fields on 'process' update_subfields Sub providing place to update fields on Build (useful for roles and compound field classes) init_object can be a method instead of an attribute =head3 Form Rendering html_prefix Flag to prefix html field names with the form name. Useful for multiple instances of the same form do_form_wrapper flag to wrap form. (build_do_form_wrapper) form_tags Various strings and flags used by rendering form_element_attr For arbitrary html attributes in the 'form' tag form_element_class Arrayref of classes for 'class="..."' in form element form_wrapper_attr For arbitrary html attributes on the form wrapper form_wrapper_class Arrayref of classes for 'class="..."' in form wrapper http_method For storing 'post' or 'get' action Store the form 'action' on submission. No default value. enctype Request enctype uuid generates a string containing an HTML field with UUID style adds a 'style' attribute to the form tag id the form tag 'id' attribute is set to the form name html_attributes hook that allows customizing html attributes (form & field) =head2 Field specific form methods options_$fieldname Sub returning options array (also 'options_method') validate_$fieldname Validation routine for field (also 'validate_method') default_$fieldname Set default for field (also 'default_method') =head1 Fields =head2 Field attributes name Field name. Must be the same as database column name or rel type Field type. From a Field class: 'Text', 'Select', etc order Set the order for fields. Default order is set by FH. dump For debugging active Arrayref of fields to set active is_active inactive Arrayref of fields to set inactive is_inactive input_without_param The value of the field if there is no input from the submitted form default Default value for the field default_method Coderef to set default or 'default_$fieldname' (by default) not_nullable Don't convert an empty field ('', etc) to undef trim Transform to trim the field deflation Function to deflate the field (in 'apply') (opposite of transform) deflate_method Coderef to deflate the field (for filling in the form) inflate_method Coderef to inflate the field (before validation) inflate_default_method Coderef to inflate value from item/init_object deflate_value_method Coderef to deflate value after validation fif_from_value Flag to use 'value' of field for 'fif'. password Remove from params and do not display in forms. =head2 Select fields options Sorted array of hashes; keys: "value", "label" options_method label_column Column to use for labels (default: name) active_column Which values to list in options sort_column Column to use for sorting (default: label_column) =head2 Field errors errors Errors associated with this field (also num_errors, clear_errors, has_errors, add_error) messages Hashref of message identifiers and text =head2 Field validation apply Array of constraint/coercion/transformation actions ( type, check, transform, message, when ) validate_method Validation coderef, or 'validate_$fieldname' in form (default) required Field is required required_when Takes a hashref of field name keys and values missing Flag set when a 'required' or 'required_when' fails maxlength Text fields. Validated. minlength Text fields. Used in validation range_start Range start for number fields range_end Range end for number fields =head2 Field attributes for DB accessor Database accessor name if different than field name unique Field should be unique in the database noupdate Don't update this field in the database writeonly Do not retrieve initial values Also see the select field _columns attributes =head2 Field rendering widget Determines which rendering widget to use for the field widget_wrapper Which wrapper widget to apply to the field element_attr Hashref to store arbitrary html attributes. label_attr Hashref for html attributes for the label wrapper_attr Hashref for html attributes for the wrapping element (div, etc) element_class Arrayref for classes for the form element wrapper_class Arrayref for classes for the form element wrapper label_class Arrayref for classes for the form element label label Text label for this field. Defaults to ucfirst field name. build_label_method provide a builder for 'label' attribute wrap_label_method provide a coderef to wrap the label id Useful for javascript (default is html_name. to prefix with form name, use 'html_prefix' in your form) build_id_method Provide a builder for 'id' attribute do_wrapper Flag to render wrapper do_label Flag to render label size Text & select fields. render Widget method to render the field ($field->render) render_element Widget method to render unwrapped field ($field->render_element) render_field Method used by Render::Simple to render field =head2 Field attributes managed by FormHandler These attributes are usually accessed in a subroutine or in a template, but are usually set only by FormHandler. ('value' may be changed as a transform _in a validation routine_.) init_value Initial value from the database value The value of your field. input Input value from parameter or initial value from database fif Retrieve (do not set) values for filling in a form =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Rendering.pod0000644000077000007700000006605512221042077023521 0ustar gshankgshankpackage HTML::FormHandler::Manual::Rendering; # ABSTRACT: how to render with FormHandler __END__ =pod =head1 NAME HTML::FormHandler::Manual::Rendering - how to render with FormHandler =head1 VERSION version 0.40050 =head1 SYNOPSIS L Rendering can be done in many different ways, from forms rendered entirely in templates with no information from FormHandler (except possibly the fill-in-the-form values) to forms that are completely rendered by FormHandler. =head1 DESCRIPTION For most situations, something in between hand-built and completely generated will probably be the best solution. For admin forms that don't need a lot of styling or special HTML, FormHandler's automatic rendering may be appropriate. FormHandler rendering may also be a good solution if you have enough forms that putting time into creating rendering widgets and themes is worthwhile. The automatic rendering is also useful when developing a new form. You can get an idea of what it looks like, and then customize it. Another situation in which FormHandler rendering may be useful is when the form is complex enough that working in Perl is a better idea than putting lots of logic into templates. All of the rendering is designed to be easily replaced with elements of your own, or to be replaced entirely. You can create your own rendering 'widgets' and load them into the fields by designating the directory in the 'widget_name_space'. You could also create a completely separate renderer that's a separate object or class that takes a form object, or a role that is applied to your form. Note that unless you set 'no_widgets' in the form, the rendering roles are automatically applied. You don't need to include anything else, unless you want to use a different renderer. =head2 Mostly templates The names of your fields must match the names of your FormHandler fields. If you use compound fields, you must use the FormHandler naming convention. Form used in examples: package MyApp::Form::Example; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar'; has_field 'save' => ( type => 'Submit' ); If you have existing forms in templates or just prefer them, you can use the 'fill-in-form' values provided with the form's 'fif' function. my $form = MyApp::Form::Example->new; $form->process( params => $params ); $c->stash( fif => $form->fif ); ...
Going a little bit farther in using FormHandler rendering, you can render each of the fields individually:
My Foo [% form.field('foo').render %]
[% form.field('bar').render %] [% form.field('save').render %]
If you don't want the wrappers, use a widget_wrapper of 'None'. has '+widget_wrapper' => ( default => 'None' ); Then you can provide the HTML in which the form elements are embedded:
[% form.field('foo').render %]
[% form.field('bar').render %]
You can also use the 'render_element' method, if you want to leave the wrapper in place, but sometimes render 'bare' html elements:
[% form.field('foo').render_element %]
If you wish to loop through the fields yourself, use the 'sorted_fields' method, since it skips inactive fields and handles the 'order' attribute. A set of Template Toolkit templates is also provided in the 'share' directory. There are individual templates for each 'widget', such as a checkbox, and there is also an all-in-one template that includes blocks for the various 'widgets'. If you want to use these templates you can just copy them to your template directory and specify the form template in your controller. See also L. =head2 Automatic rendering If you take all the defaults, you can simply render a form with C<< $form->render >>. [% form.render %] This uses the L role, which is applied to the form by default. You can use a different form rendering role by including it using 'with': with 'HTML::FormHandler::Widget::Form::Table'; has '+widget_wrapper' => ( default => 'Table' ); For the 'Table' form widget, you will also need to set the matching Table widget_wrapper. A widget role, providing the 'render' method, and a widget wrapper role, providing the 'wrap_field' method, are applied to each field when the form is built. Each field has a default widget, but you can change that by setting 'widget' to a different widget role: has_field 'foxy' => ( widget => 'MyWidget', widget_wrapper => 'MyWrapper' ); Often if you need custom rendering what you need to provide is a custom widget_wrapper. The 'widgets' render only the input elements, and that often doesn't need to be changed. If you have standard HTML that is used when rendering forms, making custom widget_wrappers is often the way to go. Default widget roles are found in the HTML::FormHandler::Widget directory, in the 'Field', 'Form', and 'Wrapper subdirectories. The name space used to look for the widget roles can be specified on a form or field basis by setting 'widget_name_space' to an arrayref of name spaces: has '+widget_name_space' => ( default => sub { ['MyApp::Form::Widget' ] } ); For the above widget ('MyWidget') and widget_name_space, you need to have a package named 'MyApp::Form::Widget::Field::MyWidget'. The HTML::FormHandler::Widget name space is always searched as the last name space. This means that you can set up an application or form specific set of widgets. Widgets in a widget directory (specified in widget_name_space) are located in either a 'Field', 'Wrapper', or 'Form' subdirectory. (Blocks are in a 'Blocks' subdirectory.) You can also create an 'all-in-one' type rendering role, using L as a basis. It used the method name 'render_field' on the form ( C<< $form->render_field('field_name') >> ) instead of the 'render' method on the field. In addition to the 'Simple' wrapper, there is a 'Bootstrap' wrapper which creates HTML formatted to use the Twitter Bootstrap 2.0 CSS. There's also a sample "theme", L, which is a role that sets the widget_wrapper to 'Bootstrap', and provides Bootstrap-type formatting of the form error message. There are a lot of different settings that control the rendering. Some of them are attributes in the form or field, and some of them are set using the 'tags' hashref in the field or the 'form_tags' hashref in the form. You can make your own copy of an existing wrapper and add features to it. However, there are so many different ways to render the HTML around a field, that it's very difficult to handle more than a short list of standard presentations in one 'wrapper'. It may be better to make a number of more atomic widget wrappers and use those rather than complicate the already fairly complicated "Simple" and "Bootstrap" wrappers more. =head2 HTML attributes Arbitrary HTML attributes on form elements (such as 'input' elements) can be specified with 'element_attr' on the field. You can also set attributes for the label with 'label_attr' and attributes for the wrapper with 'wrapper_attr'. The 'class' attributes are handled separately, and are arrayrefs (element_class, wrapper_class, label_class): has_field 'foo' => ( wrapper_class => ['form', 'special' ] ); See the documentation on L. =head2 Form settings =over 4 =item widget_wrapper The short name of the rendering wrapper widget to be applied to the fields. When the fields are constructed this is merged into fields that do not already set a widget wrapper. =item do_form_wrapper Flag set with 'sub build_do_form_wrapper{ 1 }'. Default is no form wrapper. =item form_tags Hashref of various tags used in rendering code. See the documentation for L. =item form_element_attr Hashref of arbitrary HTML attributes to be included in the form element. sub build_form_element_attr { [ ... ] } =item form_element_class Arrayref of classes to be included in the form element. form_element_class => ['hfh', 'admin'] -- or in your class -- sub build_form_element_class { ['hfh', 'admin'] } The above class would produce a form element:
=item form_wrapper_attr Hashref of arbitrary HTML attributes to be included in the form wrapper sub build_form_wrapper_attr { { name => 'formname' } } =back =head2 Form messages Some messages are rendered at the top of the form (inside the form tag) by the 'render_form_messages' method, which is implemented in L and L (which is included by the Bootstrap theme). There are three types of form messages: 'error_message', 'success_message', and 'info_message'. The 'error_message' and 'success_message' are set inside the form: has '+success_message' => ( default => 'Form successfully submitted' ); has '+error_message' => ( default => 'There were errors in your form.' ); And then are displayed after the form is validated. The 'info_message' is cleared out when a form is re-processed, and so would normally be set on the process call, or between new & process. $form->process( params => {}, info_message => 'Fill in the form' ); =head2 Field settings has_field 'foo' => ( widget => 'MyWidget', widget_wrapper => 'SpecialWrapper', element_attr => { placeholder => 'enter a foo' }, element_class => 'important', wrapper_class => ['label'], label_class => ['major'], tags => { wrapper_tag => 'fieldset' } ); =over 4 =item widget Short name of the rendering widget for this field. =item widget_wrapper Short name of the wrapping widget for this field. =item do_wrapper Flag that indicates whether or not the 'wrapper' should be rendered. =item do_label Flag that indicates whether or not a label should be rendered. =item element_attr Hashref of arbitrary HTML attributes to include in the element. Note that this does not include the 'id' and 'type' attributes, which are handled separately. The 'id' can be changed with the field's 'id' attribute. =item element_class Arrayref of classes to include in the element. =item wrapper_attr Hashref of arbitrary HTML attributes to include in the wrapper. =item wrapper_class Arrayref of classes to include in the wrapper. =item label_attr Hashref of arbitrary HTML attributes to include in the label. =item label_class Arrayref of classes to include in the label. =item build_id_method Coderef to construct the 'id'. Useful if your javascript needs a different format for the 'id'. =item build_label_method Coderef to construct the label. =item wrap_label_method Coderef to wrap the label. Used by the Simple and Bootstrap wrappers. Useful if your label contains HTML or a link. You must do your own localization and filtering if you use a 'wrap_label' method. =back =head2 html_attributes callback The form has an 'html_attributes' callback which can be used to customize, localize, or modify the various attributes when used. Types: element, wrapper, label, form_element, form_wrapper, checkbox_label sub html_attributes { my ( $self, $obj, $type, $attrs, $result ) = @_; # obj is either form or field $attr->{class} = 'label' if $type eq 'label'; $attr->{placeholder} = $self->_localize($attr->{placeholder}) if exists $attr->{placeholder}; return $attr; } This callback is called in the methods that wrap the various '_attr' attributes, i.e. element_attributes, label_attributes, wrapper_attributes, form_element_attributes, form_wrapper_attributes. =head2 Field tags The 'tags' are settings and strings which may vary by the particular widget that implements them. The best place to look for documentation on them is in the field widget, field wrapper, and form widgets that you are using. The 'tags' allow customizing rendering behavior on a per-field basis. FormHandler has a number of flags/settings that it uses; you can add your own for your custom rendering code. wrapper_tag -- the tag to use in the wrapper, default 'div' label_tag -- tag to use for label (default 'label') label_after -- string to append to label, for example ': ' to append a colon Tags can be used to switch the Simple wrapper from divs to using paragraphs instead, or to add a colon in label formatting: has_field 'my_field' => ( tags => {wrapper_tag => 'p', label_after => ': ' } ); Most of the tags are implemented by the 'wrapper' widget, so see that documentation for more details: L, L. =head3 Tag types The 'get_tag' method will check for these three types of tags and perform the appropriate action. =over 4 =item String Standard, most common type of value for a tag. has_field 'bar' => ( tags => { before_element => '...' } ); Some tags are true/false also: has_field 'foo' => ( type => 'CheckBox', tags => { no_wrapped_label => 1 } ); =item CodeRef You can supply a coderef to a tag, and it will be executed as a method on the field. This is useful for localization or other sorts of runtime changes. has_field 'bar' => ( tags => { before_element => \&bar_element } ); sub bar_element { my $self = shift; # $self is the 'bar' field return '
In a Sub
'; } =item Block You can supply a block by giving a string that consists of a '%' followed by the block name: has_block 'comment' => ( tag => 'a', content => 'This is a comment from a block', class => ['comment' ] ); has_field 'foo' => ( tags => { before_element => '%comment' } ); =back =head3 Tags and other settings for all fields Tags can be set for all fields in the form by using a 'build_update_subfields' sub, or 'widget_tags'. The 'update_subfields' hashref takes general-purpose keys 'all', 'by_flag' (compound, repeatable, contains), and 'by_type'. You can also set specific field attributes by using the field name as a key. For example, if you don't want errors to be displayed next to the fields, you need to set the 'no_errors' tag: sub build_update_subfields {{ all => { tags => { no_errors => 1 }, wrapper_class => ['myapp'] }, by_type => { Text => { element_class => ['text'] } }, by_flag => { compound => { do_wrapper => 1 } }, foo => { label => 'My Foo' }, }} -- or -- '+widget_tags' => ( default => sub { { no_errors => 1 } } ); The 'widget_tags' attribute only handles the 'tags' hashref, so if you also want to set classes or attributes, then build_update_subfields is more useful. You can also use 'build_update_subfields' in a custom compound field class. If you have defaults that are set in 'build_update_subfields' in a base class, in order to use hashrefs from both base and current classes, you will need to merge the hashes: use HTML::FormHandler::Merge ('merge'); sub build_update_subfields { my $self = shift; my $new = { all => { tags => { wrapper_tag => 'p' } } }; return merge( $new, $self->next::method(@_) ); } In a role you would have to do the equivalent with an 'around' method modifier. =head3 Repeatable field instances The repeatable field instances are constructed internally, so it's trickier to set things like wrapper tags. There are two ways to do it, using the 'init_contains' attribute on the repeatable field, and using the 'update_subfields' builder: has_field 'records' => ( type => 'Repeatable', num_when_empty => 2, init_contains => { tags => { wrapper_tag => 'fieldset' } } ); -- or -- sub build_update_subfields { { by_flag => { contains => { tags => { wrapper_tag => 'fieldset' }}}}} The 'build_update_subfields' option is mainly useful if you have multiple repeatable fields that you want to set, or if you want defaults in a base class. =head3 widget and widget_wrapper set to 'None' If you want to implement the 'render' method in a custom field, you can set 'widget' to 'None' and no widget will be applied. Setting the 'widget_wrapper' to 'None' will apply the 'None' wrapper, which simply returns the widget rendering. =head3 Error messages The default is currently to display error messages next to the rendered fields, if you're doing C<< $form->render >>. If you don't want messages next to fields, you can set the 'no_errors' tag, as discussed in the section on 'Tags and other settings...'. Note that the 'None' widget wrapper, since it doesn't render anything except the form element (input, select, etc), will not render errors next to the field. Setting the 'do_wrapper' and 'do_label' flags to 0 will still render errors. =head2 Blocks When rendering, FormHandler loops through the sorted fields in the form and executes the 'render' method on each field. Fields in FormHandler forms, particularly those that interface with a database, are usually structured in a way that matches the data structure. This doesn't always fit with the way that you want to display the form. 'Blocks' provide an alternative way of structuring the display. A 'block' is a fairly basic object that contains a 'render' method. The standard block class, L, has Moose attributes to set the HTML tag, the label, the classes, etc, plus a 'render_list' which contains the names of a list of fields or other blocks to render. Here is the definition of a fieldset block that contains two fields: has_field 'foo'; has_field 'bar'; has_block 'first_fset' => ( tag => 'fieldset, label => 'Two Fields', render_list => ['foo', 'bar'] ); The 'first_fset' block will render like this:
Two Fields
In order to actually get this block to be used when you render with C<< $form->render >>, you need to supply a 'render_list' on the form level: sub build_render_list { ['first_fset', 'submit_btn'] } You could also render it with C<< $form->block('first_fset')->render >>. Blocks should be located in a widget name space, in a 'Block' directory, or else the name should be prefixed with a '+'. has '+widget_name_space' => ( default => sub { ['MyApp::Form::Widget'] }; has_block 'first' => ( type => 'MyBlock', ... ); The 'MyBlock' above will be found in 'MyApp::Form::Widget::Block::MyBlock'. has_block 'intro' => ( type => '+MyApp::Form::Component::Intro' ); A block can inherit from L, but it doesn't have to. At a minimum it must provide 'new' and 'render' methods. If no 'type' is specified, the block is created from the L package. The following package provides a functional block: package MyApp::Component::Section; sub new { my ( $class, %args ) = @_; return bless \%args, $class; } sub form { my $self = shift; return $self->{form}; } sub render { return '

Please enter the relevant details

'; } 1; When a form is rendered, it will either loop through all of the sorted_fields OR loop through the fields and blocks listed in the 'render_list'. A render_list can contain a mix of fields and blocks. Note that you must be rendering with widgets to use block rendering. =head2 Twitter Bootstrap 2.0 rendering The main component of Bootstrap rendering is L. It produces the standard Bootstrap-style HTML such as:
These are the standard 'control' blocks for Bootstrap vertical and horizontal forms. You can apply this wrapper to all of your fields by setting the widget_wrapper in the form: has '+widget_wrapper' => ( default => 'Bootstrap' ); There is also a sample "theme": L. It sets the widget_wrapper for you and provides a 'render_form_messages' method to render a success/error messages section. There are a couple of examples in the t/bootstrap directory of Bootstrap inline and search forms, which don't use exactly the same kind of control HTML. You can always copy the existing wrapper and add your own features, with settings provided by the 'tags' hashref. =head2 Rendering themes Many of the flags and settings necessary for rendering can now be moved out into a role. Whether you want to do that or not is a matter of style and preference. The advantage is that it leaves the form class itself cleaner and easier to read. The disadvantage is that your settings come from more different places. Here's an example of a form rendering 'theme', taken from the t/bootstrap/basic.t test: package MyApp::Form::Basic::Theme; use Moose::Role; # make a wrapper around the form sub build_do_form_wrapper {1} # set the class for the form wrapper sub build_form_wrapper_class { ['span9'] } # set the class for the form element sub build_form_element_class { ['well'] } # set various rendering tags sub build_form_tags { { wrapper_tag => 'div', before => qq{

With v2.0, we have lighter and smarter defaults for form styles. No extra markup, just form controls.

\n}, after => '
', } } # the settings in 'build_update_subfields' are merged with the field # definitions before they are constructed sub build_update_subfields {{ # all fields have a label but no wrapper all => { do_wrapper => 0, do_label => 1 }, # set the element class, a placeholder in element_attr foo => { element_class => ['span3'], element_attr => { placeholder => 'Type something…' }, tags => { after_element => qq{\nAssociated help text!} } }, bar => { option_label => 'Check me out', label_class => ['checkbox'], do_label => 0 }, submit_btn => { element_class => ['btn'] }, }} Note that the value 'all' key in the update_subfields hashref will be merged into the attributes used when building all of the fields. =head2 Rendering fields The default for most fields is a 'div' wrapper and a label. If you don't want the wrapper, set C<< do_wrapper => 0 >>. If you don't want the label, set C<< do_label => 0 >>. Checkboxes are most complicated, in that the default is to have two labels. The outer label, the one that's in the same place as the label for other input elements, is set with C<< label => '...' >>. The inner label, which is the equivalent of the C<< label => '...' >> in the options array used for selects and checkbox groups, is set with C<< option_label => '...' >>. There are a number of other 'tags' to control the presentation. See L for more information, and t/render/checkbox.t for examples. Some fields by default do not render a label: Button, Submit, Reset, ButtonTag. If you do want a label with these fields, you must set the 'do_label' flag to 1: has_field 'foo' ( type => 'Button', do_label => 1 ); Select fields are also fairly complicated. They can be rendered with the 'Select', 'RadioGroup', and 'CheckboxGroup' widgets. Option groups are also supported. See L; =head2 Rendering labels A 'standard' label is built in the field if you don't supply one. The label can be provided in the field definition: has_field 'foo' => ( label => 'My Foo' ); You can also provide a method to 'build' the label: has_field 'foo' => ( build_label_method => \&build_label ); sub build_label { my $self = shift; # field method return '...'; } And a method to 'wrap' the label (used by the Simple and Bootstrap wrappers): has_field 'foo' => ( label => 'My Foo', wrap_label_method => \&wrap_label ); sub wrap_label { my ( $self, $label ) = @_; # or: my $label = $self->label; return qq{$label}; } This is particularly useful for creating labels that have links or other HTML. The 'wrap_label_method' does no filtering or localization, so you must do that yourself in the method if you need it. =head2 Rendering filter The base field class has a 'render_filter' attribute which is a coderef used to clean the values used to fill in the form for Render::Simple and the Widgets, and for some of the labels.. The default filter changes quote, ampersand, <, and > to the equivalent html entities. If you wish to use some other sort of filtering, you can use the 'render_filter' method in your form, or set a coderef on individual field objects. A 'render_filter' function in your form will be used by all fields. Setting it for a field will just be for that field. sub render_filter { my $string = shift; $string =~ s/my/MY/g; # perform some kind of transformation return $string; } -- or -- has_field 'foo' => ( render_filter => sub { ... } ); The filter is called in Render::Simple and in the widgets with C<< $self->html_filter( $fif ) >> or C<< $field->html_filter( $fif ) >>. If you want to turn off the filter for a particular field, you can set it to a sub that just returns the value: has_field 'bar' => ( render_filter => sub { shift } ); If you want a label that is unfiltered, see 'wrap_label_method'. =head1 Special rendering pseudo-fields Also see L. Blocks may be a better solution than pseudo-fields (i.e. fields that aren't actual form elements). Various 'tags' used for rendering can also be used for similar purposes. =head2 NonEditable Like a Bootstrap 'non_editable' field. Displays the field's value as a span. has_field 'non_edit' => ( type => 'NonEditable', value => 'This is a Test' ); =head2 Display L You can supply an HTML string to this field, to be displayed directly. There is no 'value' associated with this field; it's a field for rendering only. The HTML string can be built with a form or field method. Blocks or tags will often be a better solution. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/RenderingCookbook.pod0000644000077000007700000000420412221042077025174 0ustar gshankgshankpackage HTML::FormHandler::Manual::RenderingCookbook; # ABSTRACT: rendering recipes __END__ =pod =head1 NAME HTML::FormHandler::Manual::RenderingCookbook - rendering recipes =head1 VERSION version 0.40050 =head1 SYNOPSIS Collection of rendering recipes =head1 NAME HTML::FormHandler::Manual::Rendering::Cookbook =head1 Recipes =head2 Custom renderer, custom attributes You want to be able to specify the attributes that are rendered in the 'td' tag of the table renderer... First make your own copy of 'HTML::FormHandler::Widget::Wrapper::Table, in your own name space, and specify that name space in the 'widget_name_space' for the form. Change this line in the Table wrapper: $output .= '' . $self->do_render_label($result) . ''; to this: my $td_attr = process_attrs($self->get_tag('td_attr') || {} ); $output .= "" . $self->do_render_label($result) . ''; Now you can specify the attributes for the 'td' tag on a field: has_field 'foo' => ( tags => { td_attr => { class => ['emph', 'label'] } } ); =head2 Render a collection of checkboxes like a checkbox group =head2 Add a 'required' class to labels Create a custom widget wrapper: package MyApp::Form::Widget::Wrapper::CustomLabel; use Moose::Role; with 'HTML::FormHandler::Widget::Wrapper::Simple'; sub render_label { my ($self) = @_; return ''; } Or enable html5 output which adds a 'required' attribute. Or use the 'html_attributes' callback: sub html_attributes { my ( $self, $field, $type, $attr ) = @_; push @{$attr->{class}}, 'required' if ( $type eq 'label' && $field->required ); } =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Templates.pod0000644000077000007700000002206712221042077023535 0ustar gshankgshankpackage HTML::FormHandler::Manual::Templates; # ABSTRACT: using templates __END__ =pod =head1 NAME HTML::FormHandler::Manual::Templates - using templates =head1 VERSION version 0.40050 =head1 SYNOPSIS L Documentation on templates to use with L =head1 Using templates There is a FormHandler Template Toolkit rendering role at L, with a testcase in t/render_withtt.t. Normally, however, it probably won't make much sense to use both a TT parser in FormHandler, and a separate one for the "complete" templates, so the TT renderer is mainly useful for tests, or as an example of how to do TT rendering with HFH. You should create a template to render your form and then pass the template name and a template variable containing your form object to your templating or view engine, in whatever way you normally do that. If you want to use the 'process_attrs' function, you need to set that in your template variables too. A common way of using FormHandler with templates is to use the template for layout, specifying the divs and spans and wrappers, and then use the form object to render just the input fields. In your form: has '+widget_wrapper' => ( default => 'None' ); In your Catalyst controller: $c->stash( form => $form, template => 'form.tt' ); ..or do the equivalent for your web framework/view. In a form template (form.tt in the previous controller example):
My Foo [% form.field('foo').render %]
[% form.field('bar').render %]
[% form.field('save').render %] However, you can also render entirely with templates. There are lots of different ways to set up templates. There are sample templates installed in FormHandler's 'share' directory. These templates are now organized more-or-less similarly to the widget roles, with 'field', 'wrapper', and 'form' directories, but many other organizations are possible. There is also a template which combines the template rendering code into one file, 'share/templates/form/form_in_one.tt'. You can copy this template into your own TT directories, perhaps as form.tt, and then specify it as the template for your Catalyst actions. You can customize it by adding additional widget and widget_wrapper blocks, and then setting those in your field definitions. Note that widget names usually are camelcased, like the Moose roles that implement them in the Widget directory. You may want to use the non-camelcased widget/wrapper names in your TT templates, using the C<< $field->uwidget >> (un-camelcased widget name) and C<< $field->twidget >> (un-camelcased widget name + '.tt') convenience methods. ('MySpecialWidget' is the equivalent of 'my_special_widget') has_field 'my_field' => ( widget => 'MySpecialWidget' ); has_field 'another_field' => ( widget => 'YetAnotherWidget' ); And include them in a generic template: [% PROCESS widget/form_start.tt %] [% FOREACH f IN form.sorted_fields %] [% PROCESS widget/${f.twidget} %] [% END %] [% PROCESS widget/form_end.tt %] =head1 Field attributes If you want to use the 'process_attrs' function to pull in HTML attributes for the input elements, wrappers, and labels, you would need to pass that function into your TT setup. See L for an example: use HTML::FormHandler::Render::Util ('process_attrs'); $c->stash( process_attrs => &process_attrs ); # or add to TT vars in your view =head1 Sample templates The following is copied from the provided share/templates/form/form_in_one.tt file, as an example. Note that some fields, like form actions of 'submit' & 'reset', don't use the 'fif' value, but just the plain field value. [% PROCESS form_start -%]
[% FOREACH err IN form.form_errors -%] [% err %] [% END -%]
[% FOREACH f IN form.sorted_fields -%] [% WRAPPER "wrapper_${f.uwrapper}" -%][% PROCESS "${f.uwidget}" -%][% END -%] [% END -%] [% PROCESS form_end -%] [% BLOCK form_start -%] [% END -%] [% BLOCK form_end -%] [% END -%] [% BLOCK button -%] [% END -%] [% BLOCK checkbox -%] [%~ ~%] [% END -%] [% BLOCK checkbox_group -%] [% FOR option IN f.options -%] [% END -%] [% END -%] [% BLOCK compound -%] [% FOREACH sf IN f.sorted_fields -%] [% outerf = f; f = sf; -%] [% WRAPPER "wrapper_${f.uwrapper}" %][% PROCESS "${f.uwidget}" -%][% END -%] [% f = outerf -%] [% END -%] [% END -%] [% BLOCK hidden -%] [% END -%] [% BLOCK password -%] [% END -%] [% BLOCK radio_group -%] [% FOR option IN f.options -%] [% END -%] [% END -%] [% BLOCK repeatable -%] [% FOREACH rf IN f.sorted_fields -%] [% outerrf = f; f = rf; -%] [% WRAPPER "wrapper_${f.uwrapper}" %][% PROCESS "${f.uwidget}" -%][% END -%] [% f = outerrf -%] [% END -%] [% END -%] [% BLOCK reset -%] [% END -%] [% BLOCK select -%] [% END -%] [% BLOCK submit -%] [% END -%] [% BLOCK text -%] [% END -%] [% BLOCK textarea -%] [% END -%] [% BLOCK upload -%] [% END -%] [% BLOCK wrapper_simple -%] [% IF f.do_label %][% PROCESS label %][% END -%] [% content -%] [% END -%] [% BLOCK label -%] [% END -%] [% BLOCK wrapper_wrap_label -%] [%~ content ~%][%~ f.label %] [% END -%] [% BLOCK wrapper_none -%] [% content %] [% END -%] [% BLOCK wrapper_fieldset -%] [% f.label %] [% content -%] [% END -%] =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Testing.pod0000644000077000007700000000725412221042077023215 0ustar gshankgshankpackage HTML::FormHandler::Manual::Testing # ABSTRACT: testing forms __END__ =pod =head1 NAME HTML::FormHandler::Manual::Testing - testing forms =head1 VERSION version 0.40050 =head1 SYNOPSIS L One of the big advantages of FormHandler compared to many other form packages is that you can test the same form that you use in your controller. =head1 DESCRIPTION It's difficult to test forms that are instantiated in controllers with 'add_element' calls and from YAML, and that have no form class. It's one of the reasons that 'dynamic' forms generated with a field_list aren't a good idea for anything except the simplest forms. If you have a form class that contains everything that is needed for processing the form, it's really really easy to create tests for forms. Look in the FormHandler 't' directory. It's full of tests for forms. You can test that the validations work, that the database is getting updated correctly, even that the HTML that's being rendered is correct. If something isn't working correctly, it's ten times easier to debug in a test case than sitting in a controller somewhere. And when you finally start up your application and use the form, there should be very few surprises. FormHandler provides a simple function to test whether the HTML output is correct, 'is_html' in L, which uses L. If you need to build forms that use the rendering code to produce particular output, it can be helpful. =head1 Example Here's an example of a test, originally copied from one of the DBIC model tests. But you should download the tar.gz or checkout the distribution from github and browse through the tests. use Test::More; use lib 't/lib'; use_ok( 'BookDB::Form::Book'); use_ok( 'BookDB::Schema::DB'); my $schema = BookDB::Schema::DB->connect('dbi:SQLite:t/db/book.db'); ok($schema, 'get db schema'); my $form = BookDB::Form::Book->new(schema => $schema); # This is munging up the equivalent of param data from a form my $good = { 'title' => 'How to Test Perl Form Processors', 'author' => 'I.M. Author', 'genres' => [2, 4], 'format' => 2, 'isbn' => '123-02345-0502-2' , 'publisher' => 'EreWhon Publishing', }; ok( $form->process( params => $good ), 'Good data' ); my $book = $form->item; END { $book->delete }; ok ($book, 'get book object from form'); my $num_genres = $book->genres->count; is( $num_genres, 2, 'multiple select list updated ok'); is( $form->field('format')->value, 2, 'get value for format' ); my $bad_1 = { notitle => 'not req', silly_field => 4, }; ok( !$form->process( $bad_1 ), 'bad 1' ); my $bad_2 = { 'title' => "Another Silly Test Book", 'author' => "C. Foolish", 'year' => '1590', 'pages' => 'too few', 'format' => '22', }; ok( !$form->process( $bad_2 ), 'bad 2'); ok( $form->field('year')->has_errors, 'year has error' ); ok( $form->field('pages')->has_errors, 'pages has error' ); ok( !$form->field('author')->has_errors, 'author has no error' ); ok( $form->field('format')->has_errors, 'format has error' ); my $good = { title => "Another Silly Test Book", author => "C. Foolish", year => 1999, pages => 101, format => 2 }; ok( $form->process($good), 'now form validates' ); done_testing; =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual/Tutorial.pod0000644000077000007700000002457612221042077023411 0ustar gshankgshankpackage HTML::FormHandler::Manual::Tutorial; # ABSTRACT: how to use FormHandler with Catalyst __END__ =pod =head1 NAME HTML::FormHandler::Manual::Tutorial - how to use FormHandler with Catalyst =head1 VERSION version 0.40050 =head1 SYNOPSIS L A tutorial for beginners to L =head1 Using HTML::FormHandler with Catalyst This tutorial demonstrates how you can use L to manage forms, validate form input, and interface your forms with the database. =head1 Installation Use CPAN to install L =head1 Use the Tutorial application We'll use the files that were created in the L, in order to concentrate on just the bits where HTML::FormHandler is useful. You can download a tar file of the tutorial files from the Catalyst code repository. (See L.) =head2 Create an HTML::FormHandler form Untar the tutorial and make a lib/MyApp/Form directory. In that directory create the file Book.pm. package MyApp::Form::Book; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Model::DBIC'; has '+item_class' => ( default => 'Book' ); has_field 'title' => ( type => 'Text' ); has_field 'rating' => ( type => 'Integer' ); has_field 'authors' => ( type => 'Multiple', label_column => 'last_name' ); has_field 'submit' => ( type => 'Submit', value => 'Submit' ); no HTML::FormHandler::Moose; 1; This is your Form class. The form initializes the 'item_class' to the source name of your DBIx::Class result class. The form's fields are defined with the 'has_field' sugar, or in a 'field_list'. The names of the fields should match a column, relationship, or other accessor in your DBIx::Class result class. The basic fields have only a 'type', such as 'Text', or 'Integer'. These types are actually the names of L classes. 'Text' and 'Integer' are types that are provided by HTML::FormHandler, in L and L. The 'Multiple' type will allow you to easily create a multiple select list from the 'authors' relationship. The 'label_column' attribute must be defined because the column in the 'authors' table which is used to create the select list does not have the default column name ('name'). The 'submit' field is necessary if you are going to use FormHandler to render your form. It wouldn't be necessary for hand-built templates or HTML. Eventually you will want to create your own field classes, but for this simple form the default types are adequate. =head2 Connect HTML::FormHandler to your controller Edit lib/MyApp/Controller/Books.pm. Add use Moose: use Moose; BEGIN { extends 'Catalyst::Controller' } use MyApp::Form::Book; Create an attribute to hold your form: has 'form' => ( isa => 'MyApp::Form::Book', is => 'rw', lazy => 1, default => sub { MyApp::Form::Book->new } ); =head2 Add Action to Display and Save the Form In C add the following method: sub edit : Local { my ( $self, $c, $book_id ) = @_; $c->stash( template => 'books/edit.tt2', form => $self->form ); # Validate and insert/update database return unless $self->form->process( item_id => $book_id, params => $c->req->parameters, schema => $c->model('DB')->schema ); # Form validated, return to the books list $c->flash->{status_msg} = 'Book saved'; $c->res->redirect($c->uri_for('list')); } This will handle both creating new books, and updating old books. If $book_id is undefined, then HTML::FormHandler will create a new book from your form. If you pass in a DBIx::Class row object instead of a primary key, you don't need to specify the schema. =head2 Render the form Save a copy of C and create a new file that contains only: [% form.render %] =head2 Alternative hand-built Template for the form (optional) Although the automatic rendering works well, sometimes it's necessary to hand build HTML. This section contains an example of a Template Toolkit template that may be used to display a FormHandler form. In some cases, you might want to use the rendering for just the field and build custom divs or tables or whatever around it:
[% form.render_field('book') %]
If you don't want to play with HTML at this point, you can skip ahead to the next section. You could also use TT macros to do pretty sophisticated template generation. But for now, we'll stick to a straightforward TT template: Delete the single statement in C, and enter or copy the following: [% META title = 'Book Form' %] [% FOR field IN form.error_fields %] [% FOR error IN field.errors %]

[% field.label _ ': ' _ error %]

[% END %] [% END %]

[% f = form.field('title') %]

[% f = form.field('rating') %]

[% f = form.field('authors') %]

Return to book list

=head2 Add links to access create and update actions Add a link to root/src/books/list.tt2 to allow you to edit an existing book, by changing the last cell in the book list: Delete| Edit Change the link to create a book at the bottom of the file:

Create book

=head2 Test the L Create Form Start up the server for MyApp: $ script/myapp_server.pl (You'll need to login with test01/mypass if you're using the packaged tutorial.) Click the new "Create book" link at the bottom to display the form. Fill in the fields and click submit. You should be returned to the Book List page with a "Book saved" message. Magic! A new book has been created and saved to the database with very little code in your controller. Click on the 'edit' links, and edit the existing books. Changes should be saved and displayed properly. Try to add an alphabetic character to the rating field. You should get an error message. =head2 Add additional attributes to your form's fields We'll add a couple of 'label' attributes to the fields: has_field 'title' => ( type => 'Text', label => 'Title of a Book' ); has_field 'rating' => ( type => 'Integer', label => 'Rating (1-5)' ); has_field 'authors' => ( type => 'Multiple', label_column => 'last_name' ); If you want a new attribute in your fields, it's very easy to add it to your custom Field classes. package MyApp::Form::Field::Extra; use Moose; extends 'HTML::FormHandler::Field'; has 'my_attribute' => ( isa => Str, is => 'ro' ); 1; Now if your Field classes inherit from this, you can have a 'my_attribute' attribute for all your fields. Or use a Moose role instead of inheritance. You can also add attributes to the base FormHandler field class using Moose. This technique is described in L. =head1 L Validation Now we'll add more validation to ensure that users are entering correct data. Update the fields in the form file: has_field 'title' => ( type => 'Text', label => 'Title of a Book', required => 1, size => 40, minlength => 5 ); has_field 'rating' => ( type => 'Integer', label => 'Rating (1-5)', required => 1, messages => { required => 'You must rate the book' }, range_start => 1, range_end => 5 ); has_field 'authors' => ( type => 'Multiple', label_column => 'last_name', required => 1 ); We've made all the fields required. We added 'size' and 'minlength' attributes to the 'title' field. These are attributes of the 'Text' Field, which will use them to validate. We've added 'range_start' and 'range_end' attributes to the 'rating' field. Numbers entered in the form will be checked to make sure they fall within the defined range. (Another option would have been to use the 'IntRange' field type, which makes it easy to create a select list of numbers.) =head2 Add customized validation You can create a Field class for validation that will be performed on more than one field, but it is easy to perform custom validation on a per-field basis. This form doesn't really require any customized validation, so we'll add a silly field constraint. Add the following to the form: sub validate_title { my ( $self, $field ) = @_; $field->add_error("The word \'Rainbows\' is not allowed in titles") if ( $field->value =~ /Rainbows/ ); } You can also apply Moose constraints and transforms. Validation can also be performed in a form 'validate_ There are many options for validating fields in FormHandler. Some validation is from field attributes, some from form or field methods, some from 'apply' actions on the fields. =head1 Field attributes for validation Each individual field may have additional attributes that relate to validation, which are not documented here. See the individual field documentation, linked from L. =head2 required, required_when Setting the 'required' flag on a field initiates a check for the existence of some value. If the field does not have a value, the 'required' error message is issued. has_field 'section' => ( required => 1, messages => { required => 'Please provide a section' } ); Note that a required flag on a subfield -- a field inside a compound field or repeatable field -- does not cause the containing field to be required. You need to set 'required' all the way up, if that's the behavior that you want. If a field is empty and *not* required, no other field validation will be performed unless the 'validate_when_empty' flag (see below) is set. The form's 'validate' method, however, will always be called. There is also the 'required_when' attribute, which works the same way as the 'when' key on the apply actions. has_field 'fee' => ( required_when => { 'fie' => 2 } ); When a 'required' or 'required_when' check fails, a 'missing' flag is set in the result: if ( $field->missing ) { ... } =head2 range_start, range_end Starting and ending range for number fields. =head2 unique Attribute used by the DBIC model to check for uniqueness. =head2 validate_when_empty If its 'validate_when_empty' flag is set to a true value, then a field will always undergo validation when its form is processed, even when that field is empty. =head1 Validation methods =head2 validate_method You can provide a validation method for a field by setting a coderef with 'validate_method'. has_field 'fox' => ( validate_method => \&check_fox ); sub check_fox { my $self = shift; # self is the fox field unless( $self->value eq .... ) { $self->add_error('....'); } } =head2 validate_ If you provide a 'validate_' method it will be automatically used. has_field 'cat'; sub validate_cat { my ( $self, $field ) = @_; # self is the form unless ( $field->value eq ... ) { $field->add_error( '...' ); } } If the field name has periods in it, they should be replaced with underscores. =head2 form validate method A form validation method can be used to do cross-validation or validation checks that need information from more than one field. sub validate { my $self = shift; $self->field('foo')->add_error('....') if( $self->field('foo')->value eq '..' && $self->field('bar')->value eq '..' ); } =head2 field validate method You can create a custom field to contain a commonly used validation. The validation in a custom field can be done with 'apply' or by using a 'validate' method. package MyApp::Form::Field::Custom; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field'; # or a subclass of Field sub validate { .... } =head1 Apply Actions: Filters, transformations, and constraints The actions in the 'apply' array (stored in the 'actions' attribute) will be performed in the order they are specified, allowing fine-grained control over inflation and validation. You can check constraints after transformations and vice versa. You can weave all three types of actions in any order you need. The two valid 'apply' array elements are 1) Moose types and 2) hashrefs with one of three keys: 'check', 'transform', and 'type'. The hashrefs will usually also have an additional key, 'message', with a string, array or coderef providing an error message, which is localized. The 'check' key can point to a regex, arrayref of strings, or coderef. The value of the 'transform' key should be a coderef. The value of the 'type' key is a Moose type. In addition to the check and type keys, you can provide a 'when' key to only perform this validation when a particular field is a particular value: has_field 'fee'; has_field 'fie' => ( apply => [ { when => { fee => 1 }, check => qr/when/, message => 'Wrong fie' }, ]); has_field 'fo'; has_field 'fum_comp' => ( type => 'Compound' ); has_field 'fum_comp.one'; has_field 'fum_comp.two' => ( apply => [ { when => { '+fee' => [1,2,3] }, check => qr/when/, message => 'Wrong two' }, ]); The field name key in the 'when' hashref is assumed to be a field at the same "level" as this field (i.e. a sibling field in a compound). If you want to specify a field name from the form, prepend the name with a '+'. The 'when' hashref can contain multiple key/value pairs. This simply extends its test across multiple fields; all fields named in the hashref's keys must match their respective values in order for the overall 'when' test to pass. Transformations and coercions are called in an eval to catch the errors. Warnings are trapped in a sigwarn handler. See also L and L. See L for information on inflation and deflation. =head2 Moose types Moose types can be used to do both constraints and transformations. If a coercion exists it will be applied, resulting in a transformation. After coercing, the result is checked. You can use type constraints from L libraries or defined using L. FormHandler supplies a library of Moose types in L. use HTML::FormHandler::Types ('NotAllDigits'); has_field 'foo' => ( apply => [ NotAllDigits ] ); You can create your own library of types, too. Or you can create a type constraint in the form: use Moose::Util::TypeConstraints; subtype 'GreaterThan10' => as 'Int' => where { $_ > 10 } => message { "This number ($_) is not greater than 10" }; has_field 'text_gt' => ( apply=> [ 'GreaterThan10' ] ); Moose types can also be used for their coercions to do transformations. subtype 'MyInt' => as 'Int'; coerce 'MyInt' => from 'MyStr' => via { return $1 if /(\d+)/ }; You can also use the 'type' keyword with a Moose type if you want to change the message: has_field 'text_gt' => ( apply => [ { type => 'GreaterThan10', message => 'Number is too small' } ] ); =head2 transform A 'transform' changes the format of a field's value, and does not need a message. It takes a coderef. has_field 'another_field' => ( apply => [ { transform => sub{ sprintf '<%.1g>', $_[0] } } ] ); Note that transformed values are not displayed in the HTML form unless the 'fif_from_value' flag is set. The transformed values are saved to the database or returned in C<< $form->value >>. =head2 'check' regex Checks that field value matches the regex. has_field 'some_field' => ( apply => [ { check => qr/aaa/, message => 'Must contain aaa' } ], ); You can use regex libraries like L too: use Regexp::Common ('URI'); ... has_field 'my_url' => ( apply => [ { check => qr/$RE{URI}{HTTP}/, message => 'Invalid URL' } ] ); =head2 'check' arrayref (matches) Provide an arrayref of strings to match against. has_field 'set_error' => ( apply => [ { check => [ 'abc', 'bbb' ], message => 'Must be "aaa" or "bbb"' } ] ); =head2 'check' coderef Provide a validation function to check. A 'check' coderef will be passed the current value of the field and should return true or false. Note that the field is passed in as the second argument, to allow simple functions to work properly. has_field 'callback_pass' => ( apply => [ { check => \&check_callback_pass, message => 'Must contain number greater than 10', } ] ); sub check_callback_pass { my ( $value, $field ) = @_; if( $value =~ /(\d+)/ ) { return $1 > 10; } } =head2 message The message for the above checks can also be an arrayref or coderef. The arrayref is useful for localized messages. You can also provide error messages for Moose types. has_field 'message_sub' => ( apply => [ { check => [ 'abc' ], message => \&err_message } ] ); sub err_message { my ($value, $field ) = @_; return $field->name . ': Must be "abc"'; } has_field 'message_arrayref' => ( apply => [ { check => qr/aaa/, message => ['Must contain [_1]', 'aaa'] } ], ); has_field 'my_moose_type_field' => ( apply => [ { type => SomeType, message => 'Invalid ...' } ] ); =head2 actions in a field class To declare actions inside a field class use L and 'apply' sugar: package MyApp::Field::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field; apply [ 'SomeConstraint', { check => ..., message => .... } ]; 1; Actions specified with apply are cumulative. Actions may be specified in field classes and additional actions added in the 'has_field' declaration. You can see examples of field classes with 'apply' actions in the source for L and L, and in t/constraints.t. =head1 Dependency The 'dependency' attribute is an array of arrays of field names. During validation, if any field in a given group has a value that matches the pattern /\S/ (non-blank), the 'required' flag is set for all of the fields in the group. has '+dependency' => ( default => sub { [ ['address', 'city', 'state', 'zip'], ['cc_no', 'cc_expires'], ], }, ); You can also use the 'required_when' flag to do something similar. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Manual.pod0000644000077000007700000000447712221042077021604 0ustar gshankgshankpackage HTML::FormHandler::Manual # ABSTRACT: index of the manual 1; __END__ =pod =head1 NAME HTML::FormHandler::Manual - index of the manual =head1 VERSION version 0.40050 =head1 DESCRIPTION This is the L users manual. HTML::FormHandler is an HTML form handling class written in Moose. It provides facilities to write classes that represent HTML forms, and retrieves and loads data from the database. =head1 SECTIONS =head2 L Creating FormHandler forms and interfacing them with your controllers. =head2 L Organized list of FormHandler fields, with links to specific documentation. =head2 L Description of the various ways of setting defaults. =head2 L Inflation and deflation of fields =head2 L Validation of fields =head2 L Errors: setting them, getting them. =head2 L Rendering options, particularly rendering with widgets =head2 L Issues and setup for database forms. =head2 L Builds on the Catalyst tutorial. Step-by-step guide. =head2 L Test your forms =head2 L 'Howto' recipes =head2 L Cut-and-paste examples of templates to use with FormHandler =head2 L Catalyst specific documentation =head2 L Quick reference of FormHandler interface =head2 L Info on converting from Data::FormValidator =head2 L Info on converting from HTML::FormFu =head1 SUPPORT IRC: Join #formhandler on irc.perl.org Mailing list: http://groups.google.com/group/formhandler =head1 AUTHOR gshank: Gerda Shank =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Merge.pm0000644000077000007700000000374312221042077021253 0ustar gshankgshankpackage HTML::FormHandler::Merge; # ABSTRACT: internal hash merging use warnings; use Data::Clone; use base 'Exporter'; our @EXPORT_OK = ( 'merge' ); our $matrix = { 'SCALAR' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { [ $_[0], @{ $_[1] } ] }, 'HASH' => sub { $_[1] }, }, 'ARRAY' => { 'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] }, 'ARRAY' => sub { [ @{ $_[0] }, @{ $_[1] } ] }, 'HASH' => sub { $_[1] }, }, 'HASH' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { $_[0] }, 'HASH' => sub { merge_hashes( $_[0], $_[1] ) }, }, }; sub merge { my ( $left, $right ) = @_; my $lefttype = ref $left eq 'HASH' ? 'HASH' : ref $left eq 'ARRAY' ? 'ARRAY' : 'SCALAR'; my $righttype = ref $right eq 'HASH' ? 'HASH' : ref $right eq 'ARRAY' ? 'ARRAY' : 'SCALAR'; $left = clone($left); $right = clone($right); return $matrix->{$lefttype}{$righttype}->( $left, $right ); } sub merge_hashes { my ( $left, $right ) = @_; my %newhash; foreach my $leftkey ( keys %$left ) { if ( exists $right->{$leftkey} ) { $newhash{$leftkey} = merge( $left->{$leftkey}, $right->{$leftkey} ); } else { $newhash{$leftkey} = clone( $left->{$leftkey} ); } } foreach my $rightkey ( keys %$right ) { if ( !exists $left->{$rightkey} ) { $newhash{$rightkey} = clone( $right->{$rightkey} ); } } return \%newhash; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Merge - internal hash merging =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Meta/0000755000077000007700000000000012221042077020535 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Meta/Role.pm0000644000077000007700000000331112221042077021772 0ustar gshankgshankpackage # hide from Pause HTML::FormHandler::Meta::Role; # ABSTRACT: field_list and apply_list use Moose::Role; has 'field_list' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef', default => sub { [] }, handles => { add_to_field_list => 'push', clear_field_list => 'clear', has_field_list => 'count', } ); has 'apply_list' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef', default => sub { [] }, handles => { add_to_apply_list => 'push', has_apply_list => 'count', clear_apply_list => 'clear', } ); has 'page_list' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef', default => sub { [] }, handles => { add_to_page_list => 'push', has_page_list => 'count', clear_page_list => 'clear', } ); has 'block_list' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef', default => sub { [] }, handles => { add_to_block_list => 'push', has_block_list => 'count', clear_block_list => 'clear', } ); has 'found_hfh' => ( is => 'rw', default => '0' ); use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Meta::Role - field_list and apply_list =head1 VERSION version 0.40050 =head1 SYNOPSIS Add metaclass to field_list attribute =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Model/0000755000077000007700000000000012221042077020707 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Model/CDBI.pm0000644000077000007700000002747312221042077021763 0ustar gshankgshankpackage # hide from Pause HTML::FormHandler::Model::CDBI; # ABSTRACT: Class::DBI model class (non-functioning) use Moose; use Carp; use Data::Dumper; extends 'HTML::FormHandler'; our $VERSION = '0.02'; HTML::FormHandler::Model::CDBI->meta->make_immutable; sub init_item { my $self = shift; my $item_id = $self->item_id or return; return $self->item_class->retrieve($item_id); } sub BUILDARGS { my ( $self, @args ) = @_; return {@args}; } sub guess_field_type { my ( $self, $column, $class ) = @_; $class ||= $self->item_class; return unless $class && $class->isa('Class::DBI'); my @return; # Is it a direct has_a relationship? if ( my $meta = $class->meta_info('has_a')->{$column} ) { my $f_class = $meta->foreign_class; @return = $f_class->isa('DateTime') ? ('DateTime') : ( 'Select', $f_class ); # Otherwise, check for has_many } elsif ( $meta = $class->meta_info('has_many')->{$column} ) { my $f_class = $meta->foreign_class; # Is there a mapping table in between? If so need to find the # actual class for lookups -- call recursively if ( @{ $meta->args->{mapping} } ) { my $t; ( $t, $f_class ) = $self->guess_field_type( $meta->args->{mapping}[0], $f_class ); } @return = ( 'Multiple', $f_class ); } elsif ( $column =~ /_time$/ ) { @return = ('DateTime'); } else { @return = ('Text'); } return wantarray ? @return : $return[0]; } sub lookup_options { my ( $self, $field ) = @_; my $class = $self->item_class or return; return unless $class->isa('Class::DBI'); my $field_name = $field->name; my ( $type, $f_class ) = $self->guess_field_type( $field_name, $class ); return unless $f_class; # label column my $label_column = $field->label_column; return unless $f_class->find_column($label_column); # active column my $active_col = $self->can('active_column') ? $self->active_column : $field->active_column; $active_col = '' unless $f_class->find_column($active_col); # sort column my $sort_col = $field->sort_column; $sort_col = defined $sort_col && $f_class->find_column($sort_col) ? $sort_col : $label_column; my $criteria = {}; my $primary_key = $f_class->primary_column; # In cases where the f_class is the same as the item's class don't # include item in the option list -- don't want to be able to have item point to itself # Obviously, this doesn't prevent circular references. $criteria->{"$primary_key"} = { '!=', $self->item->id } if $f_class eq ref $self->item; # If there's an active column, only select active OR items already selected if ($active_col) { my @or = ( $active_col => 1 ); # But also include any existing non-active push @or, ( "$primary_key" => $field->init_value ) # init_value is scalar or array ref if $self->item && defined $field->init_value; $criteria->{'-or'} = \@or; } my @rows = $f_class->search( $criteria, { order_by => $sort_col } ); return [ map { my $label = $_->$label_column; $_->id, $active_col && !$_->$active_col ? "[ $label ]" : "$label" } @rows ]; } sub init_value { my ( $self, $field, $item ) = @_; my $column = $field->name; $item ||= $self->item; return if $field->writeonly; return unless $item && ( $item->can($column) || ( ref $item eq 'HASH' && exists $item->{$column} ) ); my @values; if ( ref $item eq 'HASH' ) { @values = $item->{$column} if ref($item) eq 'HASH'; } elsif ( !$item->isa('Class::DBI') ) { @values = $item->$column; } else { @values = map { ref $_ && $_->isa('Class::DBI') ? $_->id : $_ } $item->$column; } my $value = @values > 1 ? \@values : shift @values; $field->init_value($value); $field->value($value); } sub validate_model { my ($self) = @_; return unless $self->validate_unique; return 1; } sub validate_unique { my ($self) = @_; my @unique = map { $_->name } grep { $_->unique } $self->fields; return 1 unless @unique; my $item = $self->item; my $class = ref($item) || $self->item_class; my $found_error = 0; for my $field ( map { $self->field($_) } @unique ) { next if $field->errors; my $value = $field->value; next unless defined $value; my $name = $field->name; # unique means there can only be on in the database like it. my $match = $class->search( { $name => $value } )->first || next; next if $self->items_same( $item, $match ); my $field_error = $field->unique_message || 'Value must be unique in the database'; $field->add_error($field_error); $found_error++; } return $found_error; } sub update_model { my ($self) = @_; # Grab either the item or the object class. my $item = $self->item; my $class = ref($item) || $self->item_class; my $updated_or_created; # get a hash of all fields my %fields = map { $_->name, $_ } grep { !$_->noupdate } $self->fields; # First process the normal and has_a columns # as that data is directly stored in the object my %data; # Loads columns (including has_a) foreach my $col ( $class->columns('All') ) { next unless exists $fields{$col}; my $field = delete $fields{$col}; # If the field is flagged "clear" then set to NULL. my $value = $field->value; if ($item) { my $cur = $item->$col; next unless $value || $cur; next if $value && $cur && $value eq $cur; $item->$col($value); } else { $data{$col} = $value; } } if ($item) { $item->update; $updated_or_created = 'updated'; } else { $item = $class->create( \%data ); $self->item($item); $updated_or_created = 'created'; } # Now check for mapping/has_many in any left over fields for my $field_name ( keys %fields ) { next unless $class->meta_info('has_many'); next unless my $meta = $class->meta_info('has_many')->{$field_name}; my $field = delete $fields{$field_name}; my $value = $field->value; # Figure out which values to keep and which to add my %keep; %keep = map { $_ => 1 } ref $value ? @$value : ($value) if defined $value; # Get foreign class and its key that points to $class my $foreign_class = $meta->foreign_class; my $foreign_key = $meta->args->{foreign_key}; my $related_key = $meta->args->{mapping}->[0]; die "Failed to find related_key for field [$field] in class [$class]" unless $related_key; # Delete any items that are not to be kept for ( $foreign_class->search( { $foreign_key => $item } ) ) { $_->delete unless delete $keep{ $_->$related_key }; } # Add in new ones $foreign_class->create( { $foreign_key => $item, $related_key => $_, } ) for keys %keep; } # Save item in form object $self->item($item); return $item; } sub items_same { my ( $self, $item1, $item2 ) = @_; # returns true if both are undefined return 1 if not defined $item1 and not defined $item2; # return false if either undefined return unless defined $item1 and defined $item2; return $self->obj_key($item1) eq $self->obj_key($item2); } sub obj_key { my ( $self, $item ) = @_; return join '|', $item->table, map { $_ . '=' . ( $item->$_ || '.' ) } $item->primary_columns; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =head1 NAME HTML::FormHandler::Model::CDBI - Class::DBI model class (non-functioning) =head1 VERSION version 0.40050 =head1 SYNOPSIS package MyApplication::Form::User; use strict; use base 'HTML::FormHandler::Model::CDBI'; # Associate this form with a CDBI class has '+item_class' => ( default => 'MyDB::User' ); # Define the fields that this form will operate on sub field_list { return { [ name => 'Text', age => 'PosInteger', sex => 'Select', birthdate => 'DateTimeDMYHM', ] }; } =head1 DESCRIPTION A Class::DBI database model for HTML::FormHandler I don't use CDBI, so this module almost certainly doesn't work. It is only being left here as a starting point in case somebody is interested in getting it to work. Patches and tests gratefully accepted. =head1 METHODS =head2 item_class The name of your database class. =head2 init_item This is called first time $form->item is called. It does the equivalent of: return $self->item_class->retrieve( $self->item_id ); =head2 guess_field_type Pass in a column and assigns field types. Must set $self->item_class to return the related item class. Returns the type in scalar context, returns the type and maybe the related table in list context. Currently returns: DateTime - for a has_a relationship that isa DateTime Select - for a has_a relationship Multiple - for a has_many DateTime - if the field ends in _time Text - otherwise =head2 lookup_options Returns a array reference of key/value pairs for the column passed in. Calls $field->label_column to get the column name to use as the label. The default is "name". The labels are sorted by Perl's cmp sort. If there is an "active" column then only active are included, with the exception being if the form (item) has currently selected the inactive item. This allows existing records that reference inactive items to still have those as valid select options. The inactive labels are formatted with brackets to indicate in the select list that they are inactive. The active column name is determined by calling: $active_col = $form->can( 'active_column' ) ? $form->active_column : $field->active_column; Which allows setting the name of the active column globally if your tables are consistently named (all lookup tables have the same column name to indicate they are active), or on a per-field basis. In addition, if the foreign class is the same as the item's class (or the class returned by item_class) then options pointing to item are excluded. The reason for this is for a table column that points to the same table (self referenced), such as a "parent" column. The assumption is that a record cannot be its own parent. =head2 init_value Populate $field->value with object ids from the CDBI object. If the column expands to more than one object then an array ref is set. =head2 validate_model Validates fields that are dependent on the model. Currently, "unique" fields are checked to make sure they are unique. This validation happens after other form validation. The form already has any field values entered in $field->value at this point. =head2 validate_unique Checks that the value for the field is not currently in the database. =head2 items_same Returns true if the two passed in cdbi objects are the same object. If both are undefined returns true. =head2 obj_key returns a key for a given object, or undef if the object is undefined. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Model/Object.pm0000644000077000007700000000143212221042077022453 0ustar gshankgshankpackage HTML::FormHandler::Model::Object; # ABSTRACT: stub for Object model use Moose::Role; sub update_model { my $self = shift; my $item = $self->item; return unless $item; foreach my $field ( $self->all_fields ) { my $name = $field->name; next unless $item->can($name); $item->$name( $field->value ); } } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Model::Object - stub for Object model =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Model.pm0000644000077000007700000001067712221042077021260 0ustar gshankgshankpackage HTML::FormHandler::Model; # ABSTRACT: default model base class use Moose::Role; use Carp; has 'item' => ( is => 'rw', lazy => 1, builder => 'build_item', clearer => 'clear_item', trigger => sub { shift->set_item(@_) } ); sub build_item { return } sub set_item { my ( $self, $item ) = @_; $self->item_class( ref $item ); } has 'item_id' => ( is => 'rw', clearer => 'clear_item_id', trigger => sub { shift->set_item_id(@_) } ); sub set_item_id { } has 'item_class' => ( isa => 'Str', is => 'rw', ); sub guess_field_type { Carp::confess "Don't know how to determine field type of [$_[1]]"; } sub lookup_options { } sub validate_model { } sub clear_model { } sub update_model { } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Model - default model base class =head1 VERSION version 0.40050 =head1 SYNOPSIS This class defines the base attributes for FormHandler model classes. It is not used directly. =head1 DESCRIPTION This is an empty base class that defines methods called by HTML::FormHandler to support interfacing forms with a data store such as a database. This module provides instructions on methods to override to create a HTML::FormHandler::Model class to work with a specific object relational mapping (ORM) tool. =head1 METHODS =head2 item, build_item The "item" is initialized with "build_item" the first time $form->item is called. "item" must be defined in the model class to fetch the object based on the item id. It should return the item's object. Column values are fetched and updated by calling methods on the returned object. For example, with Class::DBI you might return: return $self->item_class->retrieve( $self->item_id ); =head2 item_id The id (primary key) of the item (object) that the form is updating or has just created. The model class should have a build_item method that can fetch the object from the item_class for this id. =head2 item_class "item_class" sets and returns a value used by the model class to access the ORM class related to a form. For example: has '+item_class' => ( default => 'User' ); This gives the model class a way to access the data store. If this is not a fixed value (as above) then do not define the method in your subclass and instead set the value when the form is created: my $form = MyApp::Form::Users->new( item_class => $class ); The value can be any scalar (or object) needed by the specific ORM to access the data related to the form. A builder for 'item_class' might be to return the class of the 'item'. =head2 guess_field_type Returns the guessed field type. The field name is passed as the first argument. This is only required if using "Auto" type of fields in your form classes. You could override this in your form class, for example, if you use a field naming convention that indicates the field type. The metadata info about the columns can be used to assign types. =head2 lookup_options Retrieve possible options for a given select field from the database. The default method returns undef. Returns an array reference of key/value pairs for the column passed in. These values are used for the values and labels for field types that provide a list of options to select from (e.g. Select, Multiple). A 'Select' type field (or a field that inherits from HTML::FormHandler::Field::Select) can set a number of scalars that control how options are looked up: label_column() - column that holds the label active_column() - column that indicates if a row is acitve sort_column() - column used for sorting the options The default for label_column is "name". =head2 validate_model Validates fields that are dependent on the model. This is called via the validation process and the model class must at least validate "unique" constraints defined in the form class. Any errors on a field found should be set by calling the field's add_error method: $field->add_error('Value must be unique in the database'); The default method does nothing. =head2 clear_model Clear out any dynamic data for persistent object =head2 update_model Update the model with validated fields =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Moose/0000755000077000007700000000000012221042077020731 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Moose/Role.pm0000644000077000007700000000323412221042077022172 0ustar gshankgshankpackage HTML::FormHandler::Moose::Role; # ABSTRACT: to add sugar to roles use Moose::Role; use Moose::Exporter; Moose::Exporter->setup_import_methods( with_caller => [ 'has_field', 'has_block', 'apply' ], also => 'Moose::Role', ); sub init_meta { my $class = shift; my %options = @_; Moose::Role->init_meta(%options); my $meta = Moose::Util::MetaRole::apply_metaroles( for => $options{for_class}, role_metaroles => { role => ['HTML::FormHandler::Meta::Role'] } ); return $meta; } sub has_field { my ( $class, $name, %options ) = @_; $class->meta->add_to_field_list( { name => $name, %options } ); } sub has_block { my ( $class, $name, %options ) = @_; $class->meta->add_to_block_list( { name => $name, %options } ); } sub apply { my ( $class, $arrayref ) = @_; $class->meta->add_to_apply_list( @{$arrayref} ); } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Moose::Role - to add sugar to roles =head1 VERSION version 0.40050 =head1 SYNOPSIS Enables the use of field specification sugar (has_field) in roles. Use this module instead of C< use Moose::Role; > package MyApp::Form::Foo; use HTML::FormHandler::Moose::Role; has_field 'username' => ( type => 'Text', ... ); has_field 'something_else' => ( ... ); no HTML::FormHandler::Moose::Role; 1; =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Moose.pm0000644000077000007700000000454712221042077021301 0ustar gshankgshankpackage HTML::FormHandler::Moose; # ABSTRACT: to add FormHandler sugar use Moose; use Moose::Exporter; use Moose::Util::MetaRole; use HTML::FormHandler::Meta::Role; Moose::Exporter->setup_import_methods( with_meta => [ 'has_field', 'has_page', 'has_block', 'apply' ], also => 'Moose', ); sub init_meta { my $class = shift; my %options = @_; Moose->init_meta(%options); my $meta = Moose::Util::MetaRole::apply_metaroles( for => $options{for_class}, class_metaroles => { class => [ 'HTML::FormHandler::Meta::Role' ] } ); return $meta; } sub has_field { my ( $meta, $name, %options ) = @_; my $names = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ]; unless ($meta->found_hfh) { my @linearized_isa = $meta->linearized_isa; if( grep { $_ eq 'HTML::FormHandler' || $_ eq 'HTML::FormHandler::Field' } @linearized_isa ) { $meta->found_hfh(1); } else { die "Package '" . $linearized_isa[0] . "' uses HTML::FormHandler::Moose without extending HTML::FormHandler[::Field]"; } } $meta->add_to_field_list( { name => $_, %options } ) for @$names; } sub has_page { my ( $meta, $name, %options ) = @_; my $names = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ]; $meta->add_to_page_list( { name => $_, %options } ) for @$names; } sub has_block { my ( $meta, $name, %options ) = @_; $meta->add_to_block_list( { name => $name, %options } ); } sub apply { my ( $meta, $arrayref ) = @_; $meta->add_to_apply_list( @{$arrayref} ); } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Moose - to add FormHandler sugar =head1 VERSION version 0.40050 =head1 SYNOPSIS Enables the use of field specification sugar (has_field). Use this module instead of C< use Moose; > package MyApp::Form::Foo; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'username' => ( type => 'Text', ... ); has_field 'something_else' => ( ... ); no HTML::FormHandler::Moose; 1; =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Page/0000755000077000007700000000000012221042077020523 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Page/Simple.pm0000644000077000007700000000101012221042077022302 0ustar gshankgshankpackage HTML::FormHandler::Page::Simple; # ABSTRACT: used by Wizard use Moose; extends 'HTML::FormHandler::Page'; 1; __END__ =pod =head1 NAME HTML::FormHandler::Page::Simple - used by Wizard =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Page.pm0000644000077000007700000000217512221042077021066 0ustar gshankgshankpackage HTML::FormHandler::Page; # ABSTRACT: used in Wizard use Moose; with 'HTML::FormHandler::Pages'; has 'name' => ( is => 'ro', isa => 'Str' ); has 'form' => ( isa => 'HTML::FormHandler', is => 'rw', weak_ref => 1, predicate => 'has_form', ); has 'fields' => ( traits => ['Array'], isa => 'ArrayRef[Str]', is => 'rw', default => sub { [] }, handles => { all_fields => 'elements', clear_fields => 'clear', push_field => 'push', num_fields => 'count', has_fields => 'count', } ); sub field { my ( $self, $field_name ) = @_; return $self->form->field($field_name); } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Page - used in Wizard =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Pages.pm0000644000077000007700000000361712221042077021253 0ustar gshankgshankpackage HTML::FormHandler::Pages; # ABSTRACT: used in Wizard use Moose::Role; has 'pages' => ( traits => ['Array'], isa => 'ArrayRef[HTML::FormHandler::Page]', is => 'rw', default => sub { [] }, auto_deref => 1, handles => { all_pages => 'elements', clear_pages => 'clear', push_page => 'push', num_pages => 'count', has_pages => 'count', set_page_at => 'set', get_page => 'get', } ); has 'page_name_space' => ( isa => 'Str|ArrayRef[Str]|Undef', is => 'rw', lazy => 1, builder => 'build_page_name_space', ); sub build_page_name_space { '' } sub page_index { my ( $self, $name ) = @_; my $index = 0; for my $page ( $self->all_pages ) { return $index if $page->name eq $name; $index++; } return; } sub page { my ( $self, $name, $die ) = @_; my $index; # if this is a full_name for a compound page # walk through the pages to get to it return undef unless ( defined $name ); if ( $name =~ /\./ ) { my @names = split /\./, $name; my $f = $self->form || $self; foreach my $pname (@names) { $f = $f->page($pname); return unless $f; } return $f; } else # not a compound name { for my $page ( $self->all_pages ) { return $page if ( $page->name eq $name ); } } return unless $die; die "Page '$name' not found in '$self'"; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Pages - used in Wizard =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Params.pm0000644000077000007700000000756312221042077021443 0ustar gshankgshankpackage # hide from Pause HTML::FormHandler::Params; # ABSTRACT: params handling use Moose; use Carp; has 'separator' => ( isa => 'Str', is => 'rw', default => '.' ); sub split_name { my ( $self, $name, $sep ) = @_; $sep ||= $self->separator; $sep = "\Q$sep"; if ( $sep eq '[]' ) { return grep { defined } ( $name =~ / ^ (\w+) # root param | \[ (\w+) \] # nested /gx ); } # These next two regexes are the escaping aware equivalent # to the following: # my ($first, @segments) = split(/\./, $name, -1); # m// splits on unescaped '.' chars. Can't fail b/c \G on next # non ./ * -> escaped anything -> non ./ * $name =~ m/^ ( [^\\$sep]* (?: \\(?:.|$) [^\\$sep]* )* ) /gx; my $first = $1; $first =~ s/\\(.)/$1/g; # remove escaping my (@segments) = $name =~ # . -> ( non ./ * -> escaped anything -> non ./ * ) m/\G (?:[$sep]) ( [^\\$sep]* (?: \\(?:.|$) [^\\$sep]* )* ) /gx; # Escapes removed later, can be used to avoid using as array index return ( $first, @segments ); } sub expand_hash { my ( $self, $flat, $sep ) = @_; my $deep = {}; $sep ||= $self->separator; for my $name ( keys %$flat ) { my ( $first, @segments ) = $self->split_name( $name, $sep ); my $box_ref = \$deep->{$first}; for (@segments) { if ( /^(0|[1-9]\d*)$/ ) { $$box_ref = [] unless defined $$box_ref; croak "HFH: param clash for $name=$_" unless ref $$box_ref eq 'ARRAY'; $box_ref = \( $$box_ref->[$1] ); } else { s/\\(.)/$1/g if $sep; # remove escaping $$box_ref = {} unless defined $$box_ref; $$box_ref = { '' => $$box_ref } if ( !ref $$box_ref ); croak "HFH: param clash for $name=$_" unless ref $$box_ref eq 'HASH'; $box_ref = \( $$box_ref->{$_} ); } } if ( defined $$box_ref ) { croak "HFH: param clash for $name value $flat->{$name}" if ref $$box_ref ne 'HASH'; $box_ref = \( $$box_ref->{''} ); } $$box_ref = $flat->{$name}; } return $deep; } sub collapse_hash { my $self = shift; my $deep = shift; my $flat = {}; $self->_collapse_hash( $deep, $flat, () ); return $flat; } sub join_name { my ( $self, @array ) = @_; my $sep = substr( $self->separator, 0, 1 ); return join $sep, @array; } sub _collapse_hash { my ( $self, $deep, $flat, @segments ) = @_; if ( !ref $deep ) { my $name = $self->join_name(@segments); $flat->{$name} = $deep; } elsif ( ref $deep eq 'HASH' ) { for ( keys %$deep ) { # escape \ and separator chars (once only, at this level) my $name = $_; if ( defined( my $sep = $self->separator ) ) { $sep = "\Q$sep"; $name =~ s/([\\$sep])/\\$1/g; } $self->_collapse_hash( $deep->{$_}, $flat, @segments, $name ); } } elsif ( ref $deep eq 'ARRAY' ) { for ( 0 .. $#$deep ) { $self->_collapse_hash( $deep->[$_], $flat, @segments, $_ ) if defined $deep->[$_]; } } else { croak "Unknown reference type for ", $self->join_name(@segments), ":", ref $deep; } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Params - params handling =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Render/0000755000077000007700000000000012221042077021066 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Render/RepeatableJs.pm0000644000077000007700000001127112221042077023767 0ustar gshankgshankpackage HTML::FormHandler::Render::RepeatableJs; # ABSTRACT: role providing method to construct repeatable javascript use Moose::Role; use JSON ('encode_json'); sub render_repeatable_js { my $self = shift; return '' unless $self->has_for_js; my $for_js = $self->for_js; my %index; my %html; my %level; foreach my $key ( keys %$for_js ) { $index{$key} = $for_js->{$key}->{index}; $html{$key} = $for_js->{$key}->{html}; $level{$key} = $for_js->{$key}->{level}; } my $index_str = encode_json( \%index ); my $html_str = encode_json( \%html ); my $level_str = encode_json( \%level ); my $js = < \$(document).ready(function() { var rep_index = $index_str; var rep_html = $html_str; var rep_level = $level_str; \$('.add_element').click(function() { // get the repeatable id var data_rep_id = \$(this).attr('data-rep-id'); // create a regex out of index placeholder var level = rep_level[data_rep_id] var re = new RegExp('\{index-' + level + '\}',"g"); // replace the placeholder in the html with the index var index = rep_index[data_rep_id]; var html = rep_html[data_rep_id]; html = html.replace(re, index); // escape dots in element id var esc_rep_id = data_rep_id.replace(/[.]/g, '\\\\.'); // append new element in the 'controls' div of the repeatable var rep_controls = \$('#' + esc_rep_id + ' > .controls'); rep_controls.append(html); // increment index of repeatable fields index++; rep_index[data_rep_id] = index; }); \$(document).on('click', '.rm_element', function() { cont = confirm('Remove?'); if (cont) { var id = \$(this).attr('data-rep-elem-id'); var esc_id = id.replace(/[.]/g, '\\\\.'); var rm_elem = \$('#' + esc_id); rm_elem.remove(); } event.preventDefault(); }); }); EOS return $js; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Render::RepeatableJs - role providing method to construct repeatable javascript =head1 VERSION version 0.40050 =head1 SYNOPSIS Creates jQuery javascript to add and delete repeatable elements. Note: This is still EXPERIMENTAL. This is an EXAMPLE. Changes are very likely to occur. Javascript is not guaranteed to be best practice. It will not work on all rendered repeatables (requires wrapper with id). It is strongly suggested that you make your own role if you use it. Then you can modify it as needed. Or just write out the rep_ data to javascript variables, and write the function in javascript. This function uses a plain javascript confirmation dialog. You almost certainly want to do something else. This javascript depends on the Repeatable field having a 'controls' div class in order to position the new elements. Use the Bootstrap wrapper or the 'controls_div' tag on the Simple wrapper. A role to be used in a Form Class: package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Render::RepeatableJs'; ... =head2 DESCRIPTION This contains one method, 'render_repeatable_js'. It's designed to be used in a template, something like: [% WRAPPER "wrapper.tt" %] [% form.render_repeatable_js %]

Editing Object ....

[% form.render %] [% END -%] It will render javascript which can be used with the AddElement field, and setting the 'setup_for_js' flag in the Repeatable field to add the ability to dynamically add a new repeatable element in a form. Note: this code is provided as an example. You may need to write your own javascript function if your situation is different. Some of the extra information (level) in this function is in preparation for handling nested repeatables, but it's not supported yet. This function operates on HTML elements that have the id of the repeatable element. That requires that the wrapper have the repeatable instance ID (now rendered by default). If you don't have wrappers around your repeatable elements, this won't work. See L for an example of rendering an HTML element that can be used to provide the AddElement button. See that field for the requirements for the add HTML. See L for an example of rendering an HTML element that can be used to provide a 'remove' button. See that field for the requirements for the remove HTML. =head1 NAME HTML::FormHandler::Render::RepeatableJs =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Render/Simple.pm0000644000077000007700000002765512221042077022674 0ustar gshankgshankpackage HTML::FormHandler::Render::Simple; # ABSTRACT: simple rendering role use Moose::Role; requires( 'sorted_fields', 'field' ); use HTML::FormHandler::Render::Util ('process_attrs', 'ucc_widget'); our $VERSION = 0.01; sub render { my $self = shift; my $output = $self->render_start; $output .= $self->render_form_errors; foreach my $field ( $self->sorted_fields ) { $output .= $self->render_field($field); } $output .= $self->render_end; return $output; } sub render_form_errors { my $self = shift; return '' unless $self->has_form_errors; my $output = "\n
"; $output .= qq{\n$_} for $self->all_form_errors; $output .= "\n
"; return $output; } sub render_field { my ( $self, $field ) = @_; if ( ref( \$field ) eq 'SCALAR' ) { $field = $self->field($field); } die "must pass field to render_field" unless ( defined $field && $field->isa('HTML::FormHandler::Field') ); # widgets should be in camel case, since they are Perl package names my $widget = ucc_widget($field->widget); return '' if $widget eq 'no_render'; my $rendered_field; my $form_render = 'render_' . $widget; if ( $self->can($form_render) ) { $rendered_field = $self->$form_render($field); } elsif ( $field->can('render') ) { $rendered_field = $field->render; } else { die "No widget method found for '$widget' in H::F::Render::Simple"; } return $self->wrap_field( $field, $rendered_field ); } sub wrap_field { my ( $self, $field, $rendered_field ) = @_; return "\n$rendered_field" if $field->uwrapper eq 'none'; return "\n$rendered_field" if ! $field->do_wrapper; my $output = "\n"; my $wrapper_tag = $field->get_tag('wrapper_tag'); $wrapper_tag ||= $field->has_flag('is_repeatable') ? 'fieldset' : 'div'; my $attrs = process_attrs($field->wrapper_attributes); $output .= qq{<$wrapper_tag$attrs>}; if( $wrapper_tag eq 'fieldset' ) { $output .= '' . $field->loc_label . ''; } elsif ( ! $field->get_tag('label_none') && $field->do_label && length( $field->label ) > 0 ) { $output .= "\n" . $self->render_label($field); } $output .= "\n$rendered_field"; $output .= qq{\n$_} for $field->all_errors; $output .= "\n"; return "$output"; } sub render_text { my ( $self, $field ) = @_; my $output = 'id . '"'; $output .= ' size="' . $field->size . '"' if $field->size; $output .= ' maxlength="' . $field->maxlength . '"' if $field->maxlength; $output .= ' value="' . $field->html_filter($field->fif) . '"'; $output .= process_attrs($field->element_attributes); $output .= ' />'; return $output; } sub render_password { my ( $self, $field ) = @_; my $output = 'id . '"'; $output .= ' size="' . $field->size . '"' if $field->size; $output .= ' maxlength="' . $field->maxlength . '"' if $field->maxlength; $output .= ' value="' . $field->html_filter($field->fif) . '"'; $output .= process_attrs($field->element_attributes); $output .= ' />'; return $output; } sub render_hidden { my ( $self, $field ) = @_; my $output = 'id . '"'; $output .= ' value="' . $field->html_filter($field->fif) . '"'; $output .= process_attrs($field->element_attributes); $output .= ' />'; return $output; } sub render_select { my ( $self, $field ) = @_; my $multiple = $field->multiple; my $id = $field->id; my $output = ''; return $output; } sub render_checkbox { my ( $self, $field ) = @_; my $output = 'id . '"'; $output .= ' value="' . $field->html_filter($field->checkbox_value) . '"'; $output .= ' checked="checked"' if $field->fif eq $field->checkbox_value; $output .= process_attrs($field->element_attributes); $output .= ' />'; return $output; } sub render_radio_group { my ( $self, $field ) = @_; my $output = "
"; my $index = 0; foreach my $option ( @{ $field->options } ) { my $id = $field->id . ".$index"; $output .= qq{
'; $index++; } return $output; } sub render_textarea { my ( $self, $field ) = @_; my $fif = $field->fif || ''; my $id = $field->id; my $cols = $field->cols || 10; my $rows = $field->rows || 5; my $name = $field->html_name; my $output = qq(); return $output; } sub render_upload { my ( $self, $field ) = @_; my $output; $output = 'id . '"'; $output .= process_attrs($field->element_attributes); $output .= ' />'; return $output; } sub render_label { my ( $self, $field ) = @_; my $attrs = process_attrs( $field->label_attributes ); my $label = $field->html_filter($field->loc_label); $label .= $field->get_tag('label_after') if( $field->tag_exists('label_after') ); my $label_tag = $field->tag_exists('label_tag') ? $field->get_tag('label_tag') : 'label'; return qq{<$label_tag$attrs for="} . $field->id . qq{">$label}; } sub render_compound { my ( $self, $field ) = @_; my $output = ''; foreach my $subfield ( $field->sorted_fields ) { $output .= $self->render_field($subfield); } return $output; } sub render_submit { my ( $self, $field ) = @_; my $output = 'id . '"'; $output .= process_attrs($field->element_attributes); $output .= ' value="' . $field->html_filter($field->_localize($field->value)) . '" />'; return $output; } sub render_reset { my ( $self, $field ) = @_; my $output = 'id . '"'; $output .= process_attrs($field->element_attributes); $output .= ' value="' . $field->html_filter($field->value) . '" />'; return $output; } sub render_captcha { my ( $self, $field ) = @_; my $output .= ''; $output .= ''; return $output; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Render::Simple - simple rendering role =head1 VERSION version 0.40050 =head1 SYNOPSIS This is a Moose role that is an example of a simple rendering routine for L. It's here as an example of how to write a custom renderer in one package, if you prefer that to using the widgets. It won't always be updated with improvements by default, because it was becoming a lot of work to update the rendering in multiple places. For a 'MyApp::Form::Renderer' which you've created and modified, in your Form class: package MyApp::Form::Silly; use Moose; extends 'HTML::FormHandler::Model::DBIC'; with 'MyApp::Form::Renderers'; In a template: [% form.render %] The widgets are rendered with C<< $field->render >>; rendering routines from a class like this use C<< $form->render_field('field_name') >> to render individual fields: [% form.render_field( 'title' ) %] =head1 DESCRIPTION This role provides HTML output routines for the 'widget' types defined in the provided FormHandler fields. Each 'widget' name has a 'widget_$name' method here. These widget routines output strings with HTML suitable for displaying form fields. The widget for a particular field can be defined in the form. You can create additional widget routines in your form for custom widgets. The fill-in-form values ('fif') are cleaned with the 'render_filter' method of the base field class. You can change the filter to suit your own needs: see L =head2 render To render all the fields in a form in sorted order (using 'sorted_fields' method). =head2 render_start, render_end These use the methods in L now. If you want to customize them, copy them to your own package. Will render the beginning and ending
tags and fieldsets. Allows for easy splitting up of the form if you want to hand-render some of the fields. [% form.render_start %] [% form.render_field('title') %] [% form.render_field('some_field') %] [% form.render_end %] =head2 render_field Render a field passing in a field object or a field name $form->render_field( $field ) $form->render_field( 'title' ) =head2 render_text Output an HTML string for a text widget =head2 render_password Output an HTML string for a password widget =head2 render_hidden Output an HTML string for a hidden input widget =head2 render_select Output an HTML string for a 'select' widget, single or multiple =head2 render_checkbox Output an HTML string for a 'checkbox' widget =head2 render_radio_group Output an HTML string for a 'radio_group' selection widget. This widget should be for a field that inherits from 'Select', since it requires the existence of an 'options' array. =head2 render_textarea Output an HTML string for a textarea widget =head2 render_compound Renders field with 'compound' widget =head2 render_submit Renders field with 'submit' widget =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Render/Table.pm0000644000077000007700000000466112221042077022462 0ustar gshankgshankpackage HTML::FormHandler::Render::Table; # ABSTRACT: render a form with a table layout use Moose::Role; with 'HTML::FormHandler::Render::Simple' => { -excludes => [ 'render', 'wrap_field', 'render_end', 'render_start' ] }; use HTML::FormHandler::Render::Util ('process_attrs'); sub render { my $self = shift; my $output = $self->render_start; $output .= $self->render_form_errors; foreach my $field ( $self->sorted_fields ) { $output .= $self->render_field($field); } $output .= $self->render_end; return $output; } sub render_start { my $self = shift; my $attrs = process_attrs($self->attributes); return qq{}; } sub render_form_errors { my $self = shift; return '' unless $self->has_form_errors; my $output = "\n"; return $output; } sub render_end { my $self = shift; my $output .= "
"; $output .= qq{\n$_} for $self->all_form_errors; $output .= "\n
\n"; $output .= "\n"; return $output; } sub wrap_field { my ( $self, $field, $rendered_field ) = @_; my $attrs = process_attrs($field->wrapper_attributes); my $output = qq{\n}; my $l_type = $field->widget eq 'Compound' ? 'legend' : 'label'; if ( $l_type eq 'label' ) { $output .= '' . $self->render_label($field) . ''; } elsif ( $l_type eq 'legend' ) { $output .= '' . $self->render_label($field) . ''; } if ( $l_type ne 'legend' ) { $output .= ''; } $output .= $rendered_field; $output .= qq{\n$_} for $field->all_errors; if ( $l_type ne 'legend' ) { $output .= "\n"; } return $output; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Render::Table - render a form with a table layout =head1 VERSION version 0.40050 =head1 SYNOPSIS Include this role in a form: package MyApp::Form::User; use Moose; with 'HTML::FormHandler::Render::Table'; Use in a template: [% form.render %] =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Render/Util.pm0000644000077000007700000000456412221042077022352 0ustar gshankgshankpackage HTML::FormHandler::Render::Util; # ABSTRACT: rendering utility use Sub::Exporter; Sub::Exporter::setup_exporter({ exports => [ 'process_attrs', 'cc_widget', 'ucc_widget' ] } ); # this is a function for processing various attribute flavors sub process_attrs { my ($attrs) = @_; my @use_attrs; my $javascript = delete $attrs->{javascript} || ''; for my $attr( sort keys %$attrs ) { my $value = ''; if( defined $attrs->{$attr} ) { if( ref $attrs->{$attr} eq 'ARRAY' ) { # we don't want class="" if no classes specified next unless scalar @{$attrs->{$attr}}; $value = join (' ', @{$attrs->{$attr}} ); } else { $value = $attrs->{$attr}; } } push @use_attrs, sprintf( '%s="%s"', $attr, $value ); } my $output = join( ' ', @use_attrs ); $output = " $output" if length $output; $output .= " $javascript" if $javascript; return $output; } sub cc_widget { my $widget = shift; return '' unless $widget; if($widget eq lc $widget) { $widget =~ s/^(\w{1})/\u$1/g; $widget =~ s/_(\w{1})/\u$1/g; } return $widget; } sub ucc_widget { my $widget = shift; if($widget ne lc $widget) { $widget =~ s/::/_/g; $widget = ucfirst($widget); my @parts = $widget =~ /([A-Z][a-z]*)/g; $widget = join('_', @parts); $widget = lc($widget); } return $widget; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Render::Util - rendering utility =head1 VERSION version 0.40050 =head1 SYNOPSIS The 'process_attrs' takes a hashref and creates an attribute string for constructing HTML. my $attrs => { some_attr => 1, placeholder => 'Enter email...", class => ['help', 'special'], }; my $string = process_attrs($attrs); ...will produce: ' some_attr="1" placeholder="Enter email..." class="help special"' If an arrayref is empty, it will be skipped. For a hash key of 'javascript' only the value will be appended (without '$key=""'); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Render/WithTT.pm0000644000077000007700000000650012221042077022610 0ustar gshankgshankpackage HTML::FormHandler::Render::WithTT; # ABSTRACT: tt rendering use Moose::Role; use File::ShareDir; use Template; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); has 'tt_include_path' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef', lazy => 1, builder => 'build_tt_include_path', handles => { add_tt_include_path => 'push', } ); sub build_tt_include_path {[]} has 'tt_config' => ( traits => ['Hash'], is => 'rw', lazy => 1, builder => 'build_tt_config', ); sub build_tt_config { my $self = shift; return { INCLUDE_PATH => [ @{ $self->tt_include_path }, File::ShareDir::dist_dir('HTML-FormHandler') . '/templates/' ] }; } # either file name string or string ref? has 'tt_template' => ( is => 'rw', isa => 'Str', lazy => 1, builder => 'build_tt_template' ); sub build_tt_template { 'form/form.tt' } has 'tt_engine' => ( is => 'rw', isa => 'Template', lazy => 1, builder => 'build_tt_engine' ); sub build_tt_engine { my $self = shift; my $tt_engine = Template->new( $self->tt_config ); return $tt_engine; } has 'tt_vars' => ( is => 'rw', traits => ['Hash'], builder => 'build_tt_vars'); sub build_tt_vars {{}} has 'default_tt_vars' => ( is => 'ro', isa => 'HashRef', lazy => 1, builder => 'build_default_tt_vars' ); sub build_default_tt_vars { my $self = shift; return { form => $self->form, process_attrs => \&process_attrs }; } has 'tt_default_options' => ( traits => ['Hash'], is => 'rw', isa => 'HashRef', lazy => 1, builder => 'build_tt_default_options', ); sub build_tt_default_options {{}} sub tt_render { my $self = shift; my $output; my $vars = { %{$self->default_tt_vars}, %{$self->tt_vars} }; $self->tt_engine->process( $self->tt_template, $vars, \$output ); if( my $exception = $self->tt_engine->{SERVICE}->{_ERROR} ) { die $exception->[0] . " " . $exception->[1] . ". So far => " . ${$exception->[2]} . "\n"; } return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Render::WithTT - tt rendering =head1 VERSION version 0.40050 =head1 SYNOPSIS A rendering role for HTML::FormHandler that allows rendering using Template::Toolkit package MyApp::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Render::WithTT'; sub build_tt_template { 'user_form.tt' } sub build_tt_include_path { ['root/templates'] } ....< define form >.... my $form = MyApp::Form->new( $form->tt_render; If you want to render with TT, you don't need this role. Just use one of the TT form templates provided, form.tt or form_in_one.tt. If you use this role to render, you are using two different TT engines, with different sets of variables, etc, which doesn't make much sense. This is mainly useful as a testing aid and an example of using the sample templates. =head1 DESCRIPTION Uses 'tt_render' instead of 'render' to allow using both TT templates and the built-in rendering. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Result/0000755000077000007700000000000012221042077021125 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Result/Role.pm0000644000077000007700000000671712221042077022377 0ustar gshankgshankpackage HTML::FormHandler::Result::Role; # ABSTRACT: role with common code for form & field results use Moose::Role; has 'name' => ( isa => 'Str', is => 'rw', required => 1 ); # do we need 'accessor' ? has 'parent' => ( is => 'rw', weak_ref => 1 ); has 'input' => ( is => 'ro', clearer => '_clear_input', writer => '_set_input', predicate => 'has_input', ); has '_results' => ( traits => ['Array'], isa => 'ArrayRef[HTML::FormHandler::Field::Result]', is => 'rw', default => sub { [] }, handles => { results => 'elements', add_result => 'push', num_results => 'count', has_results => 'count', clear_results => 'clear', find_result_index => 'first_index', set_result_at_index => 'set', _pop_result => 'pop', } ); has 'error_results' => ( traits => ['Array'], isa => 'ArrayRef', # for HFH::Result and HFH::Field::Result is => 'rw', default => sub { [] }, handles => { has_error_results => 'count', num_error_results => 'count', clear_error_results => 'clear', add_error_result => 'push', all_error_results => 'elements', } ); has 'errors' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef[Str]', default => sub { [] }, handles => { all_errors => 'elements', _push_errors => 'push', num_errors => 'count', has_errors => 'count', clear_errors => 'clear', } ); has 'warnings' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef[Str]', default => sub { [] }, handles => { all_warnings => 'elements', add_warning => 'push', num_warnings => 'count', has_warnings => 'count', clear_warnings => 'clear', } ); sub validated { !$_[0]->has_error_results && $_[0]->has_input } sub is_valid { shift->validated } # this ought to be named 'result' for consistency, # but the result objects are named 'result'. # also providing 'field' method for compatibility sub get_result { my ( $self, $name, $die ) = @_; my $index; # if this is a full_name for a compound field # walk through the fields to get to it if ( $name =~ /\./ ) { my @names = split /\./, $name; my $result = $self; foreach my $rname (@names) { $result = $result->get_result($rname); return unless $result } return $result; } else # not a compound name { for my $result ( $self->results ) { return $result if ( $result->name eq $name ); } } return unless $die; die "Field '$name' not found in '$self'"; } sub field { shift->get_result(@_) } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Result::Role - role with common code for form & field results =head1 VERSION version 0.40050 =head1 SYNOPSIS Role to hold common result attributes for L and L. =head1 NAME HTML::FormHandler::Result::Role - common code for form & field results =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Result.pm0000644000077000007700000000666512221042077021500 0ustar gshankgshankpackage HTML::FormHandler::Result; # ABSTRACT: form result object use Moose; # following is to allow the form to return an empty # hashref when value is undefined, without messing # with the way 'value' works for fields with 'HTML::FormHandler::Result::Role'; with 'HTML::FormHandler::Traits'; has 'form' => ( isa => 'HTML::FormHandler', is => 'ro', weak_ref => 1, # handles => ['render' ] ); has '_value' => ( is => 'ro', writer => '_set_value', reader => '_get_value', clearer => '_clear_value', predicate => 'has_value', ); sub value { shift->_get_value || {} } has 'form_errors' => ( traits => ['Array'], is => 'rw', isa => 'ArrayRef[Str]', default => sub { [] }, handles => { all_form_errors => 'elements', push_form_errors => 'push', num_form_errors => 'count', has_form_errors => 'count', clear_form_errors => 'clear', } ); sub validated { !$_[0]->has_error_results && $_[0]->has_input && !$_[0]->has_form_errors } has 'ran_validation' => ( is => 'rw', isa => 'Bool', default => 0 ); sub fif { my $self = shift; $self->form->fields_fif($self); } sub peek { my $self = shift; my $string = "Form Result " . $self->name . "\n"; my $indent = ' '; foreach my $res ( $self->results ) { $string .= $res->peek( $indent ); } return $string; } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Result - form result object =head1 VERSION version 0.40050 =head1 SYNOPSIS This is the Result object that maps to the Form. my $result = $self->form->run( $params ); my $result2 = $self->form->run( $other_params ); my $value = $result->field('title')->value; my $fif = $result->fif; my $field_fid = $result->field('title')->fif; =head2 DESCRIPTION Although not experimental, the 'results' have not been exercised as much as the other parts of the code. If there is missing functionality or things that don't work, please ask or report bugs. The original FormHandler 'process' method, when used with persistent forms, leaves behind state data for a particular execution of 'process'. This is not optimal or clean from an architectural point of view. The intention with the 'result' object is to separate dynamic data from static. The 'form' object is treated as a kind of result factory, which will spit out results and leave the form in a consistent state. In the current state of implementation, the result object can be used to render a form: $result->render; However there are still open questions about how much of the form/field should be forwarded to the result. At this point, the number of forwarded methods is minimal. Mechanisms to make this more customizable are being considered. Dynamic select lists are not supported yet. Static select lists (that are the same for every form execution) should work fine, but lists that are different depending on some field value will not. Most of this object is implemented in L, because it is shared with L. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Test.pm0000644000077000007700000000314712221042077021131 0ustar gshankgshankpackage HTML::FormHandler::Test; # ABSTRACT: provides is_html method used in tests use strict; use warnings; use base 'Test::Builder::Module'; use HTML::TreeBuilder; use Test::Builder::Module; our @EXPORT = ('is_html'); use Encode ('decode'); sub is_html { my ( $got, $expected, $message ) = @_; my $t1 = HTML::TreeBuilder->new; my $t2 = HTML::TreeBuilder->new; $got = decode('utf8', $got); $expected = decode('utf8', $expected); $t1->parse($got); $t1->eof; $t2->parse($expected); $t2->eof; my $out1 = $t1->as_XML; my $out2 = $t2->as_XML; $t1->delete; $t2->delete; my $tb = HTML::FormHandler::Test->builder; return $tb->is_eq($out1, $out2, $message); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Test - provides is_html method used in tests =head1 VERSION version 0.40050 =head1 SYNOPSIS Simple 'is_html' method for testing form rendering against an expected value without having to fuss with exactly matching newlines and spaces. Uses L, which uses L. See numerous examples in the 't/render' directory. use Test::More; use HTML::FormHandler::Test; use_ok('MyApp::Form::Basic'); my $form = MyApp::Form::Basic->new; $form->process; my $expected = '
'; is_html( $form->render, $expected, 'form renders ok' ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/TraitFor/0000755000077000007700000000000012221042077021401 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/TraitFor/Captcha.pm0000644000077000007700000000443412221042077023307 0ustar gshankgshankpackage HTML::FormHandler::TraitFor::Captcha; # ABSTRACT: generate and validate captchas use HTML::FormHandler::Moose::Role; use GD::SecurityImage; use HTTP::Date; requires('ctx'); has_field 'captcha' => ( type => 'Captcha', label => 'Verification' ); sub get_captcha { my $self = shift; return unless $self->ctx; my $captcha; $captcha = $self->ctx->session->{captcha}; return $captcha; } sub set_captcha { my ( $self, $captcha ) = @_; return unless $self->ctx; $self->ctx->session( captcha => $captcha ); } sub captcha_image_url { return '/captcha/image'; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::TraitFor::Captcha - generate and validate captchas =head1 VERSION version 0.40050 =head1 SYNOPSIS A role to use in a form to implement a captcha field. package MyApp::Form; use HTML::FormHandler::Moose; with 'HTML::FormHandler::TraitFor::Captcha'; or my $form = MyApp::Form->new( traits => ['HTML::FormHandler::TraitFor::Captcha'], ctx => $c ); Needs a context object set in the form's 'ctx' attribute which has a session hashref in which to store a 'captcha' hashref, such as is provided by Catalyst session plugin. =head1 METHODS =head2 get_captcha Get a captcha stored in C<< $form->ctx->{session} >> =head1 set_captcha Set a captcha in C<< $self->ctx->{session} >> =head2 captcha_image_url Default is '/captcha/image'. Override in a form to change. sub captcha_image_url { '/my/image/url/' } Example of a Catalyst action to handle the image: sub image : Local { my ( $self, $c ) = @_; my $captcha = $c->session->{captcha}; $c->response->body($captcha->{image}); $c->response->content_type('image/'. $captcha->{type}); $c->res->headers->expires( time() ); $c->res->headers->header( 'Last-Modified' => HTTP::Date::time2str ); $c->res->headers->header( 'Pragma' => 'no-cache' ); $c->res->headers->header( 'Cache-Control' => 'no-cache' ); } =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/TraitFor/I18N.pm0000644000077000007700000000621312221042077022420 0ustar gshankgshankpackage HTML::FormHandler::TraitFor::I18N; # ABSTRACT: localization use HTML::FormHandler::I18N; use Moose::Role; use Moose::Util::TypeConstraints; has 'language_handle' => ( isa => duck_type( [ qw(maketext) ] ), is => 'rw', lazy_build => 1, required => 1, ); sub _build_language_handle { my ($self) = @_; if (!$self->isa('HTML::FormHandler') && $self->has_form) { return $self->form->language_handle(); } my $lh; if ( $ENV{LANGUAGE_HANDLE} ) { if ( blessed $ENV{LANGUAGE_HANDLE} ) { $lh = $ENV{LANGUAGE_HANDLE}; } else { $lh = HTML::FormHandler::I18N->get_handle( $ENV{LANGUAGE_HANDLE} ); } } else { $lh = HTML::FormHandler::I18N->get_handle; } return $lh; } sub _localize { my ($self, @message) = @_; my $message = $self->language_handle->maketext(@message); return $message; } no Moose::Role; no Moose::Util::TypeConstraints; 1; __END__ =pod =head1 NAME HTML::FormHandler::TraitFor::I18N - localization =head1 VERSION version 0.40050 =head3 language_handle, _build_language_handle Holds a Locale::Maketext (or other duck_type class with a 'maketext' method) language handle. The language handle is used to localize the error messages in the field's 'add_error' method. It's also used in various places in rendering to localize labels and button values, etc. The builder for this attribute gets the Locale::Maketext language handle from the environment variable $ENV{LANGUAGE_HANDLE}: $ENV{LANGUAGE_HANDLE} = 'en_en'; ...or creates a default language handler using L. (Note that earlier versions required an actual object reference in ENV, which is a bad practice and no longer supported.) You can pass in an existing L subclass instance or create one in a builder. In a form class: sub _build_language_handle { MyApp::I18N::abc_de->new } Passed into new or process: my $lh = MyApp::I18N::abc_de->new; my $form = MyApp::Form->new( language_handle => $lh ); If you do not set the language_handle, then L and/or L may guess, with unexpected results. You can use non-Locale::Maketext language handles, such as L. There's an example of building a L language handle in t/xt/locale_data_localize.t in the distribution. If you don't want a particular error message to go through localization, you can use 'push_errors' and 'push_form_errors' instead of 'add_error' and 'add_form_errors'. Example of getting the language handle from the Catalyst context (where the Catalyst context is passed in with 'ctx'): has '+language_handle' => ( builder => 'get_language_handle_from_ctx' ); sub get_language_handle_from_ctx { my $self = shift; return MyApp::I18N->get_handle( @{ $self->ctx->languages } ); } =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/TraitFor/Types.pm0000644000077000007700000000152612221042077023047 0ustar gshankgshankpackage HTML::FormHandler::TraitFor::Types; # ABSTRACT: types used internally in FormHandler use Moose::Role; use Moose::Util::TypeConstraints; subtype 'HFH::ArrayRefStr' => as 'ArrayRef[Str]'; coerce 'HFH::ArrayRefStr' => from 'Str' => via { if( length $_ ) { return [$_] }; return []; }; coerce 'HFH::ArrayRefStr' => from 'Undef' => via { return []; }; no Moose::Util::TypeConstraints; 1; __END__ =pod =head1 NAME HTML::FormHandler::TraitFor::Types - types used internally in FormHandler =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Traits.pm0000644000077000007700000000634112221042077021457 0ustar gshankgshankpackage HTML::FormHandler::Traits; # ABSTRACT: customized replacement for MooseX::Traits use Moose::Role; use Class::Load qw/ load_class /; use namespace::autoclean; has '_trait_namespace' => ( init_arg => undef, isa => 'Str', is => 'bare', ); my %COMPOSED_CLASS_INDEX; # { # 'HTML::FormHandler::Field::Text' => { 'Role|Another::Role' => 1 }, # 'HTML::FormHandler::Field::Select' => { 'My::Role' => 2, # 'My::Role|Your::Role' => 3 }, # } my %COMPOSED_META; my $composed_index = 0; sub resolve_traits { my ( $class, @traits ) = @_; return map { my $orig = $_; if ( !ref $orig ) { my $transformed = transform_trait( $class, $orig ); load_class($transformed); $transformed; } else { $orig; } } @traits; } sub transform_trait { my ( $class, $name ) = @_; return $1 if $name =~ /^[+](.+)$/; my $namespace = $class->meta->find_attribute_by_name('_trait_namespace'); my $base; if ( $namespace->has_default ) { $base = $namespace->default; if ( ref $base eq 'CODE' ) { $base = $base->(); } } return $name unless $base; return join '::', $base, $name; } sub composed_class_name { my (%options) = @_; my $class = $options{class}; my $cache_key = _anon_cache_key( $options{roles} ); my $index = $COMPOSED_CLASS_INDEX{$class}{$cache_key}; if ( defined $index ) { return "${class}::$index"; } $index = ++$composed_index; $COMPOSED_CLASS_INDEX{$class}{$cache_key} = $index; return "${class}::$index"; } sub _anon_cache_key { # Makes something like Role|Role::1 return join( '|', @{ $_[0] || [] } ); } sub with_traits { my ( $class, @traits ) = @_; @traits = resolve_traits( $class, @traits ); return $class->meta unless ( scalar @traits ); my $class_name = $class->meta->name; my $new_class_name = composed_class_name( class => $class_name, roles => \@traits, ); my $meta; if ( $meta = $COMPOSED_META{$new_class_name} ) { return $meta->name; } else { $meta = $class->meta->create( $new_class_name, superclasses => [$class_name], roles => \@traits, ); $COMPOSED_META{$new_class_name} = $meta; return $meta->name; } } sub new_with_traits { my ( $class, %args ) = @_; my $traits = delete $args{traits} || []; my $new_class = $class->with_traits(@$traits); my $constructor = $new_class->meta->constructor_name; return $new_class->$constructor(%args); } no Moose::Role; 1; __END__ =pod =head1 NAME HTML::FormHandler::Traits - customized replacement for MooseX::Traits =head1 VERSION version 0.40050 =head1 SYNOPSIS Use to get a new composed class with traits: my $class = My::Form->with_traits( 'My::Trait', 'Another::Trait' ); my $form = $class->new; =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Types.pm0000644000077000007700000001567412221042077021326 0ustar gshankgshankpackage HTML::FormHandler::Types; # ABSTRACT: Moose type constraints use strict; use warnings; our $VERSION = '0.01'; use MooseX::Types -declare => [ 'PositiveNum', 'PositiveInt', 'NegativeNum', 'NegativeInt', 'SingleDigit', 'SimpleStr', 'NonEmptySimpleStr', 'Password', 'StrongPassword', 'NonEmptyStr', 'Email', 'State', 'Zip', 'IPAddress', 'NoSpaces', 'WordChars', 'NotAllDigits', 'Printable', 'PrintableAndNewline', 'SingleWord', 'Collapse', 'Upper', 'Lower', 'Trim', ]; our $class_messages = { PositiveNum => "Must be a positive number", PositiveInt => "Must be a positive integer", NegativeNum => "Must be a negative number", NegativeInt => "Must be a negative integer", SingleDigit => "Must be a single digit", SimpleStr => 'Must be a single line of no more than 255 chars', NonEmptySimpleStr => "Must be a non-empty single line of no more than 255 chars", Password => "Must be between 4 and 255 chars", StrongPassword =>"Must be between 8 and 255 chars, and contain a non-alpha char", NonEmptyStr => "Must not be empty", State => "Not a valid state", Email => "Email is not valid", Zip => "Zip is not valid", IPAddress => "Not a valid IP address", NoSpaces =>'Must not contain spaces', WordChars => 'Must be made up of letters, digits, and underscores', NotAllDigits => 'Must not be all digits', Printable => 'Field contains non-printable characters', PrintableAndNewline => 'Field contains non-printable characters', SingleWord => 'Field must contain a single word', }; use MooseX::Types::Moose ( 'Str', 'Num', 'Int' ); subtype PositiveNum, as Num, where { $_ >= 0 }, message { "Must be a positive number" }; subtype PositiveInt, as Int, where { $_ >= 0 }, message { "Must be a positive integer" }; subtype NegativeNum, as Num, where { $_ <= 0 }, message { "Must be a negative number" }; subtype NegativeInt, as Int, where { $_ <= 0 }, message { "Must be a negative integer" }; subtype SingleDigit, as PositiveInt, where { $_ <= 9 }, message { "Must be a single digit" }; subtype SimpleStr, as Str, where { ( length($_) <= 255 ) && ( $_ !~ m/\n/ ) }, message { $class_messages->{SimpleStr} }; subtype NonEmptySimpleStr, as SimpleStr, where { length($_) > 0 }, message { $class_messages->{NonEmptySimpleStr} }; subtype Password, as NonEmptySimpleStr, where { length($_) >= 4 && length($_) <= 255 }, message { $class_messages->{Password} }; subtype StrongPassword, as Password, where { ( length($_) >= 8 ) && length($_) <= 255 && (m/[^a-zA-Z]/) }, message { $class_messages->{StrongPassword} }; subtype NonEmptyStr, as Str, where { length($_) > 0 }, message { $class_messages->{NonEmptyStr} }; subtype State, as Str, where { my $value = $_; my $state = <{State} }; subtype Email, as Str, where { my $value = shift; require Email::Valid; my $valid; return ( $valid = Email::Valid->address($value) ) && ( $valid eq $value ); }, message { $class_messages->{Email} }; subtype Zip, as Str, where { /^(\s*\d{5}(?:[-]\d{4})?\s*)$/ }, message { $class_messages->{Zip} }; subtype IPAddress, as Str, where { /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ }, message { $class_messages->{IPAddress} }; subtype NoSpaces, as Str, where { ! /\s/ }, message { $class_messages->{NoSpaces} }; subtype WordChars, as Str, where { ! /\W/ }, message { $class_messages->{WordChars} }; subtype NotAllDigits, as Str, where { ! /^\d+$/ }, message { $class_messages->{NotAllDigits} }; subtype Printable, as Str, where { /^\p{IsPrint}*\z/ }, message { $class_messages->{Printable} }; subtype PrintableAndNewline, as Str, where { /^[\p{IsPrint}\n]*\z/ }, message { $class_messages->{PrintableAndNewline} }; subtype SingleWord, as Str, where { /^\w*\z/ }, message { $class_messages->{SingleWord} }; subtype Collapse, as Str, where{ ! /\s{2,}/ }; coerce Collapse, from Str, via { s/\s+/ /g; return $_; }; subtype Lower, as Str, where { ! /[[:upper:]]/ }; coerce Lower, from Str, via { lc }; subtype Upper, as Str, where { ! /[[:lower:]]/ }; coerce Upper, from Str, via { uc }; subtype Trim, as Str, where { ! /^\s+/ && ! /\s+$/ }; coerce Trim, from Str, via { s/^\s+// && s/\s+$//; return $_; }; 1; __END__ =pod =head1 NAME HTML::FormHandler::Types - Moose type constraints =head1 VERSION version 0.40050 =head1 SYNOPSIS These types are provided by MooseX::Types. These types must not be quoted when they are used: has 'posint' => ( is => 'rw', isa => PositiveInt); has_field 'email' => ( apply => [ Email ] ); Types declared using Moose::Util::TypeConstraints, on the other hand, must be quoted: has_field 'text_both' => ( apply => [ PositiveInt, 'GreaterThan10' ] ); To import these types into your forms, you must either specify (':all') or list the types you want to use: use HTML::FormHandler::Types (':all'); or: use HTML::FormHandler::Types ('Email', 'PositiveInt'); =head1 DESCRIPTION It would be possible to import the MooseX types (Common, etc), but for now we'll just re-implement them here in order to be able to change the messages and keep control of what types we provide. From MooseX::Types::Common: 'PositiveNum', 'PositiveInt', 'NegativeNum', 'NegativeInt', 'SingleDigit', 'SimpleStr', 'NonEmptySimpleStr', 'Password', 'StrongPassword', 'NonEmptyStr', =head1 Type Constraints These types check the value and issue an error message. =over =item Email Uses Email::Valid =item State Checks that the state is in a list of two uppercase letters. =item Zip =item IPAddress Must be a valid IPv4 address. =item NoSpaces No spaces in string allowed. =item WordChars Must be made up of letters, digits, and underscores. =item NotAllDigits Might be useful for passwords. =item Printable Must not contain non-printable characters. =item SingleWord Contains a single word. =back =head2 Type Coercions These types will transform the value without an error message; =over =item Collapse Replaces multiple spaces with a single space =item Upper Makes the string all upper case =item Lower Makes the string all lower case =item Trim Trims the string of starting and ending spaces =back =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Validate.pm0000644000077000007700000002407612221042077021747 0ustar gshankgshankpackage HTML::FormHandler::Validate; # ABSTRACT: validation role (internal) use Moose::Role; use Carp; has 'required' => ( isa => 'Bool', is => 'rw', default => '0' ); has 'required_when' => ( is => 'rw', isa => 'HashRef', predicate => 'has_required_when' ); has 'required_message' => ( isa => 'ArrayRef|Str', is => 'rw', ); has 'unique' => ( isa => 'Bool', is => 'rw', predicate => 'has_unique' ); has 'unique_message' => ( isa => 'Str', is => 'rw' ); has 'range_start' => ( isa => 'Int|Undef', is => 'rw' ); has 'range_end' => ( isa => 'Int|Undef', is => 'rw' ); sub test_ranges { my $field = shift; return 1 if $field->can('options') || $field->has_errors; my $value = $field->value; return 1 unless defined $value; my $low = $field->range_start; my $high = $field->range_end; if ( defined $low && defined $high ) { return $value >= $low && $value <= $high ? 1 : $field->add_error( $field->get_message('range_incorrect'), $low, $high ); } if ( defined $low ) { return $value >= $low ? 1 : $field->add_error( $field->get_message('range_too_low'), $low ); } if ( defined $high ) { return $value <= $high ? 1 : $field->add_error( $field->get_message('range_too_high'), $high ); } return 1; } sub validate_field { my $field = shift; return unless $field->has_result; $field->clear_errors; # this is only here for testing convenience # if the 'fields_for_input_without_param' flag is set, and the field doesn't have input, # copy the value to the input. if ( !$field->has_input && $field->form && $field->form->use_fields_for_input_without_param ) { $field->result->_set_input($field->value); } # handle required and required_when processing, and transfer input to value my $continue_validation = 1; if ( ( $field->required || ( $field->has_required_when && $field->match_when($field->required_when) ) ) && ( !$field->has_input || !$field->input_defined ) ) { $field->missing(1); $field->add_error( $field->get_message('required'), $field->loc_label ); if( $field->has_input ) { $field->not_nullable ? $field->_set_value($field->input) : $field->_set_value(undef); } $continue_validation = 0; } elsif ( $field->DOES('HTML::FormHandler::Field::Repeatable') ) { } elsif ( !$field->has_input ) { $continue_validation = 0; } elsif ( !$field->input_defined ) { if ( $field->not_nullable ) { $field->_set_value($field->input); # handles the case where a compound field value needs to have empty subfields $continue_validation = 0 unless $field->has_flag('is_compound'); } elsif ( $field->no_value_if_empty || $field->has_flag('is_contains') ) { $continue_validation = 0; } else { $field->_set_value(undef); $continue_validation = 0; } } return if ( !$continue_validation && !$field->validate_when_empty ); # do building of node if ( $field->DOES('HTML::FormHandler::Fields') ) { $field->_fields_validate; } else { my $input = $field->input; $input = $field->inflate( $input ) if $field->has_inflate_method; $field->_set_value( $input ); } $field->_inner_validate_field(); $field->_apply_actions; $field->validate( $field->value ); $field->test_ranges; $field->_validate($field) # form field validation method if ( $field->has_value && defined $field->value ); # validation done, if everything validated, do deflate_value for # final $form->value if( $field->has_deflate_value_method && !$field->has_errors ) { $field->_set_value( $field->deflate_value($field->value) ); } return !$field->has_errors; } sub _inner_validate_field { } sub validate { 1 } has 'actions' => ( traits => ['Array'], isa => 'ArrayRef', is => 'rw', default => sub { [] }, handles => { add_action => 'push', num_actions =>'count', has_actions => 'count', clear_actions => 'clear', } ); sub _build_apply_list { my $self = shift; my @apply_list; foreach my $sc ( reverse $self->meta->linearized_isa ) { my $meta = $sc->meta; if ( $meta->can('calculate_all_roles') ) { foreach my $role ( $meta->calculate_all_roles ) { if ( $role->can('apply_list') && $role->has_apply_list ) { foreach my $apply_def ( @{ $role->apply_list } ) { my %new_apply = %{$apply_def}; # copy hashref push @apply_list, \%new_apply; } } } } if ( $meta->can('apply_list') && $meta->has_apply_list ) { foreach my $apply_def ( @{ $meta->apply_list } ) { my %new_apply = %{$apply_def}; # copy hashref push @apply_list, \%new_apply; } } } $self->add_action(@apply_list); } sub _apply_actions { my $self = shift; my $error_message; local $SIG{__WARN__} = sub { my $error = shift; $error_message = $error; return 1; }; for my $action ( @{ $self->actions || [] } ) { $error_message = undef; # the first time through value == input my $value = $self->value; my $new_value = $value; # Moose constraints if ( !ref $action || ref $action eq 'MooseX::Types::TypeDecorator' ) { $action = { type => $action }; } if ( my $when = $action->{when} ) { next unless $self->match_when($when); } if ( exists $action->{type} ) { my $tobj; if ( ref $action->{type} eq 'MooseX::Types::TypeDecorator' ) { $tobj = $action->{type}; } else { my $type = $action->{type}; $tobj = Moose::Util::TypeConstraints::find_type_constraint($type) or die "Cannot find type constraint $type"; } if ( $tobj->has_coercion && $tobj->validate($value) ) { eval { $new_value = $tobj->coerce($value) }; if ($@) { if ( $tobj->has_message ) { $error_message = $tobj->message->($value); } else { $error_message = $@; } } else { $self->_set_value($new_value); } } $error_message ||= $tobj->validate($new_value); } # now maybe: http://search.cpan.org/~rgarcia/perl-5.10.0/pod/perlsyn.pod#Smart_matching_in_detail # actions in a hashref elsif ( ref $action->{check} eq 'CODE' ) { if ( !$action->{check}->($value, $self) ) { $error_message = $self->get_message('wrong_value'); } } elsif ( ref $action->{check} eq 'Regexp' ) { if ( $value !~ $action->{check} ) { $error_message = [$self->get_message('no_match'), $value]; } } elsif ( ref $action->{check} eq 'ARRAY' ) { if ( !grep { $value eq $_ } @{ $action->{check} } ) { $error_message = [$self->get_message('not_allowed'), $value]; } } elsif ( ref $action->{transform} eq 'CODE' ) { $new_value = eval { no warnings 'all'; $action->{transform}->($value, $self); }; if ($@) { $error_message = $@ || $self->get_message('error_occurred'); } else { $self->_set_value($new_value); } } if ( defined $error_message ) { my @message = ref $error_message eq 'ARRAY' ? @$error_message : ($error_message); if ( defined $action->{message} ) { my $act_msg = $action->{message}; if ( ref $act_msg eq 'CODE' ) { $act_msg = $act_msg->($value, $self, $error_message); } if ( ref $act_msg eq 'ARRAY' ) { @message = @{$act_msg}; } elsif ( ref \$act_msg eq 'SCALAR' ) { @message = ($act_msg); } } $self->add_error(@message); } } } sub match_when { my ( $self, $when ) = @_; my $matched = 0; foreach my $key ( keys %$when ) { my $check_against = $when->{$key}; my $from_form = ( $key =~ /^\+/ ); $key =~ s/^\+//; my $field = $from_form ? $self->form->field($key) : $self->parent->subfield( $key ); unless ( $field ) { warn "field '$key' not found processing 'when' for '" . $self->full_name . "'"; next; } my $field_fif = defined $field->fif ? $field->fif : ''; if ( ref $check_against eq 'CODE' ) { $matched++ if $check_against->($field_fif, $self); } elsif ( ref $check_against eq 'ARRAY' ) { foreach my $value ( @$check_against ) { $matched++ if ( $value eq $field_fif ); } } elsif ( $check_against eq $field_fif ) { $matched++; } else { $matched = 0; last; } } return $matched; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Validate - validation role (internal) =head1 VERSION version 0.40050 =head1 SYNOPSIS This is a role that contains validation and transformation code used by L. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/0000755000077000007700000000000012221042077021072 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/ApplyRole.pm0000644000077000007700000000360012221042077023336 0ustar gshankgshankpackage HTML::FormHandler::Widget::ApplyRole; # ABSTRACT: role to apply widgets use Moose::Role; use File::Spec; use Class::MOP; use Try::Tiny; use Class::Load qw/ load_optional_class /; use namespace::autoclean; our $ERROR; sub apply_widget_role { my ( $self, $target, $widget_name, $dir ) = @_; my $render_role = $self->get_widget_role( $widget_name, $dir ); $render_role->meta->apply($target) if $render_role; } sub get_widget_role { my ( $self, $widget_name, $dir ) = @_; my $widget_class = $self->widget_class($widget_name); my $ldir = $dir ? '::' . $dir . '::' : '::'; my $widget_ns = $self->widget_name_space; my @name_spaces = @$widget_ns; push @name_spaces, ('HTML::FormHandler::Widget', 'HTML::FormHandlerX::Widget'); my @classes; if ( $widget_class =~ s/^\+// ) { push @classes, $widget_class; } foreach my $ns (@name_spaces) { push @classes, $ns . $ldir . $widget_class; } foreach my $try (@classes) { return $try if load_optional_class($try); } die "Can't find $dir widget $widget_class from " . join(", ", @name_spaces); } # this is for compatibility with widget names like 'radio_group' # RadioGroup, Textarea, etc. also work sub widget_class { my ( $self, $widget ) = @_; return unless $widget; if($widget eq lc $widget) { $widget =~ s/^(\w{1})/\u$1/g; $widget =~ s/_(\w{1})/\u$1/g; } return $widget; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::ApplyRole - role to apply widgets =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Block/0000755000077000007700000000000012221042077022124 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Block/Bootstrap.pm0000644000077000007700000000207412221042077024442 0ustar gshankgshankpackage HTML::FormHandler::Widget::Block::Bootstrap; # ABSTRACT: block to format bare form element like bootstrap use Moose; extends 'HTML::FormHandler::Widget::Block'; has 'after_controls' => ( is => 'rw' ); sub BUILD { my $self = shift; $self->add_class('control-group'); $self->add_label_class('control-label'); $self->label_tag('label'); } sub render_from_list { my ( $self, $result ) = @_; $result ||= $self->form->result; my $output = $self->next::method($result); my $after_controls = $self->after_controls || ''; return qq{
\n$output\n$after_controls\n
\n}; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Block::Bootstrap - block to format bare form element like bootstrap =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Block.pm0000644000077000007700000001215512221042077022466 0ustar gshankgshankpackage HTML::FormHandler::Widget::Block; # ABSTRACT: base block renderer use Moose; with 'HTML::FormHandler::TraitFor::Types'; use HTML::FormHandler::Render::Util ('process_attrs'); use HTML::FormHandler::Field; has 'name' => ( is => 'ro', isa => 'Str', required => 1 ); has 'form' => ( is => 'ro', isa => 'HTML::FormHandler', required => 1, weak_ref => 1, ); has 'class' => ( is => 'rw', isa => 'HFH::ArrayRefStr', traits => ['Array'], builder => 'build_class', handles => { has_class => 'count', add_class => 'push', } ); sub build_class { [] } has 'attr' => ( is => 'rw', traits => ['Hash'], builder => 'build_attr', handles => { has_attr => 'count', set__attr => 'set', delete__attr => 'delete' }, ); sub build_attr { {} } has 'wrapper' => ( is => 'rw', isa => 'Bool', default => 1 ); has 'tag' => ( is => 'rw', isa => 'Str', default => 'div' ); has 'label' => ( is => 'rw', isa => 'Str', predicate => 'has_label' ); has 'label_tag' => ( is => 'rw', isa => 'Str' ); has 'label_class' => ( is => 'rw', isa => 'HFH::ArrayRefStr', traits => ['Array'], builder => 'build_label_class', handles => { has_label_class => 'count', add_label_class => 'push', } ); sub build_label_class { [] } has 'render_list' => ( is => 'rw', isa => 'ArrayRef[Str]', traits => ['Array'], builder => 'build_render_list', handles => { has_render_list => 'count', add_to_render_list => 'push', all_render_list => 'elements', get_render_list => 'get', } ); sub build_render_list { [] } has 'content' => ( is => 'rw' ); has 'after_plist' => ( is => 'rw' ); sub render { my ( $self, $result ) = @_; $result ||= $self->form->result; my $start_wrapper = ''; my $end_wrapper = ''; if( $self->wrapper ) { my $tag = $self->tag; # create attribute string my $attr_str = $self->render_attribute_string; $start_wrapper = qq{<$tag$attr_str>}; $end_wrapper = qq{}; } # get rendering of contained fields, if any my $rendered_fb = $self->render_from_list($result); my $content = $self->content || ''; my $after_plist = $self->after_plist || ''; # create label my $label = $self->render_label; my $block = qq{\n$start_wrapper$label$content$rendered_fb$after_plist$end_wrapper}; } sub render_attribute_string { my $self = shift; my $attr = { %{ $self->attr } }; $attr->{class} = $self->class if $self->has_class; my $attr_str = process_attrs($attr); return $attr_str; } sub render_label { my $self = shift; my $label = ''; if ( $self->has_label ) { my $label_tag = $self->label_tag || 'span'; $label_tag = 'legend' if lc $self->tag eq 'fieldset'; my $label_str = $self->form->_localize( $self->label ); my $attr_str = ''; $attr_str = process_attrs( { class => $self->label_class } ) if $self->has_label_class; $label = qq{<$label_tag$attr_str>$label_str}; } return $label; } sub render_from_list { my ( $self, $result ) = @_; my $output = ''; if ( $self->has_render_list ) { foreach my $fb ( @{ $self->render_list } ) { # it's a Field if ( $self->form->field_in_index($fb) ) { # find field result and use that my $fld_result = $result->get_result($fb); # if no result, then we shouldn't be rendering this field next unless $fld_result; $output .= $fld_result->render; } # it's a Block else { # block can't be called from a field, so this should # always be the for result my $block = $self->form->block($fb); die "found no form field or block named '$fb'\n" unless $block; $output .= $block->render($result); } } } # else nothing to render from render_list return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Block - base block renderer =head1 VERSION version 0.40050 =head1 SYNOPSIS Base block renderer to be used with L. has_block 'my_fieldset' => ( tag => 'fieldset', render_list => ['bar'], label => 'My Special Bar' ); =head1 ATTRIBUTES =over 4 =item render_list List of names of objects to render (fields and blocks) =item tag HTML tag for this block. Default 'div'. =item class Arrayref of classes for the HTML element. =item attr Other attributes for the HTML element. =item label_tag Tag to use for the label. Default: 'span'; default for 'fieldset' is 'legend'. =item label_class Classes for the label. =item label Label string. Will be localized. =back =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/0000755000077000007700000000000012221042077022115 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Button.pm0000644000077000007700000000250312221042077023726 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Button; # ABSTRACT: button field rendering widget use Moose::Role; use HTML::FormHandler::Render::Util ('process_attrs'); use namespace::autoclean; sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output = 'id . '"'; $output .= ' value="' . $self->html_filter($self->_localize($self->value)) . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Button - button field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Render a button =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/ButtonTag.pm0000644000077000007700000000253112221042077024363 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::ButtonTag; # ABSTRACT: button field rendering widget, using button tag use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub html_element { 'button' } sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output = '"; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::ButtonTag - button field rendering widget, using button tag =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Captcha.pm0000644000077000007700000000250712221042077024022 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Captcha; # ABSTRACT: Captcha field rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; return '' if $self->widget eq 'no_widget'; my $output .= ''; $output .= 'element_attributes); $output .= '/>'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Captcha - Captcha field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renderer for Captcha field =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Checkbox.pm0000644000077000007700000000263012221042077024202 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Checkbox; # ABSTRACT: HTML attributes field role use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $checkbox_value = $self->checkbox_value; my $output = 'fif eq $checkbox_value; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Checkbox - HTML attributes field role =head1 VERSION version 0.40050 =head1 SYNOPSIS Checkbox field renderer =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/CheckboxGroup.pm0000644000077000007700000000716312221042077025225 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::CheckboxGroup; # ABSTRACT: checkbox group field role use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; # loop through options my $output = ''; foreach my $option ( @{ $self->{options} } ) { if ( my $label = $option->{group} ) { $label = $self->_localize( $label ) if $self->localize_labels; my $attr = $option->{attributes} || {}; my $attr_str = process_attrs($attr); my $lattr = $option->{label_attributes} || {}; my $lattr_str= process_attrs($lattr); $output .= qq{\n$label}; foreach my $group_opt ( @{ $option->{options} } ) { $output .= $self->render_option( $group_opt, $result ); } $output .= qq{\n}; } else { $output .= $self->render_option( $option, $result ); } } $self->reset_options_index; return $output; } sub render_option { my ( $self, $option, $result ) = @_; $result ||= $self->result; # get existing values my $fif = $result->fif; my %fif_lookup; @fif_lookup{@$fif} = () if $self->multiple; # create option label attributes my $lattr = $option->{label_attributes} || {}; push @{ $lattr->{class} }, 'checkbox'; push @{ $lattr->{class} }, 'inline' if $self->get_tag('inline'); my $lattr_str = process_attrs( $lattr ); my $id = $self->id . '.' . $self->options_index; my $output .= qq{\n}; my $value = $option->{value}; $output .= qq{\n{attributes} || {}; if( defined $option->{disabled} && $option->{disabled} ) { $attr->{disabled} = 'disabled'; } if ( defined $fif && ( ( $self->multiple && exists $fif_lookup{$value} ) || ( $fif eq $value ) ) ) { $attr->{checked} = 'checked'; } $output .= process_attrs($attr); $output .= " />\n"; # handle label my $label = $option->{label}; $label = $self->_localize($label) if $self->localize_labels; $output .= $self->html_filter($label); $output .= "\n"; $self->inc_options_index; return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::CheckboxGroup - checkbox group field role =head1 VERSION version 0.40050 =head1 SYNOPSIS Checkbox group widget for rendering multiple selects. Checkbox element label class is 'checkbox', plus 'inline' if the 'inline' tag is set. Options hashrefs must have the keys 'value', and 'label'. They may have an 'attributes' hashref key. The 'checked' attribute should not be set in the options hashref. It should be set by supplying a default value or from params input. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Compound.pm0000644000077000007700000000260512221042077024242 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Compound; # ABSTRACT: compound field widget use Moose::Role; sub render_subfield { my ( $self, $result, $subfield ) = @_; my $subresult = $result->field( $subfield->name ); return "" unless $subresult; return $subfield->render( $subresult ); } sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output = ''; foreach my $subfield ( $self->sorted_fields ) { $output .= $self->render_subfield( $result, $subfield ); } $output =~ s/^\n//; # remove newlines so they're not duplicated return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Compound - compound field widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Widget for rendering a compound field. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Hidden.pm0000644000077000007700000000272212221042077023651 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Hidden; # ABSTRACT: hidden field rendering widget use Moose::Role; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output .= 'id . '"'; $output .= ' value="' . $self->html_filter($result->fif) . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= " />"; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); # wrap field unless do_label is set, which would cause unwanted # labels to be displayed return $self->wrap_field( $result, $output ) if !$self->do_label; return $output; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Hidden - hidden field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Widget for rendering a hidden field. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/NoRender.pm0000644000077000007700000000115712221042077024173 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::NoRender; # ABSTRACT: no rendering widget use Moose::Role; sub render { '' } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::NoRender - no rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a field as the empty string. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Password.pm0000644000077000007700000000263212221042077024260 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Password; # ABSTRACT: password rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my $self = shift; my $result = shift || $self->result; my $t; my $output = 'size; $output .= qq{ maxlength="$t"} if $t = $self->maxlength; $output .= ' value="' . $self->html_filter($result->fif) . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Password - password rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a password field =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/RadioGroup.pm0000644000077000007700000000746112221042077024536 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::RadioGroup; # ABSTRACT: radio group rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub type_attr { 'radio' } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output = ''; $output .= "
" if $self->get_tag('radio_br_after'); foreach my $option ( @{ $self->{options} } ) { if ( my $label = $option->{group} ) { $label = $self->_localize( $label ) if $self->localize_labels; my $attr = $option->{attributes} || {}; my $attr_str = process_attrs($attr); my $lattr = $option->{label_attributes} || {}; my $lattr_str= process_attrs($lattr); $output .= qq{\n$label}; foreach my $group_opt ( @{ $option->{options} } ) { $output .= $self->render_option( $group_opt, $result ); } $output .= qq{\n}; } else { $output .= $self->render_option( $option, $result ); } $output .= '
' if $self->get_tag('radio_br_after'); } $self->reset_options_index; return $output; } sub render_option { my ( $self, $option, $result ) = @_; $result ||= $result; my $rendered_widget = $self->render_radio( $result, $option ); my $output = $self->wrap_radio( $rendered_widget, $option->{label} ); $self->inc_options_index; return $output; } sub render_wrapped_option { my ( $self, $option, $result ) = @_; $result ||= $self->result; my $output = $self->render_option( $option, $result ); return $self->wrap_field( $result, $output ); } sub render_radio { my ( $self, $result, $option ) = @_; $result ||= $self->result; my $value = $option->{value}; my $id = $self->id . "." . $self->options_index; my $output = 'fif eq $value; $output .= process_attrs($option->{attributes}); $output .= ' />'; return $output; } sub wrap_radio { my ( $self, $rendered_widget, $option_label ) = @_; my $id = $self->id . "." . $self->options_index; my $for = qq{ for="$id"}; # use "simple" label attributes for inner label my @label_class = ('radio'); push @label_class, 'inline' if $self->get_tag('inline'); my $lattrs = process_attrs( { class => \@label_class } ); # return wrapped radio, either on left or right my $label = $self->_localize($option_label); my $output = ''; if ( $self->get_tag('label_left') ) { $output = qq{\n$label\n$rendered_widget}; } else { $output = qq{$rendered_widget\n$label\n}; } if ( $self->get_tag('radio_element_wrapper') ) { $output = qq{
$output
}; } return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::RadioGroup - radio group rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a radio group (from a 'Select' field); Tags: radio_br_after =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Repeatable.pm0000644000077000007700000000171212221042077024520 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Repeatable; # ABSTRACT: repeatable field widget use Moose::Role; with 'HTML::FormHandler::Widget::Field::Compound'; sub render_subfield { my ( $self, $result, $subfield ) = @_; my $subresult = $result->field( $subfield->name ); return "" unless $subresult or ( $self->has_flag( "is_repeatable") and $subfield->name < $self->num_when_empty ); return $subfield->render($subresult); } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Repeatable - repeatable field widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a repeatable field =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Reset.pm0000644000077000007700000000250412221042077023536 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Reset; # ABSTRACT: reset field rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output = 'id . '"'; $output .= ' value="' . $self->html_filter($self->_localize($self->value)) . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Reset - reset field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a reset field =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Role/0000755000077000007700000000000012221042077023016 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Role/HTMLAttributes.pm0000644000077000007700000000155012221042077026170 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Role::HTMLAttributes; # ABSTRACT: apply HTML attributes use Moose::Role; use HTML::FormHandler::Render::Util ('process_attrs'); sub _add_html_attributes { my $self = shift; my $output = process_attrs( $self->attributes ); return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Role::HTMLAttributes - apply HTML attributes =head1 VERSION version 0.40050 =head1 SYNOPSIS Deprecated. Only here for interim compatibility, to provide '_add_html_attributes' method. Will be removed in the future. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Role/SelectedOption.pm0000644000077000007700000000171612221042077026302 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Role::SelectedOption; # ABSTRACT: allow setting options from options keys use Moose::Role; use namespace::autoclean; sub check_selected_option { my ( $self, $option, $fif ) = @_; my $selected_key = defined($option->{'selected'}) ? $option->{'selected'} : $option->{'checked'}; if ( defined $selected_key ) { return $selected_key; } elsif ( defined $fif ) { return $fif eq $option->{'value'}; } else { return; } } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Role::SelectedOption - allow setting options from options keys =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Select.pm0000644000077000007700000000752312221042077023701 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Select; # ABSTRACT: select field rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; # create select element my $output = $self->render_select_start( $result ); # create empty select if( defined $self->empty_select ) { $output .= $self->render_empty_select; } # loop through options foreach my $option ( @{ $self->{options} } ) { if ( my $label = $option->{group} ) { $label = $self->_localize( $label ) if $self->localize_labels; $output .= qq{\n}; foreach my $group_opt ( @{ $option->{options} } ) { $output .= $self->render_option( $group_opt, $result ); } $output .= qq{\n}; } else { $output .= $self->render_option( $option, $result ); } } $self->reset_options_index; $output .= ''; return $output; } sub render_select_start { my ( $self, $result ) = @_; $result ||= $self->result; my $id = $self->id; my $output = 'id . '"'; $output .= ' value="' . $self->html_filter($self->_localize($self->value)) . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Submit - submit field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a submit field. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Text.pm0000644000077000007700000000273012221042077023401 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Text; # ABSTRACT: text field rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my $self = shift; my $result = shift || $self->result; my $t; my $rendered = $self->html_filter($result->fif); my $output = 'size; $output .= qq{ maxlength="$t"} if $t = $self->maxlength; $output .= ' value="' . $self->html_filter($result->fif) . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Text - text field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders a text field =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Textarea.pm0000644000077000007700000000252012221042077024227 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Textarea; # ABSTRACT: textarea rendering widget use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my $self = shift; my $result = shift || $self->result; my $fif = $self->html_filter($result->fif); my $id = $self->id; my $cols = $self->cols || 10; my $rows = $self->rows || 5; my $name = $self->html_name; my $output = qq(); return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Textarea - textarea rendering widget =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Field/Upload.pm0000644000077000007700000000240012221042077023673 0ustar gshankgshankpackage HTML::FormHandler::Widget::Field::Upload; # ABSTRACT: update field rendering widget use Moose::Role; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_element { my ( $self, $result ) = @_; $result ||= $self->result; my $output; $output = 'id . '"'; $output .= process_attrs($self->element_attributes($result)); $output .= ' />'; return $output; } sub render { my ( $self, $result ) = @_; $result ||= $self->result; die "No result for form field '" . $self->full_name . "'. Field may be inactive." unless $result; my $output = $self->render_element( $result ); return $self->wrap_field( $result, $output ); } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Field::Upload - update field rendering widget =head1 VERSION version 0.40050 =head1 SYNOPSIS Renders an Upload field =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Form/0000755000077000007700000000000012221042077021775 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Form/Role/0000755000077000007700000000000012221042077022676 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Form/Role/HTMLAttributes.pm0000644000077000007700000000321512221042077026050 0ustar gshankgshankpackage HTML::FormHandler::Widget::Form::Role::HTMLAttributes; # ABSTRACT: set HTML attributes on the form tag use Moose::Role; sub html_form_tag { my $self = shift; my @attr_accessors = ( [ action => 'action' ], [ id => 'name' ], [ method => 'http_method' ], [ enctype => 'enctype' ], [ class => 'css_class' ], [ style => 'style' ], ); # make the element_attr a safe default my $element_attr = {}; # Assuming that self is a form $element_attr = { %{$self->form_element_attr} } if ( $self->can( 'form_element_attr' ) ); # Assuming that self is a field $element_attr = { %{$self->element_attr} } if ( $self->can( 'element_attr' ) ); foreach my $attr_pair (@attr_accessors) { my $attr = $attr_pair->[0]; my $accessor = $attr_pair->[1]; if ( !exists $element_attr->{$attr} && defined( my $value = $self->$accessor ) ) { $element_attr->{$attr} = $self->$accessor; } } my $output = 'DOES('HTML::FormHandler::Result') ) { $result = $self; $form = $self->form; } else { $result = $self->result; $form = $self; } my $output = $form->render_start($result); $output .= $form->render_form_messages($result); if ( $form->has_render_list ) { foreach my $fb ( @{ $form->render_list } ) { # it's a Field if ( $form->field_in_index($fb) ) { # find field result and use that my $fld_result = $result->get_result($fb); # if no result, then we shouldn't be rendering this field next unless $fld_result; $output .= $fld_result->render; } # it's a Block else { # always use form level result for blocks my $block = $form->block($fb); die "found no form field or block named '$fb'\n" unless $block; $output .= $block->render($result); } } } else { foreach my $fld_result ( $result->results ) { $output .= $fld_result->render; } } $output .= $form->render_end($result); return $output; } sub render_start { my ( $self, $result ) = @_; $result ||= $self->result; my $output = ''; $output = $self->get_tag('before'); my $wtag = $self->get_tag('wrapper_tag') || 'fieldset'; # render wrapper start if not fieldset $output .= $self->render_wrapper_start($wtag, $result) if $wtag ne 'fieldset'; # render form tag my $attrs = process_attrs($self->attributes($result)); $output .= qq{}; # render wrapper start if fieldset (not legal outside form tag) $output .= $self->render_wrapper_start($wtag) if $wtag eq 'fieldset'; $output .= $self->get_tag('after_start'); return $output } sub render_wrapper_start { my ( $self, $wrapper_tag, $result ) = @_; return '' unless $self->do_form_wrapper; $result ||= $self->result; my $attrs = process_attrs($self->form_wrapper_attributes($result)); return qq{<$wrapper_tag$attrs>}; } sub render_form_errors { shift->render_form_messages(@_) } sub render_form_messages { my ( $self, $result ) = @_; $result ||= $self->result; return '' if $self->get_tag('no_form_message_div'); my $messages_wrapper_class = $self->get_tag('messages_wrapper_class') || 'form_messages'; my $output = qq{\n
}; my $error_class = $self->get_tag('error_class') || 'error_message'; if( $self->has_error_message && ( $result->has_errors || $result->has_form_errors ) ) { my $msg = $self->error_message; $msg = $self->_localize($msg); $output .= qq{\n$msg}; } if ( $result->has_form_errors ) { $output .= qq{\n$_} for $result->all_form_errors; } if( $self->has_success_message && $result->validated ) { my $msg = $self->success_message; $msg = $self->_localize($msg); my $success_class = $self->get_tag('success_class') || 'success_message'; $output .= qq{\n$msg}; } if( $self->has_info_message && $self->info_message ) { my $msg = $self->info_message; $msg = $self->_localize($msg); my $info_class = $self->get_tag('info_class') || 'info_message'; $output .= qq{\n$msg}; } $output .= "\n
"; return $output; } sub render_end { my $self = shift; my $output = $self->get_tag('before_end'); my $wtag = $self->get_tag('wrapper_tag') || 'fieldset'; $output .= $self->render_wrapper_end($wtag) if $wtag eq 'fieldset'; $output .= "\n"; $output .= $self->render_wrapper_end($wtag) if $wtag ne 'fieldset'; $output .= $self->get_tag('after'); $output .= "\n"; return $output; } sub render_wrapper_end { my ( $self, $wrapper_tag ) = @_; return '' unless $self->do_form_wrapper; return qq{\n}; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Form::Simple - widget to render a form with divs =head1 VERSION version 0.40050 =head1 SYNOPSIS Role to apply to form objects to allow rendering. This rendering role is applied to HTML::FormHandler by default. It supports block rendering. (L, L) Relevant flags: do_form_wrapper - put a wrapper around the form If the wrapper_tag is a 'fieldset' (default if not specified) the wrapper goes inside the form tags (because it's not valid to put it outside of them). If the wrapper_tag is something else, it will go around the form tags. If you're doing one kind of wrapper and want another one, you can achieve that result by using the 'before'/'after' tags or the 'after_start'/'before_end' tags. Supported tags: wrapper_tag -- tag for form wrapper; default 'fieldset' before after after_start before_end messages_wrapper_class -- default 'form_messages' error_class -- default 'error_message' error_message -- message to issue when form contains errors success_class -- default 'success_message' success_message -- message to issue when form was submitted successfully =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Form/Table.pm0000644000077000007700000000306612221042077023367 0ustar gshankgshankpackage HTML::FormHandler::Widget::Form::Table; # ABSTRACT: render a form with a table layout use Moose::Role; with 'HTML::FormHandler::Widget::Form::Simple' => { -excludes => [ 'render_start', 'render_end', 'render_form_errors' ] }; use HTML::FormHandler::Render::Util ('process_attrs'); sub render_start { my ( $self, $result ) = @_; $result ||= $self->result; my $fattrs = process_attrs($self->attributes($result)); my $wattrs = process_attrs($self->form_wrapper_attributes($result)); return qq{\n}; } sub render_form_errors { my ( $self, $result ) = @_; return '' unless $result->has_form_errors; my $output = "\n"; $output .= qq{\n$_} for $result->all_form_errors; $output .= "\n"; return $output; } sub render_end { my $self = shift; my $output .= "\n"; $output .= "\n"; return $output; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Form::Table - render a form with a table layout =head1 VERSION version 0.40050 =head1 SYNOPSIS Set in your form: has '+widget_form' => ( default => 'Table' ); Use in a template: [% form.render %] =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Theme/0000755000077000007700000000000012221042077022134 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Theme/Bootstrap.pm0000644000077000007700000000344312221042077024453 0ustar gshankgshankpackage HTML::FormHandler::Widget::Theme::Bootstrap; # ABSTRACT: sample bootstrap theme use Moose::Role; with 'HTML::FormHandler::Widget::Theme::BootstrapFormMessages'; after 'before_build' => sub { my $self = shift; $self->set_widget_wrapper('Bootstrap') if $self->widget_wrapper eq 'Simple'; }; sub build_form_element_class { ['form-horizontal'] } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Theme::Bootstrap - sample bootstrap theme =head1 VERSION version 0.40050 =head1 SYNOPSIS Also see L. Sample Bootstrap theme role. Can be applied to your subclass of HTML::FormHandler. Sets the widget wrapper to 'Bootstrap' and renders form messages using Bootstrap formatting and classes. There is an example app using Bootstrap at http://github.com:gshank/formhandler-example. This is a lightweight example of what you could do in your own custom Bootstrap theme. The heavy lifting is done by the Bootstrap wrapper, L, which you can use by itself in your form with: has '+widget_wrapper' => ( default => 'Bootstrap' ); It also uses L to render the form messages in a Bootstrap style:
....
By default this does 'form-horizontal' with 'build_form_element_class'. Implement your own sub to use 'form-vertical': sub build_form_element_class { ['form-vertical'] } =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Theme/BootstrapFormMessages.pm0000644000077000007700000000362412221042077026770 0ustar gshankgshankpackage HTML::FormHandler::Widget::Theme::BootstrapFormMessages; # ABSTRACT: role to render form messages using Bootstrap styling use Moose::Role; sub render_form_messages { my ( $self, $result ) = @_; $result ||= $self->result; my $output = ''; if ( $result->has_form_errors || $result->has_errors ) { $output = qq{\n
}; my $msg = $self->error_message; $msg ||= 'There were errors in your form'; $msg = $self->_localize($msg); $output .= qq{\n$msg}; $output .= qq{\n$_} for $result->all_form_errors; $output .= "\n
"; } elsif ( $result->validated ) { my $msg = $self->success_message; $msg ||= "Your form was successfully submitted"; $msg = $self->_localize($msg); $output = qq{\n
}; $output .= qq{\n$msg}; $output .= "\n
"; } if ( $self->has_info_message && $self->info_message ) { my $msg = $self->info_message; $msg = $self->_localize($msg); $output = qq{\n
}; $output .= qq{\n$msg}; $output .= "\n
"; } return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Theme::BootstrapFormMessages - role to render form messages using Bootstrap styling =head1 VERSION version 0.40050 =head1 DESCRIPTION Role to render form messages using Bootstrap styling. =head1 NAME HTML::FormHandler::Widget::Theme::BootstrapFormMessages =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/0000755000077000007700000000000012221042077022512 5ustar gshankgshankHTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/Base.pm0000644000077000007700000000620312221042077023723 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::Base; # ABSTRACT: common methods for widget wrappers use Moose::Role; use HTML::FormHandler::Render::Util ('process_attrs'); sub do_render_label { my ( $self, $result, $label_tag, $class ) = @_; $label_tag ||= $self->get_tag('label_tag') || 'label'; my $attr = $self->label_attributes( $result ); push @{ $attr->{class} }, @$class if $class; my $attrs = process_attrs($attr); my $label; if( $self->does_wrap_label ) { $label = $self->wrap_label( $self->label ); } else { $label = $self->get_tag('label_no_filter') ? $self->loc_label : $self->html_filter($self->loc_label); } $label .= $self->get_tag('label_after') if $label_tag ne 'legend'; my $id = $self->id; my $for = $label_tag eq 'label' ? qq{ for="$id"} : ''; return qq{<$label_tag$attrs$for>$label}; } sub wrap_checkbox { my ( $self, $result, $rendered_widget ) = @_; return $rendered_widget if( $self->get_tag('no_wrapped_label' ) ); my $label = $self->option_label || ''; if( $label eq '' && ! $self->do_label ) { $label = $self->html_filter($self->loc_label); } elsif( $label ne '' ) { $label = $self->html_filter($self->_localize($label)); } my $id = $self->id; my $for = qq{ for="$id"}; # use "simple" label attributes for inner label my @label_class = ('checkbox'); push @label_class, 'inline' if $self->get_tag('inline'); my $lattrs = process_attrs( { class => \@label_class } ); # return wrapped checkbox, either on left or right my $output = ''; if ( $self->get_tag('label_left') ) { $output = qq{\n$label\n$rendered_widget}; } else { $output = qq{$rendered_widget\n$label\n}; } if ( $self->get_tag('checkbox_element_wrapper') ) { $output = qq{
$output
}; } return $output; } # for compatibility with older code sub render_label { my $self = shift; my $attrs = process_attrs($self->label_attributes); my $label = $self->html_filter($self->loc_label); $label .= ": " unless $self->get_tag('label_no_colon'); return qq{$label}; } # this is not actually used any more, but is left here for compatibility # with user created widgets sub render_class { my ( $self, $result ) = @_; $result ||= $self->result; return process_attrs($self->wrapper_attributes($result)); } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::Base - common methods for widget wrappers =head1 VERSION version 0.40050 =head1 DESCRIPTION Provides several common methods for wrapper widgets, including 'do_render_label' and 'wrap_checkbox'. =head1 NAME HTML::FormHandler::Widget::Wrapper::Base =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/Bootstrap.pm0000644000077000007700000001161212221042077025026 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::Bootstrap; # ABSTRACT: Twitter Bootstrap 2.0 field wrapper use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); with 'HTML::FormHandler::Widget::Wrapper::Base'; sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; my $output; # is this a control group or a form action? my $form_actions = 1 if ( $self->name eq 'form_actions' || $self->type_attr eq 'submit' || $self->type_attr eq 'reset' ); # create attribute string for wrapper my $attr = $self->wrapper_attributes($result); # no 'control-group' class for Hidden fields, 'form-actions' for submit/reset my $div_class = $self->type eq 'Hidden' ? undef : $form_actions ? "form-actions" : "control-group"; unshift @{$attr->{class}}, $div_class if $div_class; my $attr_str = process_attrs( $attr ); # wrapper is always a div if ( $self->do_wrapper ) { $output .= $self->get_tag('before_wrapper'); $output .= qq{\n}; } # render the label $output .= "\n" . $self->do_render_label($result, undef, ['control-label'] ) if $self->do_label; $output .= $self->get_tag('before_element'); # the controls div for ... controls $output .= qq{\n
} unless $form_actions || !$self->do_wrapper; # yet another tag $output .= $self->get_tag('before_element_inside_div'); # handle input-prepend and input-append if( $self->get_tag('input_prepend') || $self->get_tag('input_append') || $self->get_tag('input_append_button') ) { $rendered_widget = $self->do_prepend_append($rendered_widget); } elsif( lc $self->widget eq 'checkbox' ) { $rendered_widget = $self->wrap_checkbox($result, $rendered_widget, 'label') } $output .= "\n$rendered_widget"; # various 'help-inline' bits: errors, warnings unless( $self->get_tag('no_errors') ) { $output .= qq{\n$_} for $result->all_errors; $output .= qq{\n$_} for $result->all_warnings; } # extra after element stuff $output .= $self->get_tag('after_element'); # close 'control' div $output .= '
' unless $form_actions || !$self->do_wrapper; # close wrapper if ( $self->do_wrapper ) { $output .= "\n"; $output .= $self->get_tag('after_wrapper'); } return "$output"; } sub do_prepend_append { my ( $self, $rendered_widget ) = @_; my @class; if( my $ip_tag = $self->get_tag('input_prepend' ) ) { $rendered_widget = qq{$ip_tag$rendered_widget}; push @class, 'input-prepend'; } if ( my $ia_tag = $self->get_tag('input_append' ) ) { $rendered_widget = qq{$rendered_widget$ia_tag}; push @class, 'input-append'; } if ( my $iab_tag = $self->get_tag('input_append_button') ) { my @buttons = ref $iab_tag eq 'ARRAY' ? @$iab_tag : ($iab_tag); foreach my $btn ( @buttons ) { $rendered_widget = qq{$rendered_widget}; } push @class, 'input-append'; } my $attr = process_attrs( { class => \@class } ); $rendered_widget = qq{ $rendered_widget }; return $rendered_widget; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::Bootstrap - Twitter Bootstrap 2.0 field wrapper =head1 VERSION version 0.40050 =head1 SYNOPSIS Wrapper to implement Bootstrap 2.0 style form element rendering. This wrapper does some very specific Bootstrap things, like wrap the form elements in divs with non-changeable classes. It is not as flexible as the 'Simple' wrapper, but means that you don't have to specify those classes in your form code. It wraps form elements with 'control-group' divs, and form 'actions' with 'form-actions' divs. It adds special additional wrappers for checkboxes and radio buttons, with wrapped labels. =head1 DESCRIPTION Tags supported: label_no_filter -- don't html filter the label label_after -- useful for putting a colon, or other trailing formatting before_element -- insert tag before input, outside element's control div before_element_inside_div -- insert tag before input element, inside control div input_prepend -- for Bootstrap 'input-prepend' class input_append -- for Bootstrap 'input-append' class input_append_button -- 'input-append' with button instead of span no_errors -- don't append error to field rendering after_element -- insert tag after input element =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/Bootstrap3.pm0000644000077000007700000001454412221042077025120 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::Bootstrap3; # ABSTRACT: Twitter Bootstrap 2.0 field wrapper use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); with 'HTML::FormHandler::Widget::Wrapper::Base'; sub build_wrapper_tags { { radio_element_wrapper => 1, checkbox_element_wrapper => 1, } } sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; my $output; # create attribute string for wrapper if ( $self->do_wrapper ) { my $attr = $self->wrapper_attributes($result); # no 'control-group' class for Hidden fields, 'form-actions' for submit/reset my $div_class = 'form-group'; unshift @{$attr->{class}}, $div_class; my $attr_str = process_attrs( $attr ); # wrapper is always a div $output .= $self->get_tag('before_wrapper'); $output .= qq{\n}; } # render the label $output .= "\n" . $self->do_render_label($result, undef, ['control-label'] ) if $self->do_label; $output .= $self->get_tag('before_element'); # the controls div; used to have 'controls' class. Now it comes from # the 'element_wrapper_class' my $ew_attr = $self->element_wrapper_attributes($result); my $element_wrapper_attrs = process_attrs( $ew_attr ); $output .= qq{\n} unless !$self->do_wrapper; # yet another tag $output .= $self->get_tag('before_element_inside_div'); # handle input-prepend and input-append if( $self->get_tag('input_prepend') || $self->get_tag('input_append') || $self->get_tag('input_append_button') ) { $rendered_widget = $self->do_prepend_append($rendered_widget); } elsif( lc $self->widget eq 'checkbox' ) { $rendered_widget = $self->wrap_checkbox($result, $rendered_widget, 'label') } $output .= "\n$rendered_widget"; # various 'help-inline' bits: errors, warnings unless( $self->get_tag('no_errors') ) { $output .= qq{\n$_} for $result->all_errors; $output .= qq{\n$_} for $result->all_warnings; } # extra after element stuff $output .= $self->get_tag('after_element'); # close element_wrapper 'control' div $output .= '' unless !$self->do_wrapper; # close wrapper if ( $self->do_wrapper ) { $output .= "\n"; $output .= $self->get_tag('after_wrapper'); } return "$output"; } # don't render label for checkboxes sub do_render_label { my ( $self ) = @_; return '' if $self->type_attr eq 'checkbox'; HTML::FormHandler::Widget::Wrapper::Base::do_render_label(@_); } sub add_standard_element_classes { my ( $self, $result, $class ) = @_; push @$class, 'has-error' if $result->has_errors; push @$class, 'has-warning' if $result->has_warnings; push @$class, 'disabled' if $self->disabled; push @$class, 'form-control' if $self->html_element eq 'select' || $self->html_element eq 'textarea' || $self->type_attr eq 'text' || $self->type_attr eq 'password'; } sub add_standard_wrapper_classes { my ( $self, $result, $class ) = @_; push @$class, 'has-error' if ( $result->has_error_results || $result->has_errors ); push @$class, 'has-warning' if $result->has_warnings; # TODO: has-success? } sub wrap_checkbox { my ( $self, $result, $rendered_widget ) = @_; # use the regular label my $label = $self->option_label || $self->label; $label = $self->html_filter($self->_localize($label)); my $id = $self->id; my $for = qq{ for="$id"}; # return wrapped checkbox, either on left or right return qq{
\n$label\n$rendered_widget
} if( $self->get_tag('label_left') ); return qq{
$rendered_widget\n$label\n
}; } sub do_prepend_append { my ( $self, $rendered_widget ) = @_; my @class; if( my $ip_tag = $self->get_tag('input_prepend' ) ) { $rendered_widget = qq{$ip_tag$rendered_widget}; push @class, 'input-group'; } if ( my $ia_tag = $self->get_tag('input_append' ) ) { $rendered_widget = qq{$rendered_widget$ia_tag}; push @class, 'input-group'; } if ( my $iab_tag = $self->get_tag('input_append_button') ) { my @buttons = ref $iab_tag eq 'ARRAY' ? @$iab_tag : ($iab_tag); my $group = qq{}; foreach my $btn ( @buttons ) { $group = qq{}; } $group .= qq{}; $rendered_widget = qq{$rendered_widget$group}; push @class, 'input-group'; } my $attr = process_attrs( { class => \@class } ); $rendered_widget = qq{ $rendered_widget }; return $rendered_widget; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::Bootstrap3 - Twitter Bootstrap 2.0 field wrapper =head1 VERSION version 0.40050 =head1 SYNOPSIS Wrapper to implement Bootstrap 3.0 style form element rendering. =head1 DESCRIPTION Differences from the Bootstrap 2.0 wrapper: The wrapper class is 'form-group' instead of 'control-group' The div wrapping the form element does not have the 'controls' class. Used for sizing css classes. Input prepend & append use different classes The input elements have a 'form-control' class Tags supported: label_no_filter -- don't html filter the label label_after -- useful for putting a colon, or other trailing formatting before_element -- insert tag before input, outside element's control div before_element_inside_div -- insert tag before input element, inside control div input_prepend -- for Bootstrap 'input-prepend' class input_append -- for Bootstrap 'input-append' class input_append_button -- 'input-append' with button instead of span no_errors -- don't append error to field rendering after_element -- insert tag after input element =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/Fieldset.pm0000644000077000007700000000231012221042077024603 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::Fieldset; # ABSTRACT: fieldset field wrapper use Moose::Role; use namespace::autoclean; with 'HTML::FormHandler::Widget::Wrapper::Base'; use HTML::FormHandler::Render::Util ('process_attrs'); sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; my $wattrs = process_attrs($self->wrapper_attributes); my $output .= qq{\n}; $output .= qq{\n} . $self->loc_label . ''; $output .= "\n$rendered_widget"; $output .= qq{\n$_} for $result->all_errors; $output .= "\n"; return $output; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::Fieldset - fieldset field wrapper =head1 VERSION version 0.40050 =head1 SYNOPSIS Wraps a single field in a fieldset. =head1 NAME HTML::FormHandler::Widget::Wrapper::Fieldset - fieldset field wrapper =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/None.pm0000644000077000007700000000136412221042077023753 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::None; # ABSTRACT: wrapper that doesn't wrap use Moose::Role; sub wrap_field { "\n" . $_[2] } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::None - wrapper that doesn't wrap =head1 VERSION version 0.40050 =head1 DESCRIPTION This wrapper does nothing except return the 'bare' rendered form element, as returned by the 'widget'. It does not add errors or anything else. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/Simple.pm0000644000077000007700000001006612221042077024304 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::Simple; # ABSTRACT: simple field wrapper use Moose::Role; use namespace::autoclean; use HTML::FormHandler::Render::Util ('process_attrs'); with 'HTML::FormHandler::Widget::Wrapper::Base'; sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; my $output; # get wrapper tag if set my $label_tag = $self->label_tag || ''; my $wrapper_tag; if( $self->do_wrapper ) { $output .= $self->get_tag('before_wrapper'); $wrapper_tag = $self->get_tag('wrapper_tag'); # default wrapper tags $wrapper_tag ||= $self->has_flag('is_repeatable') ? 'fieldset' : 'div'; # get attribute string my $attrs = process_attrs( $self->wrapper_attributes($result) ); # write wrapper tag $output .= qq{\n<$wrapper_tag$attrs>}; $label_tag = 'legend' if $wrapper_tag eq 'fieldset'; } # write label; special processing for checkboxes $rendered_widget = $self->wrap_checkbox($result, $rendered_widget, $label_tag) if ( lc $self->widget eq 'checkbox' ); $output .= "\n" . $self->do_render_label($result, $label_tag ) if $self->do_label; # append 'before_element' $output .= $self->get_tag('before_element'); # start controls div if ( $self->get_tag('controls_div') ) { $output .= qq{\n
}; } elsif ( $self->has_element_wrapper_class ) { my $ew_attr = $self->element_wrapper_attributes($result); my $element_wrapper_attrs = process_attrs( $ew_attr ); $output .= qq{\n}; } # the input element itself $output .= "\n$rendered_widget"; # close controls div if ( $self->get_tag('controls_div') || $self->has_element_wrapper_class ) { # end control div $output .= "\n
"; } # the 'after_element' $output .= $self->get_tag('after_element'); # the error messages unless( $self->get_tag('no_errors') ) { my $error_class = $self->get_tag('error_class') || 'error_message'; $output .= qq{\n$_} for $result->all_errors; # warnings (incompletely implemented - only on field itself) my $warning_class = $self->get_tag('warning_class') || 'warning_message'; $output .= qq{\n$_} for $result->all_warnings; } if( $self->do_wrapper ) { $output .= "\n"; $output .= $self->get_tag('after_wrapper'); } return "$output"; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::Simple - simple field wrapper =head1 VERSION version 0.40050 =head1 SYNOPSIS This is the default wrapper role. It will be installed if no other wrapper is specified and widget_wrapper is not set to 'none'. Relevant field flags: do_wrapper do_label If 'do_label' is set and not 'do_wrapper', only the label plus the form element will be rendered. Supported 'tags', all set via the 'tags' hashref on the field: wrapper_tag -- the tag to use in the wrapper, default 'div' label_tag -- tag to use for label (default 'label') label_after -- string to append to label, for example ': ' to append a colon before_element -- string that goes right before the element after_element -- string that goes right after the element no_errors -- don't issue error messages on the field error_class -- class for error messages (default 'error_message') warning_class -- class for warning messages (default 'warning_message' ) no_wrapped_label -- for checkboxes. Don't provide an inner wrapped label (from Base wrapper) Example: has_field 'foo' => ( tags => { wrapper_tag => 'span', no_errors => 1 } ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/SimpleInline.pm0000644000077000007700000000273512221042077025447 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::SimpleInline; # ABSTRACT: simple field wrapper use Moose::Role; use namespace::autoclean; with 'HTML::FormHandler::Widget::Wrapper::Base'; sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; return $rendered_widget if $self->has_flag('is_compound'); my $output = "\n"; my $tag = $self->wrapper_tag; my $start_tag = $self->get_tag('wrapper_start'); if( defined $start_tag ) { $output .= $start_tag; } else { $output .= "<$tag" . process_attrs( $self->wrapper_attributes($result) ) . ">"; } if ( $self->do_label && length( $self->label ) > 0 ) { $output .= $self->do_render_label($result); } $output .= $rendered_widget; $output .= qq{\n$_} for $result->all_errors; my $end_tag = $self->get_tag('wrapper_end'); $output .= defined $end_tag ? $end_tag : ""; return "$output\n"; } 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::SimpleInline - simple field wrapper =head1 VERSION version 0.40050 =head1 SYNOPSIS This works like the Simple Wrapper, except it doesn't wrap Compound fields. =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/Table.pm0000644000077000007700000000273712221042077024110 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::Table; # ABSTRACT: wrapper class for table layout use Moose::Role; with 'HTML::FormHandler::Widget::Wrapper::Base'; use HTML::FormHandler::Render::Util ('process_attrs'); sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; return $rendered_widget if ( $self->has_flag('is_compound') && $self->get_tag('no_compound_wrapper') ); my $output = "\nwrapper_attributes($result)) . ">"; if ( $self->has_flag('is_compound') ) { $output .= '' . $self->do_render_label($result) . ''; } elsif ( $self->do_label && length( $self->label ) > 0 ) { $output .= '' . $self->do_render_label($result) . ''; } if ( !$self->has_flag('is_compound') ) { $output .= ''; } $output .= $rendered_widget; $output .= qq{\n$_} for $result->all_errors; if ( !$self->has_flag('is_compound') ) { $output .= "\n"; } return $output; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::Table - wrapper class for table layout =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Widget/Wrapper/TableInline.pm0000644000077000007700000000242412221042077025240 0ustar gshankgshankpackage HTML::FormHandler::Widget::Wrapper::TableInline; # ABSTRACT: wrapper class for table layout that doesn't wrap compound fields use Moose::Role; with 'HTML::FormHandler::Widget::Wrapper::Base'; use HTML::FormHandler::Render::Util ('process_attrs'); sub wrap_field { my ( $self, $result, $rendered_widget ) = @_; return $rendered_widget if $self->has_flag('is_compound'); my $output = "\nwrapper_attributes($result)) . ">"; if ( $self->do_label && length( $self->label ) > 0 ) { $output .= '' . $self->do_render_label($result) . ''; } $output .= ''; $output .= $rendered_widget; $output .= qq{\n$_} for $result->all_errors; $output .= "\n"; return $output; } use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Widget::Wrapper::TableInline - wrapper class for table layout that doesn't wrap compound fields =head1 VERSION version 0.40050 =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler/Wizard.pm0000644000077000007700000000732212221042077021451 0ustar gshankgshankpackage HTML::FormHandler::Wizard; # ABSTRACT: create a multi-page form use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with ('HTML::FormHandler::BuildPages', 'HTML::FormHandler::Pages' ); sub is_wizard {1} has_field 'page_num' => ( type => 'Hidden', default => 1 ); has 'on_last_page' => ( is => 'rw', isa => 'Bool', default => 0 ); has 'stash' => ( is => 'rw', isa => 'HashRef' ); has 'save_to' => ( is => 'rw', isa => 'Str' ); # 'item', 'stash', 'temp_table' # temp_table: DBIC row or other object with three columns: # form, field, value # has 'temp_table' => ( is => 'rw' ); sub validated { my $self = shift; return $self->next::method && $self->on_last_page; } sub page_validated { my $self = shift; return $self->SUPER::validated; } sub build_active { my $self = shift; my @page_fields; foreach my $page ( $self->all_pages ) { push @page_fields, $page->all_fields; } foreach my $field_name ( @page_fields ) { $self->field($field_name)->inactive(1); } } sub set_active { my ( $self, $current_page ) = @_;; $current_page ||= $self->get_param('page_num') || 1; return if $current_page > $self->num_pages; $self->on_last_page(1) if $current_page == $self->num_pages; my $page = $self->get_page( $current_page - 1 ); foreach my $fname ( $page->all_fields ) { my $field = $self->field($fname); if ( $field ) { $field->_active(1); } else { warn "field $fname not found for page " . $page->name; } } } after 'validate_form' => sub { my $self = shift; if( $self->page_validated ) { $self->save_page; if( $self->field('page_num')->value < $self->num_pages ) { my $new_page_num = $self->field('page_num')->value + 1; $self->clear_page; $self->set_active( $new_page_num ); $self->_result_from_fields( $self->result ); $self->field('page_num')->value($new_page_num); $self->on_last_page(1) if $new_page_num == $self->num_pages; } elsif( $self->field('page_num')->value == $self->num_pages ) { $self->_set_value( $self->stash ); } } }; sub clear_page { my $self = shift; $self->clear_data; $self->clear_params; $self->processed(0); # $self->did_init_obj(0); $self->clear_result; } sub save_page { my $self = shift; my $stash = $self->stash; while ( my ($key, $value) = each %{$self->value}) { $stash->{$key} = $value; } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler::Wizard - create a multi-page form =head1 VERSION version 0.40050 =head1 SYNOPSIS This feature is EXPERIMENTAL. That means that the interface may change, and that it hasn't been fully implemented. We are actively looking for input, so if you are interested in this feature, please show up on the FormHandler mailing list or irc channel (#formhandler on irc.perl.org) to discuss. package Test::Wizard; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Wizard'; has_field 'foo'; has_field 'bar'; has_field 'zed'; has_page 'one' => ( fields => ['foo'] ); has_page 'two' => ( fields => ['bar'] ); has_page 'three' => ( fields => ['zed'] ); ... my $stash = {}; my $wizard = Test::Wizard->new( stash => $stash ); $wizard->process( params => $params ); =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/lib/HTML/FormHandler.pm0000644000077000007700000015626712221042077020226 0ustar gshankgshankpackage HTML::FormHandler; # ABSTRACT: HTML forms using Moose use Moose; extends 'HTML::FormHandler::Base'; # to make some methods overridable by roles with 'HTML::FormHandler::Model', 'HTML::FormHandler::Fields', 'HTML::FormHandler::BuildFields', 'HTML::FormHandler::TraitFor::I18N'; with 'HTML::FormHandler::InitResult'; with 'HTML::FormHandler::Widget::ApplyRole'; with 'HTML::FormHandler::Traits'; with 'HTML::FormHandler::Blocks'; use Carp; use Class::MOP; use HTML::FormHandler::Result; use HTML::FormHandler::Field; use Try::Tiny; use MooseX::Types::LoadableClass qw/ LoadableClass /; use namespace::autoclean; use HTML::FormHandler::Merge ('merge'); use Sub::Name; use Data::Clone; use 5.008; # always use 5 digits after decimal because of toolchain issues our $VERSION = '0.40050'; # for consistency in api with field nodes sub form { shift } sub is_form { 1 } sub has_form { 1 } # Moose attributes has 'name' => ( isa => 'Str', is => 'rw', default => sub { return 'form' . int( rand 1000 ) } ); sub full_name { '' } sub full_accessor { '' } has 'parent' => ( is => 'rw' ); has 'result' => ( isa => 'HTML::FormHandler::Result', is => 'ro', writer => '_set_result', clearer => 'clear_result', lazy => 1, builder => 'build_result', predicate => 'has_result', handles => [ 'input', '_set_input', '_clear_input', 'has_input', 'value', '_set_value', '_clear_value', 'has_value', 'add_result', 'results', 'validated', 'ran_validation', 'is_valid', 'form_errors', 'all_form_errors', 'push_form_errors', 'clear_form_errors', 'has_form_errors', 'num_form_errors', ], ); sub build_result { my $self = shift; my $result_class = 'HTML::FormHandler::Result'; if ( $self->widget_form ) { my $role = $self->get_widget_role( $self->widget_form, 'Form' ); $result_class = $result_class->with_traits( $role ); } my $result = $result_class->new( name => $self->name, form => $self ); return $result; } has 'index' => ( is => 'ro', isa => 'HashRef[HTML::FormHandler::Field]', traits => ['Hash'], default => sub {{}}, handles => { add_to_index => 'set', field_from_index => 'get', field_in_index => 'exists', } ); has '_repeatable_fields' => ( is => 'rw', isa => 'ArrayRef', traits => ['Array'], default => sub {[]}, handles => { add_repeatable_field => 'push', has_repeatable_fields => 'count', all_repeatable_fields => 'elements', }, ); has 'field_traits' => ( is => 'ro', traits => ['Array'], isa => 'ArrayRef', default => sub {[]}, handles => { 'has_field_traits' => 'count' } ); has 'widget_name_space' => ( is => 'ro', isa => 'HFH::ArrayRefStr', traits => ['Array'], default => sub {[]}, coerce => 1, handles => { add_widget_name_space => 'push', }, ); # it only really makes sense to set these before widget_form is applied in BUILD has 'widget_form' => ( is => 'ro', isa => 'Str', default => 'Simple', writer => 'set_widget_form' ); has 'widget_wrapper' => ( is => 'ro', isa => 'Str', default => 'Simple', writer => 'set_widget_wrapper' ); has 'do_form_wrapper' => ( is => 'rw', builder => 'build_do_form_wrapper' ); sub build_do_form_wrapper { 0 } has 'no_widgets' => ( is => 'ro', isa => 'Bool' ); has 'no_preload' => ( is => 'ro', isa => 'Bool' ); has 'no_update' => ( is => 'rw', isa => 'Bool', clearer => 'clear_no_update' ); has 'active' => ( is => 'rw', traits => ['Array'], isa => 'ArrayRef[Str]', default => sub {[]}, handles => { add_active => 'push', has_active => 'count', clear_active => 'clear', } ); has 'inactive' => ( is => 'rw', traits => ['Array'], isa => 'ArrayRef[Str]', default => sub {[]}, handles => { add_inactive => 'push', has_inactive => 'count', clear_inactive => 'clear', } ); # object with which to initialize has 'init_object' => ( is => 'rw', clearer => 'clear_init_object' ); has 'update_field_list' => ( is => 'rw', isa => 'HashRef', default => sub {{}}, traits => ['Hash'], handles => { clear_update_field_list => 'clear', has_update_field_list => 'count', set_update_field_list => 'set', }, ); has 'defaults' => ( is => 'rw', isa => 'HashRef', default => sub {{}}, traits => ['Hash'], handles => { has_defaults => 'count', clear_defaults => 'clear' }, ); has 'use_defaults_over_obj' => ( is => 'rw', isa => 'Bool', clearer => 'clear_use_defaults_over_obj' ); has 'use_init_obj_over_item' => ( is => 'rw', isa => 'Bool', clearer => 'clear_use_init_obj_over_item' ); has 'use_init_obj_when_no_accessor_in_item' => ( is => 'rw', isa => 'Bool' ); has 'use_fields_for_input_without_param' => ( is => 'rw', isa => 'Bool' ); # flags has [ 'verbose', 'processed', 'did_init_obj' ] => ( isa => 'Bool', is => 'rw' ); has 'user_data' => ( isa => 'HashRef', is => 'rw' ); has 'ctx' => ( is => 'rw', weak_ref => 1, clearer => 'clear_ctx' ); has 'html_prefix' => ( isa => 'Bool', is => 'ro' ); has 'active_column' => ( isa => 'Str', is => 'ro' ); has 'http_method' => ( isa => 'Str', is => 'ro', default => 'post' ); has 'enctype' => ( is => 'rw', isa => 'Str' ); has 'error_message' => ( is => 'rw', predicate => 'has_error_message', clearer => 'clear_error_message' ); has 'success_message' => ( is => 'rw', predicate => 'has_success_message', clearer => 'clear_success_message' ); has 'info_message' => ( is => 'rw', predicate => 'has_info_message', clearer => 'clear_info_message' ); # deprecated has 'css_class' => ( isa => 'Str', is => 'ro' ); has 'style' => ( isa => 'Str', is => 'rw' ); has 'is_html5' => ( isa => 'Bool', is => 'ro', default => 0 ); # deprecated. use form_element_attr instead has 'html_attr' => ( is => 'rw', traits => ['Hash'], default => sub { {} }, handles => { has_html_attr => 'count', set_html_attr => 'set', delete_html_attr => 'delete' }, trigger => \&_html_attr_set, ); sub _html_attr_set { my ( $self, $value ) = @_; my $class = delete $value->{class}; $self->form_element_attr($value); $self->add_form_element_class if $class; } { # create the attributes and methods for # form_element_attr, build_form_element_attr, form_element_class, # form_wrapper_attr, build_form_wrapper_atrr, form_wrapper_class no strict 'refs'; foreach my $attr ('form_wrapper', 'form_element' ) { my $add_meth = "add_${attr}_class"; has "${attr}_attr" => ( is => 'rw', traits => ['Hash'], builder => "build_${attr}_attr", handles => { "has_${attr}_attr" => 'count', "get_${attr}_attr" => 'get', "set_${attr}_attr" => 'set', "delete_${attr}_attr" => 'delete', "exists_${attr}_attr" => 'exists', }, ); # create builders for _attr my $attr_builder = __PACKAGE__ . "::build_${attr}_attr"; *$attr_builder = subname $attr_builder, sub {{}}; # create the 'class' slots has "${attr}_class" => ( is => 'rw', isa => 'HFH::ArrayRefStr', traits => ['Array'], coerce => 1, builder => "build_${attr}_class", handles => { "has_${attr}_class" => 'count', "_add_${attr}_class" => 'push', }, ); # create builders for classes my $class_builder = __PACKAGE__ . "::build_${attr}_class"; *$class_builder = subname $class_builder, sub {[]}; # create wrapper for add_to_ to accept arrayref my $add_to_class = __PACKAGE__ . "::add_${attr}_class"; my $_add_meth = __PACKAGE__ . "::_add_${attr}_class"; # create add method that takes an arrayref *$add_to_class = subname $add_to_class, sub { shift->$_add_meth((ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_)); } } } sub attributes { shift->form_element_attributes(@_) } sub form_element_attributes { my ( $self, $result ) = @_; $result ||= $self->result; my $attr = {}; $attr->{id} = $self->name; $attr->{action} = $self->action if $self->action; $attr->{method} = $self->http_method if $self->http_method; $attr->{enctype} = $self->enctype if $self->enctype; $attr->{class} = $self->css_class if $self->css_class; $attr->{style} = $self->style if $self->style; $attr = {%$attr, %{$self->form_element_attr}}; my $class = [@{$self->form_element_class}]; $attr->{class} = $class if @$class; my $mod_attr = $self->html_attributes($self, 'form_element', $attr); return ref $mod_attr eq 'HASH' ? $mod_attr : $attr; } sub form_wrapper_attributes { my ( $self, $result ) = @_; $result ||= $self->result; my $attr = {%{$self->form_wrapper_attr}}; my $class = [@{$self->form_wrapper_class}]; $attr->{class} = $class if @$class; my $mod_attr = $self->html_attributes($self, 'form_wrapper', $attr); return ref $mod_attr eq 'HASH' ? $mod_attr : $attr; } sub html_attributes { my ( $self, $obj, $type, $attrs, $result ) = @_; # deprecated 'field_html_attributes'; name changed, remove eventually if( $self->can('field_html_attributes') ) { $attrs = $self->field_html_attributes( $obj, $type, $attrs, $result ); } return $attrs; } sub has_flag { my ( $self, $flag_name ) = @_; return unless $self->can($flag_name); return $self->$flag_name; } has 'form_tags' => ( traits => ['Hash'], isa => 'HashRef', is => 'ro', builder => 'build_form_tags', handles => { _get_tag => 'get', set_tag => 'set', tag_exists => 'exists', has_tag => 'exists', }, ); sub build_form_tags {{}} sub get_tag { my ( $self, $name ) = @_; return '' unless $self->tag_exists($name); my $tag = $self->_get_tag($name); return $self->$tag if ref $tag eq 'CODE'; return $tag unless $tag =~ /^%/; ( my $block_name = $tag ) =~ s/^%//; return $self->form->block($block_name)->render if ( $self->form && $self->form->block_exists($block_name) ); return ''; } has 'for_js' => ( isa => 'HashRef', traits => ['Hash'], is => 'rw', default => sub { {} }, handles => { set_for_js => 'set', has_for_js => 'count', clear_for_js => 'clear', } ); has 'action' => ( is => 'rw' ); has 'posted' => ( is => 'rw', isa => 'Bool', clearer => 'clear_posted', predicate => 'has_posted' ); has 'params' => ( traits => ['Hash'], isa => 'HashRef', is => 'rw', default => sub { {} }, trigger => sub { shift->_munge_params(@_) }, handles => { set_param => 'set', get_param => 'get', clear_params => 'clear', has_params => 'count', }, ); sub submitted { shift->has_params } has 'dependency' => ( isa => 'ArrayRef', is => 'rw' ); has '_required' => ( traits => ['Array'], isa => 'ArrayRef[HTML::FormHandler::Field]', is => 'rw', default => sub { [] }, handles => { clear_required => 'clear', add_required => 'push', } ); # these messages could apply to either fields or form has 'messages' => ( is => 'rw', isa => 'HashRef', traits => ['Hash'], builder => 'build_messages', handles => { '_get_form_message' => 'get', '_has_form_message' => 'exists', 'set_message' => 'set', }, ); sub build_messages { {} } my $class_messages = {}; sub get_class_messages { return $class_messages; } sub get_message { my ( $self, $msg ) = @_; return $self->_get_form_message($msg) if $self->_has_form_message($msg); return $self->get_class_messages->{$msg}; } sub all_messages { my $self = shift; return { %{$self->get_class_messages}, %{$self->messages} }; } has 'params_class' => ( is => 'ro', isa => LoadableClass, coerce => 1, default => 'HTML::FormHandler::Params', ); has 'params_args' => ( is => 'ro', isa => 'ArrayRef' ); sub BUILDARGS { my $class = shift; if ( scalar @_ == 1 && ref( $_[0]) ne 'HASH' ) { my $arg = $_[0]; return blessed($arg) ? { item => $arg } : { item_id => $arg }; } return $class->SUPER::BUILDARGS(@_); } sub BUILD { my $self = shift; $self->before_build; # hook to allow customizing forms # HTML::FormHandler::Widget::Form::Simple is applied in Base $self->apply_widget_role( $self, $self->widget_form, 'Form' ) unless ( $self->no_widgets || $self->widget_form eq 'Simple' ); $self->_build_fields; # create the form fields (BuildFields.pm) $self->build_active if $self->has_active || $self->has_inactive || $self->has_flag('is_wizard'); $self->after_build; # hook for customizing return if defined $self->item_id && !$self->item; # Load values from object (if any) # Would rather not load results at all here, but skipping it breaks # existing apps that perform certain actions between 'new' and 'process'. # Added fudge flag no_preload to enable skipping. # A well-behaved program that always does ->process shouldn't need this preloading. unless( $self->no_preload ) { if ( my $init_object = $self->use_init_obj_over_item ? ($self->init_object || $self->item) : ( $self->item || $self->init_object ) ) { $self->_result_from_object( $self->result, $init_object ); } else { $self->_result_from_fields( $self->result ); } } $self->dump_fields if $self->verbose; return; } sub before_build {} sub after_build {} sub process { my $self = shift; warn "HFH: process ", $self->name, "\n" if $self->verbose; $self->clear if $self->processed; $self->setup_form(@_); $self->validate_form if $self->posted; $self->update_model if ( $self->validated && !$self->no_update ); $self->after_update_model if ( $self->validated && !$self->no_update ); $self->dump_fields if $self->verbose; $self->processed(1); return $self->validated; } sub run { my $self = shift; $self->setup_form(@_); $self->validate_form if $self->posted; $self->update_model if ( $self->validated && !$self->no_update );; my $result = $self->result; $self->clear; return $result; } sub after_update_model { my $self = shift; # This an attempt to reload the repeatable # relationships after the database is updated, so that we get the # primary keys of the repeatable elements. Otherwise, if a form # is re-presented, repeatable elements without primary keys may # be created again. There is no reliable way to connect up # existing repeatable elements with their db-created primary keys. if ( $self->has_repeatable_fields && $self->item ) { foreach my $field ( $self->all_repeatable_fields ) { # Check to see if there are any repeatable subfields with # null primary keys, so we can skip reloading for the case # where all repeatables have primary keys. my $needs_reload = 0; foreach my $sub_field ( $field->fields ) { if ( $sub_field->has_flag('is_compound') && $sub_field->has_primary_key ) { foreach my $pk_field ( @{ $sub_field->primary_key } ) { $needs_reload++ unless $pk_field->fif; } last if $needs_reload; } } next unless $needs_reload; my @names = split( /\./, $field->full_name ); my $rep_item = $self->find_sub_item( $self->item, \@names ); # $rep_item is a single row or an array of rows or undef # If we found a database item for the repeatable, replace # the existing result with a result derived from the item. if ( ref $rep_item ) { my $parent = $field->parent; my $result = $field->result; my $new_result = HTML::FormHandler::Field::Result->new( name => $field->name, parent => $parent, ); $field->init_state; $new_result = $field->_result_from_object( $result, $rep_item ); # find index of existing result my $index = $parent->result->find_result_index( sub { $_ == $result } ); # replace existing result with new result $parent->result->set_result_at_index( $index, $new_result ); } } } } sub db_validate { my $self = shift; my $fif = $self->fif; $self->process($fif); return $self->validated; } sub clear { my $self = shift; $self->clear_data; $self->clear_params; $self->clear_posted; $self->clear_ctx; $self->processed(0); $self->did_init_obj(0); $self->clear_result; $self->clear_use_defaults_over_obj; $self->clear_use_init_obj_over_item; $self->clear_no_update; $self->clear_info_message; $self->clear_for_js; } sub values { shift->value } # deprecated? sub error_field_names { my $self = shift; my @error_fields = $self->error_fields; return map { $_->name } @error_fields; } sub errors { my $self = shift; my @error_fields = $self->error_fields; my @errors = $self->all_form_errors; push @errors, map { $_->all_errors } @error_fields; return @errors; } sub errors_by_id { my $self = shift; my %errors; $errors{$_->id} = [$_->all_errors] for $self->error_fields; return \%errors; } sub errors_by_name { my $self = shift; my %errors; $errors{$_->html_name} = [$_->all_errors] for $self->error_fields; return \%errors; } sub build_errors { my $self = shift; # this puts the errors in the result foreach my $err_res (@{$self->result->error_results}) { $self->result->_push_errors($err_res->all_errors); } } sub uuid { my $form = shift; require Data::UUID; my $uuid = Data::UUID->new->create_str; return qq[]; } sub validate_form { my $self = shift; my $params = $self->params; $self->_set_dependency; # set required dependencies $self->_fields_validate; $self->validate; # empty method for users $self->validate_model; # model specific validation $self->fields_set_value; $self->build_errors; # move errors to result $self->_clear_dependency; $self->clear_posted; $self->ran_validation(1); $self->dump_validated if $self->verbose; return $self->validated; } sub validate { 1 } sub has_errors { my $self = shift; return $self->has_error_fields || $self->has_form_errors; } sub num_errors { my $self = shift; return $self->num_error_fields + $self->num_form_errors; } sub setup_form { my ( $self, @args ) = @_; if ( @args == 1 ) { $self->params( $args[0] ); } elsif ( @args > 1 ) { my $hashref = {@args}; while ( my ( $key, $value ) = each %{$hashref} ) { confess "invalid attribute '$key' passed to setup_form" unless $self->can($key); $self->$key($value); } } if ( $self->item_id && !$self->item ) { $self->item( $self->build_item ); } $self->clear_result; $self->set_active; $self->update_fields; # initialization of Repeatable fields and Select options # will be done in _result_from_object when there's an initial object # in _result_from_input when there are params # and by _result_from_fields for empty forms $self->posted(1) if ( $self->has_params && !$self->has_posted ); if ( !$self->did_init_obj ) { if ( my $init_object = $self->use_init_obj_over_item ? ($self->init_object || $self->item) : ( $self->item || $self->init_object ) ) { $self->_result_from_object( $self->result, $init_object ); } elsif ( !$self->posted ) { # no initial object. empty form must be initialized $self->_result_from_fields( $self->result ); } } # if params exist and if posted flag is either not set or set to true my $params = clone( $self->params ); if ( $self->posted ) { $self->clear_result; $self->_result_from_input( $self->result, $params, 1 ); } } # if active => [...] is set at process time, set 'active' flag sub set_active { my $self = shift; if( $self->has_active ) { foreach my $fname (@{$self->active}) { my $field = $self->field($fname); if ( $field ) { $field->_active(1); } else { warn "field $fname not found to set active"; } } $self->clear_active; } if( $self->has_inactive ) { foreach my $fname (@{$self->inactive}) { my $field = $self->field($fname); if ( $field ) { $field->_active(0); } else { warn "field $fname not found to set inactive"; } } $self->clear_inactive; } } # if active => [...] is set at build time, remove 'inactive' flags sub build_active { my $self = shift; if( $self->has_active ) { foreach my $fname (@{$self->active}) { my $field = $self->field($fname); if( $field ) { $field->clear_inactive; } else { warn "field $fname not found to set active"; } } $self->clear_active; } if( $self->has_inactive ) { foreach my $fname (@{$self->inactive}) { my $field = $self->field($fname); if( $field ) { $field->inactive(1); } else { warn "field $fname not found to set inactive"; } } $self->clear_inactive; } } sub fif { shift->fields_fif(@_) } # this is subclassed by the model, which may # do a lot more than this sub init_value { my ( $self, $field, $value ) = @_; $field->init_value($value); $field->_set_value($value); } sub _set_dependency { my $self = shift; my $depends = $self->dependency || return; my $params = $self->params; for my $group (@$depends) { next if @$group < 2; # process a group of fields for my $name (@$group) { # is there a value? my $value = $params->{$name}; next unless defined $value; # The exception is a boolean can be zero which we count as not set. # This is to allow requiring a field when a boolean is true. my $field = $self->field($name); next if $self->field($name)->type eq 'Boolean' && $value == 0; next unless HTML::FormHandler::Field::has_some_value($value); # one field was found non-blank, so set all to required for (@$group) { my $field = $self->field($_); next unless $field && !$field->required; $self->add_required($field); # save for clearing later. $field->required(1); } last; } } } sub _clear_dependency { my $self = shift; $_->required(0) for @{$self->_required}; $self->clear_required; } sub peek { my $self = shift; my $string = "Form " . $self->name . "\n"; my $indent = ' '; foreach my $field ( $self->sorted_fields ) { $string .= $field->peek( $indent ); } return $string; } sub _munge_params { my ( $self, $params, $attr ) = @_; my $_fix_params = $self->params_class->new( @{ $self->params_args || [] } ); my $new_params = $_fix_params->expand_hash($params); if ( $self->html_prefix ) { $new_params = $new_params->{ $self->name }; } $new_params = {} if !defined $new_params; $self->{params} = $new_params; } sub add_form_error { my ( $self, @message ) = @_; unless ( defined $message[0] ) { @message = ('form is invalid'); } my $out; try { $out = $self->_localize(@message); } catch { die "Error occurred localizing error message for " . $self->name . ". $_"; }; $self->push_form_errors($out); return; } sub get_default_value { } sub _can_deflate { } sub update_fields { my $self = shift; if( $self->has_update_field_list ) { my $updates = $self->update_field_list; foreach my $field_name ( keys %{$updates} ) { $self->update_field($field_name, $updates->{$field_name} ); } $self->clear_update_field_list; } if( $self->has_defaults ) { my $defaults = $self->defaults; foreach my $field_name ( keys %{$defaults} ) { $self->update_field($field_name, { default => $defaults->{$field_name} } ); } $self->clear_defaults; } } sub update_field { my ( $self, $field_name, $updates ) = @_; my $field = $self->field($field_name); unless( $field ) { die "Field $field_name is not found and cannot be updated by update_fields"; } while ( my ( $attr_name, $attr_value ) = each %{$updates} ) { confess "invalid attribute '$attr_name' passed to update_field" unless $field->can($attr_name); if( $attr_name eq 'tags' ) { $field->set_tag(%$attr_value); } else { $field->$attr_name($attr_value); } } } __PACKAGE__->meta->make_immutable; use namespace::autoclean; 1; __END__ =pod =head1 NAME HTML::FormHandler - HTML forms using Moose =head1 VERSION version 0.40050 =head1 SYNOPSIS See the manual at L. use HTML::FormHandler; # or a custom form: use MyApp::Form::User; my $form = HTML::FormHandler->new( .... ); $form->process( params => $params ); my $rendered_form = $form->render; if( $form->validated ) { # perform validated form actions } else { # perform non-validated actions } Or, if you want to use a form 'result' (which contains only the form values and error messages) instead: use MyApp::Form; # or a generic form: use HTML::FormHandler; my $form = MyApp::Form->new( .... ); my $result = $form->run( params => $params ); if( $result->validated ) { # perform validated form actions } else { # perform non-validated actions $result->render; } An example of a custom form class: package MyApp::Form::User; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; use Moose::Util::TypeConstraints; has '+item_class' => ( default => 'User' ); has_field 'name' => ( type => 'Text' ); has_field 'age' => ( type => 'PosInteger', apply => [ 'MinimumAge' ] ); has_field 'birthdate' => ( type => 'DateTime' ); has_field 'birthdate.month' => ( type => 'Month' ); has_field 'birthdate.day' => ( type => 'MonthDay' ); has_field 'birthdate.year' => ( type => 'Year' ); has_field 'hobbies' => ( type => 'Multiple' ); has_field 'address' => ( type => 'Text' ); has_field 'city' => ( type => 'Text' ); has_field 'state' => ( type => 'Select' ); has_field 'email' => ( type => 'Email' ); has '+dependency' => ( default => sub { [ ['address', 'city', 'state'], ] } ); subtype 'MinimumAge' => as 'Int' => where { $_ > 13 } => message { "You are not old enough to register" }; no HTML::FormHandler::Moose; 1; A dynamic form - one that does not use a custom form class - may be created using the 'field_list' attribute to set fields: my $form = HTML::FormHandler->new( name => 'user_form', item => $user, field_list => [ 'username' => { type => 'Text', apply => [ { check => qr/^[0-9a-z]*\z/, message => 'Contains invalid characters' } ], }, 'select_bar' => { type => 'Select', options => \@select_options, multiple => 1, size => 4, }, ], ); FormHandler does not provide a custom controller for Catalyst because it isn't necessary. Interfacing to FormHandler is only a couple of lines of code. See L for more details, or L. =head1 DESCRIPTION *** Although documentation in this file provides some overview, it is mainly intended for API documentation. See L for an introduction, with links to other documentation. HTML::FormHandler maintains a clean separation between form construction and form rendering. It allows you to define your forms and fields in a number of flexible ways. Although it provides renderers for HTML, you can define custom renderers for any kind of presentation. HTML::FormHandler allows you to define form fields and validators. It can be used for both database and non-database forms, and will automatically update or create rows in a database. It can be used to process structured data that doesn't come from an HTML form. One of its goals is to keep the controller/application program interface as simple as possible, and to minimize the duplication of code. In most cases, interfacing your controller to your form is only a few lines of code. With FormHandler you shouldn't have to spend hours trying to figure out how to make a simple HTML change that would take one minute by hand. Because you _can_ do it by hand. Or you can automate HTML generation as much as you want, with template widgets or pure Perl rendering classes, and stay completely in control of what, where, and how much is done automatically. You can define custom renderers and display your rendered forms however you want. You can split the pieces of your forms up into logical parts and compose complete forms from FormHandler classes, roles, fields, collections of validations, transformations and Moose type constraints. You can write custom methods to process forms, add any attribute you like, and use Moose method modifiers. FormHandler forms are Perl classes, so there's a lot of flexibility in what you can do. HTML::FormHandler provides rendering through roles which are applied to form and field classes (although there's no reason you couldn't write a renderer as an external object either). There are currently two flavors: all-in-one solutions like L and L that contain methods for rendering field widget classes, and the L roles, which are more atomic roles which are automatically applied to fields and form. See L for more details. (And you can easily use hand-built forms - FormHandler doesn't care.) The typical application for FormHandler would be in a Catalyst, DBIx::Class, Template Toolkit web application, but use is not limited to that. FormHandler can be used in any Perl application. More Formhandler documentation and a tutorial can be found in the manual at L. =head1 ATTRIBUTES and METHODS =head2 Creating a form with 'new' The new constructor takes name/value pairs: MyForm->new( item => $item, ); No attributes are required on new. The form's fields will be built from the form definitions. If no initial data object or defaults have been provided, the form will be empty. Most attributes can be set on either 'new' or 'process'. The common attributes to be passed in to the constructor for a database form are either item_id and schema or item: item_id - database row primary key item - database row object schema - (for DBIC) the DBIx::Class schema The following are sometimes passed in, but are also often set in the form class: item_class - source name of row dependency - (see dependency) field_list - an array of field definitions init_object - a hashref or object to provide initial values Examples of creating a form object with new: my $form = MyApp::Form::User->new; # database form using a row object my $form = MyApp::Form::Member->new( item => $row ); # a dynamic form (no form class has been defined) my $form = HTML::FormHandler::Model::DBIC->new( item_id => $id, item_class => 'User', schema => $schema, field_list => [ name => 'Text', active => 'Boolean', submit_btn => 'Submit', ], ); See the model class for more information about 'item', 'item_id', 'item_class', and 'schema' (for the DBIC model). L. FormHandler forms are handled in two steps: 1) create with 'new', 2) handle with 'process'. FormHandler doesn't care whether most parameters are set on new or process or update, but a 'field_list' argument must be passed in on 'new' since the fields are built at construction time. If you want to update field attributes on the 'process' call, you can use an 'update_field_list' or 'defaults' hashref attribute , or subclass update_fields in your form. The 'update_field_list' hashref can be used to set any field attribute. The 'defaults' hashref will update only the 'default' attribute in the field. (There are a lot of ways to set defaults. See L.) $form->process( defaults => { foo => 'foo_def', bar => 'bar_def' } ); $form->process( update_field_list => { foo => { label => 'New Label' } }); Field results are built on the 'new' call, but will then be re-built on the process call. If you always use 'process' before rendering the form, accessing fields, etc, you can set the 'no_preload' flag to skip this step. =head2 Processing the form =head3 process Call the 'process' method on your form to perform validation and update. A database form must have either an item (row object) or a schema, item_id (row primary key), and item_class (usually set in the form). A non-database form requires only parameters. $form->process( item => $book, params => $c->req->parameters ); $form->process( item_id => $item_id, schema => $schema, params => $c->req->parameters ); $form->process( params => $c->req->parameters ); This process method returns the 'validated' flag (C<< $form->validated >>). If it is a database form and the form validates, the database row will be updated. After the form has been processed, you can get a parameter hashref suitable for using to fill in the form from C<< $form->fif >>. A hash of inflated values (that would be used to update the database for a database form) can be retrieved with C<< $form->value >>. If you don't want to update the database on this process call, you can set the 'no_update' flag: $form->process( item => $book, params => $params, no_update => 1 ); =head3 params Parameters are passed in when you call 'process'. HFH gets data to validate and store in the database from the params hash. If the params hash is empty, no validation is done, so it is not necessary to check for POST before calling C<< $form->process >>. (Although see the 'posted' option for complications.) Params can either be in the form of CGI/HTTP style params: { user_name => "Joe Smith", occupation => "Programmer", 'addresses.0.street' => "999 Main Street", 'addresses.0.city' => "Podunk", 'addresses.0.country' => "UT", 'addresses.0.address_id' => "1", 'addresses.1.street' => "333 Valencia Street", 'addresses.1.city' => "San Francisco", 'addresses.1.country' => "UT", 'addresses.1.address_id' => "2", } or as structured data in the form of hashes and lists: { addresses => [ { city => 'Middle City', country => 'GK', address_id => 1, street => '101 Main St', }, { city => 'DownTown', country => 'UT', address_id => 2, street => '99 Elm St', }, ], 'occupation' => 'management', 'user_name' => 'jdoe', } CGI style parameters will be converted to hashes and lists for HFH to operate on. =head3 posted Note that FormHandler by default uses empty params as a signal that the form has not actually been posted, and so will not attempt to validate a form with empty params. Most of the time this works OK, but if you have a small form with only the controls that do not return a post parameter if unselected (checkboxes and select lists), then the form will not be validated if everything is unselected. For this case you can either add a hidden field as an 'indicator', or use the 'posted' flag: $form->process( posted => ($c->req->method eq 'POST'), params => ... ); The 'posted' flag also works to prevent validation from being performed if there are extra params in the params hash and it is not a 'POST' request. =head2 Getting data out =head3 fif (fill in form) If you don't use FormHandler rendering and want to fill your form values in using some other method (such as with HTML::FillInForm or using a template) this returns a hash of values that are equivalent to params which you may use to fill in your form. The fif value for a 'title' field in a TT form: [% form.fif.title %] Or you can use the 'fif' method on individual fields: [% form.field('title').fif %] If you use FormHandler to render your forms or field you probably won't use these methods. =head3 value Returns a hashref of all field values. Useful for non-database forms, or if you want to update the database yourself. The 'fif' method returns a hashref with the field names for the keys and the field's 'fif' for the values; 'value' returns a hashref with the field accessors for the keys, and the field's 'value' (possibly inflated) for the values. Forms containing arrays to be processed with L will have parameters with dots and numbers, like 'addresses.0.city', while the values hash will transform the fields with numbers to arrays. =head2 Accessing and setting up fields Fields are declared with a number of attributes which are defined in L. If you want additional attributes you can define your own field classes (or apply a role to a field class - see L). The field 'type' (used in field definitions) is the short class name of the field class, used when searching the 'field_name_space' for the field class. =head3 has_field The most common way of declaring fields is the 'has_field' syntax. Using the 'has_field' syntax sugar requires C< use HTML::FormHandler::Moose; > or C< use HTML::FormHandler::Moose::Role; > in a role. See L use HTML::FormHandler::Moose; has_field 'field_name' => ( type => 'FieldClass', .... ); =head3 field_list A 'field_list' is an array of field definitions which can be used as an alternative to 'has_field' in small, dynamic forms to create fields. field_list => [ field_one => { type => 'Text', required => 1 }, field_two => 'Text, ] The field_list array takes elements which are either a field_name key pointing to a 'type' string or a field_name key pointing to a hashref of field attributes. You can also provide an array of hashref elements with the name as an additional attribute. The field list can be set inside a form class, when you want to add fields to the form depending on some other state, although you can also create all the fields and set some of them inactive. sub field_list { my $self = shift; my $fields = $self->schema->resultset('SomeTable')-> search({user_id => $self->user_id, .... }); my @field_list; while ( my $field = $fields->next ) { < create field list > } return \@field_list; } =head3 update_field_list Used to dynamically set particular field attributes on the 'process' (or 'run') call. (Will not create fields.) $form->process( update_field_list => { foo_date => { format => '%m/%e/%Y', date_start => '10-01-01' } }, params => $params ); The 'update_field_list' is processed by the 'update_fields' form method, which can also be used in a form to do specific field updates: sub update_fields { my $self = shift; $self->field('foo')->temp( 'foo_temp' ); $self->field('bar')->default( 'foo_value' ); $self->next::method(); } (Note that you although you can set a field's 'default', you can't set a field's 'value' directly here, since it will be overwritten by the validation process. Set the value in a field validation method.) =head3 update_subfields Yet another way to provide settings for the field, except this one is intended for use in roles and compound fields, and is only executed when the form is initially built. It takes the same field name keys as 'update_field_list', plus 'all', 'by_flag', and 'by_type'. sub build_update_subfields {{ all => { tags => { wrapper_tag => 'p' } }, foo => { element_class => 'blue' }, }} The 'all' hash key will apply updates to all fields. (Conflicting attributes in a field definition take precedence.) The 'by_flag' hash key will apply updates to fields with a particular flag. The currently supported subkeys are 'compound', 'contains', and 'repeatable'. (For repeatable instances, in addition to 'contains' you can also use the 'repeatable' key and the 'init_contains' attribute.) This is useful for turning on the rendering wrappers for compounds and repeatables, which are off by default. (The repeatable instances are wrapped by default.) sub build_update_subfields {{ by_flag => { compound => { do_wrapper => 1 } }, by_type => { Select => { element_class => ['sel_elem'] } }, }} The 'by_type' hash key will provide values to all fields of a particular type. =head3 defaults This is a more specialized version of the 'update_field_list'. It can be used to provide 'default' settings for fields, in a shorthand way (you don't have to say 'default' for every field). $form->process( defaults => { foo => 'this_foo', bar => 'this_bar' }, ... ); =head3 active/inactive A field can be marked 'inactive' and set to active at new or process time by specifying the field name in the 'active' array: has_field 'foo' => ( type => 'Text', inactive => 1 ); ... my $form = MyApp::Form->new( active => ['foo'] ); ... $form->process( active => ['foo'] ); Or a field can be a normal active field and set to inactive at new or process time: has_field 'bar'; ... my $form = MyApp::Form->new( inactive => ['foo'] ); ... $form->process( inactive => ['foo'] ); Fields specified as active/inactive on new will have the form's inactive/active arrayref cleared and the field's inactive flag set appropriately, so that the state will be effective for the life of the form object. Fields specified as active/inactive on 'process' will have the field's '_active' flag set for the life of the request (the _active flag will be cleared when the form is cleared). The 'sorted_fields' method returns only active fields, sorted according to the 'order' attribute. The 'fields' method returns all fields. foreach my $field ( $self->sorted_fields ) { ... } You can test whether a field is active by using the field 'is_active' and 'is_inactive' methods. =head3 field_name_space Use to look for field during form construction. If a field is not found with the field_name_space (or HTML::FormHandler/HTML::FormHandlerX), the 'type' must start with a '+' and be the complete package name. =head3 fields The array of fields, objects of L or its subclasses. A compound field will itself have an array of fields, so this is a tree structure. =head3 sorted_fields Returns those fields from the fields array which are currently active. This is the method that returns the fields that are looped through when rendering. =head3 field($name), subfield($name) 'field' is the method that is usually called to access a field: my $title = $form->field('title')->value; [% f = form.field('title') %] my $city = $form->field('addresses.0.city')->value; Pass a second true value to die on errors. Since fields are searched for using the form as a base, if you want to find a sub field in a compound field method, the 'subfield' method may be more useful, since you can search starting at the current field. The 'chained' method also works: -- in a compound field -- $self->field('media.caption'); # fails $self->field('media')->field('caption'); # works $self->subfield('media.caption'); # works =head2 Constraints and validation Most validation is performed on a per-field basis, and there are a number of different places in which validation can be performed. See also L. =head3 Form class validation for individual fields You can define a method in your form class to perform validation on a field. This method is the equivalent of the field class validate method except it is in the form class, so you might use this validation method if you don't want to create a field subclass. It has access to the form ($self) and the field. This method is called after the field class 'validate' method, and is not called if the value for the field is empty ('', undef). (If you want an error message when the field is empty, use the 'required' flag and message or the form 'validate' method.) The name of this method can be set with 'set_validate' on the field. The default is 'validate_' plus the field name: sub validate_testfield { my ( $self, $field ) = @_; ... } If the field name has dots they should be replaced with underscores. Note that you can also provide a coderef which will be a method on the field: has_field 'foo' => ( validate_method => \&validate_foo ); =head3 validate This is a form method that is useful for cross checking values after they have been saved as their final validated value, and for performing more complex dependency validation. It is called after all other field validation is done, and whether or not validation has succeeded, so it has access to the post-validation values of all the fields. This is the best place to do validation checks that depend on the values of more than one field. =head2 Accessing errors Also see L. Set an error in a field with C<< $field->add_error('some error string'); >>. Set a form error not tied to a specific field with C<< $self->add_form_error('another error string'); >>. The 'add_error' and 'add_form_error' methods call localization. If you want to skip localization for a particular error, you can use 'push_errors' or 'push_form_errors' instead. has_errors - returns true or false error_fields - returns list of fields with errors errors - returns array of error messages for the entire form num_errors - number of errors in form Each field has an array of error messages. (errors, has_errors, num_errors, clear_errors) $form->field('title')->errors; Compound fields also have an array of error_fields. =head2 Clear form state The clear method is called at the beginning of 'process' if the form object is reused, such as when it is persistent in a Moose attribute, or in tests. If you add other attributes to your form that are set on each request, you may need to clear those yourself. If you do not call the form's 'process' method on a persistent form, such as in a REST controller's non-POST method, or if you only call process when the form is posted, you will also need to call C<< $form->clear >>. The 'run' method which returns a result object always performs 'clear', to keep the form object clean. =head2 Miscellaneous attributes =head3 name The form's name. Useful for multiple forms. Used for the form element 'id'. When 'html_prefix' is set it is used to construct the field 'id' and 'name'. The default is "form" + a one to three digit random number. Because the HTML standards have flip-flopped on whether the HTML form element can contain a 'name' attribute, please set a name attribute using 'form_element_attr'. =head3 init_object An 'init_object' may be used instead of the 'item' to pre-populate the values in the form. This can be useful when populating a form from default values stored in a similar but different object than the one the form is creating. The 'init_object' should be either a hash or the same type of object that the model uses (a DBIx::Class row for the DBIC model). It can be set in a variety of ways: my $form = MyApp::Form->new( init_object => { .... } ); $form->process( init_object => {...}, ... ); has '+init_object' => ( default => sub { { .... } } ); sub init_object { my $self = shift; .... } The method version is useful if the organization of data in your form does not map to an existing or database object in an automatic way, and you need to create a different type of object for initialization. (You might also want to do 'update_model' yourself.) Also see the 'use_init_obj_over_item' and the 'use_init_obj_when_no_accessor_in_item' flags, if you want to provide both an item and an init_object, and use the values from the init_object. The 'use_init_obj_when_no_accessor_in_item' flag is particularly useful when some of the fields in your form come from the database and some are process or environment type flags that are not in the database. You can provide defaults from both a database row and an 'init_object. =head3 ctx Place to store application context for your use in your form's methods. =head3 language_handle See 'language_handle' and '_build_language_handle' in L. =head3 dependency Arrayref of arrayrefs of fields. If one of a group of fields has a value, then all of the group are set to 'required'. has '+dependency' => ( default => sub { [ ['street', 'city', 'state', 'zip' ],] } ); =head2 Flags =head3 validated, is_valid Flag that indicates if form has been validated. You might want to use this flag if you're doing something in between process and returning, such as setting a stash key. ('is_valid' is a synonym for this flag) $form->process( ... ); $c->stash->{...} = ...; return unless $form->validated; =head3 ran_validation Flag to indicate that validation has been run. This flag will be false when the form is initially loaded and displayed, since validation is not run until FormHandler has params to validate. =head3 verbose, dump, peek Flag to dump diagnostic information. See 'dump_fields' and 'dump_validated'. 'Peek' can be useful in diagnosing bugs. It will dump a brief listing of the fields and results. $form->process( ... ); $form->peek; =head3 html_prefix Flag to indicate that the form name is used as a prefix for fields in an HTML form. Useful for multiple forms on the same HTML page. The prefix is stripped off of the fields before creating the internal field name, and added back in when returning a parameter hash from the 'fif' method. For example, the field name in the HTML form could be "book.borrower", and the field name in the FormHandler form (and the database column) would be just "borrower". has '+name' => ( default => 'book' ); has '+html_prefix' => ( default => 1 ); Also see the Field attribute "html_name", a convenience function which will return the form name + "." + field full_name =head3 is_html5 Flag to indicate the fields will render using specialized attributes for html5. Set to 0 by default. =head3 use_defaults_over_obj The 'normal' precedence is that if there is an accessor in the item/init_object that value is used and not the 'default'. This flag makes the defaults of higher precedence. Mainly useful if providing an empty row on create. =head3 use_init_obj_over_item If you are providing both an item and an init_object, and want the init_object to be used for defaults instead of the item. =head2 For use in HTML form_element_attr - hashref for setting arbitrary HTML attributes set in form with: sub build_form_element_attr {...} form_element_class - arrayref for setting form tag class form_wrapper_attr - hashref for form wrapper element attributes set in form with: sub build_form_wrapper_attr {...} form_wrapper_class - arrayref for setting wrapper class do_form_wrapper - flag to wrap the form http_method - For storing 'post' or 'get' action - Store the form 'action' on submission. No default value. uuid - generates a string containing an HTML field with UUID form_tags - hashref of tags for use in rendering code widget_tags - rendering tags to be transferred to fields Discouraged (use form_element_attr instead): css_class - adds a 'class' attribute to the form tag style - adds a 'style' attribute to the form tag enctype - Request enctype Note that the form tag contains an 'id' attribute which is set to the form name. The standards have been flip-flopping over whether a 'name' attribute is valid. It can be set with 'form_element_attr'. The rendering of the HTML attributes is done using the 'process_attrs' function and the 'element_attributes' or 'wrapper_attributes' method, which adds other attributes in for backward compatibility, and calls the 'html_attributes' hook. For HTML attributes, there is a form method hook, 'html_attributes', which can be used to customize/modify/localize form & field HTML attributes. Types: element, wrapper, label, form_element, form_wrapper, checkbox_label sub html_attributes { my ( $self, $obj, $type, $attrs, $result ) = @_; # obj is either form or field $attr->{class} = 'label' if $type eq 'label'; $attr->{placeholder} = $self->_localize($attr->{placeholder}) if exists $attr->{placeholder}; return $attr; } Also see the documentation in L and in L. =head1 SUPPORT IRC: Join #formhandler on irc.perl.org Mailing list: http://groups.google.com/group/formhandler Code repository: http://github.com/gshank/html-formhandler/tree/master Bug tracker: https://rt.cpan.org/Dist/Display.html?Name=HTML-FormHandler =head1 SEE ALSO L L L L L L L L L L L L =head1 CONTRIBUTORS gshank: Gerda Shank Egshank@cpan.orgE zby: Zbigniew Lukasiak Ezby@cpan.orgE t0m: Tomas Doran Ebobtfish@bobtfish.netE augensalat: Bernhard Graf Eaugensalat@gmail.comE cubuanic: Oleg Kostyuk Ecub.uanic@gmail.comE rafl: Florian Ragwitz Erafl@debian.orgE mazpe: Lester Ariel Mesa dew: Dan Thomas koki: Klaus Ita jnapiorkowski: John Napiorkowski lestrrat: Daisuke Maki hobbs: Andrew Rodland Andy Clayton boghead: Bryan Beeley Csaba Hetenyi Eisuke Oishi Lian Wan Situ Murray Nick Logan Vladimir Timofeev diegok: Diego Kuperman ijw: Ian Wells amiri: Amiri Barksdale ozum: Ozum Eldogan lukast: Lukas Thiemeier Initially based on the source code of L by Bill Moseley =head1 AUTHOR FormHandler Contributors - see HTML::FormHandler =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2013 by Gerda Shank. 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 HTML-FormHandler-0.40050/LICENSE0000644000077000007700000004367512221042076015157 0ustar gshankgshankThis software is copyright (c) 2013 by Gerda Shank. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2013 by Gerda Shank. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2013 by Gerda Shank. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End HTML-FormHandler-0.40050/Makefile.PL0000644000077000007700000000406112221042076016106 0ustar gshankgshank use strict; use warnings; use ExtUtils::MakeMaker 6.30; use File::ShareDir::Install; install_share dist => "share"; my %WriteMakefileArgs = ( "ABSTRACT" => "HTML forms using Moose", "AUTHOR" => "FormHandler Contributors - see HTML::FormHandler", "BUILD_REQUIRES" => { "PadWalker" => 0, "Test::Differences" => 0, "Test::Exception" => 0, "Test::Memory::Cycle" => "1.04", "Test::More" => "0.94" }, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.30", "File::ShareDir::Install" => "0.03" }, "DISTNAME" => "HTML-FormHandler", "EXE_FILES" => [], "LICENSE" => "perl", "NAME" => "HTML::FormHandler", "PREREQ_PM" => { "Carp" => 0, "Class::Load" => "0.06", "Data::Clone" => 0, "DateTime" => 0, "DateTime::Format::Strptime" => 0, "Email::Valid" => 0, "File::ShareDir" => 0, "File::Spec" => 0, "HTML::TreeBuilder" => "3.23", "JSON" => 0, "Locale::Maketext" => "1.09", "Moose" => "2.0007", "MooseX::Getopt" => "0.16", "MooseX::Types" => "0.20", "MooseX::Types::Common" => 0, "MooseX::Types::LoadableClass" => "0.006", "Sub::Exporter" => 0, "Sub::Name" => 0, "Try::Tiny" => 0, "aliased" => 0, "namespace::autoclean" => "0.09" }, "VERSION" => "0.40050", "test" => { "TESTS" => "t/*.t t/blocks/*.t t/bootstrap/*.t t/bootstrap3/*.t t/compound/*.t t/errors/*.t t/field_setup/*.t t/fields/*.t t/form_setup/*.t t/infl_defl/*.t t/moose/*.t t/render/*.t t/repeatable/*.t t/result/*.t t/validation/*.t t/wizard/*.t" } ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; my $pp = $WriteMakefileArgs{PREREQ_PM}; for my $mod ( keys %$br ) { if ( exists $pp->{$mod} ) { $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; } else { $pp->{$mod} = $br->{$mod}; } } } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); { package MY; use File::ShareDir::Install qw(postamble); } HTML-FormHandler-0.40050/MANIFEST0000644000077000007700000002524512221042076015274 0ustar gshankgshankChanges INSTALL LICENSE MANIFEST META.json META.yml Makefile.PL README SIGNATURE TODO dist.ini lib/HTML/FormHandler.pm lib/HTML/FormHandler/Base.pm lib/HTML/FormHandler/Blocks.pm lib/HTML/FormHandler/BuildFields.pm lib/HTML/FormHandler/BuildPages.pm lib/HTML/FormHandler/Field.pm lib/HTML/FormHandler/Field/AddElement.pm lib/HTML/FormHandler/Field/BoolSelect.pm lib/HTML/FormHandler/Field/Boolean.pm lib/HTML/FormHandler/Field/Button.pm lib/HTML/FormHandler/Field/Captcha.pm lib/HTML/FormHandler/Field/Checkbox.pm lib/HTML/FormHandler/Field/Compound.pm lib/HTML/FormHandler/Field/Date.pm lib/HTML/FormHandler/Field/DateMDY.pm lib/HTML/FormHandler/Field/DateTime.pm lib/HTML/FormHandler/Field/Display.pm lib/HTML/FormHandler/Field/Duration.pm lib/HTML/FormHandler/Field/Email.pm lib/HTML/FormHandler/Field/File.pm lib/HTML/FormHandler/Field/Float.pm lib/HTML/FormHandler/Field/Hidden.pm lib/HTML/FormHandler/Field/Hour.pm lib/HTML/FormHandler/Field/IntRange.pm lib/HTML/FormHandler/Field/Integer.pm lib/HTML/FormHandler/Field/Minute.pm lib/HTML/FormHandler/Field/Money.pm lib/HTML/FormHandler/Field/Month.pm lib/HTML/FormHandler/Field/MonthDay.pm lib/HTML/FormHandler/Field/MonthName.pm lib/HTML/FormHandler/Field/Multiple.pm lib/HTML/FormHandler/Field/Nested.pm lib/HTML/FormHandler/Field/NoValue.pm lib/HTML/FormHandler/Field/NonEditable.pm lib/HTML/FormHandler/Field/Password.pm lib/HTML/FormHandler/Field/PasswordConf.pm lib/HTML/FormHandler/Field/PosInteger.pm lib/HTML/FormHandler/Field/PrimaryKey.pm lib/HTML/FormHandler/Field/Repeatable.pm lib/HTML/FormHandler/Field/Repeatable/Instance.pm lib/HTML/FormHandler/Field/Reset.pm lib/HTML/FormHandler/Field/Result.pm lib/HTML/FormHandler/Field/RmElement.pm lib/HTML/FormHandler/Field/Second.pm lib/HTML/FormHandler/Field/Select.pm lib/HTML/FormHandler/Field/SelectCSV.pm lib/HTML/FormHandler/Field/Submit.pm lib/HTML/FormHandler/Field/Text.pm lib/HTML/FormHandler/Field/TextArea.pm lib/HTML/FormHandler/Field/TextCSV.pm lib/HTML/FormHandler/Field/Upload.pm lib/HTML/FormHandler/Field/Weekday.pm lib/HTML/FormHandler/Field/Year.pm lib/HTML/FormHandler/Fields.pm lib/HTML/FormHandler/Foo.pm lib/HTML/FormHandler/I18N.pm lib/HTML/FormHandler/I18N/bg_bg.pm lib/HTML/FormHandler/I18N/de_de.pm lib/HTML/FormHandler/I18N/en_us.pm lib/HTML/FormHandler/I18N/hu_hu.pm lib/HTML/FormHandler/I18N/it_it.pm lib/HTML/FormHandler/I18N/ja_jp.pm lib/HTML/FormHandler/I18N/pt_br.pm lib/HTML/FormHandler/I18N/ru_ru.pm lib/HTML/FormHandler/I18N/sv_se.pm lib/HTML/FormHandler/I18N/tr_tr.pm lib/HTML/FormHandler/I18N/ua_ua.pm lib/HTML/FormHandler/InitResult.pm lib/HTML/FormHandler/Manual.pod lib/HTML/FormHandler/Manual/Catalyst.pod lib/HTML/FormHandler/Manual/Cookbook.pod lib/HTML/FormHandler/Manual/Database.pod lib/HTML/FormHandler/Manual/Defaults.pod lib/HTML/FormHandler/Manual/Errors.pod lib/HTML/FormHandler/Manual/Fields.pod lib/HTML/FormHandler/Manual/FromDFV.pod lib/HTML/FormHandler/Manual/FromFF.pod lib/HTML/FormHandler/Manual/InflationDeflation.pod lib/HTML/FormHandler/Manual/Intro.pod lib/HTML/FormHandler/Manual/Reference.pod lib/HTML/FormHandler/Manual/Rendering.pod lib/HTML/FormHandler/Manual/RenderingCookbook.pod lib/HTML/FormHandler/Manual/Templates.pod lib/HTML/FormHandler/Manual/Testing.pod lib/HTML/FormHandler/Manual/Tutorial.pod lib/HTML/FormHandler/Manual/Validation.pod lib/HTML/FormHandler/Merge.pm lib/HTML/FormHandler/Meta/Role.pm lib/HTML/FormHandler/Model.pm lib/HTML/FormHandler/Model/CDBI.pm lib/HTML/FormHandler/Model/Object.pm lib/HTML/FormHandler/Moose.pm lib/HTML/FormHandler/Moose/Role.pm lib/HTML/FormHandler/Page.pm lib/HTML/FormHandler/Page/Simple.pm lib/HTML/FormHandler/Pages.pm lib/HTML/FormHandler/Params.pm lib/HTML/FormHandler/Render/RepeatableJs.pm lib/HTML/FormHandler/Render/Simple.pm lib/HTML/FormHandler/Render/Table.pm lib/HTML/FormHandler/Render/Util.pm lib/HTML/FormHandler/Render/WithTT.pm lib/HTML/FormHandler/Result.pm lib/HTML/FormHandler/Result/Role.pm lib/HTML/FormHandler/Test.pm lib/HTML/FormHandler/TraitFor/Captcha.pm lib/HTML/FormHandler/TraitFor/I18N.pm lib/HTML/FormHandler/TraitFor/Types.pm lib/HTML/FormHandler/Traits.pm lib/HTML/FormHandler/Types.pm lib/HTML/FormHandler/Validate.pm lib/HTML/FormHandler/Widget/ApplyRole.pm lib/HTML/FormHandler/Widget/Block.pm lib/HTML/FormHandler/Widget/Block/Bootstrap.pm lib/HTML/FormHandler/Widget/Field/Button.pm lib/HTML/FormHandler/Widget/Field/ButtonTag.pm lib/HTML/FormHandler/Widget/Field/Captcha.pm lib/HTML/FormHandler/Widget/Field/Checkbox.pm lib/HTML/FormHandler/Widget/Field/CheckboxGroup.pm lib/HTML/FormHandler/Widget/Field/Compound.pm lib/HTML/FormHandler/Widget/Field/Hidden.pm lib/HTML/FormHandler/Widget/Field/NoRender.pm lib/HTML/FormHandler/Widget/Field/Password.pm lib/HTML/FormHandler/Widget/Field/RadioGroup.pm lib/HTML/FormHandler/Widget/Field/Repeatable.pm lib/HTML/FormHandler/Widget/Field/Reset.pm lib/HTML/FormHandler/Widget/Field/Role/HTMLAttributes.pm lib/HTML/FormHandler/Widget/Field/Role/SelectedOption.pm lib/HTML/FormHandler/Widget/Field/Select.pm lib/HTML/FormHandler/Widget/Field/Span.pm lib/HTML/FormHandler/Widget/Field/Submit.pm lib/HTML/FormHandler/Widget/Field/Text.pm lib/HTML/FormHandler/Widget/Field/Textarea.pm lib/HTML/FormHandler/Widget/Field/Upload.pm lib/HTML/FormHandler/Widget/Form/Role/HTMLAttributes.pm lib/HTML/FormHandler/Widget/Form/Simple.pm lib/HTML/FormHandler/Widget/Form/Table.pm lib/HTML/FormHandler/Widget/Theme/Bootstrap.pm lib/HTML/FormHandler/Widget/Theme/BootstrapFormMessages.pm lib/HTML/FormHandler/Widget/Wrapper/Base.pm lib/HTML/FormHandler/Widget/Wrapper/Bootstrap.pm lib/HTML/FormHandler/Widget/Wrapper/Bootstrap3.pm lib/HTML/FormHandler/Widget/Wrapper/Fieldset.pm lib/HTML/FormHandler/Widget/Wrapper/None.pm lib/HTML/FormHandler/Widget/Wrapper/Simple.pm lib/HTML/FormHandler/Widget/Wrapper/SimpleInline.pm lib/HTML/FormHandler/Widget/Wrapper/Table.pm lib/HTML/FormHandler/Widget/Wrapper/TableInline.pm lib/HTML/FormHandler/Wizard.pm share/templates/field/button.tt share/templates/field/checkbox.tt share/templates/field/checkbox_group.tt share/templates/field/compound.tt share/templates/field/hidden.tt share/templates/field/password.tt share/templates/field/radio_group.tt share/templates/field/repeatable.tt share/templates/field/reset.tt share/templates/field/select.tt share/templates/field/submit.tt share/templates/field/text.tt share/templates/field/textarea.tt share/templates/field/upload.tt share/templates/foo/checkbox_tag.tt share/templates/foo/end_form.tt share/templates/foo/field.tt share/templates/foo/form.tt share/templates/foo/input.tt share/templates/foo/input_tag.tt share/templates/foo/label.tt share/templates/foo/start_form.tt share/templates/form/form.tt share/templates/form/form_end.tt share/templates/form/form_in_one.tt share/templates/form/form_start.tt share/templates/wrapper/fieldset.tt share/templates/wrapper/label.tt share/templates/wrapper/none.tt share/templates/wrapper/simple.tt share/templates/wrapper/wrap_label.tt t/01app.t t/basic.t t/blocks/basic.t t/blocks/block_list.t t/blocks/blocktags.t t/blocks/loading.t t/blocks/nested.t t/blocks/render_units.t t/bootstrap/basic.t t/bootstrap/control_states.t t/bootstrap/controls.t t/bootstrap/ext_controls.t t/bootstrap/inline.t t/bootstrap/other.t t/bootstrap/search.t t/bootstrap3/basic.t t/compound/basic.t t/compound/default.t t/compound/empty.t t/compound/include.t t/compound/select.t t/errors/basic.t t/errors/compound.t t/errors/form_messages.t t/errors/messages.t t/errors/req_message.t t/field_setup/compound_update_fields.t t/field_setup/defaults.t t/field_setup/disabled.t t/field_setup/id.t t/field_setup/inactive.t t/field_setup/init_object.t t/field_setup/input_param.t t/field_setup/missing.t t/field_setup/plus_field.t t/field_setup/update_fields.t t/field_setup/update_subfields.t t/fields/dates.t t/fields/display.t t/fields/fields.t t/fields/float.t t/fields/formhandlerx.t t/fields/novalue.t t/fields/password.t t/fields/repeatable.t t/fields/select.t t/fields/selectcsv.t t/fields/textcsv.t t/form_setup/api.t t/form_setup/clone.t t/form_setup/compound_field_list.t t/form_setup/config.t t/form_setup/dynamic.t t/form_setup/include.t t/form_setup/init_obj.t t/form_setup/no_update.t t/form_setup/posted.t t/form_setup/render_roles.t t/infl_defl/comp_field.t t/infl_defl/default.t t/infl_defl/defl_lives.t t/infl_defl/infl_defl.t t/infl_defl/infl_transform.t t/infl_defl/variations.t t/lib/BookDB/Form/Upload.pm t/lib/Field/Address.pm t/lib/Field/AltText.pm t/lib/Field/MyField.pm t/lib/Form/Address.pm t/lib/Form/AddressRole.pm t/lib/Form/Multiple.pm t/lib/Form/MultipleRole.pm t/lib/Form/NoExtForm.pm t/lib/Form/Person.pm t/lib/Form/PersonRole.pm t/lib/Form/Test.pm t/lib/Form/Two.pm t/lib/MyApp/Component/Section.pm t/lib/MyApp/I18N/abc_de.pm t/lib/Widget/Block/Test.pm t/lib/Widget/Field/Omega.pm t/lib/Widget/Field/TestWidget.pm t/memory_cycles.t t/moose/build_id.t t/moose/composed.t t/moose/field_traits.t t/moose/no_extend.t t/moose/subclass_roles.t t/release-eol.t t/release-no-tabs.t t/render/actions.t t/render/array.t t/render/basic.t t/render/checkbox.t t/render/checkbox_group.t t/render/classes.t t/render/compound.t t/render/compound2.t t/render/display.t t/render/errors.t t/render/escaping.t t/render/ff.t t/render/filter.t t/render/form_errors.t t/render/get_tag.t t/render/html5_attributes.t t/render/html_attr.t t/render/html_attributes.t t/render/inline.t t/render/label.t t/render/noneditable.t t/render/optgroup.t t/render/radio_group.t t/render/rep_fieldset.t t/render/repeatable.t t/render/result.t t/render/select.t t/render/simple.t t/render/submit.t t/render/table.t t/render/tags.t t/render/util.t t/render/widget_loading.t t/render/widget_tags.t t/render/widgets.t t/render/withtt.t t/repeatable/defaults.t t/repeatable/has_many.t t/repeatable/hash.t t/repeatable/js.t t/repeatable/list.t t/repeatable/nested.t t/repeatable/nested2.t t/repeatable/num_extra.t t/repeatable/reload.t t/repeatable/set_methods.t t/repeatable/subfield.t t/result/basic.t t/result/blocks.t t/result/compound.t t/result/errors.t t/result/repeatable.t t/structured.t t/template.t t/validation/apply.t t/validation/constraints.t t/validation/dependency.t t/validation/filters.t t/validation/reqwhen.t t/validation/types.t t/validation/validate_coderef.t t/validation/when.t t/var/form1.pl t/var/form1.yml t/wizard/basic.t util/check_I18N.pl util/get_messages.pl util/messages xt/02pod.t xt/add_field.t xt/captcha.t xt/captcha2.t xt/chbox_group.t xt/check_selected_option.t xt/custom_fields.t xt/cycles.t xt/dfv.t xt/display.t xt/email.t xt/eol.t xt/field_list.t xt/form_errors.t xt/init.t xt/lib/MyCatalystApp.pm xt/lib/MyCatalystApp/Controller/Captcha.pm xt/lib/MyCatalystApp/Controller/Root.pm xt/load_field.t xt/locale.t xt/locale_data_localize.t xt/mb_form.t xt/model_cdbi.t xt/multiple_forms.t xt/order.t xt/params.t xt/posted.t xt/repeatable_clone.t xt/submit.t xt/upload.t HTML-FormHandler-0.40050/META.json0000644000077000007700000000376612221042076015570 0ustar gshankgshank{ "abstract" : "HTML forms using Moose", "author" : [ "FormHandler Contributors - see HTML::FormHandler" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 4.300020, CPAN::Meta::Converter version 2.120921", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "HTML-FormHandler", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.30", "File::ShareDir::Install" : "0.03" } }, "runtime" : { "requires" : { "Carp" : "0", "Class::Load" : "0.06", "Data::Clone" : "0", "DateTime" : "0", "DateTime::Format::Strptime" : "0", "Email::Valid" : "0", "File::ShareDir" : "0", "File::Spec" : "0", "HTML::TreeBuilder" : "3.23", "JSON" : "0", "Locale::Maketext" : "1.09", "Moose" : "2.0007", "MooseX::Getopt" : "0.16", "MooseX::Types" : "0.20", "MooseX::Types::Common" : "0", "MooseX::Types::LoadableClass" : "0.006", "Sub::Exporter" : "0", "Sub::Name" : "0", "Try::Tiny" : "0", "aliased" : "0", "namespace::autoclean" : "0.09" } }, "test" : { "requires" : { "PadWalker" : "0", "Test::Differences" : "0", "Test::Exception" : "0", "Test::Memory::Cycle" : "1.04", "Test::More" : "0.94" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "http://github.com/gshank/html-formhandler/issues" }, "repository" : { "type" : "git", "url" : "git://github.com/gshank/html-formhandler.git", "web" : "http://github.com/gshank/html-formhandler" } }, "version" : "0.40050" } HTML-FormHandler-0.40050/META.yml0000644000077000007700000000213612221042076015406 0ustar gshankgshank--- abstract: 'HTML forms using Moose' author: - 'FormHandler Contributors - see HTML::FormHandler' build_requires: PadWalker: 0 Test::Differences: 0 Test::Exception: 0 Test::Memory::Cycle: 1.04 Test::More: 0.94 configure_requires: ExtUtils::MakeMaker: 6.30 File::ShareDir::Install: 0.03 dynamic_config: 0 generated_by: 'Dist::Zilla version 4.300020, CPAN::Meta::Converter version 2.120921' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: HTML-FormHandler requires: Carp: 0 Class::Load: 0.06 Data::Clone: 0 DateTime: 0 DateTime::Format::Strptime: 0 Email::Valid: 0 File::ShareDir: 0 File::Spec: 0 HTML::TreeBuilder: 3.23 JSON: 0 Locale::Maketext: 1.09 Moose: 2.0007 MooseX::Getopt: 0.16 MooseX::Types: 0.20 MooseX::Types::Common: 0 MooseX::Types::LoadableClass: 0.006 Sub::Exporter: 0 Sub::Name: 0 Try::Tiny: 0 aliased: 0 namespace::autoclean: 0.09 resources: bugtracker: http://github.com/gshank/html-formhandler/issues repository: git://github.com/gshank/html-formhandler.git version: 0.40050 HTML-FormHandler-0.40050/README0000644000077000007700000000044512221042076015016 0ustar gshankgshank This archive contains the distribution HTML-FormHandler, version 0.40050: HTML forms using Moose This software is copyright (c) 2013 by Gerda Shank. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. HTML-FormHandler-0.40050/share/0000755000077000007700000000000012221042077015236 5ustar gshankgshankHTML-FormHandler-0.40050/share/templates/0000755000077000007700000000000012221042077017234 5ustar gshankgshankHTML-FormHandler-0.40050/share/templates/field/0000755000077000007700000000000012221042077020317 5ustar gshankgshankHTML-FormHandler-0.40050/share/templates/field/button.tt0000644000077000007700000000017012221042077022201 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/checkbox.tt0000644000077000007700000000030312221042077022452 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/checkbox_group.tt0000644000077000007700000000052712221042077023676 0ustar gshankgshank[% FOR option IN f.options -%] [% END -%] HTML-FormHandler-0.40050/share/templates/field/compound.tt0000644000077000007700000000026012221042077022512 0ustar gshankgshank[% FOREACH sf IN f.sorted_fields -%] [% outerf = f; f = sf; -%] [% WRAPPER "wrapper/${f.twrapper}" %][% PROCESS "field/${f.twidget}" -%][% END -%] [% f = outerf -%] [% END -%] HTML-FormHandler-0.40050/share/templates/field/hidden.tt0000644000077000007700000000016612221042077022126 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/password.tt0000644000077000007700000000017012221042077022530 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/radio_group.tt0000644000077000007700000000042012221042077023176 0ustar gshankgshank[% FOR option IN f.options -%] [% END -%] HTML-FormHandler-0.40050/share/templates/field/repeatable.tt0000644000077000007700000000026212221042077022774 0ustar gshankgshank[% FOREACH rf IN f.sorted_fields -%] [% outerrf = f; f = rf; -%] [% WRAPPER "wrapper/${f.twrapper}" %][% PROCESS "field/${f.twidget}" -%][% END -%] [% f = outerrf -%] [% END -%] HTML-FormHandler-0.40050/share/templates/field/reset.tt0000644000077000007700000000016712221042077022016 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/select.tt0000644000077000007700000000064012221042077022147 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/submit.tt0000644000077000007700000000017012221042077022171 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/text.tt0000644000077000007700000000020212221042077021646 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/textarea.tt0000644000077000007700000000022412221042077022503 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/field/upload.tt0000644000077000007700000000014712221042077022156 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/foo/0000755000077000007700000000000012221042077020017 5ustar gshankgshankHTML-FormHandler-0.40050/share/templates/foo/checkbox_tag.tt0000644000077000007700000000017412221042077023013 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/foo/end_form.tt0000644000077000007700000000001012221042077022150 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/foo/field.tt0000644000077000007700000000036012221042077021452 0ustar gshankgshank[% IF self.wrapper_tag %]<[% self.wrapper_tag %][% process_attrs(self.wrapper_attributes) %]>[% END -%] [% IF self.label.defined %][% INCLUDE label.tt %][% END -%] [% content -%] [% IF self.wrapper_tag %][% END -%] HTML-FormHandler-0.40050/share/templates/foo/form.tt0000644000077000007700000000022212221042077021327 0ustar gshankgshank[% INCLUDE start_form.tt -%] [% FOREACH element = form.sorted_fields %][% INCLUDE "input.tt" self=element %][% END -%] [% INCLUDE end_form.tt -%] HTML-FormHandler-0.40050/share/templates/foo/input.tt0000644000077000007700000000010312221042077021521 0ustar gshankgshank[% WRAPPER field.tt %][% INCLUDE $self.field_filename %][% END -%] HTML-FormHandler-0.40050/share/templates/foo/input_tag.tt0000644000077000007700000000024412221042077022362 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/foo/label.tt0000644000077000007700000000011312221042077021442 0ustar gshankgshank[% self.label %] HTML-FormHandler-0.40050/share/templates/foo/start_form.tt0000644000077000007700000000005312221042077022546 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/form/0000755000077000007700000000000012221042077020177 5ustar gshankgshankHTML-FormHandler-0.40050/share/templates/form/form.tt0000644000077000007700000000051212221042077021511 0ustar gshankgshank[% PROCESS form/form_start.tt -%]
[% FOREACH err IN form.form_errors -%] [% err %] [% END -%]
[% FOREACH f IN form.sorted_fields -%] [% WRAPPER "wrapper/${f.twrapper}" -%][% PROCESS "field/${f.twidget}" -%][% END -%] [% END -%] [% PROCESS form/form_end.tt -%] HTML-FormHandler-0.40050/share/templates/form/form_end.tt0000644000077000007700000000001012221042077022330 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/form/form_in_one.tt0000644000077000007700000001005412221042077023042 0ustar gshankgshank[% PROCESS form_start -%]
[% FOREACH err IN form.form_errors -%] [% err %] [% END -%]
[% FOREACH f IN form.sorted_fields -%] [% WRAPPER "wrapper_${f.uwrapper}" -%][% PROCESS "${f.uwidget}" -%][% END -%] [% END -%] [% PROCESS form_end -%] [% BLOCK form_start -%] [% END -%] [% BLOCK form_end -%] [% END -%] [% BLOCK button -%] [% END -%] [% BLOCK checkbox -%] [%~ ~%] [% END -%] [% BLOCK checkbox_group -%] [% FOR option IN f.options -%] [% END -%] [% END -%] [% BLOCK compound -%] [% FOREACH sf IN f.sorted_fields -%] [% outerf = f; f = sf; -%] [% WRAPPER "wrapper_${f.uwrapper}" %][% PROCESS "${f.uwidget}" -%][% END -%] [% f = outerf -%] [% END -%] [% END -%] [% BLOCK hidden -%] [% END -%] [% BLOCK password -%] [% END -%] [% BLOCK radio_group -%] [% FOR option IN f.options -%] [% END -%] [% END -%] [% BLOCK repeatable -%] [% FOREACH rf IN f.sorted_fields -%] [% outerrf = f; f = rf; -%] [% WRAPPER "wrapper_${f.uwrapper}" %][% PROCESS "${f.uwidget}" -%][% END -%] [% f = outerrf -%] [% END -%] [% END -%] [% BLOCK reset -%] [% END -%] [% BLOCK select -%] [% END -%] [% BLOCK submit -%] [% END -%] [% BLOCK text -%] [% END -%] [% BLOCK textarea -%] [% END -%] [% BLOCK upload -%] [% END -%] [% BLOCK wrapper_simple -%] [% IF f.do_label %][% PROCESS label %][% END -%] [% content -%] [% END -%] [% BLOCK label -%] [% END -%] [% BLOCK wrapper_wrap_label -%] [%~ content ~%][%~ f.label %] [% END -%] [% BLOCK wrapper_none -%] [% content %] [% END -%] [% BLOCK wrapper_fieldset -%] [% f.label %] [% content -%] [% END -%] HTML-FormHandler-0.40050/share/templates/form/form_start.tt0000644000077000007700000000005312221042077022726 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/wrapper/0000755000077000007700000000000012221042077020714 5ustar gshankgshankHTML-FormHandler-0.40050/share/templates/wrapper/fieldset.tt0000644000077000007700000000015412221042077023064 0ustar gshankgshank[% f.label %] [% content -%] HTML-FormHandler-0.40050/share/templates/wrapper/label.tt0000644000077000007700000000013412221042077022342 0ustar gshankgshank HTML-FormHandler-0.40050/share/templates/wrapper/none.tt0000644000077000007700000000001612221042077022221 0ustar gshankgshank[% content %] HTML-FormHandler-0.40050/share/templates/wrapper/simple.tt0000644000077000007700000000020212221042077022550 0ustar gshankgshank [% IF f.do_label %][% PROCESS wrapper/label.tt %][% END -%] [% content -%] HTML-FormHandler-0.40050/share/templates/wrapper/wrap_label.tt0000644000077000007700000000024012221042077023371 0ustar gshankgshank[%~ content ~%][%~ f.label %] HTML-FormHandler-0.40050/SIGNATURE0000644000077000007700000007060212221042076015424 0ustar gshankgshankThis file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.68. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 SHA1 c74eb1e034343558b133676e036079547dc82fd9 Changes SHA1 2972c49b6d6e4d8bcc3360a734ee629ac16c6d8c INSTALL SHA1 b666e0455e9fd5ccdcadd7cc9750644700fc8c22 LICENSE SHA1 b9c482c9b1dcf02ed4537a37d61e21519cbd7316 MANIFEST SHA1 f0171a731b6f7cd67f31277c64eab66868d7c331 META.json SHA1 8b2dd78eae5202aa1b0b037ba59dda4d1f07796f META.yml SHA1 0eb93c114e9a20d13b2e85f94ba604df90377d7c Makefile.PL SHA1 30692fc884a087904a4610a36aa1758bdda82aeb README SHA1 d764cc72a4d6465a03e8d04bdbf58096068c619d TODO SHA1 5ce3261e67262d47ddb3a50dcf676c9f517932b0 dist.ini SHA1 f56b1faff723ac3c264109028ea95f5ec14f5bf3 lib/HTML/FormHandler.pm SHA1 475ed17cd645c6015e5177a3f2567037384edd71 lib/HTML/FormHandler/Base.pm SHA1 e3051944988d250e9c0b8f0bb0e12d2865757d41 lib/HTML/FormHandler/Blocks.pm SHA1 b68ea1f55f659de2c0327f86002a6bd503b51f2a lib/HTML/FormHandler/BuildFields.pm SHA1 267bcfe3e949ec816d0a5aa8931d55b4882132d8 lib/HTML/FormHandler/BuildPages.pm SHA1 57142fc433f2e434f812d9ec31fbf12c883c7258 lib/HTML/FormHandler/Field.pm SHA1 737fd731d2e95221faf075dc0ca7f3ce24e07e02 lib/HTML/FormHandler/Field/AddElement.pm SHA1 656479dc2e5d39a5888c4e40d183d3de24e72e5f lib/HTML/FormHandler/Field/BoolSelect.pm SHA1 f3fd0b0910061800367956da7276ce0d2fdd75e4 lib/HTML/FormHandler/Field/Boolean.pm SHA1 03bb0964f3fa09ba72d15f6e8a52d5931530052a lib/HTML/FormHandler/Field/Button.pm SHA1 e60c6e54b86095d06d7fe334fa4421ee051b486c lib/HTML/FormHandler/Field/Captcha.pm SHA1 ba36bcb71f313cd04888b56a5d130e20ac00bd80 lib/HTML/FormHandler/Field/Checkbox.pm SHA1 2ef252e9e0c2f1054bc885628e72ad0f0d500bf1 lib/HTML/FormHandler/Field/Compound.pm SHA1 e757c3379d86074d78b3336aca36bf15bcb07371 lib/HTML/FormHandler/Field/Date.pm SHA1 50c67d09c8ced1622638648e6ca39bf2a541d19a lib/HTML/FormHandler/Field/DateMDY.pm SHA1 4ecb86bf32a8e74dca80f587e3c3dface0e71fd5 lib/HTML/FormHandler/Field/DateTime.pm SHA1 500b08c4513619f09d2c65b1f2d4fb7aa1eceb36 lib/HTML/FormHandler/Field/Display.pm SHA1 18f55d1d9dfc681fb2a35c5b7c1b801e9e52563e lib/HTML/FormHandler/Field/Duration.pm SHA1 9658ea8f4c4c8e4aa4a1c49307f654a3ef791f99 lib/HTML/FormHandler/Field/Email.pm SHA1 8c4c4397859317811b20cc732d55b539808253d1 lib/HTML/FormHandler/Field/File.pm SHA1 3dc3bca9c24c76fcebfdfe2a4d921daebeec7eb8 lib/HTML/FormHandler/Field/Float.pm SHA1 2ab95c0cea050b800da5d01edf1af417abf2f1fc lib/HTML/FormHandler/Field/Hidden.pm SHA1 fc6fdfe8e46a05dfbd6d562df9c0a0e753091d3d lib/HTML/FormHandler/Field/Hour.pm SHA1 c2fe2abfd9b251fa14dc5540c167e9c3160996ae lib/HTML/FormHandler/Field/IntRange.pm SHA1 65ab02a0b3ba1016e27c183803112111254b76c6 lib/HTML/FormHandler/Field/Integer.pm SHA1 26ebdd04a9c2f7c4d66d6fb57558ac5419c92d3e lib/HTML/FormHandler/Field/Minute.pm SHA1 1ca9e95bf3a3db5e34c3d67f3e54fecc3515509d lib/HTML/FormHandler/Field/Money.pm SHA1 762cb43d9d59fe6fa9034da9fb9b560f50aa3d0c lib/HTML/FormHandler/Field/Month.pm SHA1 d2033dba00620450ec82446cb27cae22cf09cf95 lib/HTML/FormHandler/Field/MonthDay.pm SHA1 28e15bfaa7e1d6681ee6cb9dc579a137c980ab38 lib/HTML/FormHandler/Field/MonthName.pm SHA1 4e4440dfd2243f264be1076d3f8a16fd3d576732 lib/HTML/FormHandler/Field/Multiple.pm SHA1 6400215a3d7bd8a9b6555b9598f94ff9906de1ad lib/HTML/FormHandler/Field/Nested.pm SHA1 950994ab75103c3a5722647ac9e87d58fe9dcb0b lib/HTML/FormHandler/Field/NoValue.pm SHA1 1175a017ee426ffae3ab892eac1739a99c173865 lib/HTML/FormHandler/Field/NonEditable.pm SHA1 49a524215e8f31bfaaf827cdae36437d1aecf625 lib/HTML/FormHandler/Field/Password.pm SHA1 22484258c862081be32c1ad47dd25ca75528524b lib/HTML/FormHandler/Field/PasswordConf.pm SHA1 494eb3df68713379eed05323ab7be679d7d46e27 lib/HTML/FormHandler/Field/PosInteger.pm SHA1 d106c24db547b6d0ad937f782a81bc9a911423b1 lib/HTML/FormHandler/Field/PrimaryKey.pm SHA1 2b064570c257d9cd6d8280e25188c4cb7eb262f8 lib/HTML/FormHandler/Field/Repeatable.pm SHA1 11923cd9ad2737b57967e468212b76badf696a70 lib/HTML/FormHandler/Field/Repeatable/Instance.pm SHA1 3493c040a59b568c0a97f78050394c06046c884f lib/HTML/FormHandler/Field/Reset.pm SHA1 a42611dcd4798c09eb990acd0c908c566e726c11 lib/HTML/FormHandler/Field/Result.pm SHA1 25f3a817fe1cd58eb079a0331311e12875d8c535 lib/HTML/FormHandler/Field/RmElement.pm SHA1 a524dc0bd7b6e3443f720c6de30baf6183bf165c lib/HTML/FormHandler/Field/Second.pm SHA1 124248e44c6c3b47699aa35c6eaf9a8b0609d62b lib/HTML/FormHandler/Field/Select.pm SHA1 091c8580c99d73bb0f337322b43757bfb85d26bc lib/HTML/FormHandler/Field/SelectCSV.pm SHA1 ccb3a65ba030ba6be2915cacae0d963637048a6f lib/HTML/FormHandler/Field/Submit.pm SHA1 20f867bec94ad44e46281aecc90c547974922fa6 lib/HTML/FormHandler/Field/Text.pm SHA1 1c5b3941eb3017bab8dca98b865fca6803512623 lib/HTML/FormHandler/Field/TextArea.pm SHA1 7cfdaf180eb0a41cce256ea15b92ca7bb1ad909d lib/HTML/FormHandler/Field/TextCSV.pm SHA1 9ab90d8a02f1705b7726ab78867221b45a1eced6 lib/HTML/FormHandler/Field/Upload.pm SHA1 9f436263c3a8119e9e9480e1d50d8e0664d744f4 lib/HTML/FormHandler/Field/Weekday.pm SHA1 42016418f5afdeffe55100dabed7f6d4f5439564 lib/HTML/FormHandler/Field/Year.pm SHA1 98e5321d0989b296c89c8a8fe6605666c236b9b1 lib/HTML/FormHandler/Fields.pm SHA1 dcdeedbeeeddca8cd2da90ebb50e72570c1affa8 lib/HTML/FormHandler/Foo.pm SHA1 a69788d59860b2051af8cf26ecdeaadbdb77291a lib/HTML/FormHandler/I18N.pm SHA1 3757924976b05e999b486b4c9e0f406edc71250d lib/HTML/FormHandler/I18N/bg_bg.pm SHA1 fb5ad1ee12d4d6c08378c343f1e3975c510a3fff lib/HTML/FormHandler/I18N/de_de.pm SHA1 c8f32905f9ab2ac5b412dce1f3947ab581a5a06b lib/HTML/FormHandler/I18N/en_us.pm SHA1 3db8f1e6162b2db4ba6dd742e1b53bca07a9f6e2 lib/HTML/FormHandler/I18N/hu_hu.pm SHA1 9f4560a8fa275f88e0e8d3e8dc1008e6214c5160 lib/HTML/FormHandler/I18N/it_it.pm SHA1 f990b6e8a37278082d609ae30a130d52107a7bc7 lib/HTML/FormHandler/I18N/ja_jp.pm SHA1 9b1fe9d7301f699eeebc20a053376a061876f16f lib/HTML/FormHandler/I18N/pt_br.pm SHA1 5eb8bb557e2ab219b1880a94fe4c7dc47c50d044 lib/HTML/FormHandler/I18N/ru_ru.pm SHA1 8c8cc62a95e0787fcb87ee86cbdd80ed7d1a3680 lib/HTML/FormHandler/I18N/sv_se.pm SHA1 f4327f8c98556b79ce625a2b4d1c26065bbcd0cd lib/HTML/FormHandler/I18N/tr_tr.pm SHA1 ac0918d599e081c6303a5c3e4dbc12519fd47558 lib/HTML/FormHandler/I18N/ua_ua.pm SHA1 421999f80260c9863761c48b6eab67bba66d1705 lib/HTML/FormHandler/InitResult.pm SHA1 d1260b997a89e5465e17976bc13c4e76ccde8198 lib/HTML/FormHandler/Manual.pod SHA1 8b623976b09051e27883c365df772bfac7285d06 lib/HTML/FormHandler/Manual/Catalyst.pod SHA1 fbefbf5ebd010a9885fe4e94de6a50db84ceed15 lib/HTML/FormHandler/Manual/Cookbook.pod SHA1 467810ae0499d740c36ca5c1d81c57f31af98e0c lib/HTML/FormHandler/Manual/Database.pod SHA1 b1edd938b66b55a4349ce9becab6f6393774ab29 lib/HTML/FormHandler/Manual/Defaults.pod SHA1 6c02a90621dcaa3730165d4a39cb9be2f6c0a69b lib/HTML/FormHandler/Manual/Errors.pod SHA1 08e3933a9fbe9d6c037534efcea6c2adf21b7f6a lib/HTML/FormHandler/Manual/Fields.pod SHA1 bb85a20f590614aa8e8f8291f4561fe76eb24af4 lib/HTML/FormHandler/Manual/FromDFV.pod SHA1 d9883b6c8ba6b5fe64ec1929789ca1dd438951af lib/HTML/FormHandler/Manual/FromFF.pod SHA1 b1d5bf350a74f0e7d39a498eedab0c5be4237bde lib/HTML/FormHandler/Manual/InflationDeflation.pod SHA1 9e795dae8cbbd1be99a24d356b4086181d24d765 lib/HTML/FormHandler/Manual/Intro.pod SHA1 9ab3e29c97c2539592914fe4d1901c216bdb8f13 lib/HTML/FormHandler/Manual/Reference.pod SHA1 7f0b9efb6d48b3c40baab98d434df2d5122513bd lib/HTML/FormHandler/Manual/Rendering.pod SHA1 411e6cca076d9cc13cd6156ff05b9b1dda909ab2 lib/HTML/FormHandler/Manual/RenderingCookbook.pod SHA1 1f0c3a6fed1331959e783f40175c7b441d8ac8c3 lib/HTML/FormHandler/Manual/Templates.pod SHA1 314a81959565b8ff933b20ee7bfbcf719de7c66c lib/HTML/FormHandler/Manual/Testing.pod SHA1 d89d7226e488b37574530b5b6433fe205a4db5d9 lib/HTML/FormHandler/Manual/Tutorial.pod SHA1 5d6e242157a580afe819ef41bf2b93e829128371 lib/HTML/FormHandler/Manual/Validation.pod SHA1 cb89d3a45d10f0f22da72455e00b5e38c3eb7314 lib/HTML/FormHandler/Merge.pm SHA1 055110774dc56b9923fc681f07f63da9dbac5bc5 lib/HTML/FormHandler/Meta/Role.pm SHA1 0f9d371ae47f42e5d9202c4fdbdfe110c2f44875 lib/HTML/FormHandler/Model.pm SHA1 450a05f6fbe04e061966518d39ca9894047bec91 lib/HTML/FormHandler/Model/CDBI.pm SHA1 957db160cd7eb6ad48e3df10726aeb0c6d4cd8f2 lib/HTML/FormHandler/Model/Object.pm SHA1 4b52fd03bdf5cc2c5a6df58fdc5c9913010b142a lib/HTML/FormHandler/Moose.pm SHA1 a20663847b9c7d4ca3928d9788cb158c61248fe1 lib/HTML/FormHandler/Moose/Role.pm SHA1 033669f6299487a16c123b3c64fbe8ae5144fed2 lib/HTML/FormHandler/Page.pm SHA1 3814ed71d53ccd91badb75641160a7e96e917cc4 lib/HTML/FormHandler/Page/Simple.pm SHA1 f78a42dbe344bda175c9c39a17d8780a367e4f37 lib/HTML/FormHandler/Pages.pm SHA1 7c664262d429a84996933052d5fec25b22fd54ca lib/HTML/FormHandler/Params.pm SHA1 66e3d884e54efe6147634812ab920d22f36f00dd lib/HTML/FormHandler/Render/RepeatableJs.pm SHA1 f8f845ca71781ceed4b29935737bafa3eb695844 lib/HTML/FormHandler/Render/Simple.pm SHA1 7594a7040247f8834b8849684f56cc57b1230b51 lib/HTML/FormHandler/Render/Table.pm SHA1 8efacaf20af8064eed92bee2542e676b8d805228 lib/HTML/FormHandler/Render/Util.pm SHA1 81bb702c700b1959d9d6ffad845a65118c42e326 lib/HTML/FormHandler/Render/WithTT.pm SHA1 9a1f1bb320e2831d821b8c014bd2c13824704db2 lib/HTML/FormHandler/Result.pm SHA1 319b5eaf0eccdf536d548b0522861f9dcc79e3e9 lib/HTML/FormHandler/Result/Role.pm SHA1 38c155374e554ac9446d4e635455b9415101cd5c lib/HTML/FormHandler/Test.pm SHA1 668ccfa9482cc8ce70384b5895a9f0697439f3cf lib/HTML/FormHandler/TraitFor/Captcha.pm SHA1 fe981825ec7219e1ece1c4ceaa84088be14a4ae3 lib/HTML/FormHandler/TraitFor/I18N.pm SHA1 aac99e12d1f52b683f82101f8f10fd6b9b7191e4 lib/HTML/FormHandler/TraitFor/Types.pm SHA1 4f2b9a44e4325867a23e2a726727eec924e7197e lib/HTML/FormHandler/Traits.pm SHA1 989cb5d40f6c575a3c217e0285fd7a182caf60fa lib/HTML/FormHandler/Types.pm SHA1 e128569bcd57a1a73b2fe30c49de98c8c555669c lib/HTML/FormHandler/Validate.pm SHA1 34f382405306f4ffad121755f2b5c6d6fefc1d87 lib/HTML/FormHandler/Widget/ApplyRole.pm SHA1 e195a3e2ddcc966578afff30d1a284100fcd88b0 lib/HTML/FormHandler/Widget/Block.pm SHA1 c6a21316c161df7af2e598f38ca323751a1829e3 lib/HTML/FormHandler/Widget/Block/Bootstrap.pm SHA1 b75a965c4cfbe7f488fd35c28a5fa28ecb7879df lib/HTML/FormHandler/Widget/Field/Button.pm SHA1 1c1a2360a406642ebf94306196383aec286a169b lib/HTML/FormHandler/Widget/Field/ButtonTag.pm SHA1 09c1830538e0e2a0a1ccfb93005ac937fcc1c4de lib/HTML/FormHandler/Widget/Field/Captcha.pm SHA1 e0ac40a5c9a358f094345de5d96c542aff737bc4 lib/HTML/FormHandler/Widget/Field/Checkbox.pm SHA1 2fe4d70b3b78881c4f976c38ea00333b964aae47 lib/HTML/FormHandler/Widget/Field/CheckboxGroup.pm SHA1 85b332971ce1686dae6c3efdcc365e4a64a178cf lib/HTML/FormHandler/Widget/Field/Compound.pm SHA1 64f307e521508cff9de8c18fe0214a25103c8b96 lib/HTML/FormHandler/Widget/Field/Hidden.pm SHA1 39d2bfe96fb93035390bb0906a2b4361c773b739 lib/HTML/FormHandler/Widget/Field/NoRender.pm SHA1 bcc8f383ba60f68f6a44a12d6ac96307e27243de lib/HTML/FormHandler/Widget/Field/Password.pm SHA1 58dce1506d78e1f882fe859fdba8d97ddff9bd18 lib/HTML/FormHandler/Widget/Field/RadioGroup.pm SHA1 c2558daacdd4b306b4eb52e08d76240fc6ef39cd lib/HTML/FormHandler/Widget/Field/Repeatable.pm SHA1 6370a2c3cd185980ad04b077876bad6fe5678ac9 lib/HTML/FormHandler/Widget/Field/Reset.pm SHA1 8bc45e20a69215858096b84d74a905e3bf463907 lib/HTML/FormHandler/Widget/Field/Role/HTMLAttributes.pm SHA1 62c1a392a6a79d8df094572922a67d7608c00284 lib/HTML/FormHandler/Widget/Field/Role/SelectedOption.pm SHA1 bc89ffc58116a18a4cf33ca22026fd32851010eb lib/HTML/FormHandler/Widget/Field/Select.pm SHA1 457446410498f76e2fbbc47ebe2e3db26069c5fe lib/HTML/FormHandler/Widget/Field/Span.pm SHA1 5199949a5e0f4786c24cd5af4c6d940738d446d2 lib/HTML/FormHandler/Widget/Field/Submit.pm SHA1 cf4d69794ca58ea5e97a0779ccf5fa6b2b5bc00c lib/HTML/FormHandler/Widget/Field/Text.pm SHA1 1c0cf5996a6a1b966654547352e17c204720116f lib/HTML/FormHandler/Widget/Field/Textarea.pm SHA1 41f2ebb4edce37327c384c393158c49b6aaeb3e7 lib/HTML/FormHandler/Widget/Field/Upload.pm SHA1 e65c03e3342c0f283a930bda381b4d8fb71cba4e lib/HTML/FormHandler/Widget/Form/Role/HTMLAttributes.pm SHA1 a5c0c6c0bfc0dcaa82ba0200d3fdf9de01d61fd5 lib/HTML/FormHandler/Widget/Form/Simple.pm SHA1 7e9f5c3e7d5f27a614a4b6bd0b948f870667b0b6 lib/HTML/FormHandler/Widget/Form/Table.pm SHA1 862176d8655d09c1721439a83df1bb3cd24c0632 lib/HTML/FormHandler/Widget/Theme/Bootstrap.pm SHA1 fd8af687cf60b08b5352017ddd3ad96f51dafacb lib/HTML/FormHandler/Widget/Theme/BootstrapFormMessages.pm SHA1 fc1038ed69f677e3d6e5b6339b4a10911036c776 lib/HTML/FormHandler/Widget/Wrapper/Base.pm SHA1 897710204c61404540953fc001633165fe021b5a lib/HTML/FormHandler/Widget/Wrapper/Bootstrap.pm SHA1 87fb5c8507819ad344c323ad88d1aa1a7e9020d4 lib/HTML/FormHandler/Widget/Wrapper/Bootstrap3.pm SHA1 12358d87b405cdcb8e28b33e441be6a5d96e9606 lib/HTML/FormHandler/Widget/Wrapper/Fieldset.pm SHA1 eb04b871ebe2108f11f85a3ad86b138ddbbcf713 lib/HTML/FormHandler/Widget/Wrapper/None.pm SHA1 67b2642c4a2b37f5e590ee87c1f1a0e5b5f2ecf7 lib/HTML/FormHandler/Widget/Wrapper/Simple.pm SHA1 f150c73f121bdb19b184dda4ac4a922eb2c591e1 lib/HTML/FormHandler/Widget/Wrapper/SimpleInline.pm SHA1 7352cb117455477051253b618d16d81ea9280a50 lib/HTML/FormHandler/Widget/Wrapper/Table.pm SHA1 1eac989fb29f090a50ffb6ed80d408cd61bde966 lib/HTML/FormHandler/Widget/Wrapper/TableInline.pm SHA1 6a1303ea3c9a0b1561e89ac30b01284fe8d29088 lib/HTML/FormHandler/Wizard.pm SHA1 794da1f2f7c41af8edd5fc53e766a33072c10c91 share/templates/field/button.tt SHA1 3681fc90aef0523ca43663e10a2e2798d7a413ab share/templates/field/checkbox.tt SHA1 acc9394577fdda7a1408ee052792c70f00340c34 share/templates/field/checkbox_group.tt SHA1 86d4b217c985f6a4f501b32f4c92a8e388285f1d share/templates/field/compound.tt SHA1 67db06d5868ee6bd9ef38350325436f4da3e19ac share/templates/field/hidden.tt SHA1 44ac0e6fc4db1bcfe561598258528e6f0000f37e share/templates/field/password.tt SHA1 c5c430236706e7bcbb2d88b43c3fdd10ea7b8fa5 share/templates/field/radio_group.tt SHA1 a660124b371ce25428bc1a9f9facaa31eca9d8cb share/templates/field/repeatable.tt SHA1 cb99bbd10c2067d02e0e9b7a898e7535a1108c59 share/templates/field/reset.tt SHA1 5de099392c362013ea3dc49c17be7f01cac987fc share/templates/field/select.tt SHA1 776c16b7c5744cd197bdeb8b7b2981ec631eab31 share/templates/field/submit.tt SHA1 f539376e39505417ed106c9a8239aad1102ee226 share/templates/field/text.tt SHA1 5ed10a483ec32dde1dbcd92a9c89e364ec75c401 share/templates/field/textarea.tt SHA1 d963677f9ec0d30e1abe3a604fbc6a80c8489498 share/templates/field/upload.tt SHA1 b2faa01201389f379517e3955f686b04c045793a share/templates/foo/checkbox_tag.tt SHA1 90d389dd6cd2ad7431f9172a225eea49975a3712 share/templates/foo/end_form.tt SHA1 b128ee5ab6181501a2a7675efa8d908845e545f7 share/templates/foo/field.tt SHA1 04b81260d43b3c7a4cd324556f6e9ab47f1e2644 share/templates/foo/form.tt SHA1 d21cd1dc150147d5688ae048dc0c1f3c5ae59283 share/templates/foo/input.tt SHA1 dbc5ab703565e950469a288390dfd94be437d7d2 share/templates/foo/input_tag.tt SHA1 5886e336a836a6ce4df0559dee82cfcd30ff35eb share/templates/foo/label.tt SHA1 63cb1701b6b97d8c092b5b5afd66cd03faaf5dfd share/templates/foo/start_form.tt SHA1 378ff945471b9b88de9a04cec914d900dc2dd06f share/templates/form/form.tt SHA1 90d389dd6cd2ad7431f9172a225eea49975a3712 share/templates/form/form_end.tt SHA1 8b310b0206b7126daf1d7812a884df95e3751c70 share/templates/form/form_in_one.tt SHA1 63cb1701b6b97d8c092b5b5afd66cd03faaf5dfd share/templates/form/form_start.tt SHA1 27e0470ee115b9f96c9cef602703e71c0d71b725 share/templates/wrapper/fieldset.tt SHA1 a6f1dc7378d74c91fca73dd9cda322636c9f6dc8 share/templates/wrapper/label.tt SHA1 475346c385a4f2fdd668aab9f6b0e1f8d63522d0 share/templates/wrapper/none.tt SHA1 96b3d105205e2bde31af480ae3774c6c58c1589d share/templates/wrapper/simple.tt SHA1 edbdd55a879e452a4bbd0efad463c2be27dcb80d share/templates/wrapper/wrap_label.tt SHA1 d55b2da9ebb18c4da78ae398a555d19728397acd t/01app.t SHA1 b663474337b4ee5d1c289a543b467b05b10d3a85 t/basic.t SHA1 9ccc83343d981613ac0c71f941a206b9d50223e4 t/blocks/basic.t SHA1 d164545e6a42ed3cc2e8323abe759ace1b2f8765 t/blocks/block_list.t SHA1 0ac8fde2c904dfb30544de720fbef9c9580c4bd7 t/blocks/blocktags.t SHA1 b1f78d867fcf6aa011d9286ab1d05e018a49eddf t/blocks/loading.t SHA1 1e8ec64e5640cfccfdfe2d0d2ab921d25891a917 t/blocks/nested.t SHA1 f09cd0aba1ed89940f4b3e75d6dbd8b7b6235ab6 t/blocks/render_units.t SHA1 733e77823d3d86ea38382d691397353b87ac26ab t/bootstrap/basic.t SHA1 3dbd432c9ed298d6271af231cebb15a2563c480b t/bootstrap/control_states.t SHA1 ae16a51d2a268b80c575175904cd6795073636ae t/bootstrap/controls.t SHA1 6110d50ed54a4606e882494064c11d933bd766ae t/bootstrap/ext_controls.t SHA1 eaa1754ec21ec4fd417ba15d1cded0002169b36d t/bootstrap/inline.t SHA1 8e7d351b4f1b296106c6928785c02c69745c7338 t/bootstrap/other.t SHA1 125b1f1217851c8d468d33316a017b8756b63221 t/bootstrap/search.t SHA1 490dbee06e2eac54e62b2a8d27443b1059c64e74 t/bootstrap3/basic.t SHA1 228eebe478de9c99c42db7f8a26d2dcbca1715ff t/compound/basic.t SHA1 076f5508b176cc8df148e3c54f8c02506c61ebfc t/compound/default.t SHA1 5113d3260a143cbb4214c532b0267013485f9866 t/compound/empty.t SHA1 3484c409ef375e7c7a383fe1a21afdffa88bcb67 t/compound/include.t SHA1 e70e61d29017ca572169563f5bc5f04409d75223 t/compound/select.t SHA1 9b3346315f550ac726c9733c750ff26cf205789e t/errors/basic.t SHA1 45c2969b6e22b940d89c282f29cb12393320b5ce t/errors/compound.t SHA1 96b02b3f93ab1c096ab6e64d6cadfde6cfb887eb t/errors/form_messages.t SHA1 4da7ca25cadbdddd2c61f5fd43b8d46568e03c6a t/errors/messages.t SHA1 a98bd1e8013013d26ca7ecd2bdcac079969a255a t/errors/req_message.t SHA1 39c71d5c367b29a0ca4fc7a8b96ce19f226cb913 t/field_setup/compound_update_fields.t SHA1 d0e5f8d2b24883af8e1d835c7f777b0cc4dbcf80 t/field_setup/defaults.t SHA1 d48b9904cd8546c3fd23653628bb0aad999e7b51 t/field_setup/disabled.t SHA1 c9ce3ecb482109d83fdac934784d81f5c21ee743 t/field_setup/id.t SHA1 4867c2f3cfc14d5b82e3ce8177dc4b355771ec6c t/field_setup/inactive.t SHA1 97744c22607da7adacafa6a3952bc0568a817de8 t/field_setup/init_object.t SHA1 3963aec76768a163add932eb82d3700ef19c0820 t/field_setup/input_param.t SHA1 e5e246a2d74b0cf478c859fa7f5d59f26e4886ac t/field_setup/missing.t SHA1 99b4bed094f697439910d0106e1018a4688f209f t/field_setup/plus_field.t SHA1 5ef5f0dca7f29790083872ded07fbdaddba5b4b5 t/field_setup/update_fields.t SHA1 ee3294fdd0643d1a13fdf0f813f6b75b5c1762fa t/field_setup/update_subfields.t SHA1 4e1019b963655db1f636f62ebd76ca3793062bd2 t/fields/dates.t SHA1 b49610114fcca34d34b2d3a302efeb7a4499c8fe t/fields/display.t SHA1 7240daeb73d74c9995c18f6681fc3dc606855c52 t/fields/fields.t SHA1 32ffb3e7403b82fd033326ed8dfa1950330f8f99 t/fields/float.t SHA1 1c7e3714c63b6ca0f42bad837b9fde746bcfac2c t/fields/formhandlerx.t SHA1 eb62c3ad672f8697c0cf6eafd6a5474c904cee8d t/fields/novalue.t SHA1 03e8a04c7c3e5590deea887bb176e59dcea90472 t/fields/password.t SHA1 544f7a16def20df3347f502e3357b85a435d1671 t/fields/repeatable.t SHA1 bca0e1450a62d8da2345274fd17bfec7bb42628d t/fields/select.t SHA1 518f845cc4470d78275cd61e095dd5db90cd95fc t/fields/selectcsv.t SHA1 62c3a4d7ef2aafbfe365d29244f8030b9db298e8 t/fields/textcsv.t SHA1 7846a6c8a3ab1463756f3cb97edd688e1431a8c6 t/form_setup/api.t SHA1 4e01795fb844699a5482d0429bf678d63002b94a t/form_setup/clone.t SHA1 76bef41216e5703501cbdc5e1524ad6d9e9693df t/form_setup/compound_field_list.t SHA1 f4bf6e023e39186b1fe371e41033c689ea0fcc84 t/form_setup/config.t SHA1 f6d1ddf31d2715bc0e0fae057905658bd9234cb1 t/form_setup/dynamic.t SHA1 3322e427487ebea857befbdde07f3ec404a7047e t/form_setup/include.t SHA1 ae2a871faffb81f83a0944f50390468e869e076b t/form_setup/init_obj.t SHA1 93559f482788e583eb24d7bcb5fd1187b4e66a4b t/form_setup/no_update.t SHA1 4b95ce6100c2b052109ec965ac52c864a58f751b t/form_setup/posted.t SHA1 0972c2048c440a92020f7eba9473e09eac3105ea t/form_setup/render_roles.t SHA1 12375d16dfb7f3c00385c0834976e594172319d3 t/infl_defl/comp_field.t SHA1 8685c2e531341016ed61439c2ab9df7b50818266 t/infl_defl/default.t SHA1 e1cb08e56c1268ea619c5bf80731b90047f95735 t/infl_defl/defl_lives.t SHA1 458176d710595080122f1ee6d93adfc5938dc87d t/infl_defl/infl_defl.t SHA1 8e2043b8edf2a460282743a6edfb15859958426d t/infl_defl/infl_transform.t SHA1 cda457f835bc268a1d3c4e6d7b177ab4a4e2be51 t/infl_defl/variations.t SHA1 e330f72586852fb7f18c444008ab792c3e04b6c6 t/lib/BookDB/Form/Upload.pm SHA1 f5a80fcbc4f14894c274ea1e896067cc45143312 t/lib/Field/Address.pm SHA1 df80db13f94fe5044c995228ade46b8cd341045e t/lib/Field/AltText.pm SHA1 498e59d660a0b78b32565494484cb75ce9cad9c3 t/lib/Field/MyField.pm SHA1 3de5695a354809ff50f2900d257ec7eb195f9db7 t/lib/Form/Address.pm SHA1 3fa8edbecc482588a667d4ff0f938da4ab60b3c6 t/lib/Form/AddressRole.pm SHA1 37ae2600a3998048b253de1e96ee7b7d7597281c t/lib/Form/Multiple.pm SHA1 f01a56814525ff213d829b7499bbfef06fc90538 t/lib/Form/MultipleRole.pm SHA1 cf460b1f0a0106df286bffeae39cc321472d4e7b t/lib/Form/NoExtForm.pm SHA1 58469593246683e39a8fa3bdcf07b2669c9cb59a t/lib/Form/Person.pm SHA1 51ceda9acbd63d49a81a959adc27285a42e5ff27 t/lib/Form/PersonRole.pm SHA1 94cd172b90fd02eab01eb6e528c9e701eb914fd1 t/lib/Form/Test.pm SHA1 d6ce4712417f325870983a7e6f7d07e52c10974f t/lib/Form/Two.pm SHA1 d92e6eaa78b5fe830c108a1f91fb0aa6ea82cd39 t/lib/MyApp/Component/Section.pm SHA1 5239c744281b45b5ee49e4498fe5a04822ea1766 t/lib/MyApp/I18N/abc_de.pm SHA1 3b549a8c8d4fdc4e55ec323fac3eaf48111f8320 t/lib/Widget/Block/Test.pm SHA1 364e324607d74434dc26938a08fec784de110737 t/lib/Widget/Field/Omega.pm SHA1 7d6fdb503d9aee63eb6a79ec86d33997faf8b383 t/lib/Widget/Field/TestWidget.pm SHA1 eed8ac3446c4a13d80720503345cfb54395571b9 t/memory_cycles.t SHA1 725aeac4b9c347859d451fe700fd5988dab60055 t/moose/build_id.t SHA1 9489e970b3d3f5bd0ca82ba7704a0589546d278c t/moose/composed.t SHA1 7ad31d0a34a696fc1b05aca77302b050abeb189a t/moose/field_traits.t SHA1 1908e358a05fb889f1d3e76bc462df747766f5eb t/moose/no_extend.t SHA1 2fcb1cf1667bbf821cac0eba3389357dec953857 t/moose/subclass_roles.t SHA1 a032c41ef6887fab1b900669c2d304fab46680e2 t/release-eol.t SHA1 455d1dd1867212a665ad5ea4126b572411de300c t/release-no-tabs.t SHA1 d69dcbcff41cd0df28bf36527ddb37d447754932 t/render/actions.t SHA1 c1191d24fc2785ef40103d2191e87dfe92ee71e9 t/render/array.t SHA1 5a953cb7108e91b26087649ce7aa7cf9d213b650 t/render/basic.t SHA1 07244bfe73f681547ed1f51fa6f9b81d17ce356a t/render/checkbox.t SHA1 8541c93eca5d0804ae369fd226022c2371193621 t/render/checkbox_group.t SHA1 2e49def718be6cf8c87bbf748c8c30da146f8dc7 t/render/classes.t SHA1 b3bb4fcdaf7528300f7d3853c768812d86fe0f78 t/render/compound.t SHA1 8e30bceab9974e70669803515b19ba17848877f2 t/render/compound2.t SHA1 9d74a680a8b8420ccf65fca01ed631c25ca8e724 t/render/display.t SHA1 4e2142b5f91f776af188e3edeea4b8530ed0f59f t/render/errors.t SHA1 e9779e6c5b64ac56a003e9ce4678b1f61149c5f3 t/render/escaping.t SHA1 34169ac2fd52733922c55e3b1371ac47d3d49d9c t/render/ff.t SHA1 ae10e2cea31a74caae799668c2742e9596dc2a85 t/render/filter.t SHA1 baee60bf749cdd64dbe05404b5fbb97f9246f07b t/render/form_errors.t SHA1 10c270e17ff97aebde06110159bb3662ccdfc7b5 t/render/get_tag.t SHA1 84ca00194501a811ac364b1eb47046c25c0e7b0a t/render/html5_attributes.t SHA1 ae53d67a44b13e3c1da6fcc79bca8c9221c974f0 t/render/html_attr.t SHA1 7e9e9a99a8af797f44b433ca5407705df4e28675 t/render/html_attributes.t SHA1 429398d9de168ed3f03b6d4ce01ab1bdccc787e6 t/render/inline.t SHA1 f43bbd9c8aedd00c513e72a5748bce5355b188b3 t/render/label.t SHA1 fd4bd4b39cbfdddd9144317e2f25f4a1024ce178 t/render/noneditable.t SHA1 5b88d772b395d9f2876be4e8e1498ba6266842e2 t/render/optgroup.t SHA1 654948efd382323ce4bb0d267f4f9c168868204b t/render/radio_group.t SHA1 7175a9276b2f6cabcd4382ef846ec84157ba79d4 t/render/rep_fieldset.t SHA1 406f852e54337b6b1888398e6042f399cbffb9fc t/render/repeatable.t SHA1 b9b3e4d218182714100a52561502c9c14e34bb1b t/render/result.t SHA1 90e802e12c26b25a7f88802cfdcc3eb6f1ae9198 t/render/select.t SHA1 ff8adc4ad43ab610ad0de3df6d18fed8d1f838f4 t/render/simple.t SHA1 544a4a24dcb34fc50fc4c18ebc6aaa48f5cceb3a t/render/submit.t SHA1 7ac35647e2a90f8de10a0b8f5569ce2728c2972b t/render/table.t SHA1 17c04b46c520260fadf04cc4a7c5acdc0004dcb2 t/render/tags.t SHA1 d977f8cecf28d424cde917093535cbfc6fb27ea3 t/render/util.t SHA1 f4e53913e5882a765e13e93d84d5a498a03569fa t/render/widget_loading.t SHA1 98dcba91ccb83597ef0170ad045291bded439c41 t/render/widget_tags.t SHA1 c5d6853c0d31d2bb6f45a110cdf9343ba9e89de3 t/render/widgets.t SHA1 23ea3683a53943fcba024599cbba28761cb5b8e4 t/render/withtt.t SHA1 fe4e8b94c36d1707101b3d329069172536e4aa6e t/repeatable/defaults.t SHA1 6878da19e605199d8a6536535bd79d9bc8e9651a t/repeatable/has_many.t SHA1 388ed334251b9db9f096a6884c01ff00e3e3fe2b t/repeatable/hash.t SHA1 34075dacb6d838e9f9b88e1966678230efaf87fb t/repeatable/js.t SHA1 f50d0e2ed62e9a9a8e455aa4ccecc533801ee8ad t/repeatable/list.t SHA1 c875b3c645f32bb58ee195aae4042a6e4851dc94 t/repeatable/nested.t SHA1 4e0978924167cb79c7be49ffa104b9fffada8616 t/repeatable/nested2.t SHA1 bf316fc1f7fb0ab2e90b50244fae2f7d71b5872a t/repeatable/num_extra.t SHA1 f08e15a8370c204d16b4a4dd899f0b8c8f6f2773 t/repeatable/reload.t SHA1 fbe8ae9d5062acb07c81ac8c5d733ac2be22154a t/repeatable/set_methods.t SHA1 359087d5f8a029fabad7944fa3ff2af9c6a6ed79 t/repeatable/subfield.t SHA1 3422cd5aac318955cd48a51439875bcf859e9043 t/result/basic.t SHA1 e94436b6500a0e51fcfce7b217547775f65e58a5 t/result/blocks.t SHA1 24b760833e98e72c96a41ab615568f825b0c358b t/result/compound.t SHA1 f012b21fea7d4feb426641508f38bc8bdf40ba8c t/result/errors.t SHA1 724bf07cbf1f65c22fdf7c211ed758874f6d2aef t/result/repeatable.t SHA1 b83f7770be71ea9e5b627f0323a207bf6e94efce t/structured.t SHA1 ed8a1b0d5f149f02df2dde44f58fc814516171c8 t/template.t SHA1 2c0584c0a616ec1f3ab7cef0b4310df844d45fce t/validation/apply.t SHA1 4bd17c85985d9ba93372f43adf1ec81c9bb3bab4 t/validation/constraints.t SHA1 a7469a1fb8a0e7b3e1af9e5dfd9674eae54e8d7f t/validation/dependency.t SHA1 fa54bae035104eac9559c449aa0d80ba8e2649d3 t/validation/filters.t SHA1 8bebaf81f0a777a646972ea49a0e07b0d700effe t/validation/reqwhen.t SHA1 eb95c62810e613a8cf1fb13381a7c07cbdc7e915 t/validation/types.t SHA1 60761cd04a6350433097c91e438bef74f5ca53b7 t/validation/validate_coderef.t SHA1 bc0b36210f8c7fcc56c9fb9b805cbc993c4394bf t/validation/when.t SHA1 8e040ebd13ce2e819310418baeca2b44c46eadf0 t/var/form1.pl SHA1 2d8d392a1c132b6c6f49fbf2c8eea327d89f1104 t/var/form1.yml SHA1 d23691c4eb64f6237f37959e18ac1d40b0d5c618 t/wizard/basic.t SHA1 6cbc9f61f49670b2211f2fa5157631246b65ebf5 util/check_I18N.pl SHA1 6df1c66c420e0f42840fa6d6c46daf4290630e7a util/get_messages.pl SHA1 e2f1f4a7bd5660f67237752e4b26c57fb08e3187 util/messages SHA1 86d255a7c9f065a13049108362c949b3e35a4c24 xt/02pod.t SHA1 65ca268d24f92823c6a33fcbe7640857315e0d0e xt/add_field.t SHA1 77fc699934e5885b083a8e4485ed3d20c76550b8 xt/captcha.t SHA1 84bae2a79191282ea96e987c00439d7c72bcdad6 xt/captcha2.t SHA1 a536583356d1f37258e77d8bae3491bd3b36793e xt/chbox_group.t SHA1 2148bb20efb65181d062d0906e4d10a904735e2f xt/check_selected_option.t SHA1 1edadbbec253933b48f5185dc9c367acb8472163 xt/custom_fields.t SHA1 4c47aa23f9cdaf64d628ff069666a1caca5c385a xt/cycles.t SHA1 6fe7330dcf9aeb70f6fb8f6f17d1a691df027f36 xt/dfv.t SHA1 4e292478d5ce0ba4b4dc98cfffd15d767e442378 xt/display.t SHA1 2a3193b807b6df2f6964d98a854b95b2bac12184 xt/email.t SHA1 88654c3432fbde212db1de54ba74478ded6c38cc xt/eol.t SHA1 0f80fc1a2dd60772c5ec83096af23b54398b63c1 xt/field_list.t SHA1 e0bae1206838fa9f79e11899182601e1ede46176 xt/form_errors.t SHA1 c08a4366a8d2aad7ac677541352d5ea3cd708043 xt/init.t SHA1 a4f3c6e37039a856f555e8923dcb7b09809b24cf xt/lib/MyCatalystApp.pm SHA1 b689a246cde895936371a1b33245a5c78e4eda68 xt/lib/MyCatalystApp/Controller/Captcha.pm SHA1 aefd98c4a4d10ca1063ebc43d0ee979c01c46154 xt/lib/MyCatalystApp/Controller/Root.pm SHA1 d02a4b95dfe88f5b748acdcd1efda6f4c53012d5 xt/load_field.t SHA1 fbada2c08ac6f80f03050d4af4b0b889b03146b9 xt/locale.t SHA1 183db3551cff148adc7d611af316f3739953afec xt/locale_data_localize.t SHA1 db6e66c79f25cdcf6435f021499473475fdb15a1 xt/mb_form.t SHA1 1ce3a2c875d974a0f81e1233be575386a1bc5612 xt/model_cdbi.t SHA1 a4127156e8dad3f81624bf6b5f5683931f76f88f xt/multiple_forms.t SHA1 94f2ae2ffc6b9e178bf144604b2b68d94af2ecb3 xt/order.t SHA1 b89aefcbbc7f6fbb350932bdedd031b15c66cb9f xt/params.t SHA1 1a18e8b9e12e2a35fe3e08a40547da667c047d3a xt/posted.t SHA1 5554a5710a78de48ea5e7e639eb2483a33e72e40 xt/repeatable_clone.t SHA1 0ad8d5fe3ee1aef6f9f78b7035cf7058ad0eb814 xt/submit.t SHA1 11cfd9ee49b356722fc0b09c75b5110fed8759c2 xt/upload.t -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (Darwin) iF4EAREIAAYFAlJERAEACgkQlX0ZOkgCuchuwQD8DPRu0BuQjt88W0KqAwZjSRB6 pEz60+s89b37XLHEK0oA/0IGVsrczMyYjGf/MclAiO6jDo5rehCFYwN3/2SnU70F =ABCp -----END PGP SIGNATURE----- HTML-FormHandler-0.40050/t/0000755000077000007700000000000012221042077014377 5ustar gshankgshankHTML-FormHandler-0.40050/t/01app.t0000644000077000007700000000051012221042076015500 0ustar gshankgshankuse Test::More; use_ok( 'HTML::FormHandler::Model' ); use_ok( 'HTML::FormHandler' ); use_ok( 'HTML::FormHandler::Field' ); use_ok( 'HTML::FormHandler::Field::Select' ); use_ok( 'HTML::FormHandler::Field::Compound' ); use_ok( 'HTML::FormHandler::Field::Repeatable' ); use_ok( 'HTML::FormHandler::Model::CDBI' ); done_testing; HTML-FormHandler-0.40050/t/basic.t0000644000077000007700000001404412221042076015647 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::I18N; $ENV{LANGUAGE_HANDLE} = 'en_en'; use_ok('HTML::FormHandler'); { package My::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'testform_' ); has '+no_preload' => ( default => 1 ); has_field 'optname' => ( temp => 'First' ); has_field 'reqname' => ( required => 1 ); has_field 'somename'; has_field 'my_selected' => ( type => 'Checkbox' ); has_field 'must_select' => ( type => 'Checkbox', required => 1 ); sub field_list { return [ fruit => 'Select', optname => { temp => 'Second' } ]; } sub options_fruit { return ( 1 => 'apples', 2 => 'oranges', 3 => 'kiwi', ); } } my $form = My::Form->new; is( $form->field('optname')->temp, 'Second', 'got second optname field' ); ok( !$form->process, 'Empty data' ); is_deeply( $form->value, {}, 'no values returns hashref'); my $good = { reqname => 'hello', optname => 'not req', fruit => 2, must_select => 1, }; ok( $form->process($good), 'Good data' ); is( $form->field('somename')->value, undef, 'no value for somename' ); ok( !$form->field('somename')->has_value, 'predicate no value' ); $good->{somename} = 'testing'; $form->process($good); is( $form->field('somename')->value, 'testing', 'use input for extra data' ); is( $form->field('my_selected')->value, 0, 'correct value for unselected checkbox' ); ok( !$form->process( {} ), 'form doesn\'t validate with empty params' ); is( $form->num_errors, 0, 'form doesn\'t have errors with empty params' ); my $bad_1 = { reqname => '', optname => 'not req', fruit => 4, }; ok( !$form->process($bad_1), 'bad 1' ); ok( $form->field('fruit')->has_errors, 'fruit has error' ); ok( $form->field('reqname')->has_errors, 'reqname has error' ); ok( $form->field('must_select')->has_errors, 'must_select has error' ); ok( !$form->field('optname')->has_errors, 'optname has no error' ); is( $form->field('fruit')->id, "fruit", 'field has id' ); is( $form->field('fruit')->label, 'Fruit', 'field label' ); ok( !$form->process( {} ), 'no leftover params' ); is( $form->num_errors, 0, 'no leftover errors' ); ok( !$form->field('reqname')->has_errors, 'no leftover error in field' ); ok( !$form->field('optname')->fif, 'no lefover fif values' ); my $init_object = { reqname => 'Starting Perl', optname => 'Over Again' }; # if you're going to load something on new and expect it to work without # process, you need to have no_preload turned off $form = My::Form->new( init_object => $init_object, no_preload => 0 ); is( $form->field('optname')->value, 'Over Again', 'value with int_obj' ); $form->process( params => {} ); my $value = $form->value; ok( !$form->validated, 'form validated' ); # it's not crystal clear what the behavior should be here, but I think # this is more correct than the previous behavior # it fills in the missing fields, which is what always happened for an # initial object (as opposed to hash), but it used to behave # differently for a hash, which seems wrong # TODO verify behavior is correct my $init_obj_plus_defaults = { 'fruit' => undef, 'must_select' => 0, 'my_selected' => 0, 'optname' => 'Over Again', 'reqname' => 'Starting Perl', 'somename' => undef, }; is_deeply( $form->value, $init_obj_plus_defaults, 'value with empty params' ); $init_object->{my_selected} = 0; # checkboxes must be forced to 0 my %fif = %$init_object; $fif{somename} = ''; $fif{fruit} = ''; $fif{must_select} = 0; is_deeply( $form->fif, \%fif, 'get right fif with init_object' ); # make sure that checkbox is 0 in values $init_object->{must_select} = 1; $fif{must_select} = 1; ok( $form->process($init_object), 'form validates with params' ); #my %init_obj_value = (%$init_object, fruit => undef ); #is_deeply( $form->value, \%init_obj_value, 'value init obj' ); $init_object->{fruit} = undef; is_deeply( $form->value, $init_object, 'value init obj' ); is_deeply( $form->fif, \%fif, 'get right fif with init_object' ); $form->clear; ok( !$form->has_value, 'Form value cleared' ); ok( !$form->has_input, 'Form input cleared' ); # check that form is cleared if fif is done before process $form->fif; $form->process($init_object); is_deeply( $form->fif, \%fif, 'get right fif when process preceded by fif'); $form = HTML::FormHandler->new( field_list => [ foo => { type => 'Text', required => 1 } ] ); if ( !$form->process( params => { bar => 1, } ) ) { # On some versions, the above process() returned false, but # error_fields did not return anything. my @fields = $form->error_fields; if ( is( scalar @fields, 1, "there is an error field" ) ) { my @errors = $fields[0]->all_errors; is( scalar @errors, 1, "there is an error" ); is( $errors[0], $fields[0]->label . " field is required", "error messages match" ); } else { fail("there is an error"); fail("error messages match"); } } # 'image' input produces { foo => bar, 'foo.x' => 42, 'foo.y' => 23 } $form = HTML::FormHandler->new( name => 'baz', html_prefix => 1, field_list => [ 'foo' ] ); eval{ $form->process( params => { 'baz.foo' => 'bar', 'baz.foo.x' => 42, 'baz.foo.y' => 23 } ) }; ok( !$@, 'image field processed' ) or diag $@; is_deeply( $form->field( 'foo' )->value, { '' => 'bar', x => 42, y => 23 }, 'image field' ); { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar'; sub validate { my $self = shift; if( $self->field('foo')->value eq 'cow' ) { $self->field('foo')->value('bovine'); } } } $form = Test::Form->new; $form->process( { foo => 'cow', bar => 'horse' } ); is_deeply( $form->value, { foo => 'bovine', bar => 'horse' }, 'correct value' ); # check for hashref constructor $form = HTML::FormHandler->new( { name => 'test_form', field_list => { one => 'Text', two => 'Text' } } ); ok( $form, 'form constructed ok' ); done_testing; HTML-FormHandler-0.40050/t/blocks/0000755000077000007700000000000012221042077015654 5ustar gshankgshankHTML-FormHandler-0.40050/t/blocks/basic.t0000644000077000007700000000373712221042076017133 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; use_ok('HTML::FormHandler::Blocks'); use_ok('HTML::FormHandler::Widget::Block'); use lib ('t/lib'); { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'test_form' ); has '+widget_name_space' => ( default => sub {'Widget'} ); sub build_render_list { ['header', 'foo', 'my_fieldset'] } has_field 'foo'; has_field 'bar'; has_block 'my_fieldset' => ( tag => 'fieldset', render_list => ['bar'], label => 'My Special Bar' ); has_block 'header' => ( type => 'Test' ); sub validate_bar { my ( $self, $field ) = @_; $field->add_error('Wrong foo'); } } my $form = Test::Form->new; ok( $form, 'form built' ); my $block = $form->block('my_fieldset'); is( ref $block, 'HTML::FormHandler::Widget::Block', 'got a block' ); is_deeply( $form->render_list, ['header', 'foo', 'my_fieldset'], 'got a render list' ); is_deeply( $block->render_list, ['bar'], 'got a render list from the block' ); $form->process; my $rendered = $form->render; my $expected = '

You got to the Block! Congratulations.

My Special Bar
'; is_html( $rendered, $expected, 'block rendered ok' ); $form->process( params => { foo => 'abc', bar => 'def' } ); ok( $form->has_errors, 'form has errors' ); $rendered = $form->field('bar')->render; $expected = '
Wrong foo
'; is_html( $rendered, $expected, 'error formatted ok' ); done_testing; HTML-FormHandler-0.40050/t/blocks/block_list.t0000644000077000007700000000312312221042077020165 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; # test building block_list in a form { package MyApp::Test::Blist; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'test_form' ); has_field 'foo'; has_field 'bar'; has_field 'mix'; has_field 'submit_btn' => ( type => 'Submit' ); sub build_render_list { ['block1', 'block2', 'block3'] } sub build_block_list { return [ { name => 'block1', render_list => ['foo', 'bar'] }, { name => 'block2', render_list => ['mix'] }, { name => 'block3', render_list => ['submit_btn'] }, ]; } } my $form = MyApp::Test::Blist->new; ok( $form, 'form built' ); is( $form->has_blocks, 3, 'right number of blocks' ); my $rendered = $form->render; # test building block_list on the fly my $dyn_form = HTML::FormHandler->new( name => 'test_form', block_list => [ { name => 'block1', render_list => ['foo', 'bar'] }, { name => 'block2', render_list => ['mix'] }, { name => 'block3', render_list => ['submit_btn'] }, ], render_list => ['block1', 'block2', 'block3'], field_list => [ { type => 'Text', name => 'foo' }, { type => 'Text', name => 'bar' }, { type => 'Text', name => 'mix' }, { type => 'Submit', name => 'submit_btn' }, ], ); ok( $dyn_form, 'dynamic form built' ); is( $dyn_form->has_blocks, 3, 'right number of blocks' ); my $dyn_rendered = $dyn_form->render; is_html( $dyn_rendered, $rendered, 'both forms render the same'); done_testing; HTML-FormHandler-0.40050/t/blocks/blocktags.t0000644000077000007700000000221312221042077020010 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::BlockComment; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'test_form' ); has_block 'comment' => ( tag => 'a', content => 'This is a comment from a block', class => ['comment' ] ); has_field 'foo' => ( tags => { before_element => '%comment' } ); has_field 'bar' => ( tags => { before_element => \&bar_element } ); sub bar_element { my $self = shift; return '
In a Sub
'; } } my $form = MyApp::Form::BlockComment->new; ok( $form, 'form built' ); $form->process; my $rendered = $form->render; my $expected = '
In a Sub
'; is_html( $rendered, $expected, 'rendered as expected' ); done_testing; HTML-FormHandler-0.40050/t/blocks/loading.t0000644000077000007700000000233312221042077017457 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; use lib ('t/lib'); use_ok('MyApp::Component::Section'); my $obj = MyApp::Component::Section->new; ok( $obj, 'created section' ); my $rendered = $obj->render; my $expected = '

Please enter the relevant details

'; is_html( $rendered, $expected, 'section rendered standalone' ); { package MyApp::Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'testform' ); has_block 'section1' => ( type => '+MyApp::Component::Section' ); has_field 'foo'; has_field 'bar'; sub build_render_list { ['section1', 'foo', 'bar'] } } my $form = MyApp::Test::Form->new; ok( $form, 'form built' ); $rendered = $form->render; $expected = '

Please enter the relevant details

'; is_html( $rendered, $expected, 'rendered ok' ); done_testing; HTML-FormHandler-0.40050/t/blocks/nested.t0000644000077000007700000000454212221042077017330 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Nested::View; use HTML::FormHandler::Moose::Role; sub build_render_list { ['fset1'] } has_block 'fset1' => ( tag => 'fieldset', label => 'First Fieldset', render_list => ['foo', 'bar', 'pax', 'fset1.sub1', 'fset1.sub2'], ); has_block 'fset1.sub1' => ( tag => 'div', label => 'More Stuff', class => ['sub1'], render_list => ['fee', 'fie', 'fo'], ); has_block 'fset1.sub2' => ( tag => 'div', label => 'And Even More', class => ['sub2'], render_list => ['fum', 'man'], ); } { package MyApp::Form::Nested; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'MyApp::Form::Nested::View'; has '+name' => ( default => 'nested_form' ); has_field 'foo'; has_field 'bar'; has_field 'pax'; has_field 'fee'; has_field 'fie'; has_field 'fo'; has_field 'fum'; has_field 'man'; } my $form = MyApp::Form::Nested->new; ok( $form, 'form built' ); $form->process; my $rendered = $form->render; my $expected = '
First Fieldset
More Stuff
And Even More
'; is_html( $rendered, $expected, 'got expected rendering' ); done_testing; HTML-FormHandler-0.40050/t/blocks/render_units.t0000644000077000007700000000345512221042077020551 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::MyBlocks; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar'; has_field 'flim'; has_field 'flam'; has_field 'fee'; has_field 'fie'; has_field 'fo'; has_field 'fum'; has_block 'abc' => ( render_list => ['foo', 'fo'] ); has_block 'def' => ( render_list => ['bar', 'fum'], label => "My DEF Block", label_class => ['block', 'label'] ); has_block 'ghi' => ( render_list => ['flim', 'flam'], wrapper => 0 ); } my $form = MyApp::Form::MyBlocks->new; my $rendered .= $form->block('ghi')->render; $rendered .= $form->field('fie')->render; $rendered .= $form->block('abc')->render; $rendered .= $form->field('fee')->render; $rendered .= $form->block('def')->render; my $expected = '
My DEF Block
'; is_html($rendered, $expected, 'rendered correctly' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap/0000755000077000007700000000000012221042077016414 5ustar gshankgshankHTML-FormHandler-0.40050/t/bootstrap/basic.t0000644000077000007700000000700712221042077017666 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Basic::Theme; use Moose::Role; # make a wrapper around the form sub build_do_form_wrapper {1} # set the class for the form wrapper sub build_form_wrapper_class { ['span9'] } # set the class for the form element sub build_form_element_class { ['well'] } # set various rendering tags sub build_form_tags { { wrapper_tag => 'div', before => qq{

With v2.0, we have lighter and smarter defaults for form styles. No extra markup, just form controls.

\n}, after => '
', } } # the settings in 'build_update_subfields' are merged with the field # definitions before they are constructed sub build_update_subfields {{ # all fields have a label but no wrapper all => { do_wrapper => 0, do_label => 1 }, # set the element class, a placeholder in element_attr foo => { element_class => ['span3'], element_attr => { placeholder => 'Type something…' }, tags => { after_element => qq{\nAssociated help text!} } }, bar => { option_label => 'Check me out', label_class => ['checkbox'], do_label => 0 }, submit_btn => { element_class => ['btn'] }, }} } { package MyApp::Form::Basic; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'MyApp::Form::Basic::Theme'; with 'HTML::FormHandler::Widget::Theme::BootstrapFormMessages'; has '+name' => ( default => 'basic_form' ); has_field 'foo' => ( type => 'Text' ); has_field 'bar' => ( type => 'Checkbox' ); has_field 'submit_btn' => ( type => 'Submit', value => 'Submit', widget => 'ButtonTag' ); } my $form = MyApp::Form::Basic->new; ok( $form, 'form built' ); $form->process({}); my $rendered = $form->render; my $expected = '

With v2.0, we have lighter and smarter defaults for form styles. No extra markup, just form controls.

Associated help text!
'; is_html($rendered, $expected, 'rendered correctly'); # check foo $rendered = $form->field('foo')->render; $expected = ' Associated help text!'; is_html($rendered, $expected, 'foo field is correct' ); # check bar $rendered = $form->field('bar')->render; $expected = ''; is_html($rendered, $expected, 'bar field is correct' ); $form->process( params => {}, info_message => 'Fill in the form!' ); $rendered = $form->render_form_messages; $expected = '
Fill in the form!'; is_html($rendered, $expected, 'info message rendered ok' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap/control_states.t0000644000077000007700000002503112221042077021645 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::CtrlStates::Theme; use Moose::Role; with 'HTML::FormHandler::Widget::Theme::Bootstrap'; sub build_form_tags {{ after_start => '
Form control states', before_end => '
', }} sub build_update_subfields {{ focusedInput => { element_class => ['input-xlarge', 'focused'] }, uneditableInput => { element_class => ['input-xlarge', 'uneditable-input'] }, disabledInput => { element_class => ['input-xlarge'], element_attr => { placeholder => 'Disabled input here…' } }, optionsCheckbox2 => { element_class => ['checkbox'], option_label => 'This is a disabled checkbox' }, inputError3 => { wrapper_class => ['success'], tags => { after_element => qq{\nWoohoo!} } }, selectError => { wrapper_class => ['success'], tags => { after_element => qq{\nWoohoo!} } }, form_actions => { do_wrapper => 1, do_label => 0 }, 'form_actions.save' => { widget_wrapper => 'None', element_class => ['btn', 'btn-primary'] }, 'form_actions.cancel' => { widget_wrapper => 'None', element_class => ['btn'] }, }} } { package MyApp::Form::CtrlStates; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'MyApp::Form::CtrlStates::Theme'; has '+name' => ( default => 'test_form' ); has_field 'focusedInput' => ( default => 'This is focused…', label => 'Focused input' ); has_field 'uneditableInput' => ( type => 'NonEditable', label => 'Uneditable input', value => 'Some value here', ); has_field 'disabledInput' => ( disabled => 1, label => 'Disabled input' ); has_field 'optionsCheckbox2' => ( type => 'Checkbox', checkbox_value => "option1", disabled => 1, label => 'Disabled checkbox' ); has_field 'inputError1' => ( validate_method => \&validate_ie1, label => 'Input with warning' ); has_field 'inputError2' => ( validate_method => \&validate_ie2, label => 'Input with error' );; has_field 'inputError3' => ( label => 'Input with success' );; has_field 'selectError' => ( type => 'Select', label => 'Select with success', options => [{ value => 1, label => '1' }, { value => 2, label => '2' }, { value => 3, label => '3'}, { value => 4, label => '4' }, { value => 5, label => '5' } ], ); has_field 'form_actions' => ( type => 'Compound' ); has_field 'form_actions.save' => ( widget => 'ButtonTag', type => 'Submit', value => 'Save changes' ); has_field 'form_actions.cancel' => ( widget => 'ButtonTag', type => 'Reset', value => 'Cancel' ); sub validate_ie1 { my $self = shift; # self is the field here $self->add_warning('Something may have gone wrong'); } sub validate_ie2 { my $self = shift; $self->add_error('Please correct the error'); } } my $form = MyApp::Form::CtrlStates->new; my $params = $form->fif; $params->{inputError1} = 'entry'; $params->{inputError2} = 'xxx'; $params->{inputError3} = 'success'; $form->process( $params ); my $expected = '
'; my $rendered = $form->field('focusedInput')->render; is_html( $rendered, $expected, 'focusedInput field renders ok' ); $expected = '
Some value here
'; $rendered = $form->field('uneditableInput')->render; is_html( $rendered, $expected, 'uneditableInput field renders ok' ); $expected = '
'; $rendered = $form->field('disabledInput')->render; is_html( $rendered, $expected, 'disabledInput field renders ok' ); $expected = '
'; $rendered = $form->field('optionsCheckbox2')->render; is_html( $rendered, $expected, 'optionsCheckbox2 renders ok'); $expected = '
Something may have gone wrong
'; $rendered = $form->field('inputError1')->render; is_html( $rendered, $expected, 'inputError1 renders ok' ); $expected = '
Please correct the error
'; $rendered = $form->field('inputError2')->render; is_html( $rendered, $expected, 'inputError2 renders ok' ); # this doesn't come from actual processing; could add something to set $expected = '
Woohoo!
'; $rendered = $form->field('inputError3')->render; is_html( $rendered, $expected, 'inputError3 renders ok' ); $expected = '
Woohoo!
'; $rendered = $form->field('selectError')->render; is_html( $rendered, $expected, 'selectError rendered ok' ); $expected = '
'; $rendered = $form->field('form_actions')->render; is_html( $rendered, $expected, 'form_actions rendered ok' ); $expected = '
Form control states
There were errors in your form
Some value here
Something may have gone wrong
Please correct the error
Woohoo!
Woohoo!
'; $rendered = $form->render; is_html( $rendered, $expected, 'form rendered ok' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap/controls.t0000644000077000007700000002276712221042077020462 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Controls::Theme; use Moose::Role; # widget wrapper must be set before the fields are built in BUILD sub before_build { my $self = shift; $self->set_widget_wrapper('Bootstrap'); } sub build_do_form_wrapper {1} sub build_form_element_class {['form-horizontal']} sub build_form_wrapper_class {['span8']} sub build_form_tags {{ wrapper_tag => 'div', after_start => '
Controls Bootstrap supports', before_end => '
', }} sub build_update_subfields {{ input01 => { tags => { after_element => '

In addition to freeform text, any HTML5 text-based input appears like so.

' }, element_attr => { class => 'input-xlarge' }, }, optionsCheckbox => { option_label => 'Option one is this and that—be sure to include why it’s great' }, fileInput => { element_attr => { class => 'input-file' } }, textarea => { element_attr => { class => 'input-xlarge' } }, actions => { element_attr => { class => 'form-actions' } }, }} } { package MyApp::Form::Component::Actions; use HTML::FormHandler::Moose::Role; has_field 'actions' => ( type => 'Compound', order => 99, widget_wrapper => 'Simple', do_wrapper => 1, do_label => 0, wrapper_attr => { class => 'form-actions' } ); has_field 'actions.save' => ( type => 'Submit', widget => 'ButtonTag', element_attr => { class => ['btn', 'btn-primary'] }, widget_wrapper => 'None', value => "Save changes" ); has_field 'actions.cancel' => ( type => 'Reset', widget => 'ButtonTag', element_attr => { class => ['btn'] }, widget_wrapper => 'None', value => "Cancel" ); } { package MyApp::Form::Controls; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'MyApp::Form::Controls::Theme'; with 'MyApp::Form::Component::Actions'; has '+name' => ( default => 'control_form' ); has_field 'input01' => ( type => 'Text', label => 'Text input' ); has_field 'optionsCheckbox' => ( type => 'Checkbox', checkbox_value => "option1", label => 'Checkbox' ); has_field 'select01' => ( type => 'Select', label => 'Select list' ); has_field 'multiSelect' => ( type => 'Multiple', label => 'Multi-select' ); has_field 'fileInput' => ( type => 'File', label => 'File input' ); has_field 'textarea' => ( type => 'TextArea', cols => 20, rows => 4 ); sub options_select01 { return ( 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5 ); } sub options_multiSelect { return ( 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5 ); } } my $form = MyApp::Form::Controls->new; $form->process; my $rendered = $form->render_start; my $expected = '
Controls Bootstrap supports'; is($rendered, $expected, 'form start rendered correctly'); # text field $expected = '

In addition to freeform text, any HTML5 text-based input appears like so.

'; $rendered = $form->field('input01')->render; is_html($rendered, $expected, 'input01 field rendered correctly'); # checkbox $expected = '
'; is( $form->field('optionsCheckbox')->widget_wrapper, 'Bootstrap', 'correct wrapper' ); $rendered = $form->field('optionsCheckbox')->render; is_html($rendered, $expected, 'optionsCheckbox field rendered correctly'); # select $expected = '
'; $rendered = $form->field('select01')->render; is_html($rendered, $expected, 'select01 renders ok' ); # multiple select $expected = '
'; $rendered = $form->field('multiSelect')->render; is_html($rendered, $expected, 'multiSelect renders ok' ); # file $expected = '
'; $rendered = $form->field('fileInput')->render; is_html($rendered, $expected, 'fileInput render ok' ); # textarea $expected = '
'; $rendered = $form->field('textarea')->render; is_html($rendered, $expected, 'textarea renders ok' ); # actions $expected = '
'; $rendered = $form->field('actions')->render; is_html($rendered, $expected, 'actions renders ok' ); $expected = '
Controls Bootstrap supports

In addition to freeform text, any HTML5 text-based input appears like so.

'; $rendered = $form->render; is_html( $rendered, $expected, 'form renders correctly' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap/ext_controls.t0000644000077000007700000003465212221042077021336 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::ExtControls; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Widget::Theme::Bootstrap'; sub build_form_tags {{ after_start => "
\nExtending form controls", }} sub build_process_list { ['form_sizes', 'prependedInput', 'appendedInput', 'inlineCheckboxes', 'checkboxList'] } has_block 'form_sizes' => ( type => 'Bootstrap', render_list => ['text1', 'text2', 'text3'], label => 'Form sizes', after_controls => '

Use the same .span* classes from the grid system for input sizes.

', ); # these three fields are in the 'form_sizes' block has_field 'text1' => ( widget_wrapper => 'None', element_class => ['span2'], element_attr => { placeholder => '.span2' } ); has_field 'text2' => ( widget_wrapper => 'None', element_class => ['span3'], element_attr => { placeholder => '.span3' } ); has_field 'text3' => ( widget_wrapper => 'None', element_class => ['span4'], element_attr => { placeholder => '.span4' } ); has_field 'prependedInput' => ( label => 'Prepended text', size => 16, element_class => ['span2'], tags => { input_prepend => '@', after_element => '

Here\'s some help text

' } ); has_field 'appendedInput' => ( label => 'Appended text', size => 16, element_class => ['span2'], tags => { input_append => '.00', after_element => '

Here\'s more help text

' } ); has_field 'appendedPrependedInput' => ( label => 'Append and prepend', size => 16, element_class => ['span2'], tags => { input_append => '.00', input_prepend => '$' }, ); has_field 'appendedInputButton' => ( label => 'Append with button', size => 16, element_class => ['span2'], tags => { input_append_button => 'Go!' }, ); has_field 'appendedInputButtons' => ( label => 'Two-button append', size => 16, element_class => ['span2'], tags => { input_append_button => ['Search', 'Options'] }, ); has_field 'inlineCheckboxes' => ( type => 'Multiple', widget => 'CheckboxGroup', label => 'Inline checkboxes', tags => { inline => 1 }, options => [ { value => 'option1', label => '1' }, { value => 'option2', label => '2' }, { value => 'option3', label => '3' } ], ); has_block 'checkboxList' => ( type => 'Bootstrap', label => 'Checkboxes', render_list => ['optionsCheckboxList1', 'optionsCheckboxList2', 'optionsCheckboxList3' ], after_controls => '

Note: Labels surround all the options for much larger click areas and a more usable form.

' ); has_field 'optionsCheckboxList1' => ( type => 'Checkbox', do_wrapper => 0, do_label => 0, checkbox_value => 'option1', option_label => 'Option one is this and that—be sure to include why it’s great', ); has_field 'optionsCheckboxList2' => ( type => 'Checkbox', do_wrapper => 0, do_label => 0, checkbox_value => 'option2', option_label => 'Option two can also be checked and included in form results', ); has_field 'optionsCheckboxList3' => ( type => 'Checkbox', do_wrapper => 0, do_label => 0, checkbox_value => 'option3', option_label => 'Option three can—yes, you guessed it—also be checked and included in form results', ); has_field 'optionsRadios' => ( type => 'Multiple', widget => 'RadioGroup', label => 'Radio buttons', options => [ { value => 'option1', label => 'Option one is this and that—be sure to include why it’s great' }, { value => 'option2', label => 'Option two can is something else and selecting it will deselect option one' }, ] ); } my $form = MyApp::Form::ExtControls->new; ok( $form, 'form built' ); $form->process; my $expected = '

Use the same .span* classes from the grid system for input sizes.

'; my $rendered = $form->block('form_sizes')->render; is_html( $rendered, $expected, 'form_sizes block rendered ok' ); $expected = '
@

Here\'s some help text

'; $rendered = $form->field('prependedInput')->render; is_html( $rendered, $expected, 'prependedInput rendered ok' ); $expected = '
.00

Here\'s more help text

'; $rendered = $form->field('appendedInput')->render; is_html( $rendered, $expected, 'appendedinput rendered ok' ); $expected = '
$.00
'; $rendered = $form->field('appendedPrependedInput')->render; is_html( $rendered, $expected, 'appendedPrependedInput rendered ok' ); $expected = '
'; $rendered = $form->field('appendedInputButton')->render; is_html( $rendered, $expected, 'appendedInputButton rendered ok' ); $expected = '
'; $rendered = $form->field('appendedInputButtons')->render; is_html( $rendered, $expected, 'appendedInputButtons rendered ok' ); $expected = '
'; $rendered = $form->field('inlineCheckboxes')->render; is_html( $rendered, $expected, 'inlineCheckboxes rendered ok' ); $expected = ''; $rendered = $form->field('optionsCheckboxList1')->render; is_html( $rendered, $expected, 'optionsCheckboxList1 rendered ok by itself'); $expected = '

Note: Labels surround all the options for much larger click areas and a more usable form.

'; $rendered = $form->block('checkboxList')->render; is_html( $rendered, $expected, 'checkbox group block renders ok' ); $expected = '
'; $rendered = $form->field('optionsRadios')->render; is_html( $rendered, $expected, 'radio rendered ok' ); $expected = '
Extending form controls

Use the same .span* classes from the grid system for input sizes.

@

Here\'s some help text

.00

Here\'s more help text

Note: Labels surround all the options for much larger click areas and a more usable form.

'; done_testing; HTML-FormHandler-0.40050/t/bootstrap/inline.t0000644000077000007700000000443512221042077020065 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::InLine::Theme; use Moose::Role; # form tag classes sub build_form_element_class { ['well', 'form-search'] } # form wrapper class sub build_form_wrapper_class { ['span9'] } # turn on form wrapper, set the tag to 'div' (default is fieldset) sub build_do_form_wrapper {1} sub build_form_tags {{ wrapper_tag => 'div', before => '

Inline form

Inputs are block level to start. For .form-inline and .form-horizontal, we use inline-block.

', after => '
', no_form_message_div => 1, }} # update individual fields sub build_update_subfields {{ email => { element_class => ['input-small'], element_attr => { placeholder => 'Email' } }, password => { element_class => ['input-small'], element_attr => { placeholder => 'Password' }, tags => { wrapper_tag => 0 } }, go => { element_class => ['btn'] }, }} } { package MyApp::Form::InLine; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'MyApp::Form::InLine::Theme'; has '+name' => ( default => 'inline_form' ); has '+widget_wrapper' => ( default => 'None' ); has_field 'email' => ( type => 'Email' ); has_field 'password' => ( type => 'Password' ); has_field 'go' => ( type => 'Submit', widget => 'ButtonTag', value => 'Go' ) } my $form = MyApp::Form::InLine->new; $form->process; my $rendered = $form->render; my $expected = '

Inline form

Inputs are block level to start. For .form-inline and .form-horizontal, we use inline-block.

'; is_html( $rendered, $expected, 'form renders correctly' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap/other.t0000644000077000007700000000176112221042077017727 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+widget_wrapper' => ( default => 'Bootstrap' ); has_field 'foo' => ( type => 'Hidden' ); has_field 'bar' => ( tags => { before_element_inside_div => '

Start

', after_element => '

End

', } ); } my $form = MyApp::Form::Test->new; ok( $form ); my $rendered = $form->field('foo')->render; my $expected = '
'; is_html( $rendered, $expected, 'rendered ok' ); $rendered = $form->field('bar')->render; $expected = '

Start

End

'; is_html( $rendered, $expected, 'tags rendered ok' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap/search.t0000644000077000007700000000352612221042077020054 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Search::Theme; use Moose::Role; sub build_form_tags {{ before => '

Search form

Reflecting default WebKit styles, just add .form-search for extra rounded search fields.

', after => '
', no_form_message_div => 1, }} # classes for form element sub build_form_element_class { ['well', 'form-search'] } # field updates sub build_update_subfields {{ searchterm => { widget_wrapper => 'None', element_attr => { class => ['input-medium', 'search-query'] }}, submitbtn => { widget => 'ButtonTag', widget_wrapper => 'None', element_attr => { class => ['btn'] } }, }} } { package MyApp::Form::Search; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'MyApp::Form::Search::Theme'; has '+name' => ( default => 'search_form' ); has '+http_method' => ( default => 'get' ); has_field 'searchterm' => ( type => 'Text' ); has_field 'submitbtn' => ( type => 'Submit', value => 'Search' ); } my $expected = '

Search form

Reflecting default WebKit styles, just add .form-search for extra rounded search fields.

'; my $form = MyApp::Form::Search->new; $form->process; my $rendered = $form->render; is_html($rendered, $expected, 'renders correctly' ); done_testing; HTML-FormHandler-0.40050/t/bootstrap3/0000755000077000007700000000000012221042077016477 5ustar gshankgshankHTML-FormHandler-0.40050/t/bootstrap3/basic.t0000644000077000007700000000615012221042077017747 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; use_ok('HTML::FormHandler::Widget::Wrapper::Bootstrap3'); { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+widget_wrapper' => ( default => 'Bootstrap3' ); has_field 'foo' => ( required => 1 ); has_field 'bar'; has_field 'active' => ( type => 'Checkbox' ); has_field 'vegetables' => ( type => 'Multiple', widget => 'RadioGroup' ); sub options_vegetables { return ( 1 => 'lettuce', 2 => 'broccoli', 3 => 'carrots', 4 => 'peas', ); } has_field 'save' => ( type => 'Submit', element_wrapper_class => ['col-lg-offset-2', 'col-lg-10'] ); } my $form = MyApp::Form::Test->new; ok( $form, 'form builds' ); my $expected = '
'; my $rendered = $form->field('foo')->render; is_html( $rendered, $expected, 'foo renders ok' ); $expected = '
'; $rendered = $form->field('active')->render; is_html( $rendered, $expected, 'checkbox renders ok' ); $expected = '
'; $rendered = $form->field('save')->render; is_html( $rendered, $expected, 'submit button renders ok' ); $expected = '
'; $rendered = $form->field('vegetables')->render; is_html( $rendered, $expected, 'radio group renders' ); # after processing $form->process( params => { bar => 'bar' } ); $expected = '
Foo field is required
'; $rendered = $form->field('foo')->render; is_html( $rendered, $expected, 'foo renders ok with error' ); done_testing; HTML-FormHandler-0.40050/t/compound/0000755000077000007700000000000012221042077016223 5ustar gshankgshankHTML-FormHandler-0.40050/t/compound/basic.t0000644000077000007700000001127612221042077017500 0ustar gshankgshankuse Test::More; use lib 't/lib'; use_ok( 'HTML::FormHandler::Field::Duration'); use HTML::FormHandler::I18N; $ENV{LANGUAGE_HANDLE} = 'en_en'; my $field = HTML::FormHandler::Field::Duration->new( name => 'duration' ); $field->build_result; ok( $field, 'get compound field'); my $input = { hours => 1, minutes => 2, }; $field->_set_input($input); is_deeply( $field->input, $input, 'field input is correct'); is_deeply( $field->fif, $input, 'field fif is same'); { package Duration::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'name' => ( type => 'Text' ); has_field 'duration' => ( type => 'Duration' ); has_field 'duration.hours' => ( type => 'Nested' ); has_field 'duration.minutes' => ( type => 'Nested' ); } my $form = Duration::Form->new; ok( $form, 'get compound form' ); ok( $form->field('duration'), 'duration field' ); ok( $form->field('duration.hours'), 'duration.hours field' ); my $params = { name => 'Testing', 'duration.hours' => 2, 'duration.minutes' => 30 }; $form->process( params => $params ); ok( $form->validated, 'form validated' ); is_deeply($form->fif, $params, 'get fif with right value'); is( $form->field('duration')->value->hours, 2, 'duration value is correct'); $form->process( params => { name => 'Testing', 'duration.hours' => 'abc', 'duration.minutes' => 'xyz' } ); ok( $form->has_errors, 'form does not validate' ); my @errors = $form->errors; is( $errors[0], 'Invalid value for Duration: Hours', 'correct error message' ); { package Form::Start; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'name' => ( type => 'Text' ); has_field 'start_date' => ( type => 'DateTime' ); has_field 'start_date.month' => ( type => 'Month' ); has_field 'start_date.day' => ( type => 'MonthDay' ); has_field 'start_date.year' => ( type => 'Year' ); sub validate_start_date_month { my ( $self, $field ) = @_; $field->add_error("That month is not available") if( $field->value == 8 ); } } my $dtform = Form::Start->new; ok( $dtform, 'datetime form' ); $params = { name => 'DT_testing', 'start_date.month' => '10', 'start_date.day' => '2', 'start_date.year' => '2008' }; $dtform->process( params => $params ); ok( $dtform->validated, 'form validated' ); is( $dtform->field('start_date')->value->mdy, '10-02-2008', 'datetime value'); $params->{'start_date.month'} = 8; $dtform->process( params => $params ); ok( !$dtform->validated, 'form did not validate' ); ok( $dtform->has_errors, 'form has error' ); @errors = $dtform->errors; is_deeply( $errors[0], 'That month is not available', 'correct error' ); { package Field::MyCompound; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'aaa'; has_field 'bbb'; } { package Form::TestValues; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'compound' => ( type => '+Field::MyCompound', apply => [ { check => sub { $_[0]->{aaa} eq 'aaa'}, message => 'Must be "aaa"' } ] ); } $form = Form::TestValues->new; ok( $form, 'Compound form with separate fields declarations created' ); $params = { 'compound.aaa' => 'aaa', 'compound.bbb' => 'bbb', }; $form->process( params => $params ); is_deeply( $form->values, { compound => { aaa => 'aaa', bbb => 'bbb' } }, 'Compound with separate fields - values in hash' ); is_deeply( $form->fif, $params, 'get fif from compound field' ); $form->process( params => { 'compound.aaa' => undef } ); ok( !$form->field( 'compound' )->has_errors, 'Not required compound with empty sub values is not checked'); { package Compound; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'year' => ( type => 'Integer', required => 1, ); has_field 'month' => ( type => 'Integer', range_start => 1, range_end => 12, ); has_field 'day' => ( type => 'Integer', range_start => 1, range_end => 31, ); sub default { return { year => undef, month => undef, day => undef }; } } { package Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'date' => ( type => '+Compound', required => 1 ); has_field 'foo'; } my $f = Form->new; $f->process( { 'date.day' => '18', 'date.month' => '2', 'date.year' => '2010' } ); is_deeply( $f->field('date')->value, { year => 2010, month => 2, day => 18 }, 'correct value' ); $f = Form->new; $f->process( { foo => 'testing' } ); is_deeply( $f->field('date')->value, { year => undef, month => undef, day => undef }, 'correct default' ); done_testing; HTML-FormHandler-0.40050/t/compound/default.t0000644000077000007700000000315412221042077020037 0ustar gshankgshankuse strict; use warnings; use Test::More; # tests that a 'default' hashref on a compound field works { { package MyApp::Test::Compound; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'comp_foo' => ( type => 'Compound', default => { one => 1, two => 2, three => 3 } ); has_field 'comp_foo.one'; has_field 'comp_foo.two'; has_field 'comp_foo.three'; } my $form = MyApp::Test::Compound->new; ok( $form ); $form->process; is( $form->field('comp_foo.one')->value, 1, 'value is one' ); is_deeply( $form->field('comp_foo')->value, { one => 1, two => 2, three => 3 }, 'value for compound is correct' ); } # tests that default object for a compound field works # object provided by default_method { { package MyApp::Foo; use Moose; has 'one' => ( is => 'ro', default => 1 ); has 'two' => ( is => 'ro', default => 2 ); has 'three' => ( is => 'ro', default => 3 ); } { package MyApp::Test::Compound2; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'comp_foo' => ( type => 'Compound', default_method => \&default_comp_foo ); has_field 'comp_foo.one'; has_field 'comp_foo.two'; has_field 'comp_foo.three'; sub default_comp_foo { return MyApp::Foo->new; } } my $form = MyApp::Test::Compound2->new; ok( $form ); is( $form->field('comp_foo.one')->value, 1, 'value is one' ); is( ref $form->field('comp_foo')->item, 'MyApp::Foo', 'item saved' ); } done_testing; HTML-FormHandler-0.40050/t/compound/empty.t0000644000077000007700000000352712221042077017555 0ustar gshankgshankuse strict; use warnings; use Test::More; # tests behavior for an empty compound field, where the compund field value # is undef { { package MyApp::Test::Compound; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'comp_foo' => ( type => 'Compound', default => { one => 1, two => 2, three => 3 } ); has_field 'comp_foo.one'; has_field 'comp_foo.two'; has_field 'comp_foo.three'; has_field 'bar'; } my $form = MyApp::Test::Compound->new; ok( $form ); my $params = { 'comp_foo.one' => '', 'comp_foo.two' => '', 'comp_foo.three' => '', 'bar' => 'my_bar', }; $form->process( params => $params ); my $value = $form->value; my $exp_value = { comp_foo => undef, bar => 'my_bar', }; is_deeply( $value, $exp_value, 'got expected value' ); } # tests behavior for an empty compound field with 'not_nullable', where the # compund field contains empty values { { package MyApp::Test::Compound; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'comp_foo' => ( type => 'Compound', not_nullable => 1 ); has_field 'comp_foo.one'; has_field 'comp_foo.two'; has_field 'comp_foo.three'; has_field 'bar'; } my $form = MyApp::Test::Compound->new; ok( $form ); my $params = { 'comp_foo.one' => '', 'comp_foo.two' => '', 'comp_foo.three' => '', 'bar' => 'my_bar', }; $form->process( params => $params ); my $value = $form->value; my $exp_value = { comp_foo => { one => undef, two => undef, three => undef, }, bar => 'my_bar', }; is_deeply( $value, $exp_value, 'got expected value' ); } done_testing; HTML-FormHandler-0.40050/t/compound/include.t0000644000077000007700000000222612221042077020035 0ustar gshankgshankuse strict; use warnings; use Test::More; # Test using the 'include' attribute on a compound field # to limit the fields that are built. # alternative to active/inactive. Created for a situation # in which there are a very large number of fields in a # field/role and you don't want the overhead of building them # and then setting them activie/inactive. { package Test::FooField; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'foo'; has_field 'bar'; has_field 'box'; has_field 'mix'; has_field 'nix'; } my $field = Test::FooField->new( name => 'muddly' ); ok( $field ); { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'fiddly'; has_field 'muddly' => ( type => '+Test::FooField', include => ['foo', 'mix', 'nix'] ); # no subfields. kinda weird... has_field 'middly' => ( type => '+Test::FooField', include => [ 'empty' ] ); } my $form = Test::Form->new; ok( $form ); is( $form->field('muddly')->num_fields, 3, 'right number of fields' ); is( $form->field('middly')->num_fields, 0, 'right number of fields' ); done_testing; HTML-FormHandler-0.40050/t/compound/select.t0000644000077000007700000000223212221042077017666 0ustar gshankgshankuse strict; use warnings; use Test::More; { package Test::Field::MyComp; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; has_field 'flim' => ( type => 'Select', options_method => \&options_flim ); has_field 'flam' => ( type => 'Multiple', options_method => \&options_flam ); has_field 'flot'; sub options_flim { my $self = shift; return [ { value => 1, label => 'one' }, { value => 2, label => 'two' } ]; } sub options_flam { my $self = shift; return [ { value => 1, label => 'red' }, { value => 2, label => 'blue' } ]; } } { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+field_name_space' => ( default => sub { [ 'Test::Field' ] } ); has_field 'floo' => ( type => 'MyComp' ); has_field 'ploot'; } my $form = Test::Form->new; ok( $form, 'form built' ); my $flim_options = $form->field('floo.flim')->options; is( scalar @$flim_options, 2, 'right number of flim options' ); my $flam_options = $form->field('floo.flam')->options; is( scalar @$flam_options, 2, 'right number of flam options' ); done_testing; HTML-FormHandler-0.40050/t/errors/0000755000077000007700000000000012221042077015713 5ustar gshankgshankHTML-FormHandler-0.40050/t/errors/basic.t0000644000077000007700000001057112221042076017164 0ustar gshankgshankuse strict; use warnings; use Test::More; use_ok( 'HTML::FormHandler' ); use HTML::FormHandler::I18N; $ENV{LANGUAGE_HANDLE} = 'en_en'; { package My::Form; use Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'testform_' ); sub field_list { return [ reqname => { type => 'Text', required => 1, messages => { required => 'You must supply a reqname' }, }, fruit => 'Select', optname => 'Text', silly_name => { type =>'Text', set_validate => 'valid_silly' } ]; } sub options_fruit { return ( 1 => 'apples', 2 => 'oranges', 3 => 'kiwi', ); } sub valid_silly { my ( $self, $field ) = @_; $field->add_error( 'Not a valid silly_name' ) unless $field->value eq 'TeStInG'; } } my $form = My::Form->new; my $bad_1 = { optname => 'not req', fruit => 4, silly_name => 'what??', }; ok( !$form->process( $bad_1 ), 'bad 1' ); ok( $form->has_errors, 'form has error' ); my $errors_by_id = { fruit => [q{'4' is not a valid value}], reqname => ['You must supply a reqname'], silly_name => ['Not a valid silly_name'], }; is_deeply( $form->errors_by_id, $errors_by_id, 'right errors' ); is_deeply( $form->errors_by_name, $errors_by_id, 'errors by name are the same as by id'); ok( $form->field('fruit')->has_errors, 'fruit has error' ); ok( $form->field('reqname')->has_errors, 'reqname has error' ); ok( !$form->field('optname')->has_errors, 'optname has no error' ); ok( $form->field('silly_name')->has_errors, 'silly_name has error' ); ok( $form->has_errors, 'form has errors' ); my @fields = $form->error_fields; ok( @fields, 'error fields' ); my @errors = $form->errors; is_deeply( \@errors, [ 'You must supply a reqname', '\'4\' is not a valid value', 'Not a valid silly_name' ], 'errors from form' ); is( $form->num_errors, 3, 'number of errors' ); my @field_names = $form->error_field_names; is_deeply( \@field_names, [ 'reqname', 'fruit', 'silly_name' ], 'error field names' ); is( $form->field('fruit')->id, "fruit", 'field has id' ); { package Repeatable::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'my_test'; has_field 'addresses' => ( type => 'Repeatable', auto_id => 1 ); has_field 'addresses.street'; has_field 'addresses.city'; has_field 'addresses.country'; sub validate_addresses_city { my ( $self, $field ) = @_; $field->add_error("Invalid City: " . $field->value) if( $field->value !~ /City/ ); } } my $init_object = { my_test => 'repeatable_errors', addresses => [ { street => 'First Street', city => 'Prime', country => 'Utopia', id => 0, }, { street => 'Second Street', city => 'Secondary', country => 'Graustark', id => 1, }, { street => 'Third Street', city => 'Tertiary City', country => 'Atlantis', id => 2, } ] }; $form = Repeatable::Form->new; ok( $form, 'form created'); $form->process( $init_object ); ok( !$form->validated, 'form did not validate' ); is( $form->num_errors, 2, 'form has two errors' ); my $rendered_field = $form->field('addresses')->render; ok( $rendered_field, 'rendered field with auto_id ok' ); { package Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( type => 'Repeatable', required => 1); has_field 'foo.bar' => ( type => 'Text', required => 1); has_field 'foo.optional' => ( type => 'Text', required => 0); } $form = Form->new; ok(!$form->process( params => { 'foo.0.bar' => '' , 'foo.1.bar' => '' }), 'Processing a form with empty fields should not validate'); ok(!$form->process( params => { 'foo.0.bar' => '' , 'foo.1.bar' => 'Test' }), 'Processing a form with some empty fields should not validate'); ok(!$form->process( params => { 'foo.0.bar' => 'Test' , 'foo.1.bar' => '' }), 'Processing a form with some empty fields should not validate'); ok($form->process( params => { 'foo.0.bar' => 'Test' , 'foo.1.bar' => 'Test' }), 'Processing a form with all inputs validates'); done_testing; HTML-FormHandler-0.40050/t/errors/compound.t0000644000077000007700000000364412221042077017733 0ustar gshankgshankuse strict; use warnings; use Test::More; # shows behavior of required flag in compound fields { package MyApp::Form::User; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'userform' ); has_field 'name'; has_field 'email'; has_field 'address' => ( type => 'Compound' ); has_field 'address.city' => ( required => 1 ); has_field 'address.state' => ( required => 1 ); } my $form = MyApp::Form::User->new; my $params = { name => 'John Doe', email => 'jdoe@gmail.com', }; # no errors if compound subfields are required but missing # and compound field is not required $form->process( params => $params ); ok( $form->validated, 'no errors in form' ); # error if one field is entered and not the other # and compound field is not required $form->process( params => { %$params, 'address.city' => 'New York' } ); ok( $form->has_errors, 'error with one field filled' ); # errors if compound subfields are required & compound is required $form->process( update_field_list => { address => { required => 1 } }, params => $params ); ok( $form->has_errors, 'errors in form' ); # tests that errors are propagated up the tree, and aren't duplicated { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'name'; has_field 'comp1' => ( type => 'Compound' ); has_field 'comp1.comment'; has_field 'comp1.comp2' => ( type => 'Compound' ); has_field 'comp1.comp2.one'; has_field 'comp1.comp2.two' => ( type => 'PosInteger' ); } $form = MyApp::Form::Test->new; ok( $form ); $form->process; $params = { name => 'test', 'comp1.comment' => 'This is a test', 'comp1.comp2.one' => 1, 'comp1.comp2.two' => 'abc', }; $form->process( params => $params ); ok( $form->has_errors, 'form has errors' ); my @errors = $form->errors; is( scalar @errors, 2, 'right number of errors' ); done_testing; HTML-FormHandler-0.40050/t/errors/form_messages.t0000644000077000007700000000270512221042077020736 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'testform' ); has '+success_message' => ( default => 'Successfully Submitted' ); has_field 'foo'; has_field 'bar'; } my $form = MyApp::Test::Form->new; my $params = { foo => 'myfoo', bar => 'mybar', }; $form->process( params => $params ); ok( $form->validated, 'form validated' ); my $rendered = $form->render; my $expected = '
Successfully Submitted
'; is_html( $rendered, $expected, 'success message rendered ok' ); $form->process( params => {} ); $rendered = $form->render; unlike( $rendered, qr/class="success_message"/, 'no success message when not validated' ); $form->process( params => {}, info_message => 'Please fill out the form completely' ); $rendered = $form->render; like( $rendered, qr/class="info_message"/, 'info message is there' ); $form->process( params => {} ); $rendered = $form->render; unlike( $rendered, qr/class="info_message"/, 'info message has been cleared' ); done_testing; HTML-FormHandler-0.40050/t/errors/messages.t0000644000077000007700000000102112221042077017701 0ustar gshankgshankuse strict; use warnings; use Test::More; { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; sub build_messages { { required => 'You must supply this field', } } has_field 'foo' => ( type => 'Text', required => 1 ); has_field 'bar'; } my $form = Test::Form->new; ok( $form, 'form built'); $form->process( params => { bar => 1} ); my @errors = $form->errors; is( $errors[0], 'You must supply this field', 'form has errors' ); done_testing; HTML-FormHandler-0.40050/t/errors/req_message.t0000644000077000007700000000125612221042077020377 0ustar gshankgshankuse strict; use warnings; use Test::More; { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( required => 1, required_message => "You must supply a FOO!" ); has_field 'bar' => ( required => 1, messages => { required => 'You must supply a BAR!' } ); has_field 'baz'; } my $form = Test::Form->new; $form->process( params => {} ); $form->process( params => { baz => 'True' } ); my @errors = $form->errors; is( scalar @errors, 2, 'right number of errors' ); is( $errors[0], 'You must supply a FOO!', 'right message for foo' ); is( $errors[1], 'You must supply a BAR!', 'right message for bar' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/0000755000077000007700000000000012221042077016702 5ustar gshankgshankHTML-FormHandler-0.40050/t/field_setup/compound_update_fields.t0000644000077000007700000000315712221042077023611 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Field::Record; use HTML::FormHandler::Moose; extends 'HTML::FormHandler::Field::Compound'; sub build_update_subfields {{ all => { wrapper_class => 'wrap', tags => { 'before_element' => '

A comment

' } }, 'sam' => { label_class => 'sam_label' }, }} has_field 'flot'; has_field 'jet'; has_field 'sam'; } { package MyApp::Form::Complex; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'test_form' ); has '+field_name_space' => ( default => sub{ ['MyApp::Form::Field'] } ); has_field 'foo'; has_field 'bar' => ( type => 'Record' ); } my $form = MyApp::Form::Complex->new; ok( $form, 'form built' ); $form->process; my $rendered = $form->render; my $expected = '

A comment

A comment

A comment

'; is_html( $rendered, $expected, 'rendered as expected' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/defaults.t0000644000077000007700000001702312221042077020701 0ustar gshankgshankuse strict; use warnings; use Test::More; use lib 't/lib'; { package Test::Defaults; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( default => 'default_foo' ); has_field 'bar' => ( default => '' ); has_field 'bax' => ( default => 'default_bax' ); } my $form = Test::Defaults->new; my $cmp_fif = { foo => 'default_foo', bar => '', bax => 'default_bax', }; # test that defaults in fields are used in # filling in the form is_deeply( $form->fif, $cmp_fif, 'fif has right defaults' ); $form->process( params => {} ); is_deeply( $form->fif, $cmp_fif, 'fif has right defaults' ); # test that an init_object overrides defaults in fields my $init_obj = { foo => '', bar => 'testing', bax => '' }; $form->process( init_object => $init_obj, params => {} ); is_deeply( $form->fif, { foo => '', bar => 'testing', bax => '' }, 'object overrides defaults'); { package Test::DefaultsX; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( default_over_obj => 'default_foo' ); has_field 'bar' => ( default_over_obj => '' ); has_field 'bax' => ( default_over_obj => 'default_bax' ); } # test that the 'default_over_obj' type defaults override an init_object/item $form = Test::DefaultsX->new; $form->process( init_object => $init_obj, params => {} ); is( $form->field('foo')->default_over_obj, 'default_foo', 'foo correct' ); is_deeply( $form->fif, $cmp_fif, 'fif uses defaults overriding object' ); { package My::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'testform_' ); has_field 'optname' => ( temp => 'First' ); has_field 'reqname' => ( required => 1 ); has_field 'somename'; } $form = My::Form->new( init_object => {reqname => 'Starting Perl', optname => 'Over Again' } ); # additional test for init_object provided defaults ok( $form, 'non-db form created OK'); is( $form->field('optname')->value, 'Over Again', 'get right value from form'); $form->process({}); ok( !$form->validated, 'form validated' ); is( $form->field('reqname')->fif, 'Starting Perl', 'get right fif with init_object'); { package My::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'initform_' ); has_field 'foo'; has_field 'bar'; has_field 'bax' => ( default => 'default_bax' ); has '+init_object' => ( default => sub { { foo => 'initfoo' } } ); sub default_bar { 'init_value_bar' } } $form = My::Form->new; # test default_ methods in form # plus init_object defined in form class # plus default is used when init_object doesn't have key/accessor is( $form->field('foo')->value, 'initfoo', 'value from init_object' ); is( $form->field('foo')->fif, 'initfoo', 'fif ok' ); is( $form->field('bar')->value, 'init_value_bar', 'value from field default meth' ); is( $form->field('bar')->fif, 'init_value_bar', 'fif ok' ); is( $form->field('bax')->value, 'default_bax', 'value from field default' ); is( $form->field('bax')->fif, 'default_bax', 'fif ok' ); { package Mock::Object; use Moose; has 'foo' => ( is => 'rw' ); has 'bar' => ( is => 'rw' ); has 'baz' => ( is => 'rw' ); } { package Test::Object; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Model::Object'; sub BUILD { my $self = shift; my $var = 'test'; } has_field 'foo'; has_field 'bar'; has_field 'baz'; has_field 'bax' => ( default => 'bax_from_default' ); has_field 'zero' => ( type => 'PosInteger', default => 0 ); has_field 'foo_list' => ( type => 'Multiple', default => [1,3], options => [{ value => 1, label => 'One'}, { value => 2, label => 'Two'}, { value => 3, label => 'Three'}, ] ); sub init_object { my $self = shift; return { bar => 'initbar' }; } } my $obj = Mock::Object->new( foo => 'myfoo', bar => 'mybar', baz => 'mybaz' ); $form = Test::Object->new; $form->process( item => $obj, item_id => 1, params => {} ); # test that item is used for value is( $form->field('foo')->value, 'myfoo', 'field value from item'); is( $form->field('foo')->fif, 'myfoo', 'field fif from item'); is( $form->field('bar')->value, 'mybar', 'field value from item'); is( $form->field('bar')->fif, 'mybar', 'field fif from item'); # test that non-item default is used is( $form->field('bax')->value, 'bax_from_default', 'non-item field value from default' ); is( $form->field('zero')->value, 0, 'zero default works'); is_deeply( $form->field('foo_list')->value, [1,3], 'multiple default works' ); { package Test::Object2; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Model::Object'; has_field 'foo'; has_field 'bar'; has_field 'baz'; } $form = Test::Object2->new; # test that init_object values are used with 'use_init_obj_over_item' flag my $def_obj = Mock::Object->new( foo => 'def_myfoo', bar => 'def_mybar', baz => 'def_mybaz' ); my $new_obj = Mock::Object->new; $form->process( item => $new_obj, init_object => $def_obj, use_init_obj_over_item => 1 ); is( $form->field('foo')->value, 'def_myfoo', 'value from init_object not item' ); is( $form->field('foo')->fif, 'def_myfoo', 'fif from init_object not item' ); # test that flag is cleared for next run $form->process( item => $new_obj, init_object => $def_obj ); is( $form->field('foo')->value, undef, 'next process without that flag' ); { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has 'quuz' => ( is => 'ro', default => 'some_quux' ); has_field 'foo'; has_field 'bar'; sub init_object { my $self = shift; return { foo => $self->quuz, bar => 'bar!' }; } } $form = Test::Form->new; is( $form->field('foo')->value, 'some_quux', 'field initialized by init_object' ); { package Mock::Object2; use Moose; has 'meow' => ( is => 'rw' ); } { package Test::Object; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Model::Object'; has_field 'meow' => ( default => 'this_should_get_overridden' ); } $obj = Mock::Object2->new( meow => 'the_real_meow' ); $form = Test::Object->new; $form->process( item => $obj, item_id => 1, params => {} ); is( $form->field('meow')->value, 'the_real_meow', 'defaults should not override actual item values'); { package Test::Form2; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar'; has_field 'win'; } $form = Test::Form2->new; $form->process( defaults => { foo => 'foo_def', bar => 'bar_def', win => 'win_def' } ); ok( ! $form->validated, 'no params so not validated' ); is( $form->field('foo')->default, 'foo_def', 'foo has default' ); is( $form->field('foo')->fif, 'foo_def', 'fif is correct' ); is( $form->field('bar')->default, 'bar_def', 'bar has right default' ); is( $form->field('bar')->fif, 'bar_def', 'bar has correct fif' ); is_deeply( $form->fif, { foo => 'foo_def', bar => 'bar_def', win => 'win_def' }, 'right fif' ); $form->process( init_object => { foo => 'foo_from_obj', bar => 'bar_from_obj', win => 'win_from_obj' }, defaults => { bar => 'bar_from_defaults', foo => undef }, use_defaults_over_obj => 1, ); is( $form->field('bar')->fif, 'bar_from_defaults', 'defaults used instead of init_object' ); is( $form->field('foo')->fif, 'foo_from_obj', 'no value from default set to undef' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/disabled.t0000644000077000007700000000455612221042077020650 0ustar gshankgshankuse strict; use warnings; use Test::More; use Data::Dumper; # tests that an init_value provided by item/init_object is used # for disabled fields, and that disabled fields are not validated. { { package MyApp::Test::Form1; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( type => 'Select', disabled => 1 ); has_field 'bar'; has_field 'user' => ( type => 'Compound', required => 1 ); has_field 'user.email_address' => ( disabled => 1, required => 1, validate_method => \&check_email ); sub check_email { my $self = shift; if ( $self->value && $self->value =~ /joe/ ) { $self->add_error("No emails with 'joe'"); } } has_field 'save' => ( type => 'Submit' ); } my $form = MyApp::Test::Form1->new; my $init_object = { foo => 'my_foo', bar => 'my_bar', user => { email_address => 'joe@nowhere.com' }, }; $form->process( init_object => $init_object, params => {} ); my $fif = { foo => 'my_foo', bar => 'my_bar', 'user.email_address' => 'joe@nowhere.com', }; is_deeply( $form->fif, $fif, 'fif is correct' ); my $submitted = { bar => 'subm_bar', }; $form->process( init_object => $init_object, params => $submitted ); $fif->{bar} = 'subm_bar'; is_deeply( $form->fif, $fif, 'right fif after submission, init_object' ); $init_object->{bar} = 'subm_bar'; is_deeply( $form->value, $init_object, 'right value' ); } { { package MyApp::Test::Form2; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( type => 'Select', disabled => 1, default => 'def_foo' ); has_field 'bar' => ( default => 'def_bar' ); has_field 'save' => ( type => 'Submit' ); } my $form = MyApp::Test::Form2->new; $form->process( params => {} ); my $fif = { foo => 'def_foo', bar => 'def_bar' }; is_deeply( $form->fif, $fif, 'fif is correct using defaults' ); my $submitted = { bar => 'subm_bar' }; $form->process( params => $submitted ); is_deeply( $form->fif, { foo => 'def_foo', bar => 'subm_bar' }, 'right fif after submission' ); is_deeply( $form->value, { foo => 'def_foo', bar => 'subm_bar' } ); } done_testing; HTML-FormHandler-0.40050/t/field_setup/id.t0000644000077000007700000000252112221042077017463 0ustar gshankgshankuse strict; use warnings; use Test::More; # test dynamic field ID { package My::DynamicFieldId; use Moose::Role; around 'id' => sub { my $orig = shift; my $self = shift; my $form_name = $self->form->name; return $form_name . "." . $self->full_name; }; } { package My::CustomIdForm; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'F123' ); has '+html_prefix' => ( default => 1 ); has '+field_traits' => ( default => sub { ['My::DynamicFieldId'] } ); has_field 'foo'; has_field 'bar'; } my $form = My::CustomIdForm->new; is( $form->field('foo')->id, 'F123.foo', 'got correct id' ); # test providing a coderef for field ID building { package MyApp::CustomId; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; sub build_update_subfields { { all => { build_id_method => \&custom_id } } } has_field 'foo' => ( type => 'Compound' ); has_field 'foo.one'; has_field 'foo.two'; has_field 'foo.three'; sub custom_id { my $self = shift; my $full_name = $self->full_name; $full_name =~ s/\./_/g; return $full_name; } } $form = MyApp::CustomId->new; ok( $form, 'form built' ); is( $form->field('foo.two')->id, 'foo_two', 'got correct id' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/inactive.t0000644000077000007700000000421112221042077020667 0ustar gshankgshankuse strict; use warnings; use Test::More; use Test::Exception; { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'bar'; has_field 'foo' => ( inactive => 1 ); has_field 'foo_checkbox' => ( type => 'Checkbox', inactive => 1 ); } my $form = Test::Form->new; ok( $form, 'form builds' ); $form->process; is( $form->num_fields, 3, 'right number of fields' ); is( scalar @{$form->sorted_fields}, 1, 'right number of sorted fields' ); throws_ok( sub { $form->field('foo')->render }, qr/No result for form field/, 'dies when rendering inactive field' ); $form->field('foo')->clear_inactive; is( scalar @{$form->sorted_fields}, 2, 'right number of sorted fields after clear_inactive' ); my $fif = { bar => 'bar_test', foo => 'foo_test', }; $form->process($fif); ok( $form->validated, 'form validated' ); is_deeply( $form->fif, $fif, 'fif is correct' ); is_deeply( $form->value, $fif, 'value is correct' ); $form = Test::Form->new; my $active = ['foo']; $form->process( active => [@{$active}], params => $fif ); is_deeply( $active, ['foo'], 'active hashref still there' ); ok( $form->validated, 'form validated' ); is_deeply( $form->fif, $fif, 'fif is correct' ); is_deeply( $form->value, $fif, 'value is correct' ); # tests for setting active fields inactive { package Test::Another; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar'; has_field 'gaz'; } $form = Test::Another->new( inactive => ['bar'] ); ok( $form->field('bar')->is_inactive, 'field is inactive' ); ok( $form->field('foo')->is_active, 'field is active' ); my $html = $form->render; unlike($html, qr/input.*name="bar"/, 'inactive field is hidden'); like($html, qr/input.*name="foo"/, 'active field is shown'); $form = Test::Another->new; $form->process( inactive => ['gaz'], params => {} ); ok( $form->field('gaz')->is_inactive, 'field is inactive' ); ok( $form->field('foo')->is_active, 'field is active' ); $html = $form->render; unlike($html, qr/input.*name="gaz"/, 'inactive field is hidden'); like($html, qr/input.*name="foo"/, 'active field is shown'); done_testing; HTML-FormHandler-0.40050/t/field_setup/init_object.t0000644000077000007700000000236112221042077021362 0ustar gshankgshankuse strict; use warnings; use Test::More; # this tests that a multiple select with value from an init_object # has the right value with both a hashref and a blessed object { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( type => 'Select', multiple => 1 ); sub options_foo { [ 1 => 'One', 2 => 'Two', 3 => 'Three', 4 => 'Four', ] } has_field 'bar'; } { package FooObject; use Moose; has 'foo' => ( is => 'ro', isa => 'ArrayRef', traits => ['Array'], handles => { 'has_foo' => 'count', } ); has 'bar' => ( is => 'ro', isa => 'Str' ); } my $form = MyApp::Form::Test->new; ok( $form ); # try with hashref my $init_obj = { foo => [1], bar => 'my_test', }; $form->process( init_object => $init_obj ); is_deeply( $form->field('foo')->value, [1], 'right value for foo field with hashref init_obj' ); # try with object my $foo = FooObject->new( foo => [1], bar => 'my_test', ); $form->process( init_object => $foo ); is_deeply( $form->field('foo')->value, [1], 'right value for foo field with object init_obj' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/input_param.t0000644000077000007700000000153112221042077021406 0ustar gshankgshankuse strict; use warnings; use Test::More; { package Test::HTML::FormHandler::InputParam; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'base_name' => ( type => 'Text', required => 1, input_param=> 'input_name', ); } my $form1 = Test::HTML::FormHandler::InputParam->new; ok( $form1, 'Created Form' ); my %params1 = ( input_name => 'This is a mapped input',); my $result1 = $form1->process(params=>\%params1); ok( $result1, 'got good result' ); ok( !$form1->has_errors, 'No errors' ); my $form2 = Test::HTML::FormHandler::InputParam->new; ok( $form2, 'Created Form' ); my %params2 = ( base_name => 'This is a mapped input' ); my $result2 = $form2->process(params=>\%params2); ok( ! $result2, 'got correct failing result' ); ok( $form2->has_errors, 'Yes there are errors' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/missing.t0000644000077000007700000000122112221042077020534 0ustar gshankgshankuse strict; use warnings; use Test::More; { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'test_form' ); has_field 'foo' => ( required => 1 ); has_field 'bar' => ( required => 1 );; } my $form = MyApp::Form::Test->new; $form->process( params => {} ); ok( ! $form->field('bar')->missing, 'bar is not missing' ); ok( ! $form->field('foo')->missing, 'foo is not missing' ); $form->process( params => { foo => 'my_foo', bar => '' } ); ok( $form->field('bar')->missing, 'bar is missing' ); ok( ! $form->field('foo')->missing, 'foo is not missing' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/plus_field.t0000644000077000007700000000134512221042077021220 0ustar gshankgshankuse strict; use warnings; use Test::More; { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; with 'MyApp::Form::Role::Factors'; has_field '+bar_one' => ( accessor => 'dimension_value_ids' ); has_field '+bar_two' => ( accessor => 'factor_value_ids' ); } { package MyApp::Form::Role::Factors; use HTML::FormHandler::Moose::Role; has_field bar_one => ( type => 'Repeatable', required => 0 ); has_field 'bar_one.contains' => ( type => 'Integer' ); has_field bar_two => ( type => 'Repeatable', required => 0 ); has_field 'bar_two.contains' => ( type => 'Integer' ); } my $form = MyApp::Form::Test->new; ok( $form ); done_testing; HTML-FormHandler-0.40050/t/field_setup/update_fields.t0000644000077000007700000000454712221042077021711 0ustar gshankgshankuse strict; use warnings; use Test::More; { package Test::Dates; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar' => ( type => 'Hidden' ); has_field 'foo_date' => ( type => 'Date' ); } my $form = Test::Dates->new; my $field_updates = { foo_date => { format => '%m/%e/%Y', date_start => '10-01-01' }, bar => { default => 'formabc' }, }; my $fif = {}; $form->process( update_field_list => $field_updates, params => $fif ); is( $form->field('foo_date')->date_start, '10-01-01', 'field updated' ); is( $form->field('foo_date')->format, '%m/%e/%Y', 'field updated' ); is( $form->field('bar')->value, 'formabc', 'hidden field has custom value' ); $fif = $form->fif; $fif->{foo} = 'testing'; $fif->{foo_date} = '10-06-22'; $form->process( update_field_list => $field_updates, params => $fif ); is( $form->field('foo_date')->date_start, '10-01-01', 'field updated' ); is( $form->field('foo_date')->format, '%m/%e/%Y', 'field updated' ); is( $form->field('bar')->value, 'formabc', 'hidden field has custom value' ); is( $form->field('foo')->value, 'testing', 'correct value for foo' ); { package Test::Field::Updates; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has 'form_id' => ( isa => 'Str', is => 'rw' ); has_field 'foo'; has_field 'bar'; has_field 'blah' => ( type => 'Hidden' ); sub update_fields { my $self = shift; $self->field('foo')->temp( 'foo_temp' ); $self->field('bar')->default( 'foo_value' ); $self->field('blah')->default( $self->form_id ); } } $form = Test::Field::Updates->new; $fif = {}; $form->process( form_id => 'page1form', params => $fif ); is( $form->field('foo')->temp, 'foo_temp', 'foo field updated' ); is( $form->field('bar')->value, 'foo_value', 'foo value updated from default' ); is( $form->field('blah')->value, 'page1form', 'right value for hidden field' ); $fif = $form->fif; $fif->{foo} = 'fooforall'; $fif->{bar} = 'barbiedoll'; $form->process( form_id => 'page1form', params => $fif ); is( $form->field('foo')->temp, 'foo_temp', 'foo field updated' ); is( $form->field('bar')->value, 'barbiedoll', 'foo value updated from default' ); is( $form->field('blah')->value, 'page1form', 'right value for hidden field' ); is( $form->field('foo')->value, 'fooforall', 'right value for foo' ); done_testing; HTML-FormHandler-0.40050/t/field_setup/update_subfields.t0000644000077000007700000000353312221042077022415 0ustar gshankgshankuse strict; use warnings; use Test::More; # tests that 'by_flag' works for contains # Test 'by_type' flag { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; sub build_update_subfields {{ by_flag => { contains => { wrapper_class => ['rep_elem'] } }, by_type => { 'Select' => { wrapper_class => ['sel_wrapper'] }, 'BoolSelect' => { element_class => ['sel_elem'] }, }, }} has_field 'records' => ( type => 'Repeatable' ); has_field 'records.one'; has_field 'records.two'; has_field 'foo' => ( type => 'Select', options => [ { value => 1, label => 'One' }, { value => 2, label => 'Two' }] ); has_field 'bar' => ( type => 'BoolSelect' ); } my $form = Test::Form->new; $form->process; is_deeply( $form->field('records.0')->wrapper_class, ['rep_elem'], 'contains has correct class by flag' ); is_deeply( $form->field('foo')->wrapper_class, ['sel_wrapper'], 'correct class by type' ); is_deeply( $form->field('bar')->element_class, ['sel_elem'], 'correct class by type' ); # test using update_subfields with base class { package Test::Form::Base; use Moose; extends 'HTML::FormHandler'; sub build_update_subfields { return { all => { tags => { no_errors => 1 } } }; } } { package Test::Form::Special; use HTML::FormHandler::Moose; extends 'Test::Form::Base'; use HTML::FormHandler::Merge ('merge'); sub build_update_subfields { my $self = shift; my $new = { all => { tags => { wrapper_tag => 'p' } } }; return merge( $new, $self->next::method(@_) ); } has_field 'foo'; has_field 'bar'; } $form = Test::Form::Special->new; ok( $form ); my $expected = { no_errors => 1, wrapper_tag => 'p' }; is_deeply( $form->field('foo')->tags, $expected, 'got expected tags' ); done_testing; HTML-FormHandler-0.40050/t/fields/0000755000077000007700000000000012221042077015645 5ustar gshankgshankHTML-FormHandler-0.40050/t/fields/dates.t0000644000077000007700000001305512221042076017135 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::I18N; $ENV{LANGUAGE_HANDLE} = 'en_en'; # # DateMDY # my $class = 'HTML::FormHandler::Field::DateMDY'; use_ok($class); my $field = $class->new( name => 'test_field', ); $field->build_result; ok( defined $field, 'new() called for DateMDY' ); $field->_set_input('10/02/2009'); $field->validate_field; ok( $field->validated, 'No errors 1' ); ok( $field->value->isa('DateTime'), 'isa DateTime' ); $field->reset_result; $field->_set_input('14/40/09'); $field->validate_field; ok( $field->has_errors, 'Has error 1' ); is( $field->fif, '14/40/09', 'Correct value' ); $field->reset_result; $field->_set_input('02/29/2009'); $field->validate_field; ok( $field->has_errors, 'Has error 2' ); is( $field->fif, '02/29/2009', 'isa DateTime' ); $field->reset_result; $field->_set_input('12/31/2008'); $field->validate_field; ok( $field->validated, 'No errors 2' ); is( $field->fif, $field->value->strftime("%m/%d/%Y", 'fif ok' ), 'fif ok'); $field->reset_result; $field->_set_input('07/07/09'); ok( $field->validated, 'No errors 3' ); $field->reset_result; $field->_deflate_and_set_value( DateTime->new( year => 2008, month => 12, day => 31 ) ); is( $field->fif, '12/31/2008', 'fif from value ok'); # # Date # $class = 'HTML::FormHandler::Field::Date'; use_ok($class); $field = $class->new( name => 'test_field', format => "mm/dd/yy" ); $field->build_result; ok( defined $field, 'new() called for DateMDY' ); $field->_set_input('02/10/2009'); $field->validate_field; ok( $field->validated, 'No errors 1' ); ok( $field->value->isa('DateTime'), 'isa DateTime' ); $field->reset_result; $field->date_start('2009-10-01'); $field->_set_input('08/01/2009'); $field->validate_field; ok( $field->has_errors, 'Date is too early' ); is( $field->fif, '08/01/2009', 'Correct value' ); $field->clear_date_start; $field->reset_result; $field->date_end('2010-01-01'); $field->_set_input('02/01/2010'); $field->validate_field; ok( $field->has_errors, 'date is too late'); $field->_set_input('02/29/2009'); $field->validate_field; ok( $field->has_errors, 'Not a valid date' ); is( $field->fif, '02/29/2009', 'isa DateTime' ); $field->reset_result; $field->_set_input('12/31/2008'); $field->validate_field; ok( $field->validated, 'No errors 2' ); is( $field->fif, $field->value->strftime("%m/%d/%Y", 'fif ok' ), 'fif ok'); $field->reset_result; $field->_deflate_and_set_value( DateTime->new( year => 2008, month => 12, day => 31 ) ); is( $field->fif, '12/31/2008', 'fif from deflated value ok'); $field->format("%d-%m-%Y"); $field->_set_input('07-07-09'); $field->validate_field; ok( $field->validated, 'No errors 3' ); is( $field->fif, '07-07-09', 'fif ok'); { package Test::Date; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; with 'HTML::FormHandler::Render::Simple'; has_field 'start_date' => ( type => 'Date' ); has_field 'end_date' => ( type => 'Date', required => 1 ); } my $form = Test::Date->new; ok( $form, 'form with date created' ); ok( $form->render_field('end_date'), 'date field renders' ); ok( !$form->process( params => { end_date => '' } ), "Didn't validate Test::Date with empty string for end_date" ); $form = Test::Date->new( item => { end_date => '1999-01-01', start_date => '' } ); ok( !$form->process( params => { } ), "Didn't validate Test::Date with empty params" ); ok( $form->process( params => { end_date => '1999-12-31' } ), "Didn't validate Test::Date with empty params" ); # # DateTime # $class = 'HTML::FormHandler::Field::DateTime'; use_ok($class); #$field = $class->new( name => 'test_field', format => "mm/dd/yy" ); $field = $class->new( name => 'test_field', field_list => [ year => 'Integer', month => 'Integer', day => 'Integer' ] ); ok( defined $field, 'new() called for DateTime' ); $field->build_result; $field->_set_input({ month => 2, day => 10, year => 2009 }); $field->test_validate_field; ok( $field->validated, 'No errors 1' ); ok( $field->value && $field->value->isa('DateTime'), 'isa DateTime' ); is( $field->value->ymd, '2009-02-10', 'correct DateTime' ); $field = $class->new( name => 'test_field', field_list => [ year => 'Integer', month => { type => 'Integer', range_start => 9, range_end => 12 }, day => 'Integer' ] ); ok( $field, 'field compiles and builds' ); $field->reset_result; my $date_hash = { month => 5, day => 10, year => 2009 }; $field->_set_input($date_hash); $field->test_validate_field; ok( $field->has_errors, 'Date is wrong month' ); is( $field->fif, $date_hash, 'Correct value' ); $field->reset_result; $date_hash = { month => 10, day => 32, year => 2009 }; $field->_set_input($date_hash); $field->test_validate_field; ok( $field->has_errors, 'Date is wrong month' ); like( $field->errors->[0], qr/Not a valid/, 'DateTime error message' ); is( $field->fif, $date_hash, 'Correct value' ); { package Test::DateTime; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'my_date' => ( type => 'DateTime' ); has_field 'my_date.month'; has_field 'my_date.year'; has_field 'my_date.day'; } $form = Test::DateTime->new; my $dt = DateTime->new( year => '2010', month => '02', day => '22' ); $form->process( init_object => { foo => 'abc', my_date => $dt } ); is_deeply( $form->field('my_date')->fif, { year => '2010', month => '2', day => '22' }, 'right fif from obj with date' ); my $fif = $form->fif; is( $fif->{'my_date.day'}, '22', 'right fif day'); $fif->{'my_date.day'} = '15'; $form->process( params => $fif ); ok( $form->validated, 'form validated' ); is( $form->field('my_date')->value->mdy, '02-15-2010', 'right value for my_date' ); done_testing; HTML-FormHandler-0.40050/t/fields/display.t0000644000077000007700000000117612221042077017504 0ustar gshankgshankuse strict; use warnings; use Test::More; # this tests to make sure that a display field can take a value { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( type => 'Display', label => undef, required => 0, noupdate => 1 ); has_field 'bar' => ( type => 'Text' ); } my $form = MyApp::Form::Test->new; ok( $form ); my $init_obj = { foo => 'some foo', bar => 'a bar', }; $form->process( init_object => $init_obj, params => {} ); is( $form->field('foo')->value, 'some foo', 'foo field has a value' ); my $rendered = $form->render; done_testing; HTML-FormHandler-0.40050/t/fields/fields.t0000644000077000007700000004356112221042077017311 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::I18N; $ENV{LANGUAGE_HANDLE} = 'en_en'; # # Boolean # my $class = 'HTML::FormHandler::Field::Boolean'; use_ok($class); my $field = $class->new( name => 'test', ); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input(1); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, 1, 'Test true == 1' ); $field->_set_input(0); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); is( $field->value, 0, 'Test true == 0' ); $field->_set_input('checked'); $field->validate_field; ok( !$field->has_errors, 'Test for errors 3' ); is( $field->value, 1, 'Test true == 1' ); $field->_set_input('0'); $field->validate_field; ok( !$field->has_errors, 'Test for errors 4' ); is( $field->value, 0, 'Test true == 0' ); # checkbox $class = 'HTML::FormHandler::Field::Checkbox'; use_ok($class); $field = $class->new( name => 'test', ); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input(1); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, 1, 'input 1 is 1' ); $field->_set_input(0); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); is( $field->value, 0, 'input 0 is 0' ); $field->_set_input('checked'); $field->validate_field; ok( !$field->has_errors, 'Test for errors 3' ); is( $field->value, 'checked', 'value is "checked"' ); $field->_set_input(undef); $field->validate_field; ok( !$field->has_errors, 'Test for errors 4' ); is( $field->value, 0, 'input undef is 0' ); $field = $class->new( name => 'test_field2', required => 1 ); $field->build_result; $field->_set_input(0); $field->validate_field; ok( $field->has_errors, 'required field fails with 0' ); # email $class = 'HTML::FormHandler::Field::Email'; use_ok($class); $field = $class->new( name => 'test', ); $field->build_result; ok( defined $field, 'new() called' ); my $address = 'test@example.com'; $field->_set_input( $address ); $field->validate_field; ok( !$field->has_errors, 'Email Test for errors 1' ); is( $field->value, $address, 'is value input string' ); my $Address = 'Test@example.com'; $field->_set_input( $Address ); $field->validate_field; ok( !$field->has_errors, 'Email Test for errors 2' ); is( $field->value, lc($Address), 'is value input string' ); $field->preserve_case(1); $field->_set_input( $Address ); $field->validate_field; ok( !$field->has_errors, 'Email Test for errors 3' ); is( $field->value, $Address, 'field was not lowercased' ); $field->_set_input( 'test @ example . com' ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 3' ); is( $field->value, 'test@example.com', 'is email-valid corrected input string' ); # hidden $class = 'HTML::FormHandler::Field::Hidden'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); my $string = 'Some text'; $field->_set_input( $string ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, $string, 'is value input string'); $field->_set_input( '' ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); is( $field->value, undef, 'is value input string'); $field->required(1); $field->validate_field; ok( $field->has_errors, 'Test for errors 3' ); $field->_set_input('hello'); $field->required(1); $field->validate_field; ok( !$field->has_errors, 'Test for errors 3' ); is( $field->value, 'hello', 'Check again' ); $field->maxlength( 3 ); $field->validate_field; ok( $field->has_errors, 'Test for too long' ); $field->maxlength( 5 ); $field->validate_field; ok( !$field->has_errors, 'Test for right length' ); $field->minlength( 10 ); $field->validate_field; ok( $field->has_errors, 'Test not long enough' ); $field->minlength( 5 ); $field->validate_field; ok( !$field->has_errors, 'Test just long enough' ); $field->minlength( 4 ); $field->validate_field; ok( !$field->has_errors, 'Test plenty long enough' ); # integer $class = 'HTML::FormHandler::Field::Integer'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->reset_result; ok( defined $field, 'new() called' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, 1, 'Test value == 1' ); $field->_set_input( 0 ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); is( $field->value, 0, 'Test value == 0' ); $field->_set_input( 'checked' ); $field->validate_field; ok( $field->has_errors, 'Test non integer' ); is( $field->errors->[0], 'Value must be an integer', 'correct error'); $field->_set_input( '+10' ); $field->validate_field; ok( !$field->has_errors, 'Test positive' ); is( $field->value, 10, 'Test value == 10' ); $field->_set_input( '-10' ); $field->validate_field; ok( !$field->has_errors, 'Test negative' ); is( $field->value, -10, 'Test value == -10' ); $field->_set_input( '-10.123' ); $field->validate_field; ok( $field->has_errors, 'Test real number' ); $field->range_start( 10 ); $field->_set_input( 9 ); $field->validate_field; ok( $field->has_errors, 'Test 9 < 10 fails' ); $field->_set_input( 100 ); $field->validate_field; ok( !$field->has_errors, 'Test 100 > 10 passes ' ); $field->range_end( 20 ); $field->_set_input( 100 ); $field->validate_field; ok( $field->has_errors, 'Test 10 <= 100 <= 20 fails' ); $field->range_end( 20 ); $field->_set_input( 15 ); $field->validate_field; ok( !$field->has_errors, 'Test 10 <= 15 <= 20 passes' ); $field->_set_input( 10 ); $field->validate_field; ok( !$field->has_errors, 'Test 10 <= 10 <= 20 passes' ); $field->_set_input( 20 ); $field->validate_field; ok( !$field->has_errors, 'Test 10 <= 20 <= 20 passes' ); $field->_set_input( 21 ); $field->validate_field; ok( $field->has_errors, 'Test 10 <= 21 <= 20 fails' ); $field->_set_input( 9 ); $field->validate_field; ok( $field->has_errors, 'Test 10 <= 9 <= 20 fails' ); # intrange.t $class = 'HTML::FormHandler::Field::IntRange'; use_ok( $class ); $field = $class->new( name => 'test_field', range_start => 30, range_end => 39, ); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 30 ); $field->validate_field; ok( !$field->has_errors, '30 in range' ); $field->_set_input( 39 ); $field->validate_field; ok( !$field->has_errors, '39 in range' ); $field->_set_input( 35 ); $field->validate_field; ok( !$field->has_errors, '35 in range' ); $field->_set_input( 29 ); $field->validate_field; ok( $field->has_errors, '29 out of range' ); $field->_set_input( 40 ); $field->validate_field; ok( $field->has_errors, '40 out of range' ); # minute $class = 'HTML::FormHandler::Field::Minute'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 0 ); $field->validate_field; ok( !$field->has_errors, '0 in range' ); $field->_set_input( 59 ); $field->validate_field; ok( !$field->has_errors, '59 in range' ); $field->_set_input( 12 ); $field->validate_field; ok( !$field->has_errors, '12 in range' ); $field->_set_input( -1 ); $field->validate_field; ok( $field->has_errors, '-1 out of range' ); $field->_set_input( 60 ); $field->validate_field; ok( $field->has_errors, '60 out of range' ); # money $class = 'HTML::FormHandler::Field::Money'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( ' $123.45 ' ); $field->validate_field; ok( !$field->has_errors, 'Test for errors " $123.00 "' ); is( $field->value, 123.45, 'Test value == 123.45' ); $field->_set_input( ' $12x3.45 ' ); $field->validate_field; ok( $field->has_errors, 'Test for errors " $12x3.45 "' ); is( $field->errors->[0], 'Value cannot be converted to money', 'get error' ); $field->_set_input( 2345 ); $field->validate_field; is( $field->value, '2345.00', 'transformation worked: 2345 => 2345.00' ); # monthday $class = 'HTML::FormHandler::Field::MonthDay'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, '1 in range' ); $field->_set_input( 31 ); $field->validate_field; ok( !$field->has_errors, '31 in range' ); $field->_set_input( 12 ); $field->validate_field; ok( !$field->has_errors, '12 in range' ); $field->_set_input( 0 ); $field->validate_field; ok( $field->has_errors, '0 out of range' ); $field->_set_input( 32 ); $field->validate_field; ok( $field->has_errors, '32 out of range' ); # monthname $class = 'HTML::FormHandler::Field::MonthName'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); for ( 1 .. 12 ) { $field->_set_input( $_ ); $field->validate_field; ok( !$field->has_errors, $_ . ' is valid' ); } $field->_set_input( 0 ); $field->validate_field; ok( $field->has_errors, '0 is not valid day of the week' ); $field->_set_input( 13 ); $field->validate_field; ok( $field->has_errors, '13 is not valid day of the week' ); #month $class = 'HTML::FormHandler::Field::Month'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, '1 in range' ); $field->_set_input( 12 ); $field->validate_field; ok( !$field->has_errors, '59 in range' ); $field->_set_input( 6 ); $field->validate_field; ok( !$field->has_errors, '6 in range' ); $field->_set_input( 0 ); $field->validate_field; ok( $field->has_errors, '0 out of range' ); $field->_set_input( 13 ); $field->validate_field; ok( $field->has_errors, '60 out of range' ); $field->_set_input( 'March' ); $field->validate_field; ok( $field->has_errors, 'March is not numeric' ); is( $field->errors->[0], "'March' is not a valid value", 'is error message' ); # multiple $class = 'HTML::FormHandler::Field::Multiple'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->options([ { value => 1, label => 'one' }, { value => 2, label => 'two' }, { value => 3, label => 'three' }, ]); ok( $field->options, 'options method called' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is_deeply( $field->value, [1], 'Test 1 => [1]' ); $field->_set_input( [1] ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); ok( eq_array( $field->value, [1], 'test array' ), 'Check [1]'); $field->_set_input( [1,2] ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 3' ); ok( eq_array( $field->value, [1,2], 'test array' ), 'Check [1,2]'); $field->_set_input( [1,2,4] ); $field->validate_field; ok( $field->has_errors, 'Test for errors 4' ); is( $field->errors->[0], "'4' is not a valid value", 'Error message' ); # password tested separately. requires a form. # posinteger $class = 'HTML::FormHandler::Field::PosInteger'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, 1, 'Test value == 1' ); $field->_set_input( 0 ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); is( $field->value, 0, 'Test value == 0' ); $field->_set_input( 'checked' ); $field->validate_field; ok( $field->has_errors, 'Test non integer' ); $field->_set_input( '+10' ); $field->validate_field; ok( !$field->has_errors, 'Test positive' ); is( $field->value, 10, 'Test value == 10' ); $field->_set_input( '-10' ); $field->validate_field; ok( $field->has_errors, 'Test negative' ); $field->_set_input( '-10.123' ); $field->validate_field; ok( $field->has_errors, 'Test real number ' ); # second $class = 'HTML::FormHandler::Field::Second'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 0 ); $field->validate_field; ok( !$field->has_errors, '0 in range' ); $field->_set_input( 59 ); $field->validate_field; ok( !$field->has_errors, '59 in range' ); $field->_set_input( 12 ); $field->validate_field; ok( !$field->has_errors, '12 in range' ); $field->_set_input( -1 ); $field->validate_field; ok( $field->has_errors, '-1 out of range' ); $field->_set_input( 60 ); $field->validate_field; ok( $field->has_errors, '60 out of range' ); # select $class = 'HTML::FormHandler::Field::Select'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); ok( $field->options, 'Test for init_options failure in 0.09' ); my $options = [ { value => 1, label => 'one' }, { value => 2, label => 'two' }, { value => 3, label => 'three' }, ]; $field->options($options); ok( $field->options, 'Test for set options failure' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, 1, 'Test true == 1' ); $field->_set_input( [1] ); $field->validate_field; ok( $field->has_errors, 'Test for errors array' ); $field->_set_input( [1,4] ); $field->validate_field; ok( $field->has_errors, 'Test for errors 4' ); is( $field->errors->[0], 'This field does not take multiple values', 'Error message' ); $field = $class->new( name => 'test_prompt', 'empty_select' => "Choose a Number", options => $options, required => 1 ); is( $field->num_options, 3, 'right number of options'); # textarea $class = 'HTML::FormHandler::Field::TextArea'; use_ok( $class ); $field = $class->new( name => 'comments', cols => 40, rows => 3 ); $field->build_result; ok( $field, 'get TextArea field'); $field->_set_input("Testing, testing, testing... This is a test"); $field->validate_field; ok( !$field->has_errors, 'field has no errors'); $field->maxlength( 10 ); $field->validate_field; ok( $field->has_errors, 'field has errors'); is( $field->errors->[0], 'Field should not exceed 10 characters. You entered 43', 'Test for too long' ); # text $class = 'HTML::FormHandler::Field::Text'; use_ok( $class ); $field = $class->new( name => 'test',); $field->build_result; ok( defined $field, 'new() called' ); $string = 'Some text'; $field->_set_input( $string ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); is( $field->value, $string, 'is value input string'); $field->_set_input( '' ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 2' ); is( $field->value, undef, 'is value input string'); $field->required(1); $field->validate_field; ok( $field->has_errors, 'Test for errors 3' ); $field->_set_input('hello'); $field->required(1); $field->validate_field; ok( !$field->has_errors, 'Test for errors 3' ); is( $field->value, 'hello', 'Check again' ); $field->maxlength( 3 ); $field->validate_field; is( $field->errors->[0], 'Field should not exceed 3 characters. You entered 5', 'Test for too long' ); $field->maxlength( 5 ); $field->validate_field; ok( !$field->has_errors, 'Test for right length' ); $field->minlength( 10 ); $field->minlength_message('[_3] field must be at least [quant,_1,character]'); $field->validate_field; is( $field->errors->[0], 'Test field must be at least 10 characters', 'Test not long enough' ); $field->minlength( 5 ); $field->validate_field; ok( !$field->has_errors, 'Test just long enough' ); $field->minlength( 4 ); $field->validate_field; ok( !$field->has_errors, 'Test plenty long enough' ); $field = $class->new( name => 'test_not_nullable', not_nullable => 1); $field->build_result; $field->input(''); $field->validate_field; is( $field->value, '', 'empty string'); # weekday $class = 'HTML::FormHandler::Field::Weekday'; use_ok( $class ); $field = $class->new( name => 'test_field',); $field->build_result; ok( defined $field, 'new() called' ); for ( 0 .. 6 ) { $field->_set_input( $_ ); $field->validate_field; ok( !$field->has_errors, $_ . ' is valid' ); } $field->_set_input( -1 ); $field->validate_field; ok( $field->has_errors, '-1 is not valid day of the week' ); $field->_set_input( 7 ); $field->validate_field; ok( $field->has_errors, '7 is not valid day of the week' ); # year $class = 'HTML::FormHandler::Field::Year'; use_ok( $class ); $field = $class->new( name => 'test_field' ); $field->build_result; ok( defined $field, 'new() called' ); $field->_set_input( 0 ); $field->validate_field; ok( $field->has_errors, '0 is bad year' ); $field->_set_input( (localtime)[5] + 1900 ); $field->validate_field; ok ( !$field->has_errors, 'Now is just a fine year' ); $field->_set_input( 2100 ); $field->validate_field; ok( $field->has_errors, '2100 makes the author really old' ); # float $class = 'HTML::FormHandler::Field::Float'; use_ok( $class ); $field = $class->new( name => 'float_test' ); $field->build_result; ok( defined $field, 'field built' ); $field->_set_input( '2.0' ); $field->validate_field; ok( !$field->has_errors, 'accepted 2.0 value' ); $field->_set_input( '2.000' ); $field->validate_field; ok( $field->has_errors, 'error for 3 decimal places' ); is( $field->errors->[0], 'May have a maximum of 2 digits after the decimal point, but has 3', 'error message correct' ); $field->size(4); $field->_set_input( '12345.00' ); $field->validate_field; ok( $field->has_errors, 'error for size' ); is( $field->errors->[0], 'Total size of number must be less than or equal to 4, but is 7', 'error message correct' ); $field->_set_input( '12.30' ); $field->validate_field; ok( $field->validated, 'field validated' ); # Boolean select $class = 'HTML::FormHandler::Field::BoolSelect'; use_ok( $class ); $field = $class->new( name => 'boolselect' ); $field->build_result; ok( defined $field, 'field built' ); $field->_set_input( '' ); $field->validate_field; ok( !$field->has_errors, 'accepted null value' ); $field->_set_input( 1 ); $field->validate_field; ok( !$field->has_errors, 'accepted 1 value' ); $field->_set_input( 0 ); $field->validate_field; ok( !$field->has_errors, 'accepted 0 value' ); done_testing; HTML-FormHandler-0.40050/t/fields/float.t0000644000077000007700000000121012221042076017130 0ustar gshankgshankuse strict; use warnings; use Test::More; { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'float1' => ( type => 'Float', decimal_symbol_for_db => ',' ); has_field 'float2' => ( type => 'Float' ); has_field 'float3' => ( type => 'Float' ); } my $form = Test::Form->new; my $params = { float1 => '+1.00', float2 => '3.35', float3 => '44.0' }; $form->process( params => $params ); my $float1 = $form->field('float1')->value; is( $float1, '1,00', 'float has been deflated' ); my $float2 = $form->field('float2')->value; is( $float2, '3.35', 'correct float value' ); done_testing; HTML-FormHandler-0.40050/t/fields/formhandlerx.t0000644000077000007700000000214512221042077020525 0ustar gshankgshankuse strict; use warnings; use Test::More; use Test::Exception; { package HTML::FormHandlerX::Widget::Field::TesT; use Moose::Role; sub render { '

field rendered...

' } } { package HTML::FormHandlerX::Field::TesT; use Moose; extends 'HTML::FormHandler::Field::Text'; has '+widget' => ( default => 'TesT' ); } { package Test::HTML::FormHandler::TextFormHandlerX; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'field_name' => ( type => 'TesT', required => 1, ); } my $form = Test::HTML::FormHandler::TextFormHandlerX->new; ok( $form, 'created Form' ); my %params = ( field_name => 'This is a field', ); $form->process(params=>\%params); is( $form->field('field_name')->widget, 'TesT', 'got right widget name' ); is( $form->field('field_name')->render, '

field rendered...

', 'field rendered' ); { package Test::Fail; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo' => ( type => 'Fail' ); } dies_ok( sub { Test::Fail->new }, 'form dies with invalid field' ); done_testing; HTML-FormHandler-0.40050/t/fields/novalue.t0000644000077000007700000000131012221042077017476 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Test; { package MyApp::Form::Test; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'foo'; has_field 'bar'; has_field 'sbmt' => ( type => 'Submit', value => 'Test', default_method => \&default_submit ); sub default_submit { my $self = shift; my $value = $self->value; $value .= "_from_method"; } } my $form = MyApp::Form::Test->new; ok( $form ); my $expected = '
'; my $rendered = $form->field('sbmt')->render; is_html( $rendered, $expected, 'submit button renders ok' ); done_testing; HTML-FormHandler-0.40050/t/fields/password.t0000644000077000007700000000621012221042077017673 0ustar gshankgshankuse strict; use warnings; use Test::More; use lib 't/lib'; use_ok( 'HTML::FormHandler::Field::Text' ); my $field = HTML::FormHandler::Field::Text->new( name => 'password', type => 'Text', required => 1, password => 1, ); $field->build_result; is( $field->password, 1, 'password is set'); $field->_set_value('abcdef'); is( $field->value, 'abcdef', 'set and get value' ); is( $field->fif, '', 'no fif for password'); $field = HTML::FormHandler::Field::Text->new( name => 'not_password', type => 'Text', required => 1, ); $field->build_result; is( $field->password, undef, 'password is not set'); $field->_set_value('abcdef'); is( $field->value, 'abcdef', 'set and get value' ); is( $field->fif, 'abcdef', 'get fif'); use HTML::FormHandler; use_ok( 'HTML::FormHandler::Field::Password' ); { package My::Form; use Moose; extends 'HTML::FormHandler'; sub field_list { return [ login => 'Text', username => 'Text', password => { type => 'Password', ne_username => 'username', minlength => 6, }, ]; } } my $form = My::Form->new; $field = $form->field('password'); my $params = { username => 'my4username', password => 'something' }; $form->process( $params ); ok( $field, 'got password field' ); $field->_set_input( '2192ab201def' ); $field->validate_field; ok( !$field->has_errors, 'Test for errors 1' ); $field->_set_input( 'ab1' ); $field->validate_field; ok( $field->has_errors, 'too short' ); $field->_set_input( 'my4username' ); $field->validate_field; ok( $field->has_errors, 'matches username' ); $field->_set_input( '' ); $field->validate_field; ok( !$field->has_errors, 'empty password accepted' ); is($field->noupdate, 1, 'noupdate has been set on password field' ); my $pass = 'my4user5name'; $field->_set_input( $pass ); $field->validate_field; ok( !$field->has_errors, 'just right' ); is ( $field->value, $pass, 'Input and value match' ); { package Password::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+field_name_space' => ( default => 'Field' ); has_field 'password' => ( type => 'Password', required => 1 ); has_field '_password' => ( type => 'PasswordConf', messages => { required => 'You must enter the password a second time' }, ); } $form = Password::Form->new; ok( $form, 'form created' ); $params = { password => '', _password => '', }; $form->process( params => $params ); ok( !$form->validated, 'form validated' ); ok( !$form->field('password')->noupdate, q[noupdate is 'false' on password field] ); ok( $form->field('_password')->has_errors, 'Password confirmation has errors' ); is( $form->field('_password')->errors->[0], 'You must enter the password a second time' ); $form->process( params => { password => 'aaaaaa', _password => 'bbbb' } ); ok( $form->field('_password')->has_errors, 'Password confirmation has errors' ); $form->process( params => { password => 'aaaaaa', _password => 'aaaaaa' } ); ok( $form->validated, 'password confirmation validated' ); done_testing; HTML-FormHandler-0.40050/t/fields/repeatable.t0000644000077000007700000000201112221042077020130 0ustar gshankgshankuse strict; use warnings; use Test::More; use_ok('HTML::FormHandler::Field::Repeatable'); # test to verify that contains hashref passed in to construction of # a repeatable field is used to build the 'contains' field my $args = { name => 'test_field', init_contains => { wrapper_class => ['hfh', 'repinst'] }, }; my $field = HTML::FormHandler::Field::Repeatable->new( %$args ); ok( $field, 'field built' ); $field->init_state; is_deeply( $field->contains->wrapper_class, ['hfh', 'repinst'], 'attribute set' ); { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'my_name'; has_field 'my_records' => ( type => 'Repeatable', num_when_empty => 2, init_contains => { wrapper_class =>['hfh'] }, ); has_field 'my_records.one'; has_field 'my_records.two'; } my $form = Test::Form->new; ok( $form, 'form built' ); is_deeply( $form->field('my_records')->contains->wrapper_class, ['hfh'], 'worked in form'); my $rendered = $form->render; done_testing; HTML-FormHandler-0.40050/t/fields/select.t0000644000077000007700000001552112221042077017315 0ustar gshankgshankuse strict; use warnings; use Test::More; use HTML::FormHandler::Field::Text; { package Test::Form; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has '+name' => ( default => 'options_form' ); has_field 'test_field' => ( type => 'Text', label => 'TEST', id => 'f99', ); has_field 'fruit' => ( type => 'Select' ); has_field 'vegetables' => ( type => 'Multiple' ); has_field 'empty' => ( type => 'Multiple', no_option_validation => 1 ); has_field 'build_attr' => ( type => 'Select' ); sub default_fruit { 2 } # the following sometimes happens with db options sub options_empty { ([]) } has 'options_fruit' => ( is => 'rw', traits => ['Array'], default => sub { [1 => 'apples', 2 => 'oranges', 3 => 'kiwi'] } ); sub options_vegetables { return ( 1 => 'lettuce', 2 => 'broccoli', 3 => 'carrots', 4 => 'peas', ); } has 'options_build_attr' => ( is => 'ro', traits => ['Array'], lazy_build => 1 ); sub _build_options_build_attr { return [ 1 => 'testing', 2 => 'moose', 3 => 'attr builder', ]; } } my $form = Test::Form->new; ok( $form, 'create form'); my $veg_options = [ {'label' => 'lettuce', 'value' => 1 }, {'label' => 'broccoli', 'value' => 2 }, {'label' => 'carrots', 'value' => 3 }, {'label' => 'peas', 'value' => 4 } ]; my $field_options = $form->field('vegetables')->options_ref; is_deeply( $field_options, $veg_options, 'get options for vegetables' ); my $fruit_options = [ {'label' => 'apples', 'value' => 1 }, {'label' => 'oranges', 'value' => 2 }, {'label' => 'kiwi', 'value' => 3 } ]; $field_options = $form->field('fruit')->options; is_deeply( $field_options, $fruit_options, 'get options for fruit' ); my $build_attr_options = [ {'label' => 'testing', 'value' => 1 }, {'label' => 'moose', 'value' => 2 }, {'label' => 'attr builder', 'value' => 3 } ]; $field_options = $form->field('build_attr')->options_ref; is_deeply( $field_options, $build_attr_options, 'get options for fruit' ); is( $form->field('fruit')->value, 2, 'initial value ok'); $form->process( params => {}, init_object => { vegetables => undef, fruit => undef, build_attr => undef } ); $field_options = $form->field('vegetables')->options; is_deeply( $field_options, $veg_options, 'get options for vegetables after process' ); $field_options = $form->field('fruit')->options; is_deeply( $field_options, $fruit_options, 'get options for fruit after process' ); $field_options = $form->field('build_attr')->options; is_deeply( $field_options, $build_attr_options, 'get options for fruit after process' ); my $params = { fruit => 2, vegetables => [2,4], empty => 'test', }; $form->process( $params ); ok( $form->validated, 'form validated' ); is( $form->field('fruit')->value, 2, 'fruit value is correct'); is_deeply( $form->field('vegetables')->value, [2,4], 'vegetables value is correct'); is_deeply( $form->fif, { fruit => 2, vegetables => [2, 4], empty => ['test'], test_field => '', build_attr => '' }, 'fif is correct'); is_deeply( $form->values, { fruit => 2, vegetables => [2, 4], empty => ['test'], build_attr => undef }, 'values are correct'); is( $form->field('vegetables')->as_label, 'broccoli, peas', 'multiple as_label works'); $params = { fruit => 2, vegetables => 4, }; $form->process($params); is_deeply( $form->field('vegetables')->value, [4], 'value for vegetables correct' ); is_deeply( $form->field('vegetables')->fif, [4], 'fif for vegetables correct' ); { package Test::Form2; use HTML::FormHandler::Moose; extends 'HTML::FormHandler'; has_field 'my_list' => ( type => 'Select' ); # this adds a 'selected' hash key to an option as an alternative # to setting the default for the field. sub options_my_list { return [ { value => 1, label => 'One', selected => 1, }, { value => 2, label => 'Two', }, { value => 3, label => 'Three', } ]; } } $form = Test::Form2->new; ok( $form, 'form built' ); my $rendered_field = $form->field('my_list')->render; like( $rendered_field, qr/