├── README.md ├── lib ├── autoform2.pm └── autoform2 │ ├── Controller │ └── Autoform.pm │ ├── Data │ └── AutodataTypeC.pm │ ├── Model │ └── Pages.pm │ └── Other │ ├── Init.pm │ └── Token.pm ├── public ├── css │ └── autoform.css └── js │ ├── action.js │ ├── sending.js │ └── validation.js ├── script └── autoform2 ├── t └── basic.t └── templates ├── autoform └── t.html.ep └── layouts └── default.html.ep /README.md: -------------------------------------------------------------------------------- 1 | # autoform2 2 | 3 | Autoform based on the Mojolicious, Vue.js and transactions. 4 | -------------------------------------------------------------------------------- /lib/autoform2.pm: -------------------------------------------------------------------------------- 1 | package autoform2; 2 | use Mojo::Base 'Mojolicious'; 3 | use Mojo::mysql; 4 | 5 | use utf8; 6 | use autoform2::Other::Init; 7 | 8 | sub startup 9 | # ////////////////////////////////////////////////// 10 | { 11 | my $self = shift; 12 | 13 | my $config = $self->plugin( 'Config' ); 14 | 15 | $self->secrets( $config->{ secrets } ); 16 | 17 | start_init( $self ); 18 | 19 | my $r = $self->routes; 20 | 21 | $r->any( '/' )->to( 'autoform#new_token' ); 22 | 23 | $r->any( '/t/:token' )->to( 'autoform#t' ); 24 | 25 | $r->post( '/data/:token' )->to( 'autoform#data' ); 26 | } 27 | 28 | 1; 29 | -------------------------------------------------------------------------------- /lib/autoform2/Controller/Autoform.pm: -------------------------------------------------------------------------------- 1 | package autoform2::Controller::Autoform; 2 | 3 | use utf8; 4 | use Mojo::Base 'Mojolicious::Controller'; 5 | use Data::Dumper; 6 | 7 | sub new_token 8 | # ////////////////////////////////////////////////// 9 | { 10 | my $self = shift; 11 | 12 | my $new_token = $self->app->token->generation(); 13 | 14 | $self->redirect_to( "/t/$new_token" ); 15 | } 16 | 17 | sub t 18 | # ////////////////////////////////////////////////// 19 | { 20 | my $self = shift; 21 | 22 | my $token = $self->app->token->get( $self ); 23 | 24 | $self->render( token => $token ); 25 | } 26 | 27 | sub data 28 | # ////////////////////////////////////////////////// 29 | { 30 | my $self = shift; 31 | 32 | my $token = $self->app->token->get( $self ); 33 | 34 | my $tmp_data = $self->req->json; 35 | 36 | my $tmp_page_num = ( $tmp_data->{ direction } eq 'next' ? 2 : 1); 37 | 38 | $self->render( json => $self->app->model->pages->get_page( $tmp_page_num ) ); 39 | } 40 | 41 | 1; 42 | -------------------------------------------------------------------------------- /lib/autoform2/Data/AutodataTypeC.pm: -------------------------------------------------------------------------------- 1 | package autoform2::Data::AutodataTypeC; 2 | 3 | use utf8; 4 | use Exporter; 5 | 6 | @ISA = qw( Exporter ); 7 | our @EXPORT = qw( dummy_data ); 8 | 9 | sub dummy_data 10 | # ////////////////////////////////////////////////// 11 | { 12 | my $standart_date_check = 'zD^(([012]\d|3[01])\.((0\d)|(1[012]))\.(19\d\d|20[0-3]\d))$'; 13 | my $standart_date_check_opt = 'D^(([012]\d|3[01])\.((0\d)|(1[012]))\.(19\d\d|20[0-3]\d))$'; 14 | 15 | return { 16 | 1 => [ 17 | { 18 | type => 'input', 19 | name => 's_date', 20 | label => 'Дата начала поездки', 21 | comment => 'Введите предполагаемую дату начала поездки', 22 | example => '31.12.1900', 23 | check => $standart_date_check, 24 | check_logic => [ 25 | { 26 | condition => 'now_or_later', 27 | offset => '[collect_date_offset]', 28 | }, 29 | { 30 | condition => 'now_or_earlier', 31 | offset => 270, 32 | equality_is_also_fail => 1, 33 | full_error => 'Действует ограничение на максимальную дату вылета: не более [offset] с текущей даты', 34 | }, 35 | ], 36 | db => { 37 | table => 'Appointments', 38 | name => 'SDate', 39 | }, 40 | special => 'datepicker, mask', 41 | minimal_date => 'current', 42 | }, 43 | { 44 | type => 'input', 45 | name => 'f_date', 46 | label => 'Дата окончания запрашиваемой визы', 47 | comment => 'Введите предполагаемую дату окончания запрашиваемой визы', 48 | example => '31.12.1900', 49 | check => $standart_date_check, 50 | check_logic => [ 51 | { 52 | condition => 'equal_or_later', 53 | table => 'Appointments', 54 | name => 'SDate', 55 | error => 'Дата начала поездки', 56 | }, 57 | ], 58 | db => { 59 | table => 'Appointments', 60 | name => 'FDate', 61 | }, 62 | special => 'datepicker, mask', 63 | minimal_date => 's_date', 64 | }, 65 | { 66 | type => 'input', 67 | name => 'rulname', 68 | label => 'Фамилия', 69 | comment => 'Введите фамилию на русском языке так, как она указана во внутреннем паспорте', 70 | example => 'Петров', 71 | check => 'zWЁ\s\-', 72 | check_logic => [ 73 | { 74 | condition => 'english_only_for_not_rf_citizen', 75 | full_error => 'Для граждан РФ фамилию необходимо вводить на русском языке', 76 | }, 77 | ], 78 | db => { 79 | table => 'AppData', 80 | name => 'RLName', 81 | }, 82 | format => 'capitalized' 83 | }, 84 | { 85 | type => 'input', 86 | name => 'rufname', 87 | label => 'Имя', 88 | comment => 'Введите имя на русском языке так, как оно указано во внутреннем паспорте', 89 | example => 'Петр', 90 | check => 'zWЁ\s\-', 91 | check_logic => [ 92 | { 93 | condition => 'english_only_for_not_rf_citizen', 94 | full_error => 'Для граждан РФ имя необходимо вводить на русском языке', 95 | }, 96 | ], 97 | db => { 98 | table => 'AppData', 99 | name => 'RFName', 100 | }, 101 | format => 'capitalized' 102 | }, 103 | ], 104 | 2 => [ 105 | { 106 | type => 'input', 107 | name => 'visaother', 108 | label => 'Основная цель поездки', 109 | comment => 'Укажите цель предполагаемой поездки', 110 | check => 'zWN\s\-', 111 | db => { 112 | table => 'AppData', 113 | name => 'VisaOther', 114 | }, 115 | format => 'capslock', 116 | }, 117 | ], 118 | }; 119 | }; 120 | 121 | 1; -------------------------------------------------------------------------------- /lib/autoform2/Model/Pages.pm: -------------------------------------------------------------------------------- 1 | package autoform2::Model::Pages; 2 | 3 | use utf8; 4 | use Exporter; 5 | use Data::Dumper; 6 | use autoform2::Data::AutodataTypeC; 7 | 8 | sub get_page 9 | # ////////////////////////////////////////////////// 10 | { 11 | my ( $self, $pid ) = @_; 12 | 13 | return dummy_data()->{ $pid }; 14 | } 15 | 16 | 17 | 18 | 1; -------------------------------------------------------------------------------- /lib/autoform2/Other/Init.pm: -------------------------------------------------------------------------------- 1 | package autoform2::Other::Init; 2 | 3 | use utf8; 4 | use Exporter; 5 | use autoform2::Other::Token; 6 | use autoform2::Model::Pages; 7 | 8 | @ISA = qw( Exporter ); 9 | our @EXPORT = qw( start_init ); 10 | 11 | sub start_init 12 | # ////////////////////////////////////////////////// 13 | { 14 | my $self = shift; 15 | 16 | $self->helper( 17 | db => sub { 18 | return Mojo::mysql->strict_mode( 'mysql://remoteuser:userremote@127.0.0.1/vcs' )->db; 19 | } 20 | ); 21 | 22 | $self->helper( 'token.generation' => \&{ autoform2::Other::Token::generation } ); 23 | $self->helper( 'token.get' => \&{ autoform2::Other::Token::get } ); 24 | $self->helper( 'token.check' => \&{ autoform2::Other::Token::check } ); 25 | $self->helper( 'model.pages.get_page' => \&{ autoform2::Model::Pages::get_page } ); 26 | } 27 | 28 | 1; -------------------------------------------------------------------------------- /lib/autoform2/Other/Token.pm: -------------------------------------------------------------------------------- 1 | package autoform2::Other::Token; 2 | 3 | use Mojo::Base 'Mojolicious::Controller'; 4 | use utf8; 5 | use Exporter; 6 | use Math::Random::Secure qw( irand ); 7 | 8 | sub generation 9 | # ////////////////////////////////////////////////// 10 | { 11 | my $self = shift; 12 | 13 | my $transaction = $self->db->begin; 14 | 15 | my $appid = $self->db->query( " 16 | INSERT INTO AutoToken ( 17 | AutoAppID, AutoAppDataID, AutoSchengenAppDataID, Step, LastError, Finished, Draft, StartDate, LastIP) 18 | VALUES (0, 0, 0, ?, '', 0, 0, now(), ?)", 19 | 1, $self->tx->remote_address 20 | )->last_insert_id; 21 | 22 | my $appidcode = "-$appid-"; 23 | 24 | my @alph = split( //, '0123456789abcdefghigklmnopqrstuvwxyz' ); 25 | 26 | my $token = undef; 27 | 28 | $token .= $alph[ int( irand( 36 ) ) ] for ( 1..64 ); 29 | 30 | substr( $token, 10, length( $appidcode ) ) = $appidcode; 31 | 32 | $self->db->query( " 33 | UPDATE AutoToken SET Token = ? WHERE ID = ?", $token, $appid 34 | ); 35 | 36 | $transaction->commit; 37 | 38 | return $token; 39 | } 40 | 41 | sub get 42 | # ////////////////////////////////////////////////// 43 | { 44 | my ( $self, $c ) = @_; 45 | 46 | my $token = $c->param( "token" ) || undef; 47 | 48 | $token =~ s/[^0-9a-z\-_]//g; 49 | 50 | return $token; 51 | } 52 | 53 | sub check 54 | # ////////////////////////////////////////////////// 55 | { 56 | my $self = shift; 57 | 58 | return 'dummy sub'; 59 | } 60 | 61 | 1; -------------------------------------------------------------------------------- /public/css/autoform.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 25px; 3 | } 4 | 5 | .inputTable { 6 | list-style: none; 7 | padding: 0; 8 | } 9 | 10 | .inputBox { 11 | display: block; 12 | margin-top: 20px; 13 | } 14 | 15 | .field { 16 | border: 1px solid gray; 17 | } 18 | 19 | .fieldFail { 20 | border: 1px solid red; 21 | } -------------------------------------------------------------------------------- /public/js/action.js: -------------------------------------------------------------------------------- 1 | var action = { 2 | change(object, token, data) { 3 | 4 | fetch( '/data/' + token, { 5 | method: 'POST', 6 | body: data, 7 | }) 8 | .then((response) => { 9 | if (response.ok) { 10 | return response.json(); 11 | } 12 | }) 13 | .then((json) => { 14 | object.elements = json; 15 | }) 16 | }, 17 | } 18 | 19 | export default action; -------------------------------------------------------------------------------- /public/js/sending.js: -------------------------------------------------------------------------------- 1 | var sending = { 2 | 3 | pack( form, direction ) { 4 | 5 | var data = { 6 | 'direction': direction, 7 | }; 8 | 9 | form.elements.forEach( function( item ) { 10 | data[ item.name ] = item.val; 11 | }); 12 | 13 | return JSON.stringify( data ); 14 | }, 15 | } 16 | 17 | export default sending; -------------------------------------------------------------------------------- /public/js/validation.js: -------------------------------------------------------------------------------- 1 | var validation = { 2 | 3 | all_ok( form ) { 4 | 5 | var all_error = ''; 6 | 7 | form.elements.forEach( function( item ) { 8 | 9 | var next_error = error( item.val, item.check ); 10 | 11 | if ( !isEmpty( next_error ) ) 12 | all_error += item.name + ': ' + next_error + '\n'; 13 | }); 14 | 15 | if ( isEmpty( all_error ) ) 16 | return true; 17 | else { 18 | alert( all_error ); 19 | return false; 20 | } 21 | }, 22 | 23 | ok( input, check ) { 24 | return ( isEmpty( error( input.value, check ) ) ? true : false ); 25 | }, 26 | 27 | 28 | } 29 | 30 | function error( value, check ) { 31 | 32 | if ( ( value == '' ) && ( check == '' ) ) 33 | return ''; 34 | 35 | if ( value == undefined ) 36 | value = ''; 37 | 38 | var val = value.replace(/^\s+|\s+$/g, ''); 39 | 40 | if ( /z/.test( check ) && ( val == '' ) ) { 41 | 42 | return 'Cant be empty'; 43 | } 44 | 45 | if ( /D/.test( check ) ) { 46 | 47 | var date_reg = new RegExp( check.replace( /(z|D)/g, '' ) ); 48 | 49 | if ( !date_reg.test( val ) && ! (val == '') ) { 50 | 51 | return 'Wrong date format'; 52 | } 53 | } 54 | else { 55 | var regexp = ''; 56 | 57 | if ( /W/.test( check ) ) { 58 | regexp += 'A-Za-z'; 59 | } 60 | if ( /Ё/.test( check ) ) { 61 | regexp += 'А-ЯЁа-яё'; 62 | } 63 | if ( /N/.test( check ) ) { 64 | regexp += '0-9'; 65 | } 66 | 67 | var regexp_add = check.replace( /(z|W|Ё|N)/g, '' ); 68 | 69 | var input_reg = new RegExp( '[^' + regexp + regexp_add + ']' ); 70 | 71 | var reverse_reg = new RegExp( '[' + regexp + regexp_add + ']', "g" ); 72 | 73 | if ( input_reg.test( val ) && ( val != '' ) ) { 74 | 75 | return 'Wrong symbols: ' + reverse_reg; 76 | } 77 | } 78 | 79 | return ''; 80 | }; 81 | 82 | function isEmpty( str ) { 83 | return ( str.trim() == '' ? true : false ); 84 | } 85 | 86 | export default validation; -------------------------------------------------------------------------------- /script/autoform2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Mojo::File qw(curfile); 7 | use lib curfile->dirname->sibling('lib')->to_string; 8 | use Mojolicious::Commands; 9 | 10 | Mojolicious::Commands->start_app('autoform2'); 11 | -------------------------------------------------------------------------------- /t/basic.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | use Test::Mojo; 5 | 6 | my $t = Test::Mojo->new('autoform2'); 7 | $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i); 8 | 9 | done_testing(); 10 | -------------------------------------------------------------------------------- /templates/autoform/t.html.ep: -------------------------------------------------------------------------------- 1 | % layout 'default'; 2 | % title 'autoform'; 3 | 4 |
5 | 18 | 19 |   20 | 21 | 22 |


23 | tmp_debug
24 | 29 |
30 | 31 | -------------------------------------------------------------------------------- /templates/layouts/default.html.ep: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 | 9 | 10 | <%= content %> 11 | 12 | --------------------------------------------------------------------------------