Mercurial > pub > Impl
view Lib/DOM/Providers/Page.pm @ 71:d92d5ddaf524
docs
author | wizard |
---|---|
date | Thu, 25 Mar 2010 13:05:18 +0300 |
parents | 16ada169ca75 |
children |
line wrap: on
line source
use strict; package DOM::Providers::Page; use Template::Provider; #use PerfCounter; use DOM::Page; use Common; use Encode; our @ISA= qw(Object Exporter); our $UseIndexPage; #optional our $PagesPath; #required our $IncludesPath; #optional our $CacheSize; #optional our $CachePath; #optional our $Encoding; #optional our $AllowExtPath; #optional our $PageResolver; #optional BEGIN { DeclareProperty('PageResolver'); DeclareProperty('PagesBase'); DeclareProperty('IndexPage'); DeclareProperty('TemplatesProvider'); DeclareProperty('PageEnc'); } sub as_list { return( map { UNIVERSAL::isa($_,'ARRAY') ? @{$_} : defined $_ ? $_ : () } @_ ); } sub GetProviderInfo { return { Name => 'Page', Host => 'DOM::Site', Methods => { LoadPage => \&SiteLoadPage, ReleasePage => \&SiteReleasePage, } } } sub CTOR { my ($this,%args) = @_; $this->{$PageResolver} = $args{'PageResolver'}; $this->{$PagesBase} = $args{'TemplatesPath'}; $this->{$IndexPage} = $args{'IndexPage'} || 'index.html'; $this->{$PageEnc} = $args{'Encoding'}; $this->{$TemplatesProvider} = new Template::Provider( INCLUDE_PATH => [$this->{$PagesBase}, as_list($args{'IncludePath'}) ], COMPILE_DIR => $args{'CachePath'}, CACHE_SIZE => $args{'CacheSize'}, ENCODING => $args{'Encoding'}, ABSOLUTE => $AllowExtPath, RELATIVE => $AllowExtPath, INTERPOLATE => 1, PRE_CHOMP => 3); } sub ResolveId { my ($this,$pageId) = @_; if ($this->{$PageResolver} && UNIVERSAL::can($this->{$PageResolver},'ResolveId')) { return $this->{$PageResolver}->ResolveId($pageId); } else { return grep { $_ } split /\//,$pageId; } } sub MakePageId { my ($this,$raPath) = @_; if ($this->{$PageResolver} && UNIVERSAL::can($this->{$PageResolver},'MakeId')) { return $this->{$PageResolver}->MakeId($raPath); } else { return join '/',@$raPath; } } sub PageIdToURL { my ($this,$pageId) = @_; if ($this->{$PageResolver} && UNIVERSAL::can($this->{$PageResolver},'PageIdToURL')) { return $this->{$PageResolver}->PageIdToURL($pageId); } else { return '/'.$pageId; } } sub SiteLoadPage { my ($this,$site,$pageId) = @_; return $site->RegisterObject('Page', $this->LoadPage($pageId, Site => $site)); } sub LoadPage { my ($this,$pageId,%args) = @_; #StartTimeCounter('LoadPageTime'); my @pathPage = $this->ResolveId($pageId); my $pageNode = $this->LoadNode(\@pathPage); pop @pathPage; my @pathNode; # поскольку путь указан относительно корневого контейнера, то нужно его добавить в начало my @NavChain = map { push @pathNode, $_; $this->LoadNode(\@pathNode); } ('.',@pathPage); if ($pageNode->{'Type'} eq 'Section') { push @NavChain,$pageNode; $pageNode = $this->LoadNode($pageNode->{'pathIndexPage'}); } # формируем меню страницы my %PageMenus; foreach my $MenuSet (map { $_->{'Menus'}} @NavChain, $pageNode->{'Menus'} ) { foreach my $menuName (keys %$MenuSet) { if ($PageMenus{$menuName}) { $PageMenus{$menuName}->Merge($MenuSet->{$menuName}); } else { $PageMenus{$menuName} = $MenuSet->{$menuName}; } } } # формируем ключевые слова и свойства my @keywords; my %Props; foreach my $PropSet ( (map { $_->{'Props'}} @NavChain), $pageNode->{'Props'} ) { if(ref $PropSet->{'Keywords'} eq 'ARRAY') { push @keywords, @{$PropSet->{'Keywords'}}; } elsif (not ref $PropSet->{'Keywords'} and exists $PropSet->{'Keywords'}) { push @keywords, $PropSet->{'Keywords'}; } while (my ($prop,$value) = each %$PropSet) { next if $prop eq 'Keywords'; $Props{$prop} = $value; } } #StopTimeCounter('LoadPageTime'); # загружаем шаблон #StartTimeCounter('FetchTime'); my ($Template,$error) = $this->{$TemplatesProvider}->fetch($pageNode->{'TemplateFileName'}); die new Exception("Failed to load page $pageId",$Template ? $Template->as_string : 'Failed to parse') if $error; #StopTimeCounter('FetchTime'); my $page = new DOM::Page(TemplatesProvider => $this->{$TemplatesProvider}, Properties => \%Props, Menus => \%PageMenus, NavChain => \@NavChain, Template => $Template, %args); $page->Properties->{url} = $this->PageIdToURL($pageId); return $page; } sub LoadNode { my ($this,$refNodePath) = @_; my $fileNameNode = $this->{$PagesBase} . join('/',grep $_, @$refNodePath); my $fileNameMenus; my $fileNameProps; my %Node; if ( -d $fileNameNode ) { $Node{'Type'} = 'Section'; $fileNameMenus = $fileNameNode . '/.menu.pl'; $fileNameProps = $fileNameNode . '/.prop.pl'; } elsif ( -e $fileNameNode ) { $Node{'Type'} = 'Page'; $Node{'TemplateFileName'} = join('/',@$refNodePath);; $fileNameMenus = $fileNameNode . '.menu.pl'; $fileNameProps = $fileNameNode . '.prop.pl'; } else { die new Exception("Page not found: $fileNameNode"); } if ( -f $fileNameProps ) { local ${^ENCODING}; my $dummy = ''; open my $hnull,'>>',\$dummy; local (*STDOUT,*STDIN) = ($hnull,$hnull); $Node{'Props'} = do $fileNameProps or warn "can't parse $fileNameProps: $@"; } if ( -f $fileNameMenus ) { local ${^ENCODING}; my $dummy = ''; open my $hnull,'>>',\$dummy; local (*STDOUT,*STDIN) = ($hnull,$hnull); $Node{'Menus'} = do $fileNameMenus or warn "can't parse $fileNameMenus: $@"; } if ($Node{'Menus'}) { my %Menus; foreach my $menu (keys %{$Node{'Menus'}}) { $Menus{$menu} = new DOM::PageMenu( DATA => $Node{'Menus'}->{$menu} ); } $Node{'Menus'} = \%Menus; } $Node{'pathIndexPage'} = [@$refNodePath, $Node{'Props'}->{'IndexPage'} || $this->{$IndexPage}] if $Node{'Type'} eq 'Section'; return \%Node; } sub SiteReleasePage { my ($this,$site) = @_; my $page = $site->Objects()->{'Page'}; $page->Release() if $page; return 1; } sub construct { my $self = shift; return new DOM::Providers::Page(TemplatesPath => $PagesPath, IncludePath => $IncludesPath, IndexPage => $UseIndexPage, CachePath => $CachePath, CacheSize => $CacheSize, Encoding => $Encoding); } sub DecodeData { my ($Encoding, $data) = @_; if (ref $data) { if (ref $data eq 'SCALAR') { my $decoded = Encode::decode($Encoding,$$data,Encode::LEAVE_SRC); return \$decoded; } elsif (UNIVERSAL::isa($data, 'HASH')) { return {map {Encode::decode($Encoding,$_,Encode::LEAVE_SRC),DecodeData($Encoding,$data->{$_})} keys %$data }; } elsif (UNIVERSAL::isa($data, 'ARRAY')) { return [map {DecodeData($Encoding,$_)} @$data]; } elsif (ref $data eq 'REF') { my $decoded = DecodeData($Encoding,$$data); return \$decoded; } else { die new Exception('Cant decode data type', ref $data); } } else { return Encode::decode($Encoding,$data,Encode::LEAVE_SRC); } } 1; =pod Хранилище шаблонов на основе файловой системы. Хранилище состоит из разделов, каждый раздел имеет набор свойств и меню Специальны свойства разделов Keywords Ключевые слова Name Название IndexPage страница по умолчанию В разделах находятся страницы, каждая страница имеет набор свойств и меню При загрузке страницы полностью загружаются все родительские контейнеры, При этом одноименные меню сливаются, Свойства keywords объеъединяются, Если имя страницы не задано, то используется имя раздела =cut