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 00000000..977620d5 Binary files /dev/null and b/eg/cookbook/log.sqlite3 differ 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 00000000..5b3cbdde Binary files /dev/null and b/eg/cookbook/pages-markdown.sqlite3 differ diff --git a/eg/cookbook/pages-markdown.sqlite3-shm b/eg/cookbook/pages-markdown.sqlite3-shm new file mode 100644 index 00000000..a8bbf144 Binary files /dev/null and b/eg/cookbook/pages-markdown.sqlite3-shm differ 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 00000000..f5a00f0f Binary files /dev/null and b/eg/cookbook/pages.sqlite3 differ diff --git a/eg/cookbook/pages.sqlite3-shm b/eg/cookbook/pages.sqlite3-shm new file mode 100644 index 00000000..929e4c22 Binary files /dev/null and b/eg/cookbook/pages.sqlite3-shm differ 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