Catalyst::Helper::Controller::InstantCRUD - [One line description of module's purpose here]
package Catalyst::Helper::View::InstantCRUD;
our $VERSION = '0.08';
use warnings;
use strict;
use Carp;
use Path::Class;
use List::Util qw(first);
sub mk_compclass {
my ( $self, $helper, $schema, $m2m, $bridges ) = @_;
my $file = $helper->{file};
$helper->render_file( 'compclass', $file );
my @classes = map {
$bridges->{ $_ } ? () : $_
} $schema->sources;
my $dir = dir( $helper->{dir}, 'root' );
$helper->mk_dir($dir);
# TT View
$helper->mk_component( $helper->{app}, 'view', $helper->{name}, 'TT' );
# table menu
my $table_menu = '| <a href="[% base %]">Home</a> | <a href="[% base %]restricted">Restricted Area </a> |';
for my $c (@classes) {
$table_menu .= ' <a href="[% base %]' . lc($c) . qq{">$c</a> |};
}
$helper->mk_file( file( $dir, 'table_menu.tt' ), $table_menu );
# static files
$helper->render_file( home => file( $dir, 'home.tt' ) );
$helper->render_file( restricted => file( $dir, 'restricted.tt' ) );
$helper->mk_file( file( $dir, 'wrapper.tt' ), $helper->get_file( __PACKAGE__, 'wrapper' ) );
$helper->mk_file( file( $dir, 'login.tt' ), $helper->get_file( __PACKAGE__, 'login' ) );
$helper->mk_file( file( $dir, 'pager.tt' ), $helper->get_file( __PACKAGE__, 'pager' ) );
# $helper->mk_file( file( $dir, 'destroy.tt' ), $helper->get_file( __PACKAGE__, 'destroy' ) );
my $staticdir = dir( $helper->{dir}, 'root', 'static' );
$helper->mk_dir( $staticdir );
$helper->render_file( style => file( $staticdir, 'pagingandsort.css' ) );
$helper->render_file( form_style => file( $staticdir, 'form.css' ) );
# javascript
# $helper->mk_file( file( $staticdir, 'doubleselect.js' ),
# HTML::Widget::Element::DoubleSelect->js_lib );
# templates
for my $class (@classes){
my $classdir = dir( $helper->{dir}, 'root', lc $class );
$helper->mk_dir( $classdir );
$helper->{field_configs} = _get_column_config( $schema, $class, $m2m ) ;
my $source = $schema->source($class);
$helper->{primary_keys} = [ $source->primary_columns ];
$helper->{base_pathpart} = '/' . lc $class . '/';
foreach my $page (qw/list view edit destroy/) {
$helper->render_file( $page => file( $classdir, "${page}.tt" ));
}
}
return 1;
}
sub _mk_label {
my $name = shift;
return join ' ', map { ucfirst } split '_', $name;
}
sub _get_column_config {
my( $schema, $class, $m2m ) = @_;
my @configs;
my $source = $schema->source($class);
my %bridge_cols;
for my $rel ( $source->relationships ) {
my $info = $source->relationship_info($rel);
$bridge_cols{$_} = 1 for _get_self_cols( $info->{cond} );
$m2m->{$class} and next if first { $_->[1] eq $rel } @{$m2m->{$class}};
my $config = {
name => $rel,
label => _mk_label( $rel ),
};
$config->{multiple} = 1 if $info->{attrs}{accessor} eq 'multi';
push @configs, $config;
}
for my $column ( $source->columns ) {
next if $bridge_cols{$column};
push @configs, {
name => $column,
label => _mk_label( $column ),
};
}
if( $m2m->{$class} ) {
for my $m ( @{$m2m->{$class}} ){
push @configs, {
name => $m->[0],
label => _mk_label( $m->[0] ),
multiple => 1,
};
}
}
return \@configs;
}
sub _get_self_cols{
my $cond = shift;
my @cols;
if ( ref $cond eq 'ARRAY' ){
for my $c1 ( @$cond ){
push @cols, get_self_cols( $c1 );
}
}
elsif ( ref $cond eq 'HASH' ){
for my $key ( values %{$cond} ){
if( $key =~ /self\.(.*)/ ){
push @cols, $1;
}
}
}
return @cols;
}
1; # Magic true value required at end of module
__DATA__
1;
__list__
[% TAGS <+ +> %]
<table>
<tr>
<+ FOR column = field_configs +>
<+- IF column.multiple -+>
<th> <+ column.name +> </th>
<+ ELSE +>
<th> [% order_by_column_link('<+ column.name +>', '<+ column.label+>') %] </th>
<+ END +>
<+ END +>
</tr>
[% WHILE (row = result.next) %]
<tr>
<+ FOR column = field_configs +>
<td>
<+ IF column.multiple +>
[% FOR val = row.<+ column.name +>; val; ', '; END %]
<+ ELSE +>
[% row.<+ column.name +> %]
<+ END +>
</td>
<+ END +>
[% SET id = row.$pri %]
<td><a href="[% c.uri_for_action( '<+ base_pathpart +><+ IF rest +>by_id'<+ ELSE +>view'<+ END +>, [], <+ FOR key = primary_keys +>row.<+ key +>, <+ END +> ) %]">View</a></td>
<td><a href="[% c.uri_for_action( '<+ base_pathpart +><+ IF rest +>by_id'<+ ELSE +>edit'<+ END +>, [], <+ FOR key = primary_keys +>row.<+ key +>, <+ END +><+ IF rest +>,'edit'<+ END +> ) %]">Edit</a></td>
<td><a href="[% c.uri_for_action( '<+ base_pathpart +><+ IF rest +>by_id'<+ ELSE +>destroy'<+ END +>, [], <+ FOR key = primary_keys +>row.<+ key +>, <+ END +><+ IF rest +>,'destroy'<+ END +> ) %]">Delete</a></td>
</tr>
[% END %]
</table>
[% PROCESS pager.tt %]
<br/>
<a href="[% c.uri_for_action('<+ base_pathpart +><+ IF rest +>create_form<+ ELSE +>edit<+ END +>' ) %]">Add</a>
__view__
[% TAGS <+ +> %]
<table name="view">
<+ FOR column = field_configs +>
<tr>
<td class="view_label"><b><+ column.label +>:</b></td>
<td>
<+ IF column.multiple +>
[% FOR val = item.<+ column.name +>; val; ', '; END %]
<+ ELSE +>
[% item.<+ column.name +> %]
<+ END +>
</td>
</tr>
<+ END +>
</table>
<hr/>
<a href="[% c.uri_for_action('<+ base_pathpart +>edit', <+ FOR key = primary_keys +>item.<+ key +>, <+ END +> ) %]">Edit</a>
<hr/>
<a href="[% c.uri_for_action('<+ base_pathpart +>list' ) %]">List</a>
__edit__
[% TAGS <+ +> %]
[% widget %]
<br>
<a href="[% c.uri_for_action( '<+ base_pathpart +>list' ) %]">List</a>
<hr>
[% form.render %]
__destroy__
[% TAGS <+ +> %]
[% destroywidget %]
<br>
<a href="[% c.uri_for_action( '<+ base_pathpart +>list' ) %]">List</a>
__wrapper__
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>[% appname %]</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="[%base%]static/pagingandsort.css" type="text/css" rel="stylesheet"/>
<link href="[%base%]static/form.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<div class="table_menu">
[% PROCESS table_menu.tt %]
</div>
<div class="content">
[% content %]
</div>
</body>
</html>
__login__
[% widget %]
__pager__
[% IF pager %]
<div class="pager">
<div class="counter">
Page [% pager.current_page %] of [% pager.last_page %]
</div>
<div>
[% IF pager.previous_page %]
<span><a href="[% c.req.uri_with( page => pager.first_page ) %]">«</a></span>
<span><a href="[% c.req.uri_with( page => pager.previous_page ) %]"><</a></span>
[% END %]
[%
start = (pager.current_page - 3) > 0 ? (pager.current_page - 3) : 1;
end = (pager.current_page + 3) < pager.last_page ? (pager.current_page + 3) : pager.last_page;
FOREACH page IN [ start .. end ]
%]
[% IF pager.current_page == page %]
<span class="current"> [% page %] </span>
[% ELSE %]
<span> <a href="[% c.req.uri_with( page => page ) %]">[% page %]</a> </span>
[% END %]
[% END %]
[% IF pager.next_page %]
<span><a href="[% c.req.uri_with( page => pager.next_page ) %]">></a></span>
<span><a href="[% c.req.uri_with( page => pager.last_page ) %]">»</a></span>
[% END %]
</div>
</div>
[% END %]
__restricted__
This is the restricted area - available only after loggin in.
__home__
This is an application generated by
<a href="http://search.cpan.org/dist/Catalyst-Example-InstantCRUD/lib/Catalyst/Example/InstantCRUD.pm">Catalyst::Example::InstantCRUD</a>
- a generator of simple database applications for the
<a href="http://catalyst.perl.org">Catalyst</a> framework.
See also
<a href="http://search.cpan.org/dist/Catalyst-Manual/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
and
<a href="http://search.cpan.org/dist/Catalyst-Manual/lib/Catalyst/Manual/Tutorial.pod">Catalyst::Manual::Intro</a>
__style__
/* HTML TAGS */
body {
font: bold 12px Verdana, sans-serif;
background-color:#F8F8F8;
color: #00283F;
}
.table_menu {
text-align: center;
font-size: 16px;
padding: 15px 15px 15px 15px;
color: #7CBFE5;
}
.content {
clear: both;
padding: 30px 12px 12px 12px;
font-size: 16px;
}
hr {
border: 1px solid #7CBFE5;
margin: 10px 0 15px 0;
}
A {
text-decoration: none;
color:#006DAC;
font-weight: bold;
}
A:visited {
color:#0073B5;
}
A:hover {
text-decoration: underline;
color:#006DAC;
}
#title {
z-index: 6;
width: 100%;
height: 18px;
margin-top: 10px;
font-size: 90%;
border-bottom: 1px solid #ddf;
text-align: left;
}
input.submit:hover {
color: #fff;
background-color: #7d95b5;
}
table {
margin: 0 auto 20px auto;
background-color: #ffffff;
border-collapse: collapse;
}
table .view_label{
background-color: #DFF6E6;
}
th {
background-color: #DFF6E6;
border: 1px solid #7CBFE5;
font: bol 14px Verdana, sans-serif;
padding: 4px 4px 4px 4px;
}
tr.alternate { background-color:#e3eaf0; }
tr:hover { background-color: #b5cadc; }
td {
font: 14px Verdana, sans-serif;
border: 1px solid #7CBFE5;
padding: 4px 4px 4px 4px;
}
.action {
border: 1px outset #7d95b5;
}
.action:hover {
color: #fff;
text-decoration: none;
background-color: #7d95b5;
}
.pager {
font: bold 14px Verdana, sans-serif;
color: #7CBFE5;
text-align: center;
border: solid 1px #e2e2e2;
border-left: 0;
border-right: 0;
padding: 15px 0 15px 0;
background-color: #EBF8EF;
}
.pager .counter{
padding: 0 0 10px 0;
}
.pager a {
padding: 2px 6px 2px 6px;
}
.pager a:hover {
color: #fff;
background: #7d95b5;
text-decoration: none;
}
.pager .current {
padding: 2px 6px;
font-weight: bold;
vertical-align: top;
}
.pager .current-page {
padding: 2px 6px;
font-weight: bold;
vertical-align: top;
}
__form_style__
fieldset {
padding: 12px 12px 12px 12px;
border: 1px solid #7CBFE5;
background-color: #FFFFFF;
}
.main_fieldset {
font-size: 12px;
}
.main_fieldset fieldset {
margin: 20px 0 20px 0;
}
fieldset input,
fieldset password,
fieldset radio,
fieldset select,
fieldset textarea
{
margin: 8px 0 8px 0;
}
label {
float: left;
width: 120px;
margin: 8px 10px 8px 0;
text-align: right;
}
.main_fieldset fieldset label{
width: 108px;
font-weight: normal;
}
fieldset .error_message {
display: block;
color: #ff0000;
margin: 20px 0 20px 0;
}
fieldset .error input,
fieldset .error textarea,
fieldset .error select {
background-color: #FFF0F0;
border: 1px solid #ff0000;
}
#submit{
margin: 20px 0 10px 0;
padding: 2px 2px 2px 2px;
background-color:#DFF6E6;;
font: bold 14px Verdana, sans-serif;
color:#006DAC;
}
fieldset .radiogroup span label {
/* undo the above style */
float: none;
width: auto;
text-align: left;
padding-right: 0;
}
fieldset.checkboxgroup,
fieldset.radiogroup
{
margin: 0;
margin-left: 12em;
padding: 0;
width: auto;
}
fieldset.radiogroup.label {
border: 0;
margin-left: 0em;
}
__END__