sidebar filter
[scpubgit/SCS.git] / lib / SCSite.pm
1 package SCSite;
2
3 use IO::All;
4 use SCSite::PageSet;
5 use Web::Simple;
6
7 has pages => (is => 'lazy');
8
9 has filters => (is => 'lazy');
10
11 has _layout_zoom => (is => 'lazy');
12
13 sub default_config {
14   (
15     pages_dir => 'share/content',
16     template_dir => 'share/skin',
17   )
18 }
19
20 sub _build_pages {
21   my ($self) = @_;
22   SCSite::PageSet->new(base_dir => io->dir($self->config->{pages_dir}))
23 }
24
25 sub _build_filters {
26   my ($self) = @_;
27   require SCSite::SubListFilter;
28   require SCSite::SidebarFilter;
29   +{
30     map +($_ => "SCSite::${_}Filter"->new_from_site($self)),
31       qw(SubList Sidebar)
32   }
33 }
34
35 sub dispatch_request {
36   my $self = shift;
37   sub (/feed/**/) {
38     $self->_http_response(500 => 'text/plain' => 'Not implemented');
39   },
40   sub (/) {
41     $self->_page_http_response(200 => $self->_find_page('index'));
42   },
43   sub (/**) {
44     [ 302, [ 'Location' => "/$_[1]/" ], [] ]
45   },
46   sub (/**/) {
47     my ($code, $page) = map {
48       $_ ? (200, $_) : (404, $self->_error_page(404))
49     } $self->_find_page($_[1]);
50     $self->_page_http_response($code => $page);
51   }
52 }
53
54 sub _error_page {
55   my ($self, $code) = @_;
56   $self->_find_page("error_${code}");
57 }
58
59 sub _find_page {
60   my ($self, $path) = @_;
61   $self->pages->get({ path => $path });
62 }
63
64 sub _http_response {
65   my ($self, $code, $type, $content) = @_;
66   [ $code, [ 'Content-type' => $type ], [ $content ] ];
67 }
68
69 sub _page_http_response {
70   my ($self, $code, $page) = @_;
71   [ $code, [ 'Content-type' => 'text/html' ], $self->_render_page($page) ];
72 }
73
74 sub _render_page {
75   my ($self, $page) = @_;
76   my $zoom = $self->_layout_zoom;
77   my %filters = %{$self->filters};
78   $zoom->select('.page.title')->replace_content($page->title)
79        ->select('meta[name=description]')->replace_content($page->description)
80        ->select('meta[name=keywords]')->replace_content($page->keywords)
81        ->select('.main')->replace_content(\$page->body)
82        ->apply(sub {
83            foreach my $fname (sort keys %filters) {
84              my $cb = $filters{$fname}->callback_for($page);
85              $_ = $_->select(".${fname}")->collect({
86                         filter => $cb, passthrough => 1
87                       });
88            }
89            $_
90          })
91        ->to_fh
92 }
93
94 sub _build__layout_zoom {
95   my ($self) = @_;
96   HTML::Zoom->from_file(
97     io->dir($self->config->{template_dir})->catfile('layout.html')
98   )->memoize;
99 }
100
101 sub run_if_script {
102   return $_[0]->to_psgi_app if caller(1);
103   my $class = shift;
104   my @config_keys = keys %{{$class->default_config}};
105   require Getopt::Long;
106   my %config;
107   Getopt::Long::GetOptions(
108     map +("$_=s" => \$config{$_}), @config_keys
109   );
110   delete $config{$_} for grep !defined($config{$_}), keys %config;
111   my $new = $class->new(config => \%config);
112   $new->run(@_)
113 }
114
115 __PACKAGE__->run_if_script;