Minor fixes to make updating actually work.
[catagits/App-IdiotBox.git] / lib / App / IdiotBox.pm
CommitLineData
e30ed59d 1package App::IdiotBox;
2
625f105e 3use App::IdiotBox::Common qw(@SupportedFormats);
e30ed59d 4use Web::Simple __PACKAGE__;
d7497a23 5use FindBin;
6use HTML::Zoom;
998cc52c 7use HTML::Zoom::FilterBuilder::Template;
02ea620e 8use List::Util qw(first);
d7497a23 9
f29e9b6f 10use App::IdiotBox::Announcement;
11use App::IdiotBox::Bucket;
12use App::IdiotBox::Video;
d7497a23 13
362a8766 14has $_ => (is => 'ro') for qw(recent_announcements buckets);
15
8b9d3d54 16sub default_config {
71a02d85 17 template_dir => 'share/html',
18 store => 'SQLite',
19 db_file => 'var/lib/idiotbox.db',
998cc52c 20 base_url => 'http://localhost:3000/',
ebba317f 21 base_dir => do { use FindBin; $FindBin::Bin },
8b9d3d54 22}
e30ed59d 23
71a02d85 24sub BUILD {
25 my $self = shift;
26 my $store;
27 ($store = $self->config->{store}) =~ /^(\w+)$/
28 or die "Store config should be just a name, got ${store} instead";
29 my $store_class = "App::IdiotBox::Store::${store}";
30 eval "require ${store_class}; 1"
31 or die "Couldn't load ${store} store: $@";
32 $store_class->bind($self);
33}
34
8b9d3d54 35sub dispatch_request {
36 my $self = shift;
e30ed59d 37 sub (/) { $self->show_front_page },
f29e9b6f 38
2da7f57f 39 # Admin
26b4958e 40 sub (/admin/) {
41 sub (%new_name=&new_slug=) {
42 my ($self, $name, $slug) = @_;
43
f29e9b6f 44 my ($nb, $err) = App::IdiotBox::Bucket->create(
26b4958e 45 slug => $slug,
46 name => $name,
f29e9b6f 47 );
48
2da7f57f 49 return $self->show_admin_page(message => $err) if $err;
f29e9b6f 50
51 my $nb = $self->buckets->add($nb);
26b4958e 52
2da7f57f 53 $self->show_admin_page(message => "New bucket created");
26b4958e 54 },
55 },
56 sub (/admin/) { $self->show_admin_page },
57
2da7f57f 58 sub (/admin/bucket/*/...) {
26b4958e 59 my $bucket = $self->buckets->get({ slug => $_[1] });
f29e9b6f 60
2da7f57f 61 # Admin, video
62 sub (/video/*/...) {
63 my $video = $bucket->videos->get({ slug => $_[1] });
64
65 sub (POST + %edit_video=&*) {
66 my ($self, undef, $args) = @_;
67 delete $args->{edit_video};
68
69 my ($uv, $err) = $video->update(%$args);
70
71 return $self->show_edit_video_page($video, message => $err) if $err;
72
56640bce 73 $bucket->videos->replace($video, $uv);
2da7f57f 74
75 $self->show_edit_video_page($video, message => "Video updated");
76 },
77
78 sub (/) { $self->show_edit_video_page($video); }
79 },
80
81 sub (POST + %edit_bucket=&*) {
56640bce 82 my ($self, undef, $args) = @_;
83 delete $args->{edit_bucket};
84
85 my ($ub, $err) = $bucket->update(%$args);
f29e9b6f 86
2da7f57f 87 return $self->show_admin_page(message => $err) if $err;
f29e9b6f 88
89 $self->buckets->replace($bucket, $ub);
90
26b4958e 91 $self->show_bucket_edited_page($bucket);
92 },
93 sub (/) {
94 $self->show_edit_bucket_page($bucket);
95 },
96 sub (/delete/) {
97 $self->show_confirm_delete_bucket_page($bucket)
98 },
99 sub (/delete/yes/) {
100 $self->buckets->remove({ slug => $bucket->slug });
101 $self->show_bucket_deleted_page($bucket->slug);
102 },
103 },
2da7f57f 104
105 # Normal site
8b9d3d54 106 sub (/*/...) {
e30ed59d 107 my $bucket = $self->buckets->get({ slug => $_[1] });
8b9d3d54 108 sub (/) {
109 $self->show_bucket($bucket)
110 },
111 sub (/*/) {
112 $self->show_video($bucket->videos->get({ slug => $_[1] }));
113 }
e30ed59d 114 }
8b9d3d54 115}
e30ed59d 116
847de56a 117sub show_front_page {
118 my $self = shift;
e30ed59d 119 my $ann = $self->recent_announcements;
120 $self->html_response(
1a1c4f78 121 front_page => sub {
122 $_->select('#announcement-list')
123 ->repeat_content($ann->map(sub {
124 my $obj = $_;
125 sub {
126 $_->select('.bucket-name')->replace_content($obj->bucket->name)
02ea620e 127 ->select('.made-at')->replace_content($obj->made_at)
fb836c4b 128 ->select('.bucket-link')->set_attribute(
129 'href' => $obj->bucket->slug.'/'
130 )
1a1c4f78 131 ->select('.new-videos')->replace_content($obj->video_count)
132 ->select('.total-videos')->replace_content(
133 $obj->bucket->video_count
134 )
135 }
136 }))
137 }
e30ed59d 138 );
139}
140
26b4958e 141sub show_admin_page {
142 my $self = shift;
143 my %opts = @_;
2da7f57f 144 my $message = $opts{message} || '';
26b4958e 145
146 my $bucket = $self->buckets;
147 $self->html_response(
148 admin => sub {
149 $_->select('#bucket-list')
150 ->repeat_content($bucket->map(sub {
151 my $obj = $_;
152 sub {
153 $_->select('.bucket-slug')->replace_content($obj->slug)
154 ->select('.bucket-name')->replace_content($obj->name)
155 ->select('.edit-link')->set_attribute(
2da7f57f 156 'href' => 'bucket/'.$obj->slug.'/'
26b4958e 157 )
158 ->select('.delete-link')->set_attribute(
2da7f57f 159 'href' => 'bucket/'.$obj->slug.'/delete/'
26b4958e 160 )
161 }
162 }))
2da7f57f 163 ->select('.message-text')->replace_content($message)
26b4958e 164
165 }
166
167 );
168}
169
170sub show_confirm_delete_bucket_page {
171 my ($self, $bucket) = @_;
2da7f57f 172 $self->html_response('delete_bucket' => sub {
26b4958e 173 $_->select('.bucket-name')->replace_content($bucket->name)
174 ->select('.confirm-yes')->set_attribute(
175 'href' => 'yes/'
176 )
177 });
178}
179
180sub show_edit_bucket_page {
181 my ($self, $bucket, %opt) = @_;
2da7f57f 182 my $message = $opt{message} || '';
183 $self->html_response('edit_bucket' => sub {
26b4958e 184 $_->select('.bucket-name')->replace_content($bucket->name)
2da7f57f 185 ->select('.bucket-slug')->replace_content($bucket->slug)
186 ->select('.message-text')->replace_content($message)
187 ->select('#video-list')->repeat_content($bucket->videos->map(sub {
188 my $video = $_;
189 sub {
190 $_->select('.video-name')->replace_content($video->name)
191 ->select('.video-author')->replace_content($video->author)
192 ->select('.video-link')->set_attribute(
193 href => 'video/'.$video->slug.'/'
194 )
195 }
196 }))
197
198 });
199}
200
201sub show_edit_video_page {
202 my ($self, $video, %opt) = @_;
203 my $message = $opt{message} || '';
204 $self->html_response('edit_video' => sub {
205 $_->select('.video-name')->set_attribute(value => $video->name)
206 ->select('.author-name')->set_attribute(value => $video->author)
207 ->select('.bucket-slug')->replace_content($video->bucket->slug)
208 ->select('.slug-name')->replace_content($video->slug)
209 ->select('.message-text')->replace_content($message)
26b4958e 210 });
211}
212
213sub show_bucket_deleted_page {
214 my ($self, $name) = @_;
2da7f57f 215 $self->html_response('deleted_bucket' => sub {
26b4958e 216 $_->select('.bucket-name')->replace_content($name)
217 });
218}
219
220sub show_bucket_edited_page {
221 my ($self, $name) = @_;
2da7f57f 222 $self->html_response('edited_bucket' => sub {
26b4958e 223 $_->select('.bucket-name')->replace_content($name)
224 });
225}
226
847de56a 227sub show_bucket {
228 my ($self, $bucket) = @_;
1a1c4f78 229 $self->html_response(bucket => sub {
230 $_->select('.bucket-name')->replace_content($bucket->name)
231 ->select('#video-list')->repeat_content($bucket->videos->map(sub {
232 my $video = $_;
233 sub {
234 $_->select('.video-name')->replace_content($video->name)
235 ->select('.video-author')->replace_content($video->author)
236 ->select('.video-link')->set_attribute(
fb836c4b 237 href => $video->slug.'/'
1a1c4f78 238 )
239 }
240 }))
241 });
d7497a23 242}
243
847de56a 244sub show_video {
245 my ($self, $video) = @_;
02ea620e 246 my $video_file = first {
247 -e join('/', $self->config->{base_dir}, $_)
248 } map {
249 join('/', $video->bucket->slug, $video->slug, $video->file_name.".$_")
625f105e 250 } @SupportedFormats;
1a1c4f78 251 $self->html_response(video => sub {
998cc52c 252 my $video_url =
253 $self->base_url
6df05090 254 .($video_file||'NO FILE FOUND SORRY');
998cc52c 255
1a1c4f78 256 $_->select('.video-name')->replace_content($video->name)
257 ->select('.author-name')->replace_content($video->author)
258 ->select('.bucket-link')->set_attribute(
fb836c4b 259 href => '../'
1a1c4f78 260 )
261 ->select('.bucket-name')->replace_content($video->bucket->name)
262 ->select('.video-details')->replace_content($video->details)
998cc52c 263 ->select('script')->template_text_raw({ video_url => $video_url });
1a1c4f78 264 });
d7497a23 265}
266
847de56a 267sub html_response {
268 my ($self, $template_name, $selectors) = @_;
1a1c4f78 269 my $io = $self->_zoom_for($template_name => $selectors)->to_fh;
e30ed59d 270 return [ 200, [ 'Content-Type' => 'text/html' ], $io ]
271}
272
847de56a 273sub _template_filename_for {
274 my ($self, $name) = @_;
d7497a23 275 $self->{config}{template_dir}.'/'.$name.'.html';
276}
277
847de56a 278sub _layout_zoom {
279 my $self = shift;
d7497a23 280 $self->{layout_zoom} ||= HTML::Zoom->from_file(
281 $self->_template_filename_for('layout')
e30ed59d 282 )
283}
284
847de56a 285sub _zoom_for {
286 my ($self, $template_name, $selectors) = @_;
e30ed59d 287 ($self->{zoom_for_template}{$template_name} ||= do {
288 my @body;
d7497a23 289 HTML::Zoom->from_file(
290 $self->_template_filename_for($template_name)
e30ed59d 291 )
1a1c4f78 292 ->select('#main-content')->collect_content({ into => \@body })
d7497a23 293 ->run;
1a1c4f78 294 $self->_layout_zoom
295 ->select('#main-content')->replace_content(\@body)
296 ->memoize;
297 })->apply($selectors);
e30ed59d 298}
299
847de56a 300sub base_url {
301 my $self = shift;
998cc52c 302 $self->{base_url} ||= do {
303 (my $u = $self->config->{base_url}) =~ s/\/$//;
304 "${u}/";
305 }
306}
307
847de56a 308sub _run_cli {
309 my $self = shift;
ebba317f 310 unless (@ARGV == 1 && $ARGV[0] eq 'import') {
311 return $self->SUPER::_run_cli(@_);
312 }
313 $self->cli_import;
314}
315
847de56a 316sub _cli_usage {
317 my $self = shift;
ebba317f 318 "To import data into your idiotbox install, chdir into a directory\n".
319 "containing video files and run:\n".
320 "\n".
321 " $0 import\n".
322 "\n".
323 $self->SUPER::_cli_usage(@_);
324}
325
847de56a 326sub cli_import {
327 my $self = shift;
ebba317f 328 require App::IdiotBox::Importer;
329 App::IdiotBox::Importer->run($self);
330}
331
e30ed59d 3321;