view Lib/DOM/Page.pm @ 104:196bf443b5e1

DOM::Schema RC0 inflators support, validation and some other things, Minor and major fixes almost for everything. A 'Source' property of the ValidationErrors generated from a NodeSet or a NodeList is subject to change in the future.
author wizard
date Tue, 11 May 2010 02:42:59 +0400
parents 16ada169ca75
children
line wrap: on
line source

package DOM::Page;
use Common;
use Template::Context;
use strict;

our @ISA = qw(Object);
our $AUTOLOAD;

BEGIN {
    DeclareProperty(Title => ACCESS_ALL);
    DeclareProperty(NavChain => ACCESS_READ);
    DeclareProperty(Menus => ACCESS_READ);
    DeclareProperty(Properties => ACCESS_READ);
    DeclareProperty(Template => ACCESS_READ);
    DeclareProperty(TemplatesProvider => ACCESS_NONE);
    DeclareProperty(Site => ACCESS_READ);
}

sub CTOR {
    my ($this,%args) = @_;
    $this->{$Site} = $args{'Site'};
    $this->{$TemplatesProvider} = $args{'TemplatesProvider'};
    $this->{$Properties} = $args{'Properties'} || {};
    $this->{$Title} = $args{'Template'}->Title() || $args{'Properties'}->{'Title'};
    $this->{$Template} =  $args{'Template'};
    $this->{$NavChain} = $args{'NavChain'};
    $this->{$Menus} = $args{'Menus'};
}

sub Render {
    my ($this,$hOut) = @_;
    
    my $context = new Template::Context({
        VARIABLES => $this->{$Site}->Objects(),
        LOAD_TEMPLATES => $this->{$TemplatesProvider}
    });
    
    print $hOut $this->{$Template}->process($context);
}

sub Dispose {
    my ($this) = @_;
    
    undef %$this;
    
    $this->SUPER::Dispose;
}

sub Container {
    my ($this) = @_;
    my $nav = $this->{$NavChain};
    return $nav->[@{$nav}-1];
}

sub AUTOLOAD {
    my $this = shift;
    
    my $name = $AUTOLOAD;
    $name =~ s/.*://;
    
    return $this->{$Properties}->{$name};
}

=pod
Меню
    [
        Элемент меню
            {
                Key => Ключ пункта меню, для быстрого обращения к элементу и слиянии меню
                Name => Имя пункта меню, которое будет видель пользователь
                Expand => флаг того, что меню выбрано
                Value => {[ элемент меню ...] | что-то еще, обычно урл}
            }
    ]
=cut

package DOM::PageMenu;
use Common;

our @ISA = qw(Object);

BEGIN {
    DeclareProperty('Items'); # массив
    DeclareProperty('Keys'); # ключи для пунктов меню, если таковые имеются
}

sub CTOR {
    my ($this,%args) = @_;
    if (ref $args{'DATA'} eq 'ARRAY') {
        foreach my $item (@{$args{'DATA'}}) {
            if (ref $item eq 'HASH') {
                $this->Append($item->{'Name'},_ProcessData($item->{'Value'}), Expand => $item->{'Expand'}, Key => $item->{'Key'}, Url => $item->{'Url'});
            } elsif (ref $item eq 'ARRAY') {
                $this->Append($item->[0],_ProcessData($item->[1]), Expand => $item->[2], Key => $item->[3], Url => $item->[4]);
            }
        }
    }
}

sub Item {
    my ($this,$index) = @_;
    
    return $this->{$Items}[$index];
}

sub ItemByKey {
    my ($this,$key) = @_;
    
    return $this->{$Keys}->{$key};
}

sub InsertBefore {
    my ($this,$index,$name,$data,%options) = @_;
    
    my $item = {Name => $name, Value => _ProcessData($data), %options};
    splice @{$this->{$Items}},$index,0,$item;
    
    if ($options{'Key'}) {
        $this->{$Keys}->{$options{'Key'}} = $item;
    }
}

sub Append {
    my ($this,$name,$data,%options) = @_;
    
    my $item = {Name => $name, Value => _ProcessData($data), %options};
    
    push @{$this->{$Items}},$item;
    
    if ($options{'Key'}) {
        $this->{$Keys}->{$options{'Key'}} = $item;
    }
}

sub SubMenu {
    my ($this,$path) = @_;
    my $item = $this;
    foreach my $key ( split /\/+/,$path ) {
        $item = $item->{$Keys}->{$key};
        if (not $item ) {
            die new Exception('Item does\'t exist', $path, $key);
        }
        $item = $item->{Value};
        if (not UNIVERSAL::isa($item,'DOM::PageMenu')) {
            $item = ($this->{$Keys}->{$key}->{Value} = new DOM::PageMenu());
        }
    }
    
    return $item;
}

sub Dump {
    use Data::Dumper;
    
    return Dumper(shift);
}

sub AppendItem {
    my ($this,$item) = @_;
    
    push @{$this->{$Items}},$item;
    
    if ($item->{'Key'}) {
        $this->{$Keys}->{$item->{'Key'}} = $item;
    }
}

sub RemoveAt {
    my ($this,$index) = @_;
    
    my $item = splice @{$this->{$Items}},$index,1;
    
    if ($item->{'Key'}) {
        delete $this->{$Keys}->{$item->{'Key'}};
    }
    
    return 1;
}

sub ItemsCount {
    my $this = shift;
    return scalar(@{$this->{$Items}});
}

sub Sort {
    my $this = shift;
    
    $this->{$Items} = \sort { $a->{'Name'} <=> $b->{'Name'} } @{$this->{$Items}};
    
    return 1;
}

sub as_list {
    my $this = shift;
    return $this->{$Items} || [];
}

sub Merge {
    my ($this,$that) = @_;
    
    foreach my $itemThat ($that->Items) {
        my $itemThis = $itemThat->{'Key'} ? $this->{$Keys}->{$itemThat->{'Key'}} : undef;
        if ($itemThis) {
            $this->MergeItems($itemThis,$itemThat);
        } else {
            $this->AppendItem($itemThat);
        }
    }
}

sub MergeItems {
    my ($this,$itemLeft,$itemRight) = @_;
    
    while (my ($prop,$value) = each %{$itemRight}) {
        if ($prop eq 'Value') {
            if (UNIVERSAL::isa($itemLeft->{$prop},__PACKAGE__) && UNIVERSAL::isa($value,__PACKAGE__)) {
                $itemLeft->{$prop}->Merge($value);
            } else {
                $itemLeft->{$prop} = $value if defined $value;
            }
        } else {
            $itemLeft->{$prop} = $value if defined $value;
        }
    }
    
    return 1;
}

sub _ProcessData {
    my $refData = shift;
    
    if (ref $refData eq 'ARRAY') {
        return new DOM::PageMenu(DATA => $refData);
    } else {
        return $refData;
    }
}



1;