unstable switchover before template renaming. added searchpath for widgets trying...
groditi [Tue, 2 Oct 2007 20:05:44 +0000 (20:05 +0000)]
33 files changed:
lib/ComponentUI/View/Site.pm
lib/Reaction/Meta/Attribute.pm
lib/Reaction/UI/LayoutSet.pm
lib/Reaction/UI/LayoutSet/TT.pm
lib/Reaction/UI/View.pm
lib/Reaction/UI/ViewPort/GridView.pm [new file with mode: 0644]
lib/Reaction/UI/ViewPort/GridView/Role/Order.pm [new file with mode: 0644]
lib/Reaction/UI/ViewPort/GridView/Role/Pager.pm [new file with mode: 0644]
lib/Reaction/UI/ViewPort/GridView/Row.pm [new file with mode: 0644]
lib/Reaction/UI/ViewPort/ListView.pm
lib/Reaction/UI/ViewPort/ObjectView.pm
lib/Reaction/UI/Widget/GridView.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/GridView/Row.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/ListView.pm
lib/Reaction/UI/Widget/Value.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/Boolean.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/Collection.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/DateTime.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/List.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/Number.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/RelatedObject.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/String.pm [new file with mode: 0644]
lib/Reaction/UI/Widget/Value/Text.pm [new file with mode: 0644]
share/skin/default/layout/grid_view [new file with mode: 0644]
share/skin/default/layout/list_view
share/skin/default/layout/value/boolean [new file with mode: 0644]
share/skin/default/layout/value/collection [new file with mode: 0644]
share/skin/default/layout/value/date_time [new file with mode: 0644]
share/skin/default/layout/value/list [new file with mode: 0644]
share/skin/default/layout/value/number [new file with mode: 0644]
share/skin/default/layout/value/related_object [new file with mode: 0644]
share/skin/default/layout/value/string [new file with mode: 0644]
share/skin/default/layout/value/text [new file with mode: 0644]

index cbb2d28..fa89c0a 100644 (file)
@@ -7,6 +7,9 @@ class Site is TT, which {
 
 };
 
+1;
+
+__END__;
 
 use Class::MOP;
 
index 38035d5..9f444df 100644 (file)
@@ -30,6 +30,9 @@ around _process_options => sub {
       $options->{default} =  $fail ?
         sub { confess "${name} must be provided before calling reader" } :
           sub{ shift->$builder };
+
+      $options->{clearer} ||= ($name =~ /^_/) ? "_clear${name}" : "clear_${name}"
+        if $build;
     }
 
     #we are using this everywhere so might as well move it here.
index 1cdb0e9..b19c92b 100644 (file)
@@ -10,15 +10,19 @@ class LayoutSet which {
   has 'name' => (is => 'ro', required => 1);
 
   has 'source_file' => (is => 'rw', lazy_fail => 1);
+  has 'file_extension'=> (isa => 'Str', is => 'rw', lazy_build => 1);
+
+  implements build_file_extension => as { 'html' };
 
   implements 'BUILD' => as {
     my ($self, $args) = @_;
     my @path = @{$args->{search_path}||[]};
     confess "No search_path provided" unless @path;
     my $found;
+    my $ext = $self->file_extension;
     SEARCH: foreach my $path (@path) {
-      my $cand = $path->file($self->name);
-      print STDERR $cand,"\n";
+      my $cand = $path->file($self->name . ".${ext}");
+      #print STDERR $cand,"\n";
       if ($cand->stat) {
         $self->_load_file($cand);
         $found = 1;
@@ -48,7 +52,7 @@ class LayoutSet which {
     my $widget = join('',   map { ucfirst($_) } split('_', $self->name));
     $widget    = join('::', map { ucfirst($_) } split('/', $widget));
 
-    print STDERR "--- ", $self->name, " maps to widget $widget \n";
+    #print STDERR "--- ", $self->name, " maps to widget $widget \n";
 
     return $widget;
   };
index 02b3cde..b77d0d5 100644 (file)
@@ -8,6 +8,8 @@ class TT is LayoutSet, which {
 
   has 'tt_view' => (is => 'rw', isa => View, lazy_fail => 1);
 
+  implements build_file_extension => as { 'tt' };
+
   implements 'BUILD' => as {
     my ($self, $args) = @_;
 
index 987909c..081a16f 100644 (file)
@@ -26,6 +26,7 @@ class View which {
   sub BUILD{
     my $self = shift;
     my $skin_name = $self->skin_name;
+    #XXX i guess we will add the path to installed reaction templates here
     my $skin_path = $self->app->path_to('share','skin',$skin_name);
     confess("'${skin_path}' is not a valid path for skin '${skin_name}'")
       unless -d $skin_path;
@@ -58,15 +59,21 @@ class View which {
     my ($self, $layout_set) = @_;
     my $base = $self->blessed;
     my $tail = $layout_set->widget_type;
-    my $class = join('::', $base, 'Widget', $tail);
-    eval { Class::MOP::load_class($class) };
-    confess "Couldn't load widget '$class': $@" if $@;
-    return $class;
+    # eventually more stuff will go here i guess?
+    my $app_name = ref $self->app || $self->app;
+
+    my @search_path = ($base, $app_name, 'Reaction::UI');
+    my @haystack    = map { join '::', $_, 'Widget', $tail } @search_path;
+    for my $class (@haystack){
+      eval { Class::MOP::load_class($class) };
+      $@ ? next : return $class;
+    }
+    confess "Couldn't load widget '$tail': tried: @haystack";
   };
 
   implements 'layout_set_for' => as {
     my ($self, $vp) = @_;
-    print STDERR "Getting layoutset for VP ".(ref($vp) || "SC:".$vp)."\n";
+    #print STDERR "Getting layoutset for VP ".(ref($vp) || "SC:".$vp)."\n";
     my $lset_name = eval { $vp->layout };
     confess "Couldn't call layout method on \$vp arg ${vp}: $@" if $@;
     unless (length($lset_name)) {
@@ -75,7 +82,7 @@ class View which {
       my @fragments = split('::', $last);
       $_ = join("_", split(/(?=[A-Z])/, $_)) for @fragments;
       $lset_name = lc(join('/', @fragments));
-      print STDERR "--- $vp_class is rendered as $lset_name\n";
+      #print STDERR "--- $vp_class is rendered as $lset_name\n";
     }
     my $cache = $self->_layout_set_cache;
     return $cache->{$lset_name} ||= $self->create_layout_set($lset_name);
diff --git a/lib/Reaction/UI/ViewPort/GridView.pm b/lib/Reaction/UI/ViewPort/GridView.pm
new file mode 100644 (file)
index 0000000..542af62
--- /dev/null
@@ -0,0 +1,69 @@
+package Reaction::UI::ViewPort::GridView;
+
+use Reaction::Class;
+
+use aliased 'Reaction::UI::ViewPort::GridView::Row';
+use aliased 'Reaction::InterfaceModel::Collection';
+
+class GridView is 'Reaction::UI::ViewPort', which {
+
+  has exclude_columns => ( isa => 'ArrayRef', is => 'ro' );
+  has column_names    => ( isa => 'ArrayRef', is => 'ro', lazy_build => 1);
+  has rows            => ( isa => 'ArrayRef', is => 'ro', lazy_build => 1);
+  has row_args        => ( isa => 'HashRef',  is => 'ro');
+
+  has collection         => (isa => Collection, is => 'ro', required   => 1);
+  has current_collection => (isa => Collection, is => 'rw', lazy_build => 1);
+
+  implements build_rows => as{
+    my $self = shift;
+
+    my (@rows, $i);
+    for my $object ( $self->current_collection->members ){
+      my $row = Row->new
+        (
+         ctx            => $self->ctx,
+         object         => $object,
+         location       => join('-', $self->location, 'row', ++$i),
+         column_order   => $self->column_order, #XXX clean from ViewPort
+         exclude_fields => $self->exclude_columns || [],
+         $self->has_row_args ? %{ $self->row_args } : (),
+
+        );
+      push(@rows, $row);
+    }
+    return \@rows;
+  };
+
+  implements build_column_names => as {
+    my ($self) = @_;
+    my %excluded = map { $_ => undef }
+      @{ $self->has_exclude_columns ? $self->exclude_columns : [] };
+    #XXX this abuse of '_im_class' needs to be fixed ASAP
+    my $object_class = $self->collection->_im_class;
+    my @fields = $object_class->meta->compute_all_applicable_attributes;
+    #eliminate excluded fields & treat names that start with an underscore as private
+    @fields = grep {$_->name !~ /^_/ && !exists $excluded{$_->name} } @fields;
+    #eliminate fields marked as collections, or fields that are arrayrefs
+    @fields = grep {
+      !($_->has_type_constraint &&
+        ($_->type_constraint->is_a_type_of('ArrayRef') ||
+         eval {$_->type_constraint->name->isa('Reaction::InterfaceModel::Collection')} ||
+         eval { $_->_isa_metadata->isa('Reaction::InterfaceModel::Collection') }
+        )
+       )  } @fields;
+
+    #order the columns all nice and pretty, and only get fields with readers, duh
+    return $self->sort_by_spec
+      ( $self->column_order, [ map { (($_->get_read_method) || ()) } @fields] );
+  };
+
+  implements build_current_collection => as {
+    shift->collection;
+  };
+
+};
+
+
+
+1;
diff --git a/lib/Reaction/UI/ViewPort/GridView/Role/Order.pm b/lib/Reaction/UI/ViewPort/GridView/Role/Order.pm
new file mode 100644 (file)
index 0000000..9684052
--- /dev/null
@@ -0,0 +1,29 @@
+package Reaction::UI::ViewPort::GridView::Role::Order;
+
+use Reaction::Role;
+
+role Order, which {
+
+  has order_by      => (isa => 'Str', is => 'rw', trigger_adopt('order_by'));
+  has order_by_desc => (isa => 'Int', is => 'rw', trigger_adopt('order_by'), lazy_build => 1);
+
+  around build_current_collection => sub {
+    my $orig = shift;
+    my ($self) = @_;
+    my $collection = $orig->(@_);
+    my %attrs;
+
+    #XXX DBICism that needs to be fixed
+    if ($self->has_order_by) {
+      $attrs{order_by} = $self->order_by;
+      $attrs{order_by} .= ' DESC' if ($self->order_by_desc);
+    }
+
+    return $collection->where(undef, \%attrs);
+  };
+
+  around accept_events => sub { ('order_by', 'order_by_desc', shift->(@_)); };
+
+};
+
+1;
diff --git a/lib/Reaction/UI/ViewPort/GridView/Role/Pager.pm b/lib/Reaction/UI/ViewPort/GridView/Role/Pager.pm
new file mode 100644 (file)
index 0000000..276a413
--- /dev/null
@@ -0,0 +1,36 @@
+package Reaction::UI::ViewPort::GridView::Role::Pager;
+
+use Reaction::Role;
+
+use aliased 'Reaction::InterfaceModel::Collection';
+
+role Pager, which {
+
+  has paged_collection => (isa => Collection, is => 'rw', lazy_build => 1);
+
+  has pager    => (isa => 'Data::Page', is => 'rw', lazy_build => 1);
+  has page     => (isa => 'Int', is => 'rw', lazy_build => 1, trigger_adopt('page'));
+  has per_page => (isa => 'Int', is => 'rw', lazy_build => 1, trigger_adopt('page'));
+
+  implements build_page     => as { 1  };
+  implements build_per_page => as { 10 };
+
+  implements build_pager => as { shift->paged_collection->pager };
+
+  implements adopt_page => as {
+    my ($self) = @_;
+    $self->clear_paged_collection;
+    $self->clear_pager;
+  };
+
+  around accept_events => sub { ('page', shift->(@_)); };
+
+  implements build_paged_collection => sub {
+    my ($self) = @_;
+    my $collection = $self->current_collection;
+    return $collection->where(undef, {rows => $self->per_page})->page($self->page);
+  };
+
+};
+
+1;
diff --git a/lib/Reaction/UI/ViewPort/GridView/Row.pm b/lib/Reaction/UI/ViewPort/GridView/Row.pm
new file mode 100644 (file)
index 0000000..2edb06a
--- /dev/null
@@ -0,0 +1,110 @@
+package Reaction::UI::ViewPort::GridView::Row;
+
+use Reaction::Class;
+
+class Row is 'Reaction::UI::ViewPort::ObjectView', which {
+
+  around build_fields_for_type_Num => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/number'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_Int => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/number'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_Bool => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/boolean'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+
+  around build_fields_for_type_Str => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/string'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_SimpleStr => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/string'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_Enum => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/string'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_DateTime => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/date_time'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_ArrayRef => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/list'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_Reaction_InterfaceModel_Collection => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/collection'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+  around build_fields_for_type_Reaction_InterfaceModel_Object => sub {
+    my ($orig, $self, $attr, $args) = @_;
+    $args->{Field}{$attr->name}{layout} = 'value/related_object'
+      unless( exists  $args->{Field}{$attr->name}         &&
+              exists  $args->{Field}{$attr->name}{layout} &&
+              defined $args->{Field}{$attr->name}{layout}
+            );
+    $orig->($self, $attr, $args);
+  };
+
+};
+
+1;
index 08b7517..94e4de1 100644 (file)
 package Reaction::UI::ViewPort::ListView;
 
 use Reaction::Class;
-use Data::Page;
-use Text::CSV_XS;
-use Scalar::Util qw/blessed/;
 
-class ListView is 'Reaction::UI::ViewPort', which {
-  has collection => (isa => 'Reaction::InterfaceModel::Collection',
-                       is => 'rw', required => 1);
+class ListView is 'Reaction::UI::ViewPort::GridView', which {
 
-  has current_collection => (
-    isa => 'Reaction::InterfaceModel::Collection', is => 'rw',
-    lazy_build => 1, clearer => 'clear_current_collection',
-  );
-
-  has current_page_collection => (
-    isa => 'Reaction::InterfaceModel::Collection', is => 'rw',
-    lazy_build => 1, clearer => 'clear_current_page_collection',
-  );
-
-  has page => (
-    isa => 'Int', is => 'rw', required => 1,
-    default => sub { 1 }, trigger_adopt('page'),
-  );
-
-  has pager => (
-    isa => 'Data::Page', is => 'rw',
-    lazy_build => 1, clearer => 'clear_pager',
-  );
-
-  has per_page => (
-    isa => 'Int', is => 'rw', predicate => 'has_per_page',
-    default => sub { 10 }, trigger_adopt('page'),
-    clearer => 'clear_per_page',
-  );
-
-  has field_names => (is => 'rw', isa => 'ArrayRef', lazy_build => 1);
-
-  has field_label_map => (is => 'rw', isa => 'HashRef', lazy_build => 1);
-
-  has order_by => (
-    isa => 'Str', is => 'rw', predicate => 'has_order_by',
-    trigger_adopt('order_by')
-  );
-
-  has order_by_desc => (
-    isa => 'Int', is => 'rw', default => sub { 0 },
-    trigger_adopt('order_by')
-  );
-
-  has row_action_prototypes => (isa => 'ArrayRef', is => 'ro', lazy_build => 1);
-
-  has exclude_columns =>
-      ( is => 'rw', isa => 'ArrayRef', required => 1, default => sub{ [] } );
-
-  implements BUILD => as {
-    my ($self, $args) = @_;
-    if ($args->{unpaged}) {
-      $self->clear_per_page;
-    }
-  };
-
-  sub field_label { shift->field_label_map->{+shift}; }
-
-  implements build_pager => as {
-    my ($self) = @_;
-    return $self->current_page_collection->pager;
-  };
-
-  implements adopt_page => as {
-    my ($self) = @_;
-    $self->clear_current_page_collection;
-    $self->clear_pager;
-  };
-
-  implements adopt_order_by => as {
-    my ($self) = @_;
-    $self->clear_current_collection;
-    $self->clear_current_page_collection;
-  };
-
-  implements build_current_collection => as {
-    my ($self) = @_;
-    my %attrs;
-
-    #XXX DBICism that needs to be fixed
-    if ($self->has_order_by) {
-      $attrs{order_by} = $self->order_by;
-      if ($self->order_by_desc) {
-        $attrs{order_by} .= ' DESC';
-      }
-    }
-    return $self->collection->where(undef, \%attrs);
-  };
-
-  implements build_current_page_collection => as {
-    my ($self) = @_;
-    my %attrs;
-    return $self->current_collection unless $self->has_per_page;
-    $attrs{rows} = $self->per_page;
-    return $self->current_collection->where(undef, \%attrs)->page($self->page);
-  };
-
-  implements all_current_rows => as {
-    return shift->current_collection->members;
-  };
-
-  implements current_rows => as {
-    return shift->current_page_collection->members;
-  };
-
-  implements build_field_names => as {
-    my ($self) = @_;
-    #XXX candidate for future optimization
-    my %excluded = map { $_ => undef } @{ $self->exclude_columns };
-
-    #XXX this abuse of '_im_class' needs to be fixed ASAP
-    my $object_class = $self->current_collection->_im_class;
-    my @fields = $object_class->meta->compute_all_applicable_attributes;
-    #eliminate excluded fields & treat names that start with an underscore as private
-    @fields = grep {$_->name !~ /^_/ && !exists $excluded{$_->name} } @fields;
-    #eliminate fields marked as collections, or fields that are arrayrefs
-    @fields = grep {
-      !($_->has_type_constraint &&
-        ($_->type_constraint->is_a_type_of('ArrayRef') ||
-         eval {$_->type_constraint->name->isa('Reaction::InterfaceModel::Collection')} ||
-         eval { $_->_isa_metadata->isa('Reaction::InterfaceModel::Collection') }
-        )
-       )  } @fields;
-
-    #for(grep { $_->has_type_constraint } @fields){
-      #my $tcname = $_->type_constraint->name;
-      #print STDERR $_->name, "\t", $tcname, "\n";
-      #use Data::Dumper;
-      #print STDERR Dumper($_->type_constraint);
-    #}
-
-    #order the columns all nice and pretty, and only get fields with readers, duh
-    return $self->sort_by_spec
-      ( $self->column_order, [ map { (($_->get_read_method) || ()) } @fields] );
-  };
-
-  implements build_field_label_map => as {
-    my ($self) = @_;
-    my %labels;
-    foreach my $name (@{$self->field_names}) {
-      $labels{$name} = join(' ', map { ucfirst } split('_', $name));
-    }
-    return \%labels;
-  };
-
-  #XXX this has to go soon, I recommend that Objects hold a registry of their actions
-  #and that they can be queried about it somehow
-  implements build_row_action_prototypes => as {
-    my $self = shift;
-    my $ctx = $self->ctx;
-    return [
-      { label => 'View', action => sub {
-        [ '', 'view', [ @{$ctx->req->captures},   $_[0]->__id ] ] } },
-      { label => 'Edit', action => sub {
-        [ '', 'update', [ @{$ctx->req->captures}, $_[0]->__id ] ] } },
-      { label => 'Delete', action => sub {
-        [ '', 'delete', [ @{$ctx->req->captures}, $_[0]->__id ] ] } },
-    ];
-  };
-
-  implements row_actions_for => as {
-    my ($self, $row) = @_;
-    my @act;
-    my $c = $self->ctx;
-    foreach my $proto (@{$self->row_action_prototypes}) {
-      my %new = %$proto;
-      my ($c_name, $a_name, @rest) = @{delete($new{action})->($row)};
-      $new{label} = delete($new{label})->($row) if ref $new{label} eq 'CODE';
-      $new{uri} = $c->uri_for(
-                    $c->controller($c_name)->action_for($a_name),
-                    @rest
-                  );
-      push(@act, \%new);
-    }
-    return \@act;
-  };
-
-  implements export_to_csv => as {
-    my ($self) = @_;
-    my $csv = Text::CSV_XS->new( {  binary => 1 } );
-    my $output;
-    my $exporter = sub {
-      $csv->combine( @_ );
-      $output .= $csv->string."\r\n";
-    };
-    $self->export_to_data($exporter);
-    my $res = $self->ctx->res;
-    $res->content_type('text/csv');
-    my $path = $self->ctx->req->path;
-    my @parts = split(/\//, $path);
-    $res->header(
-      'Content-disposition' => 'attachment; filename='.pop(@parts).'.csv'
-    );
-    $res->body($output);
-  };
-
-  implements export_to_data => as {
-    my ($self, $exporter) = @_;
-    $self->export_header_data($exporter);
-    $self->export_body_data($exporter);
-  };
-
-  implements export_header_data => as {
-    my ($self, $exporter) = @_;
-    my @names = @{$self->field_names};
-    my %labels = %{$self->field_label_map};
-    $exporter->( map { $labels{$_} } @names );
-  };
-
-  implements export_body_data => as {
-    my ($self, $exporter) = @_;
-    my @names = @{$self->field_names};
-    foreach my $row ($self->all_current_rows) {
-      my @row_data;
-      foreach $_ (@names) {
-        my $data = $row->$_;
-        if (blessed($data) && $data->can("display_name")) {
-          $data = $data->display_name;
-        }
-        push(@row_data, $data);
-      }
-      $exporter->( @row_data );
-    }
-  };
-
-  override accept_events => sub { ('page', 'order_by', 'order_by_desc', 'export_to_csv', super()); };
+  does 'Reaction::UI::ViewPort::GridView::Role::Order';
+  does 'Reaction::UI::ViewPort::GridView::Role::Pager';
 
 };
 
 1;
-
-=head1 NAME
-
-Reaction::UI::ViewPort::ListView - Page layout block for rows of DBIx::Class::ResultSets
-
-=head1 SYNOPSIS
-
-  # Create a new ListView
-  # $stack isa Reaction::UI::FocusStack object
-  # Assuming you have a DBIC model with an Actors table
-  my $lv = $stack->push_viewport(
-    'Reaction::UI::ViewPort::ListView',
-    collection => $ctx->model('DBIC::Actors'),     # a DBIx::Class::ResultSet
-    page => 1,                                     # 1 is default
-    per_page => 10,                                # 10 is default
-    field_names => [qw/name age/],
-    field_label_map => {
-      'name' => 'Name',
-      'age' => 'Age',
-    },
-    order_by => 'name',
-  );
-
-=head1 DESCRIPTION
-
-Use this ViewPort to display the contents of a
-L<DBIx::Class::ResultSet> as paged sets of rows. The default display
-shows 10 rows per page, unsorted.
-
-TODO: Add a filter_by which allows us to restrict the content?
-(Scenario: user has a paged display of data, user selects one value in
-a column and clicks "filter by this value", and then only rows
-containing that value are shown.
-
-=head1 ATTRIIBUTES
-
-=head2 collection
-
-This mandatory attribute must be an object derived from
-L<DBIx::Class::ResultSet> representing the search result or result
-source(Table) you wish to display in the ListView.
-
-The collection is used as the basis to create a refined set of data to
-show in the current ListView, this is stored in
-L<current_collection>. The data can further be refined and restricted
-by passing in or later changing the L<order_by> or L<page>
-attributes. The
-
-=head2 order_by
-
-A string representing the C<ORDER BY> part of the SQL statement, for
-more info see L<DBIx::Class::ResultSet/Attributes>
-
-=head2 order_by_desc
-
-By default, sorting is done in ascending order, set this to true to
-sort in descending order. Changing this attribute will cause the
-L<current_collection> to be cleared and recreated on the next access .
-
-=head2 exclude_columns
-
-
-
-=head2 page
-
-The page number of the current search result, this will default to
-1. If set explicitly on the ListView object, the current search result
-and the pager will be cleared and recreated on the next access.
-
-=head2 per_page
-
-The number of rows of data to list on each page. Changing this value
-on the ListView object will cause the L<current_page_collection> and
-the L<pager> to be cleared and recreated on the next access. This will
-default to 10 if unset.
-
-=head2 unpaged
-
-Set this to a true value if you really don't want your results shown
-in pages.
-
-=head2 field_names
-
-An array reference of field names to show in the ListView. These must
-exist as accessors in the L<DBIx::Class::ResultSource> describing the
-L<DBIx::Class::ResultSet> passed to L<collection>.
-
-If not set, this will default to the list of attributes in the
-L<DBIx::Class::ResultSource> which do not begin with an underscore,
-and don't have a type of either ArrayRef or
-C<DBIx::Class::ResultSet>. In short, all the non-private and
-non-relation attributes.
-
-=head2 field_label_map
-
-A hash reference mapping the L<field_names> to the column labels used
-to describe them in the ListView display.
-
-If not set, the label values will default to the L<field_names> with
-the initial characters capitalised and underscores turned into spaces.
-
-=head2 row_action_prototypes
-
-  row_action_prototypes => [
-    { label => 'Edit', action => sub { [ '', 'update', [ $_[0]->id ] ] } },
-    { label => 'Delete', action => sub { [ '', 'delete', [ $_[0]->id ] ] } },
-  ];
-
-Prototypes describing the actions that can be done on the rows of
-ListView data. This is an array reference of hash refs describing the
-name of each action with a C<label>, and the actual C<action> that
-takes place. The code reference stored in the C<action > will be
-called with a L<DBIx::Class::Row> object, it should return a list of a
-L<Catalyst::Controller> name, the name of an action in that
-controller, and any other parameters that need to be passed to
-it. C<label> may be a scalar value or a code reference, in the later case
-it will be called with the same parameters as C<action> and the return value
-will be used as the C<label> value.
-
-The example above shows the default actions if this attribute is not set.
-
-=head2 current_collection
-
-This contains the currently used L<DBIx::Class::ResultSet>
-representing the ListViews data, it is based on the L<collection>
-ResultSet, refined using the L<order_by> and L<order_by_desc> attributes.
-
-The current_collection will be cleared and recreated if the
-L<order_by> or L<order_by_desc> attributes are changed on the ListView
-object.
-
-=head2 current_rows
-
-=head2 all_current_rows
-
-=head2 pager
-
-A L<Data::Page> object representing the data for the current search
-result, it is cleared and reset when either L<page> or L<order_by> are
-changed.
-
-=head2 current_page_collection
-
-This contains contains a single page of the contents of the
-L<current_collection>, with the L<per_page> number of rows
-requested. If the L<page>, L<per_page>, L_order_by> or
-L<order_by_desc> attributes are changed on the ListView object, the
-current_page_collection is cleared and recreated.
-
-=head1 METHODS
-
-=head2 row_actions_for
-
-=over 4
-
-=item Arguments: none
-
-=back
-
-Returns an array reference of uris and labels representing the actions
-set in L<row_action_prototypes>. L<Catalyst/uri_for> is used to
-construct these.
-
-=head2 export_header_data
-
-=over 4
-
-=item Arguments: $exporter
-
-=back
-
-  $lv->export_head_data($exporter);
-
-C<$exporter> should be a code reference which will export lists of
-data passed to it. This method calls the C<exporter> code reference
-passing it the labels from the L<field_label_map> using the current
-set of L<field_names>.
-
-=head2 export_body_data
-
-=over 4
-
-=item Arguments: $exporter
-
-=back
-
-  $lv->export_body_data($exporter);
-
-C<$exporter> should be a code reference which will export lists of
-data passed to it. This method calls the C<exporter> code reference
-with an array of rows containing the data values of each of the
-current L<field_values>.
-
-=head2 export_to_data
-
-=over 4
-
-=item Arguments: $exporter
-
-=back
-
-  $lv->export_to_data($exporter);
-
-C<$exporter> should be a code reference which will export lists of
-data passed to it. This method calls L<export_header_data> and
-L<export_body_data> with C<exporter>.
-
-=head2 export_to_csv
-
-=over 4
-
-=item Arguments: none
-
-=back
-
-  $lv->export_to_csv();
-
-Fills the L<Catalyst::Response> body with CSV data of the
-L<current_collection> using L<export_to_data> and L<Text::CSV_XS>.
-
-=head2 field_label
-
-=over 4
-
-=item Arguments: $field_name
-
-=back
-
-Returns the label for the given C<field_name>, using L<field_label_map>.
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
index 7e1cac1..2dc7f63 100644 (file)
@@ -10,11 +10,12 @@ use aliased 'Reaction::UI::ViewPort::DisplayField::DateTime';
 use aliased 'Reaction::UI::ViewPort::DisplayField::RelatedObject';
 use aliased 'Reaction::UI::ViewPort::DisplayField::List';
 use aliased 'Reaction::UI::ViewPort::DisplayField::Collection';
+use aliased 'Reaction::InterfaceModel::Object';
+
 
 class ObjectView is 'Reaction::UI::ViewPort', which {
-  has object => (
-    isa => 'Reaction::InterfaceModel::Object', is => 'ro', required => 1
-  );
+  has object         => (isa => Object, is => 'ro', required => 1);
+  has ordered_fields => (is => 'rw', isa => 'ArrayRef', lazy_build => 1);
 
   has _field_map => (
     isa => 'HashRef', is => 'rw', init_arg => 'fields', lazy_build => 1,
@@ -23,7 +24,7 @@ class ObjectView is 'Reaction::UI::ViewPort', which {
   has exclude_fields =>
       ( is => 'rw', isa => 'ArrayRef', required => 1, default => sub{ [] } );
 
-  has ordered_fields => (is => 'rw', isa => 'ArrayRef', lazy_build => 1);
+
 
   implements fields => as { shift->_field_map };
 
diff --git a/lib/Reaction/UI/Widget/GridView.pm b/lib/Reaction/UI/Widget/GridView.pm
new file mode 100644 (file)
index 0000000..bb2f526
--- /dev/null
@@ -0,0 +1,72 @@
+package Reaction::UI::Widget::GridView;
+
+use Reaction::UI::WidgetClass;
+
+class GridView, which {
+  widget renders [ qw/header rows footer/
+                   => { viewport => func('self', 'viewport') }
+                 ];
+
+  header      renders [ 'header_row' ];
+  header_row  renders [ header_cell over func('viewport', 'column_names') ];
+  header_cell renders [ string { $_ } ];
+
+  footer      renders [ 'footer_row' ];
+  footer_row  renders [ footer_cell over func('viewport', 'column_names') ];
+  footer_cell renders [ string { $_ } ];
+
+  rows renders [ viewport over func('viewport','rows') ];
+
+};
+
+1;
+
+
+=for layout widget
+<table>
+  [% header %]
+<tbody>
+  [% rows %]
+</tbody>
+<tfoot>
+  [% footer %]
+</tfoot>
+</table>
+
+=for layout header
+
+<thead>
+  [% content %]
+</thead>
+
+=for layout header_row
+
+<tr>
+  [% content %]
+</tr>
+
+=for layout header_cell
+
+<th> [% content %] </th>
+
+=for layout footer
+
+<tfoot>
+  [% content %]
+</tfoot>
+
+=for layout footer_row
+
+<tr> [% content %] </tr>
+
+=for layout footer_cell
+
+<td> [% content %] </td>
+
+=for layout rows
+
+<tbody>
+  [% content %]
+</tbody>
+
+=cut
diff --git a/lib/Reaction/UI/Widget/GridView/Row.pm b/lib/Reaction/UI/Widget/GridView/Row.pm
new file mode 100644 (file)
index 0000000..8ed46e0
--- /dev/null
@@ -0,0 +1,46 @@
+package Reaction::UI::Widget::ObjectView;
+
+use Reaction::UI::WidgetClass;
+
+class ObjectView, which {
+  widget renders [ cells => { viewport => func('self', 'viewport') } ];
+  cells  renders [ cell over func('viewport', 'ordered_fields')   ];
+  cell   renders [ 'viewport' ];
+};
+
+1;
+
+__END__;
+
+
+=head1 NAME
+
+Reaction::UI::Widget::GridView::Row
+
+=head1 DESCRIPTION
+
+=head1 FRAGMENTS
+
+=head2 widget
+
+Additional variables available in topic hash: "viewport".
+
+Renders "cells"
+
+=head2 cells
+
+Sequentially renders the C<ordered_fields> of the viewport as "cell"
+
+=head2 cell
+
+renders the cell value
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
index 7d9801c..9c525ee 100644 (file)
@@ -1,56 +1,14 @@
 package Reaction::UI::Widget::ListView;
 
 use Reaction::UI::WidgetClass;
-use aliased 'Reaction::UI::ViewPort::ListView' => 'ListView_VP';
 
-class ListView, which {
+class ListView is 'Reaction::UI::Widget::GridView', which {
+#  widget renders [ qw/pager header rows footer/
+#                   => { viewport => func('self', 'viewport') }
+#                 ];
 
-  has 'viewport' => (isa => ListView_VP, is => 'ro', required => 1);
-
-  widget renders [
-    qw(header body) => { viewport => func(self => 'viewport') }
-  ];
-
-  header renders [ header_entry over func(viewport => 'field_names') ];
-
-  header_entry renders [ string { $_{viewport}->field_label_map->{ $_ } } ];
-
-  body renders [ row over func(viewport => 'current_page_collection') ];
-
-  row renders [
-    col_entry over func(viewport => 'field_names') => { row => $_ }
-  ];
-
-  col_entry renders [
-    string {
-      my $proto = $_{row}->$_;
-      if (blessed($proto) && $proto->can('display_name')) {
-        return $proto->display_name;
-      }
-      return "${proto}";
-    }
-  ];
+#  header_cell renders [ string { $_ } ];
 
 };
 
 1;
-
-__END__;
-
-=head1 NAME
-
-Reaction::UI::Widget::ListView
-
-=head1 DESCRIPTION
-
-=head2 viewport
-
-=head1 AUTHORS
-
-See L<Reaction::Class> for authors.
-
-=head1 LICENSE
-
-See L<Reaction::Class> for the license.
-
-=cut
diff --git a/lib/Reaction/UI/Widget/Value.pm b/lib/Reaction/UI/Widget/Value.pm
new file mode 100644 (file)
index 0000000..07db1c0
--- /dev/null
@@ -0,0 +1,40 @@
+package Reaction::UI::Widget::Value;
+
+use Reaction::UI::WidgetClass;
+
+class Value, which {
+  widget renders [ qw/value/ => { viewport => func(self => 'viewport') } ];
+  value  renders [ string { $_{viewport}->value } ];
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value
+
+=head1 DESCRIPTION
+
+=head1 FRAGMENTS
+
+=head2 widget
+
+Additional variables available in topic hash: "viewport".
+
+Renders "label" and "field"
+
+=head2 field
+
+ C<content> will contain the value, if any,  of the field.
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/Boolean.pm b/lib/Reaction/UI/Widget/Value/Boolean.pm
new file mode 100644 (file)
index 0000000..21a0f3f
--- /dev/null
@@ -0,0 +1,35 @@
+package Reaction::UI::Widget::Value::Boolean;
+
+use Reaction::UI::WidgetClass;
+
+class Boolean is 'Reaction::UI::Widget::Value', which {
+  value  renders [ string { $_{viewport}->value_string } ];
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::Boolean
+
+=head1 DESCRIPTION
+
+See L<Reaction::UI::Widget::Value>
+
+=head1 FRAGMENTS
+
+=head2 value
+
+C<content> contains the viewport's value_string
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/Collection.pm b/lib/Reaction/UI/Widget/Value/Collection.pm
new file mode 100644 (file)
index 0000000..073911d
--- /dev/null
@@ -0,0 +1,44 @@
+package Reaction::UI::Widget::Value::Collection;
+
+use Reaction::UI::WidgetClass;
+
+class Collection, which {
+  widget renders [ qw/list/ =>  { viewport => func(self => 'viewport') } ];
+  list   renders [ item over func('viewport', 'value_names') ];
+  item   renders [ string { $_ } ];
+};
+
+1;
+
+__END__;
+
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::Collection
+
+=head1 DESCRIPTION
+
+=head1 FRAGMENTS
+
+=head2 widget
+
+renders C<label> and C<list> passing additional variable "viewport"
+
+=head2 list
+
+renders fragment item over the viewport's C<value_names>
+
+=head2 item
+
+C<content> contains the value of the current item ($_ / $_{_})
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/DateTime.pm b/lib/Reaction/UI/Widget/Value/DateTime.pm
new file mode 100644 (file)
index 0000000..63159c5
--- /dev/null
@@ -0,0 +1,35 @@
+package Reaction::UI::Widget::Value::DateTime;
+
+use Reaction::UI::WidgetClass;
+
+class DateTime is 'Reaction::UI::Widget::Value', which {
+  value renders [ string { $_{viewport}->value_string } ];
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::DateTime
+
+=head1 DESCRIPTION
+
+See L<Reaction::UI::Widget::Value>
+
+=head1 FRAGMENTS
+
+=head2 value
+
+C<content> contains the viewport's value_string
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/List.pm b/lib/Reaction/UI/Widget/Value/List.pm
new file mode 100644 (file)
index 0000000..bd713e4
--- /dev/null
@@ -0,0 +1,43 @@
+package Reaction::UI::Widget::Value::List;
+
+use Reaction::UI::WidgetClass;
+
+class List, which {
+  widget renders [ qw/list item/ =>  { viewport => func(self => 'viewport') } ];
+  list   renders [ item over func('viewport', 'value_names') ];
+  item   renders [ string { $_{_} } ];
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::List
+
+=head1 DESCRIPTION
+
+=head1 FRAGMENTS
+
+=head2 widget
+
+renders C<label> passing additional variable "viewport"
+
+=head2 list
+
+renders fragment item over the viewport's C<value_names>
+
+=head2 item
+
+C<content> contains the value of the current item ($_ / $_{_})
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/Number.pm b/lib/Reaction/UI/Widget/Value/Number.pm
new file mode 100644 (file)
index 0000000..4d895e1
--- /dev/null
@@ -0,0 +1,29 @@
+package Reaction::UI::Widget::Value::Number;
+
+use Reaction::UI::WidgetClass;
+
+class Number is 'Reaction::UI::Widget::Value', which {
+
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::Number
+
+=head1 DESCRIPTION
+
+See L<Reaction::UI::Widget::Value>
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/RelatedObject.pm b/lib/Reaction/UI/Widget/Value/RelatedObject.pm
new file mode 100644 (file)
index 0000000..4617514
--- /dev/null
@@ -0,0 +1,35 @@
+package Reaction::UI::Widget::Value::RelatedObject;
+
+use Reaction::UI::WidgetClass;
+
+class RelatedObject is 'Reaction::UI::Widget::Value', which {
+  value  renders [ string { $_{viewport}->value_string } ];
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::RelatedObject
+
+=head1 DESCRIPTION
+
+See L<Reaction::UI::Widget::Value>
+
+=head1 FRAGMENTS
+
+=head2 value
+
+C<content> contains the viewport's value_string
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/String.pm b/lib/Reaction/UI/Widget/Value/String.pm
new file mode 100644 (file)
index 0000000..366355d
--- /dev/null
@@ -0,0 +1,29 @@
+package Reaction::UI::Widget::Value::String;
+
+use Reaction::UI::WidgetClass;
+
+class String is 'Reaction::UI::Widget::Value', which {
+
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::String
+
+=head1 DESCRIPTION
+
+See L<Reaction::UI::Widget::Value>
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/lib/Reaction/UI/Widget/Value/Text.pm b/lib/Reaction/UI/Widget/Value/Text.pm
new file mode 100644 (file)
index 0000000..0fe4cdd
--- /dev/null
@@ -0,0 +1,29 @@
+package Reaction::UI::Widget::Value::Text;
+
+use Reaction::UI::WidgetClass;
+
+class Text is 'Reaction::UI::Widget::Value', which {
+
+};
+
+1;
+
+__END__;
+
+=head1 NAME
+
+Reaction::UI::Widget::Value::Text
+
+=head1 DESCRIPTION
+
+See L<Reaction::UI::Widget::Value>
+
+=head1 AUTHORS
+
+See L<Reaction::Class> for authors.
+
+=head1 LICENSE
+
+See L<Reaction::Class> for the license.
+
+=cut
diff --git a/share/skin/default/layout/grid_view b/share/skin/default/layout/grid_view
new file mode 100644 (file)
index 0000000..d4826b0
--- /dev/null
@@ -0,0 +1,49 @@
+=for layout widget
+
+<table>
+  [% header %]
+<tbody>
+  [% rows %]
+</tbody>
+<tfoot>
+  [% footer %]
+</tfoot>
+</table>
+
+=for layout header
+
+<thead>
+  [% content %]
+</thead>
+
+=for layout header_row
+
+<tr>
+  [% content %]
+</tr>
+
+=for layout header_cell
+
+<th> [% content %] </th>
+
+=for layout footer
+
+<tfoot>
+  [% content %]
+</tfoot>
+
+=for layout footer_row
+
+<tr> [% content %] </tr>
+
+=for layout footer_cell
+
+<td> [% content %] </td>
+
+=for layout rows
+
+<tbody>
+  [% content %]
+</tbody>
+
+=cut
index e69de29..d4826b0 100644 (file)
@@ -0,0 +1,49 @@
+=for layout widget
+
+<table>
+  [% header %]
+<tbody>
+  [% rows %]
+</tbody>
+<tfoot>
+  [% footer %]
+</tfoot>
+</table>
+
+=for layout header
+
+<thead>
+  [% content %]
+</thead>
+
+=for layout header_row
+
+<tr>
+  [% content %]
+</tr>
+
+=for layout header_cell
+
+<th> [% content %] </th>
+
+=for layout footer
+
+<tfoot>
+  [% content %]
+</tfoot>
+
+=for layout footer_row
+
+<tr> [% content %] </tr>
+
+=for layout footer_cell
+
+<td> [% content %] </td>
+
+=for layout rows
+
+<tbody>
+  [% content %]
+</tbody>
+
+=cut
diff --git a/share/skin/default/layout/value/boolean b/share/skin/default/layout/value/boolean
new file mode 100644 (file)
index 0000000..1ce3367
--- /dev/null
@@ -0,0 +1,9 @@
+=for layout widget
+
+[% content %]
+
+=for layout value
+
+[% content | html %]
+
+=cut
\ No newline at end of file
diff --git a/share/skin/default/layout/value/collection b/share/skin/default/layout/value/collection
new file mode 100644 (file)
index 0000000..d376ecc
--- /dev/null
@@ -0,0 +1,15 @@
+=for layout widget
+
+[% list  %]
+
+=for layout list
+
+<ul>
+[% content %]
+</ul>
+
+=for layout item
+
+<li>[% content | html %]</li>
+
+=cut
diff --git a/share/skin/default/layout/value/date_time b/share/skin/default/layout/value/date_time
new file mode 100644 (file)
index 0000000..e35741c
--- /dev/null
@@ -0,0 +1,9 @@
+=for layout widget
+
+[% content %]
+
+=for layout value
+
+[% content | html %]
+
+=cut
diff --git a/share/skin/default/layout/value/list b/share/skin/default/layout/value/list
new file mode 100644 (file)
index 0000000..eea4e02
--- /dev/null
@@ -0,0 +1,15 @@
+=for layout widget
+
+[% list  %]
+
+=for layout list
+
+<ul>
+[% content %]
+</ul>
+
+=for layout item
+
+<li>[% content | html %]</li>
+
+=cut
\ No newline at end of file
diff --git a/share/skin/default/layout/value/number b/share/skin/default/layout/value/number
new file mode 100644 (file)
index 0000000..e35741c
--- /dev/null
@@ -0,0 +1,9 @@
+=for layout widget
+
+[% content %]
+
+=for layout value
+
+[% content | html %]
+
+=cut
diff --git a/share/skin/default/layout/value/related_object b/share/skin/default/layout/value/related_object
new file mode 100644 (file)
index 0000000..e35741c
--- /dev/null
@@ -0,0 +1,9 @@
+=for layout widget
+
+[% content %]
+
+=for layout value
+
+[% content | html %]
+
+=cut
diff --git a/share/skin/default/layout/value/string b/share/skin/default/layout/value/string
new file mode 100644 (file)
index 0000000..1ce3367
--- /dev/null
@@ -0,0 +1,9 @@
+=for layout widget
+
+[% content %]
+
+=for layout value
+
+[% content | html %]
+
+=cut
\ No newline at end of file
diff --git a/share/skin/default/layout/value/text b/share/skin/default/layout/value/text
new file mode 100644 (file)
index 0000000..1ce3367
--- /dev/null
@@ -0,0 +1,9 @@
+=for layout widget
+
+[% content %]
+
+=for layout value
+
+[% content | html %]
+
+=cut
\ No newline at end of file