From 679ae52ea9e2cbcb4237d0a48a024745623b5c4e Mon Sep 17 00:00:00 2001 From: Doug Bell Date: Wed, 29 Jul 2020 18:06:00 -0500 Subject: [PATCH] add some more cookbook entries and example apps --- eg/cookbook/custom-filter-full.pl | 46 ++++++ eg/cookbook/custom-filter-lite.pl | 30 ++++ eg/cookbook/log.sqlite3 | Bin 0 -> 8192 bytes eg/cookbook/pages-html.pl | 42 ++++++ eg/cookbook/pages-markdown.pl | 47 ++++++ eg/cookbook/pages-markdown.sqlite3 | Bin 0 -> 16384 bytes eg/cookbook/pages-markdown.sqlite3-shm | Bin 0 -> 32768 bytes eg/cookbook/pages-markdown.sqlite3-wal | 0 eg/cookbook/pages.sqlite3 | Bin 0 -> 16384 bytes eg/cookbook/pages.sqlite3-shm | Bin 0 -> 32768 bytes eg/cookbook/pages.sqlite3-wal | 0 lib/Yancy/Help/Cookbook.pod | 193 +++++++++++++++++++++++++ 12 files changed, 358 insertions(+) create mode 100644 eg/cookbook/custom-filter-full.pl create mode 100644 eg/cookbook/custom-filter-lite.pl create mode 100644 eg/cookbook/log.sqlite3 create mode 100644 eg/cookbook/pages-html.pl create mode 100644 eg/cookbook/pages-markdown.pl create mode 100644 eg/cookbook/pages-markdown.sqlite3 create mode 100644 eg/cookbook/pages-markdown.sqlite3-shm create mode 100644 eg/cookbook/pages-markdown.sqlite3-wal create mode 100644 eg/cookbook/pages.sqlite3 create mode 100644 eg/cookbook/pages.sqlite3-shm create mode 100644 eg/cookbook/pages.sqlite3-wal diff --git a/eg/cookbook/custom-filter-full.pl b/eg/cookbook/custom-filter-full.pl new file mode 100644 index 00000000..201cde38 --- /dev/null +++ b/eg/cookbook/custom-filter-full.pl @@ -0,0 +1,46 @@ +use Mojo::Base -signatures; +package MyApp::Controller::Log { + use Mojo::Base 'Yancy::Controller::Yancy', -signatures; + sub list_log( $self ) { + my $levels = $self->every_param( 'log_level' ); + if ( @$levels ) { + # Include only log levels requested + $self->stash( filter => { log_level => $levels } ); + } + return $self->SUPER::list; + } +} + +package MyApp { + use Mojo::Base 'Mojolicious', -signatures; + sub startup( $self ) { + push @{ $self->renderer->classes }, 'main'; + push @{ $self->routes->namespaces }, 'MyApp::Controller'; + + # Download log.db: http://github.com/preaction/Yancy/tree/master/eg/cookbook/log.sqlite3 + $self->plugin( Yancy => { + backend => 'sqlite:log.sqlite3', + read_schema => 1, + } ); + + $self->routes->get( '/' )->to( + controller => 'Log', + action => 'list_log', + schema => 'log', + template => 'log', + ); + } +} + +Mojolicious::Commands->new->start_app( 'MyApp' ); +__DATA__ +@@ log.html.ep +%= form_for current_route, begin + % for my $log_level ( qw( debug info warn error ) ) { + %= label_for "log_level_$log_level", begin + %= ucfirst $log_level + %= check_box log_level => $log_level + % end + % } + %= submit_button 'Filter' +% end diff --git a/eg/cookbook/custom-filter-lite.pl b/eg/cookbook/custom-filter-lite.pl new file mode 100644 index 00000000..b7eebc3e --- /dev/null +++ b/eg/cookbook/custom-filter-lite.pl @@ -0,0 +1,30 @@ +use Mojolicious::Lite -signatures; +# Download log.sqlite3: https://github.com/preaction/Yancy/tree/master/eg/cookbook/log.sqlite3 +plugin Yancy => { backend => 'sqlite:log.sqlite3', read_schema => 1 }; +under sub( $c ) { + my $levels = $c->every_param( 'log_level' ); + if ( @$levels ) { + # Include only log levels requested + $c->stash( filter => { log_level => $levels } ); + } + return 1; +}; +get '/' => { + controller => 'Yancy', + action => 'list', + schema => 'log', + template => 'log', +}; +app->start; +__DATA__ +@@ log.html.ep +%= form_for current_route, begin + % for my $log_level ( qw( debug info warn error ) ) { + %= label_for "log_level_$log_level", begin + %= ucfirst $log_level + %= check_box log_level => $log_level + % end + % } + %= submit_button 'Filter' +% end +%= include 'yancy/table' diff --git a/eg/cookbook/log.sqlite3 b/eg/cookbook/log.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..977620d5e29b4ea4f0a195e958b7fab5483e914a GIT binary patch literal 8192 zcmeI$!A^rf5CG7H+ITWGX^a;SlU~$B2?4c+lUUc2xAXEhf_zM7=&AMt=Vsj`*=32N87QFyw>i=6eSd@OUW_iO2aBR{5#~n^kXKYwWPb zdt-_d1qqM<36KB@kN^pg011!)36KB@kieIKSF5vCw=0W66^`xL+je`~*$2n<++ELg zkAxO5RcWq4-QyTTA@W3eU|Y5wn{~G0K0k;e|CNfndG)^lv;uK=haEe}N9^%4X(eiA zL0qPJ0#DfZ%VH#+)=QX)>|U0ZWwni}Y$wgf>POif&)FMlQRpgHKrSC(RDEEk#0V2V E0Pst8Jpcdz literal 0 HcmV?d00001 diff --git a/eg/cookbook/pages-html.pl b/eg/cookbook/pages-html.pl new file mode 100644 index 00000000..ebbf8e2a --- /dev/null +++ b/eg/cookbook/pages-html.pl @@ -0,0 +1,42 @@ + +use Mojolicious::Lite -signatures; +use Mojo::JSON qw( true false ); + +# Download this database: https://github.com/preaction/Yancy/tree/master/eg/cookbook/pages.sqlite3 +plugin Yancy => { + backend => 'sqlite:pages.sqlite3', + read_schema => 1, + schema => { + pages => { + title => 'Pages', + description => 'These are the pages in your site.', + 'x-id-field' => 'path', + required => [qw( path content )], + properties => { + page_id => { + type => 'integer', + readOnly => true, + }, + path => { + type => 'string', + }, + content => { + type => 'string', + format => 'html', + }, + }, + }, + }, +}; + +app->routes->get( '/*path', { path => 'index' } )->to({ + controller => 'Yancy', + action => 'get', + schema => 'pages', + template => 'page', +}); + +app->start; +__DATA__ +@@ page.html.ep +%== $item->{content} diff --git a/eg/cookbook/pages-markdown.pl b/eg/cookbook/pages-markdown.pl new file mode 100644 index 00000000..7a32a024 --- /dev/null +++ b/eg/cookbook/pages-markdown.pl @@ -0,0 +1,47 @@ + +use Mojolicious::Lite -signatures; +use Mojo::JSON qw( true false ); + +# Download this database: https://github.com/preaction/Yancy/tree/master/eg/cookbook/pages-markdown.sqlite3 +plugin Yancy => { + backend => 'sqlite:pages-markdown.sqlite3', + read_schema => 1, + schema => { + pages => { + title => 'Pages', + description => 'These are the pages in your site.', + 'x-id-field' => 'path', + required => [qw( path content )], + properties => { + page_id => { + type => 'integer', + readOnly => true, + }, + path => { + type => 'string', + }, + markdown => { + type => 'string', + format => 'markdown', + 'x-html-field' => 'content', + }, + content => { + type => 'string', + format => 'html', + }, + }, + }, + }, +}; + +app->routes->get( '/*path', { path => 'index' } )->to({ + controller => 'Yancy', + action => 'get', + schema => 'pages', + template => 'page', +}); + +app->start; +__DATA__ +@@ page.html.ep +%== $item->{content} diff --git a/eg/cookbook/pages-markdown.sqlite3 b/eg/cookbook/pages-markdown.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..5b3cbdde465c0f5efb7729302e7d1a7c0164366c GIT binary patch literal 16384 zcmeI&!EVzq7zc1CnHWUcf`m{wehQaWnpRbCOe4Y+K}G3UNGB?UDzkXRuqGw(G;uwbDvT^DG{-5l|`Rvs0FQ?Rbc{r3x&`DL#xS|K_HnVN^h=?)f=(?e+ zt*iC3-JV}Yo4YO?w)c>>yaQ%$U$E^9@2hSiAOHafKmY;|fB*y_009U<00LJCcq?zk zzO)0D_v}sI_pOzu_}f!4=kG*gKAinDiL->V`0+5Iz~lnjp|&xVISocx@+3*=MLKvM zrxSXXOm=mrD(EOq_n*cojYosSaYCbmj7H<(aF=Gh{*YJaC1uGgo%@wPu1Y0J^=Fzo zdUc<#_zi37>Nq$2zHc#E=HgTH@l-$IPx+au%=y%?Onc_=`}6#o7Kdpd009U<00Izz z00bZa0SG_<0uWfYz?PoS^6oHQ5D1Rwwb2tWV=5P$##AOL~?NnrWj+GPGRUfnhO z<@agOZlk+yR>+2QQH6eAUe=8=?-h_NX;Gck)aYIIJ=cAGB*nQIGvVd&fTl69~oJW5%TiBhOu_33T7AO7S#}^0DwQC5w tJTh0i`p#P3H>L{$0uX=z1Rwwb2tWV=5P$##AOL}XOW>yC-!gwYd;tLc6CknNbA+0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!8Aw0*%zIZl=g_H#-sdF3|fv zHYJd;K(o4QDB}gKMxa)pl{&RE0|EpH5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U gAV7cs0RjXF5FkK+009C72oNAZfB*pk1pY(d32N9Vz5oCK literal 0 HcmV?d00001 diff --git a/eg/cookbook/pages-markdown.sqlite3-wal b/eg/cookbook/pages-markdown.sqlite3-wal new file mode 100644 index 00000000..e69de29b diff --git a/eg/cookbook/pages.sqlite3 b/eg/cookbook/pages.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..f5a00f0ff7036cc834b1068b90473920653831c2 GIT binary patch literal 16384 zcmeI%Jx{_w7zglcOMFY9Ze5=+NkSw<$3m>6QDUu93z5a9!XZttD4anC2WBTekRQW| z58~>z!NkoF7B&ATckOf6^m_N(u6NaNBEhLQ9NmY44%jYJRdz(g7}I3U$g0Y!Ec2xO z6-nPup|Sl#%IY^v%`I4Np})%%0Rad=00Izz00bZa0SG_<0uWeVK%eV#_L__{W9p`1 z7|JXV;q8Dwgm-+LUbLELTfR-cb?n%bPy6z;M@bSyU8=jjePVlb>DA9IuT5umyC@%u zKD8{bc4~RlbnA_#P40zHZqsp!)EUNt$AWzOT6QhFsUT-DS$|0@k5JGyjEbSKDDLv- z@#8@5@PqJ44AXXyt~V&Bx9|J;l|O?QAOHafKmY;|fB*y_009U<00I#B8v@T8)#@Gytg6I*hD9rZ<5Kp z%xrddz5~4Oc@R?6W7b0Gv|s1@wBMUwCd_oYbn@exJy z)lcX6?U2Vz|M9M=m&Hs~5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXFR8gQ7n#Ii&Rou-^1j+?k zfyM`*fpw$R`705&5>&$=v0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N i0t5&UAV7cs0RjXF5FkK+009C72oNAZfB=F25O@L_vnrSX literal 0 HcmV?d00001 diff --git a/eg/cookbook/pages.sqlite3-wal b/eg/cookbook/pages.sqlite3-wal new file mode 100644 index 00000000..e69de29b diff --git a/lib/Yancy/Help/Cookbook.pod b/lib/Yancy/Help/Cookbook.pod index cd834c8b..ab28b40d 100644 --- a/lib/Yancy/Help/Cookbook.pod +++ b/lib/Yancy/Help/Cookbook.pod @@ -31,3 +31,196 @@ exact schemas and fields you want to allow. See L for the complete example. + +=head1 How do I handle custom forms for searching or filtering content? + +To handle a custom search form for the L +there are two options: + +=over + +=item 1. Use an C intermediate destination to process the form and set the C stash + +In this option, we use L +to handle the form before the final action is called. + + # Download this example: https://github.com/preaction/Yancy/tree/master/eg/cookbook/custom-filter-lite.pl + use Mojolicious::Lite -signatures; + # Download log.sqlite3: https://github.com/preaction/Yancy/tree/master/eg/cookbook/log.sqlite3 + plugin Yancy => { backend => 'sqlite:log.sqlite3', read_schema => 1 }; + under sub( $c ) { + my $levels = $c->every_param( 'log_level' ); + if ( @$levels ) { + # Include only log levels requested + $c->stash( filter => { log_level => $levels } ); + } + return 1; + }; + get '/' => { + controller => 'Yancy', + action => 'list', + schema => 'log', + template => 'log', + }; + app->start; + __DATA__ + @@ log.html.ep + %= form_for current_route, begin + % for my $log_level ( qw( debug info warn error ) ) { + %= label_for "log_level_$log_level", begin + %= ucfirst $log_level + %= check_box log_level => $log_level + % end + % } + %= submit_button 'Filter' + % end + %= include 'yancy/table' + +=item 2. Create a custom controller action to process the form and then call the "list" action + +In this option, we extend the Yancy controller to add our own action. +Then, we can call the action we want to end up at (in this case, the +"list" action). + + # Download this example: https://github.com/preaction/Yancy/tree/master/eg/cookbook/custom-filter-full.pl + use Mojo::Base -signatures; + package MyApp::Controller::Log { + use Mojo::Base 'Yancy::Controller::Yancy', -signatures; + sub list_log( $self ) { + my $levels = $self->every_param( 'log_level' ); + if ( @$levels ) { + # Include only log levels requested + $self->stash( filter => { log_level => $levels } ); + } + return $self->SUPER::list; + } + } + + package MyApp { + use Mojo::Base 'Mojolicious', -signatures; + sub startup( $self ) { + push @{ $self->renderer->classes }, 'main'; + push @{ $self->routes->namespaces }, 'MyApp::Controller'; + + # Download log.db: http://github.com/preaction/Yancy/tree/master/eg/cookbook/log.sqlite3 + $self->plugin( Yancy => { + backend => 'sqlite:log.sqlite3', + read_schema => 1, + } ); + + $self->routes->get( '/' )->to( + controller => 'Log', + action => 'list_log', + schema => 'log', + template => 'log', + ); + } + } + + Mojolicious::Commands->new->start_app( 'MyApp' ); + __DATA__ + @@ log.html.ep + %= form_for current_route, begin + % for my $log_level ( qw( debug info warn error ) ) { + %= label_for "log_level_$log_level", begin + %= ucfirst $log_level + %= check_box log_level => $log_level + % end + % } + %= submit_button 'Filter' + % end + +=back + +=head1 How can I allow users to create arbitrary pages in the Yancy editor? + +Create a schema for the pages (perhaps, named C). This schema must at +least have an ID (perhaps in C), a path (in a C field), and some +content (in a C field). We should use the C field as Yancy's ID +field so that we can more easily look up the pages. + + # Download this database: https://github.com/preaction/Yancy/tree/master/eg/cookbook/pages.sqlite3 + use Mojolicious::Lite -signatures; + plugin Yancy => { + backend => 'sqlite:pages.sqlite3', + schema => { + pages => { + title => 'Pages', + description => 'These are the pages in your site.', + 'x-id-field' => 'path', + required => [qw( path content )], + properties => { + page_id => { + type => 'integer', + readOnly => 1, + }, + path => { + type => 'string', + }, + content => { + type => 'string', + format => 'html', + }, + }, + }, + }, + }; + +Once we have a schema, we need to render these pages. We want these +pages to show up when the user visits the page's C, so we need +a route that captures everything. Because our route captures everything, +it should be the last route we declare (Mojolicious routes are checked +in-order for matching). We also want to allow a default path of "index", +so we give a default value for the C stash. + + app->routes->get( '/*path', { path => 'index' } )->to({ + controller => 'Yancy', + action => 'get', + schema => 'pages', + template => 'page', + }); + +Now we need a template to display the content. There isn't much to this +template: + + @@ page.html.ep + %== $item->{content} + +L + +If you want your users to use Markdown instead of HTML, add another +field for the Markdown (called C), and have the editor render +the Markdown into HTML in the C field. + + use Mojolicious::Lite -signatures; + # Download this database: https://github.com/preaction/Yancy/tree/master/eg/cookbook/pages-md.sqlite3 + plugin Yancy => { + backend => 'sqlite:pages-markdown.sqlite3', + schema => { + pages => { + title => 'Pages', + description => 'These are the pages in your site.', + required => [qw( path markdown )], + properties => { + page_id => { + type => 'integer', + readOnly => 1, + }, + path => { + type => 'string', + }, + markdown => { + type => 'string', + format => 'markdown', + 'x-html-field' => 'content', + }, + content => { + type => 'string', + format => 'html', + }, + }, + }, + }, + }; + +L