7a1786d51e8137724b0685abe8a67975e230be3a
[scpubgit/App-SCS.git] / lib / App / SCS / Page.pm
1 package App::SCS::Page;
2
3 use IO::All;
4 use Time::Local qw(timelocal);
5 use Data::Pond qw(pond_read_datum pond_write_datum);
6 use List::Util qw(reduce);
7 use Module::Runtime qw(use_module);
8 use HTML::Zoom;
9 use Moo;
10
11 has "_$_" => (is => 'ro', init_arg => $_) for qw(page_set);
12
13 sub _page_set_class { ref($_[0]->_page_set) }
14 sub _top_dir { $_[0]->_page_set->top_dir }
15 sub _my_path { io->dir($_[0]->_top_dir)->catdir($_[0]->path) }
16
17 with 'App::SCS::Role::PageChildren';
18
19 has $_ => (is => 'ro') for qw(
20   title subtitle description keywords plugins html created path
21 );
22
23 has plugin_config => (is => 'lazy');
24
25 sub _build_plugin_config {
26   my ($self) = @_;
27
28   my $pluginref = $self->plugins
29     ? pond_read_datum('[ '.$self->plugins.' ]')
30     : [];
31
32   my @dirs = io->dir($self->path)->splitdir;
33   my $path = '';
34
35   foreach my $dir (@dirs) {
36       $path .= "/$dir";
37       #/home/.../share/pages/blog/config.pond etc
38
39       my $file = $self->_top_dir . "$path/config.pond";
40
41       next if !-f $file;
42
43       next if io($file)->empty;
44
45       my $content = io($file)->slurp;
46
47       my $config = pond_read_datum($content);
48       push @$pluginref, @{$config->{plugins}};
49   }
50
51
52   return $pluginref;
53 }
54
55 sub has_plugin_config { exists $_[0]->plugin_config->{$_[1]} }
56
57 sub with_plugin_config {
58   my ($self, $with_name, $with_config) = @_;
59   my @orig = @{$self->plugin_config};
60   my @new;
61   while (my ($name, $config) = splice @orig, 0, 2) {
62     push @new, (
63       $name eq $with_name
64         ? ($name, { %$config, %$with_config })
65         : ($name, $config)
66     );
67   }
68   return $self->with(plugins => pond_write_datum(\@new));
69 }
70
71 has _raw_page_plugins => (is => 'lazy', builder => sub {
72   my ($self) = @_;
73   my $plugin_config = $self->plugin_config;
74   my ($plugin_map, $defaults) = @{$self->_page_set->plugin_config}
75                                  {qw(plugin_map defaults)};
76   my @spec = (@$defaults, @$plugin_config);
77   my @plugins;
78   while (my ($name, $config) = splice @spec, 0, 2) {
79     my $info = $plugin_map->{$name};
80     push @plugins,
81       use_module($info->{class})->new(
82         ($info->{config}||sub{})->(), %$config, page => $self,
83         plugin_map => $plugin_map, # some things will need this
84       );
85   }
86   return \@plugins;
87 });
88
89 has _page_plugins => (is => 'lazy', builder => sub {
90   my ($self) = @_;
91   my $raw = $self->_raw_page_plugins;
92   reduce { $b->filter_plugins($a) } $raw, @$raw;
93 });
94
95 sub published_at {
96   $_[0]->created
97     ? scalar localtime timelocal
98         map +(@{$_}[0..3], $_->[4]-1, $_->[5]-1900),
99           [ reverse split '\D+', $_[0]->created ]
100     : ''
101 }
102
103 sub to_app {
104   my ($self) = @_;
105   return sub { $self->to_psgi_response(@_) };
106 }
107
108 sub to_psgi_response {
109   my ($self, $env) = @_;
110
111   if (my $cb = $env->{'App::SCS::Command::Generate.extra_pages'}) {
112     $cb->($_->extra_pages) for @{$self->page_plugins};
113   }
114
115   $self->_psgi_response;
116 }
117
118 has _psgi_response => (is => 'lazy');
119
120 sub _build__psgi_response {
121   my ($self) = @_;
122
123   my $psgi_res = [
124     200, [ 'Content-type' => 'text/html' ], $self->_content_zoom->to_fh
125   ];
126
127   return reduce {
128     $b->filter_psgi_response($a)
129   } $psgi_res, @{$self->_page_plugins};
130 }
131
132 sub _content_zoom {
133   my ($self) = @_;
134   return reduce {
135     $b->filter_content_zoom($a)
136   } $self->_html_zoom, @{$self->_page_plugins};
137 }
138
139 sub _html_zoom {
140   my ($self) = @_;
141   return reduce {
142     $b->filter_html_zoom($a)
143   } HTML::Zoom->from_html($self->html), @{$self->_page_plugins};
144 }
145
146 sub body {
147   my ($self) = @_;
148   HTML::Zoom->from_html($self->html)
149             ->collect(body => { into => \my @ev })
150             ->run;
151   HTML::Zoom->from_events(\@ev)->to_html;
152 }
153
154 no Moo;
155
156 sub with {
157   my $self = shift;
158   return ref($self)->new(%$self, @_);
159 }
160
161 1;