3 use App::IdiotBox::Common qw(@SupportedFormats);
4 use Web::Simple __PACKAGE__;
7 use HTML::Zoom::FilterBuilder::Template;
8 use List::Util qw(first);
10 use App::IdiotBox::Announcement;
11 use App::IdiotBox::Bucket;
12 use App::IdiotBox::Video;
14 has $_ => (is => 'ro') for qw(recent_announcements buckets);
17 template_dir => 'share/html',
19 db_file => 'var/lib/idiotbox.db',
20 base_url => 'http://localhost:3000/',
21 base_dir => do { use FindBin; $FindBin::Bin },
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);
35 sub dispatch_request {
37 sub (/) { $self->show_front_page },
40 sub (%new_name=&new_slug=) {
41 my ($self, $name, $slug) = @_;
43 my ($nb, $err) = App::IdiotBox::Bucket->create(
48 return $self->show_admin_page(error => $err) if $err;
50 my $nb = $self->buckets->add($nb);
52 $self->show_admin_page;
55 sub (/admin/) { $self->show_admin_page },
58 my $bucket = $self->buckets->get({ slug => $_[1] });
60 my ($self, $new_name) = @_;
62 my ($ub, $err) = $bucket->update(
66 return $self->show_admin_page(error => $err) if $err;
68 $self->buckets->replace($bucket, $ub);
70 $self->show_bucket_edited_page($bucket);
73 $self->show_edit_bucket_page($bucket);
76 $self->show_confirm_delete_bucket_page($bucket)
79 $self->buckets->remove({ slug => $bucket->slug });
80 $self->show_bucket_deleted_page($bucket->slug);
84 my $bucket = $self->buckets->get({ slug => $_[1] });
86 $self->show_bucket($bucket)
89 $self->show_video($bucket->videos->get({ slug => $_[1] }));
96 my $ann = $self->recent_announcements;
99 $_->select('#announcement-list')
100 ->repeat_content($ann->map(sub {
103 $_->select('.bucket-name')->replace_content($obj->bucket->name)
104 ->select('.made-at')->replace_content($obj->made_at)
105 ->select('.bucket-link')->set_attribute(
106 'href' => $obj->bucket->slug.'/'
108 ->select('.new-videos')->replace_content($obj->video_count)
109 ->select('.total-videos')->replace_content(
110 $obj->bucket->video_count
118 sub show_admin_page {
121 my $error = $opts{error} || '';
123 my $bucket = $self->buckets;
124 $self->html_response(
126 $_->select('#bucket-list')
127 ->repeat_content($bucket->map(sub {
130 $_->select('.bucket-slug')->replace_content($obj->slug)
131 ->select('.bucket-name')->replace_content($obj->name)
132 ->select('.edit-link')->set_attribute(
133 'href' => $obj->slug.'/'
135 ->select('.delete-link')->set_attribute(
136 'href' => $obj->slug.'/delete/'
140 ->select('.error-text')->replace_content($error)
147 sub show_confirm_delete_bucket_page {
148 my ($self, $bucket) = @_;
149 $self->html_response('delete' => sub {
150 $_->select('.bucket-name')->replace_content($bucket->name)
151 ->select('.confirm-yes')->set_attribute(
157 sub show_edit_bucket_page {
158 my ($self, $bucket, %opt) = @_;
159 my $error = $opt{error} || '';
160 $self->html_response('edit' => sub {
161 $_->select('.bucket-name')->replace_content($bucket->name)
162 ->select('.error-text')->replace_content($error);
166 sub show_bucket_deleted_page {
167 my ($self, $name) = @_;
168 $self->html_response('deleted' => sub {
169 $_->select('.bucket-name')->replace_content($name)
173 sub show_bucket_edited_page {
174 my ($self, $name) = @_;
175 $self->html_response('edited' => sub {
176 $_->select('.bucket-name')->replace_content($name)
181 my ($self, $bucket) = @_;
182 $self->html_response(bucket => sub {
183 $_->select('.bucket-name')->replace_content($bucket->name)
184 ->select('#video-list')->repeat_content($bucket->videos->map(sub {
187 $_->select('.video-name')->replace_content($video->name)
188 ->select('.video-author')->replace_content($video->author)
189 ->select('.video-link')->set_attribute(
190 href => $video->slug.'/'
198 my ($self, $video) = @_;
199 my $video_file = first {
200 -e join('/', $self->config->{base_dir}, $_)
202 join('/', $video->bucket->slug, $video->slug, $video->file_name.".$_")
204 $self->html_response(video => sub {
207 .($video_file||'NO FILE FOUND SORRY');
209 $_->select('.video-name')->replace_content($video->name)
210 ->select('.author-name')->replace_content($video->author)
211 ->select('.bucket-link')->set_attribute(
214 ->select('.bucket-name')->replace_content($video->bucket->name)
215 ->select('.video-details')->replace_content($video->details)
216 ->select('script')->template_text_raw({ video_url => $video_url });
221 my ($self, $template_name, $selectors) = @_;
222 my $io = $self->_zoom_for($template_name => $selectors)->to_fh;
223 return [ 200, [ 'Content-Type' => 'text/html' ], $io ]
226 sub _template_filename_for {
227 my ($self, $name) = @_;
228 $self->{config}{template_dir}.'/'.$name.'.html';
233 $self->{layout_zoom} ||= HTML::Zoom->from_file(
234 $self->_template_filename_for('layout')
239 my ($self, $template_name, $selectors) = @_;
240 ($self->{zoom_for_template}{$template_name} ||= do {
242 HTML::Zoom->from_file(
243 $self->_template_filename_for($template_name)
245 ->select('#main-content')->collect_content({ into => \@body })
248 ->select('#main-content')->replace_content(\@body)
250 })->apply($selectors);
255 $self->{base_url} ||= do {
256 (my $u = $self->config->{base_url}) =~ s/\/$//;
263 unless (@ARGV == 1 && $ARGV[0] eq 'import') {
264 return $self->SUPER::_run_cli(@_);
271 "To import data into your idiotbox install, chdir into a directory\n".
272 "containing video files and run:\n".
276 $self->SUPER::_cli_usage(@_);
281 require App::IdiotBox::Importer;
282 App::IdiotBox::Importer->run($self);