├── README.md
├── t
└── basic.t
├── script
└── autoform2
├── public
├── css
│ └── autoform.css
└── js
│ ├── sending.js
│ ├── action.js
│ └── validation.js
├── lib
├── autoform2
│ ├── Model
│ │ └── Pages.pm
│ ├── Other
│ │ ├── Init.pm
│ │ └── Token.pm
│ ├── Controller
│ │ └── Autoform.pm
│ └── Data
│ │ └── AutodataTypeC.pm
└── autoform2.pm
└── templates
├── layouts
└── default.html.ep
└── autoform
└── t.html.ep
/README.md:
--------------------------------------------------------------------------------
1 | # autoform2
2 |
3 | Autoform based on the Mojolicious, Vue.js and transactions.
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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;
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/templates/layouts/default.html.ep:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= title %>
5 |
6 |
7 |
8 |
9 |
10 | <%= content %>
11 |
12 |
--------------------------------------------------------------------------------
/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/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/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/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;
--------------------------------------------------------------------------------
/templates/autoform/t.html.ep:
--------------------------------------------------------------------------------
1 | % layout 'default';
2 | % title 'autoform';
3 |
4 |
5 |
18 |
19 |
20 |
21 |
22 |
23 |
tmp_debug
24 |
25 | -
26 | name {{ index }}, {{ elem.name }}: {{ elem.val }} ( {{ elem.check }} )
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------