view Lib/DOM/Page.pm @ 59:0f3e369553bd

Rewritten property implementation (probably become slower but more flexible) Configuration infrastructure in progress (in the aspect of the lazy activation) Initial concept for the code generator
author wizard
date Tue, 09 Mar 2010 02:50:45 +0300
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;