Catalyst::Helper::Controller::InstantCRUD - [One line description of module's purpose here]


Catalyst-Example-InstantCRUD documentation Contained in the Catalyst-Example-InstantCRUD distribution.

Index


Code Index:


Catalyst-Example-InstantCRUD documentation Contained in the Catalyst-Example-InstantCRUD distribution.

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 ) %]">&laquo;</a></span>
           <span><a href="[% c.req.uri_with( page => pager.previous_page ) %]">&lt;</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 ) %]">&gt;</a></span>
           <span><a href="[% c.req.uri_with( page => pager.last_page ) %]">&raquo;</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__