0
|
1 use strict;
|
|
2
|
|
3 package DOM::Providers::Page;
|
|
4 use Template::Provider;
|
|
5 #use PerfCounter;
|
|
6 use DOM::Page;
|
|
7 use Common;
|
|
8 use Encode;
|
|
9
|
|
10 our @ISA= qw(Object Exporter);
|
|
11
|
|
12 our $UseIndexPage; #optional
|
|
13 our $PagesPath; #required
|
|
14 our $IncludesPath; #optional
|
|
15 our $CacheSize; #optional
|
|
16 our $CachePath; #optional
|
|
17 our $Encoding; #optional
|
|
18 our $AllowExtPath; #optional
|
|
19 our $PageResolver; #optional
|
|
20
|
|
21
|
|
22 BEGIN {
|
|
23 DeclareProperty('PageResolver');
|
|
24 DeclareProperty('PagesBase');
|
|
25 DeclareProperty('IndexPage');
|
|
26 DeclareProperty('TemplatesProvider');
|
|
27 DeclareProperty('PageEnc');
|
|
28 }
|
|
29
|
|
30 sub as_list {
|
|
31 return( map { UNIVERSAL::isa($_,'ARRAY') ? @{$_} : defined $_ ? $_ : () } @_ );
|
|
32 }
|
|
33
|
|
34 sub GetProviderInfo {
|
|
35 return {
|
|
36 Name => 'Page',
|
|
37 Host => 'DOM::Site',
|
|
38 Methods => {
|
|
39 LoadPage => \&SiteLoadPage,
|
|
40 ReleasePage => \&SiteReleasePage,
|
|
41 }
|
|
42 }
|
|
43 }
|
|
44
|
|
45 sub CTOR {
|
|
46 my ($this,%args) = @_;
|
|
47
|
|
48 $this->{$PageResolver} = $args{'PageResolver'};
|
|
49 $this->{$PagesBase} = $args{'TemplatesPath'};
|
|
50 $this->{$IndexPage} = $args{'IndexPage'} || 'index.html';
|
|
51 $this->{$PageEnc} = $args{'Encoding'};
|
|
52 $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);
|
|
53 }
|
|
54
|
|
55 sub ResolveId {
|
|
56 my ($this,$pageId) = @_;
|
|
57
|
|
58 if ($this->{$PageResolver} && UNIVERSAL::can($this->{$PageResolver},'ResolveId')) {
|
|
59 return $this->{$PageResolver}->ResolveId($pageId);
|
|
60 } else {
|
|
61 return grep { $_ } split /\//,$pageId;
|
|
62 }
|
|
63 }
|
|
64
|
|
65 sub MakePageId {
|
|
66 my ($this,$raPath) = @_;
|
|
67
|
|
68 if ($this->{$PageResolver} && UNIVERSAL::can($this->{$PageResolver},'MakeId')) {
|
|
69 return $this->{$PageResolver}->MakeId($raPath);
|
|
70 } else {
|
|
71 return join '/',@$raPath;
|
|
72 }
|
|
73 }
|
|
74
|
|
75 sub PageIdToURL {
|
|
76 my ($this,$pageId) = @_;
|
|
77
|
|
78 if ($this->{$PageResolver} && UNIVERSAL::can($this->{$PageResolver},'PageIdToURL')) {
|
|
79 return $this->{$PageResolver}->PageIdToURL($pageId);
|
|
80 } else {
|
|
81 return '/'.$pageId;
|
|
82 }
|
|
83 }
|
|
84
|
|
85 sub SiteLoadPage {
|
|
86 my ($this,$site,$pageId) = @_;
|
|
87
|
|
88 return $site->RegisterObject('Page', $this->LoadPage($pageId, Site => $site));
|
|
89 }
|
|
90 sub LoadPage {
|
|
91 my ($this,$pageId,%args) = @_;
|
|
92
|
|
93 #StartTimeCounter('LoadPageTime');
|
|
94
|
|
95 my @pathPage = $this->ResolveId($pageId);
|
|
96
|
|
97 my $pageNode = $this->LoadNode(\@pathPage);
|
|
98
|
|
99 pop @pathPage;
|
|
100
|
|
101 my @pathNode;
|
|
102
|
|
103 # ,
|
|
104 my @NavChain = map { push @pathNode, $_; $this->LoadNode(\@pathNode); } ('.',@pathPage);
|
|
105
|
|
106 if ($pageNode->{'Type'} eq 'Section') {
|
|
107 push @NavChain,$pageNode;
|
|
108 $pageNode = $this->LoadNode($pageNode->{'pathIndexPage'});
|
|
109 }
|
|
110
|
|
111 #
|
|
112 my %PageMenus;
|
|
113 foreach my $MenuSet (map { $_->{'Menus'}} @NavChain, $pageNode->{'Menus'} ) {
|
|
114 foreach my $menuName (keys %$MenuSet) {
|
|
115 if ($PageMenus{$menuName}) {
|
|
116 $PageMenus{$menuName}->Merge($MenuSet->{$menuName});
|
|
117 } else {
|
|
118 $PageMenus{$menuName} = $MenuSet->{$menuName};
|
|
119 }
|
|
120 }
|
|
121 }
|
|
122
|
|
123 #
|
|
124 my @keywords;
|
|
125 my %Props;
|
|
126 foreach my $PropSet ( (map { $_->{'Props'}} @NavChain), $pageNode->{'Props'} ) {
|
|
127 if(ref $PropSet->{'Keywords'} eq 'ARRAY') {
|
|
128 push @keywords, @{$PropSet->{'Keywords'}};
|
|
129 } elsif (not ref $PropSet->{'Keywords'} and exists $PropSet->{'Keywords'}) {
|
|
130 push @keywords, $PropSet->{'Keywords'};
|
|
131 }
|
|
132
|
|
133 while (my ($prop,$value) = each %$PropSet) {
|
|
134 next if $prop eq 'Keywords';
|
|
135 $Props{$prop} = $value;
|
|
136 }
|
|
137 }
|
|
138
|
|
139 #StopTimeCounter('LoadPageTime');
|
|
140 #
|
|
141
|
|
142 #StartTimeCounter('FetchTime');
|
|
143 my ($Template,$error) = $this->{$TemplatesProvider}->fetch($pageNode->{'TemplateFileName'});
|
|
144 die new Exception("Failed to load page $pageId",$Template ? $Template->as_string : 'Failed to parse') if $error;
|
|
145 #StopTimeCounter('FetchTime');
|
|
146
|
|
147 my $page = new DOM::Page(TemplatesProvider => $this->{$TemplatesProvider}, Properties => \%Props, Menus => \%PageMenus, NavChain => \@NavChain, Template => $Template, %args);
|
|
148 $page->Properties->{url} = $this->PageIdToURL($pageId);
|
|
149 return $page;
|
|
150 }
|
|
151
|
|
152 sub LoadNode {
|
|
153 my ($this,$refNodePath) = @_;
|
|
154
|
|
155 my $fileNameNode = $this->{$PagesBase} . join('/',grep $_, @$refNodePath);
|
|
156 my $fileNameMenus;
|
|
157 my $fileNameProps;
|
|
158
|
|
159 my %Node;
|
|
160
|
|
161 if ( -d $fileNameNode ) {
|
|
162 $Node{'Type'} = 'Section';
|
|
163 $fileNameMenus = $fileNameNode . '/.menu.pl';
|
|
164 $fileNameProps = $fileNameNode . '/.prop.pl';
|
|
165 } elsif ( -e $fileNameNode ) {
|
|
166 $Node{'Type'} = 'Page';
|
|
167 $Node{'TemplateFileName'} = join('/',@$refNodePath);;
|
|
168 $fileNameMenus = $fileNameNode . '.menu.pl';
|
|
169 $fileNameProps = $fileNameNode . '.prop.pl';
|
|
170 } else {
|
|
171 die new Exception("Page not found: $fileNameNode");
|
|
172 }
|
|
173
|
|
174 if ( -f $fileNameProps ) {
|
|
175 local ${^ENCODING};
|
|
176 my $dummy = '';
|
|
177 open my $hnull,'>>',\$dummy;
|
|
178 local (*STDOUT,*STDIN) = ($hnull,$hnull);
|
|
179 $Node{'Props'} = do $fileNameProps or warn "can't parse $fileNameProps: $@";
|
|
180 }
|
|
181
|
|
182 if ( -f $fileNameMenus ) {
|
|
183 local ${^ENCODING};
|
|
184 my $dummy = '';
|
|
185 open my $hnull,'>>',\$dummy;
|
|
186 local (*STDOUT,*STDIN) = ($hnull,$hnull);
|
|
187 $Node{'Menus'} = do $fileNameMenus or warn "can't parse $fileNameMenus: $@";
|
|
188 }
|
|
189
|
|
190 if ($Node{'Menus'}) {
|
|
191 my %Menus;
|
|
192 foreach my $menu (keys %{$Node{'Menus'}}) {
|
|
193 $Menus{$menu} = new DOM::PageMenu( DATA => $Node{'Menus'}->{$menu} );
|
|
194 }
|
|
195 $Node{'Menus'} = \%Menus;
|
|
196 }
|
|
197
|
|
198 $Node{'pathIndexPage'} = [@$refNodePath, $Node{'Props'}->{'IndexPage'} || $this->{$IndexPage}] if $Node{'Type'} eq 'Section';
|
|
199
|
|
200 return \%Node;
|
|
201 }
|
|
202
|
|
203 sub SiteReleasePage {
|
|
204 my ($this,$site) = @_;
|
|
205
|
|
206 my $page = $site->Objects()->{'Page'};
|
|
207 $page->Release() if $page;
|
|
208
|
|
209 return 1;
|
|
210 }
|
|
211
|
|
212 sub construct {
|
|
213 my $self = shift;
|
|
214
|
|
215 return new DOM::Providers::Page(TemplatesPath => $PagesPath, IncludePath => $IncludesPath, IndexPage => $UseIndexPage, CachePath => $CachePath, CacheSize => $CacheSize, Encoding => $Encoding);
|
|
216 }
|
|
217
|
|
218 sub DecodeData {
|
|
219 my ($Encoding, $data) = @_;
|
|
220
|
|
221 if (ref $data) {
|
|
222 if (ref $data eq 'SCALAR') {
|
|
223 my $decoded = Encode::decode($Encoding,$$data,Encode::LEAVE_SRC);
|
|
224 return \$decoded;
|
|
225 } elsif (UNIVERSAL::isa($data, 'HASH')) {
|
|
226 return {map {Encode::decode($Encoding,$_,Encode::LEAVE_SRC),DecodeData($Encoding,$data->{$_})} keys %$data };
|
|
227 } elsif (UNIVERSAL::isa($data, 'ARRAY')) {
|
|
228 return [map {DecodeData($Encoding,$_)} @$data];
|
|
229 } elsif (ref $data eq 'REF') {
|
|
230 my $decoded = DecodeData($Encoding,$$data);
|
|
231 return \$decoded;
|
|
232 } else {
|
|
233 die new Exception('Cant decode data type', ref $data);
|
|
234 }
|
|
235 } else {
|
|
236 return Encode::decode($Encoding,$data,Encode::LEAVE_SRC);
|
|
237 }
|
|
238 }
|
|
239
|
|
240 1;
|
|
241
|
|
242 =pod
|
|
243 .
|
|
244
|
|
245 ,
|
|
246
|
|
247 Keywords
|
|
248 Name
|
|
249 IndexPage
|
|
250
|
|
251 ,
|
|
252
|
|
253 ,
|
|
254 ,
|
|
255 keywords ,
|
|
256 ,
|
|
257
|
|
258 =cut |