File: | lib/Yukki/Web/View/Page.pm |
Coverage: | 35.2% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Yukki::Web::View::Page; | ||||||
2 | |||||||
3 | 1 1 | 3348 10 | use v5.24; | ||||
4 | 1 1 1 | 9 4 19 | use utf8; | ||||
5 | 1 1 1 | 38 4 21 | use Moo; | ||||
6 | |||||||
7 | 1 1 1 | 588 4 20 | use Type::Utils; | ||||
8 | |||||||
9 | 1 1 1 | 1581 2 15 | use namespace::clean; | ||||
10 | |||||||
11 | extends 'Yukki::Web::View'; | ||||||
12 | |||||||
13 | # ABSTRACT: render HTML for viewing and editing wiki pages | ||||||
14 | |||||||
15 - 19 | =head1 DESCRIPTION Renders wiki pages. =cut | ||||||
20 | |||||||
21 | has blank_template => ( | ||||||
22 | is => 'ro', | ||||||
23 | isa => class_type('Template::Pure'), | ||||||
24 | lazy => 1, | ||||||
25 | builder => '_build_blank_template', | ||||||
26 | ); | ||||||
27 | |||||||
28 | sub _build_blank_template { | ||||||
29 | shift->prepare_template( | ||||||
30 | 0 | 0 | template => 'page/blank.html', | ||||
31 | directives => [ | ||||||
32 | '#yukkiname' => 'page', | ||||||
33 | '#create-page@href' => 'link', | ||||||
34 | '#file-list' => 'attachments | encoded_string', | ||||||
35 | ], | ||||||
36 | ); | ||||||
37 | } | ||||||
38 | |||||||
39 | has view_template => ( | ||||||
40 | is => 'ro', | ||||||
41 | isa => class_type('Template::Pure'), | ||||||
42 | lazy => 1, | ||||||
43 | builder => '_build_view_template', | ||||||
44 | ); | ||||||
45 | |||||||
46 | sub _build_view_template { | ||||||
47 | shift->prepare_template( | ||||||
48 | 1 | 28 | template => 'page/view.html', | ||||
49 | directives => [ | ||||||
50 | '#yukkitext' => 'html | encoded_string', | ||||||
51 | ], | ||||||
52 | ); | ||||||
53 | } | ||||||
54 | |||||||
55 | has history_template => ( | ||||||
56 | is => 'ro', | ||||||
57 | isa => class_type('Template::Pure'), | ||||||
58 | lazy => 1, | ||||||
59 | builder => '_build_history_template', | ||||||
60 | ); | ||||||
61 | |||||||
62 | sub _build_history_template { | ||||||
63 | shift->prepare_template( | ||||||
64 | template => 'page/history.html', | ||||||
65 | directives => [ | ||||||
66 | 'form@action' => 'form_action', | ||||||
67 | '.revision' => { | ||||||
68 | 'rev<-revisions' => [ | ||||||
69 | '.first-revision input@value' => 'rev.object_id', | ||||||
70 | '.second-revision input@value' => 'rev.object_id', | ||||||
71 | '.date' => 'rev.time_ago', | ||||||
72 | '.author' => 'rev.author_name', | ||||||
73 | '.diffstat' => '+={rev.lines_added}/-={rev.lines_removed}', | ||||||
74 | '.comment' => 'rev.comment | default("(no comment)")', | ||||||
75 | '.first-revision input' => sub { | ||||||
76 | 0 | 0 | my ($t, $input, $vars) = @_; | ||||
77 | $input->attr(checked => 'checked') | ||||||
78 | 0 | 0 | if $vars->{index} == 2; | ||||
79 | }, | ||||||
80 | '.second-revision input' => sub { | ||||||
81 | 0 | 0 | my ($t, $input, $vars) = @_; | ||||
82 | $input->attr(checked => 'checked') | ||||||
83 | 0 | 0 | if $vars->{index} == 1; | ||||
84 | }, | ||||||
85 | 0 | 0 | ], | ||||
86 | }, | ||||||
87 | ], | ||||||
88 | ); | ||||||
89 | } | ||||||
90 | |||||||
91 | has diff_template => ( | ||||||
92 | is => 'ro', | ||||||
93 | isa => class_type('Template::Pure'), | ||||||
94 | lazy => 1, | ||||||
95 | builder => '_build_diff_template', | ||||||
96 | ); | ||||||
97 | |||||||
98 | sub _build_diff_template { | ||||||
99 | shift->prepare_template( | ||||||
100 | 0 | 0 | template => 'page/diff.html', | ||||
101 | directives => [ | ||||||
102 | '#diff' => 'html | encoded_string', | ||||||
103 | ], | ||||||
104 | ); | ||||||
105 | } | ||||||
106 | |||||||
107 | has edit_template => ( | ||||||
108 | is => 'ro', | ||||||
109 | isa => class_type('Template::Pure'), | ||||||
110 | lazy => 1, | ||||||
111 | builder => '_build_edit_template', | ||||||
112 | ); | ||||||
113 | |||||||
114 | sub _build_edit_template { | ||||||
115 | shift->prepare_template( | ||||||
116 | 0 | 0 | template => 'page/edit.html', | ||||
117 | directives => [ | ||||||
118 | '#yukkiname' => 'page', | ||||||
119 | '#yukkitext' => 'source', | ||||||
120 | '#yukkitext_position@value' => 'position', | ||||||
121 | '#preview-yukkitext' => 'html | encoded_string', | ||||||
122 | '#attachments-list' => 'attachments | encoded_string', | ||||||
123 | ], | ||||||
124 | ); | ||||||
125 | } | ||||||
126 | |||||||
127 | has rename_template => ( | ||||||
128 | is => 'ro', | ||||||
129 | isa => class_type('Template::Pure'), | ||||||
130 | lazy => 1, | ||||||
131 | builder => '_build_rename_template', | ||||||
132 | ); | ||||||
133 | |||||||
134 | sub _build_rename_template { | ||||||
135 | shift->prepare_template( | ||||||
136 | 0 | 0 | template => 'page/rename.html', | ||||
137 | directives => [ | ||||||
138 | '#yukkiname' => 'page', | ||||||
139 | '#yukkiname_new@value' => 'page', | ||||||
140 | ], | ||||||
141 | ); | ||||||
142 | } | ||||||
143 | |||||||
144 | has remove_template => ( | ||||||
145 | is => 'ro', | ||||||
146 | isa => class_type('Template::Pure'), | ||||||
147 | lazy => 1, | ||||||
148 | builder => '_build_remove_template', | ||||||
149 | ); | ||||||
150 | |||||||
151 | sub _build_remove_template { | ||||||
152 | shift->prepare_template( | ||||||
153 | 0 | 0 | template => 'page/remove.html', | ||||
154 | directives => [ | ||||||
155 | '.yukkiname' => 'page', | ||||||
156 | '#cancel_remove@href' => 'return_link', | ||||||
157 | ], | ||||||
158 | ); | ||||||
159 | } | ||||||
160 | |||||||
161 | has attachments_template => ( | ||||||
162 | is => 'ro', | ||||||
163 | isa => class_type('Template::Pure'), | ||||||
164 | lazy => 1, | ||||||
165 | builder => '_build_attachments_template', | ||||||
166 | ); | ||||||
167 | |||||||
168 | sub _build_attachments_template { | ||||||
169 | shift->prepare_template( | ||||||
170 | 0 | 0 | template => 'page/attachments.html', | ||||
171 | directives => [ | ||||||
172 | '.file' => { | ||||||
173 | 'file<-files' => [ | ||||||
174 | '@id' => 'file.file_id', | ||||||
175 | '.filename' => 'file.file_name | encoded_string', | ||||||
176 | '.size' => 'file.file_size', | ||||||
177 | '.action' => 'file.action | encoded_string', | ||||||
178 | ], | ||||||
179 | }, | ||||||
180 | ], | ||||||
181 | ); | ||||||
182 | } | ||||||
183 | |||||||
184 - 191 | =head1 METHODS =head2 blank Renders a page that links to the edit page for this location. This helps you create the links. =cut | ||||||
192 | |||||||
193 | sub blank { | ||||||
194 | 0 | 1 | 0 | my ($self, $ctx, $vars) = @_; | |||
195 | |||||||
196 | 0 | 0 | my $link = "/page/edit/$vars->{repository}/$vars->{page}"; | ||||
197 | |||||||
198 | 0 | 0 | $ctx->response->page_title($vars->{title}); | ||||
199 | 0 | 0 | $ctx->response->breadcrumb($vars->{breadcrumb}); | ||||
200 | |||||||
201 | return $self->render_page( | ||||||
202 | template => $self->blank_template, | ||||||
203 | context => $ctx, | ||||||
204 | vars => { | ||||||
205 | page => $vars->{page}, | ||||||
206 | link => $link, | ||||||
207 | 0 | 0 | attachments => $self->attachments($ctx, $vars->{files}), | ||||
208 | }, | ||||||
209 | ); | ||||||
210 | } | ||||||
211 | |||||||
212 - 216 | =head2 page_navigation Sets up the page navigation menu. =cut | ||||||
217 | |||||||
218 | sub page_navigation { | ||||||
219 | 1 | 1 | 14 | my ($self, $response, $this_action, $vars) = @_; | |||
220 | |||||||
221 | 1 | 4 | for my $action (qw( edit history rename remove )) { | ||||
222 | 4 | 21 | next if $action eq $this_action; | ||||
223 | |||||||
224 | $response->add_navigation_item([ qw( page page_bottom ) ] => { | ||||||
225 | label => ucfirst $action, | ||||||
226 | 4 | 24 | href => join('/', 'page', $action, $vars->{repository}, $vars->{page}), | ||||
227 | sort => 20, | ||||||
228 | }); | ||||||
229 | } | ||||||
230 | |||||||
231 | 1 1 | 7 38 | for my $view_name (keys %{ $self->app->settings->page_views }) { | ||||
232 | 3 | 55 | my $view_info = $self->app->settings->page_views->{$view_name}; | ||||
233 | |||||||
234 | 3 | 22 | next if $view_info->{hide}; | ||||
235 | |||||||
236 | 2 | 5 | my $args = "?view=$view_name"; | ||||
237 | 2 | 6 | $args = '' if $view_name eq 'default'; | ||||
238 | |||||||
239 | $response->add_navigation_item([ qw( page page_bottom ) ] => { | ||||||
240 | label => $view_info->{label}, | ||||||
241 | href => join('/', 'page/view', $vars->{repository}, $vars->{page}) | ||||||
242 | . $args, | ||||||
243 | sort => $view_info->{sort}, | ||||||
244 | 2 | 13 | }); | ||||
245 | } | ||||||
246 | } | ||||||
247 | |||||||
248 - 252 | =head2 view Renders a page as a view. =cut | ||||||
253 | |||||||
254 | sub view { | ||||||
255 | 1 | 1 | 6 | my ($self, $ctx, $vars) = @_; | |||
256 | 1 | 5 | my $file = $vars->{file}; | ||||
257 | |||||||
258 | 1 | 54 | $ctx->response->page_title($vars->{title}); | ||||
259 | 1 | 80 | $ctx->response->breadcrumb($vars->{breadcrumb}); | ||||
260 | |||||||
261 | 1 | 77 | my $html = $file->fetch_formatted($ctx, -1); | ||||
262 | |||||||
263 | 1 | 26 | $self->page_navigation($ctx->response, 'view', $vars); | ||||
264 | |||||||
265 | 1 | 23 | return $self->render_page( | ||||
266 | template => $self->view_template, | ||||||
267 | context => $ctx, | ||||||
268 | vars => { | ||||||
269 | 'html' => $html, | ||||||
270 | }, | ||||||
271 | ); | ||||||
272 | } | ||||||
273 | |||||||
274 - 278 | =head2 history Display the history for a page. =cut | ||||||
279 | |||||||
280 | sub history { | ||||||
281 | 0 | 1 | my ($self, $ctx, $vars) = @_; | ||||
282 | |||||||
283 | 0 | $ctx->response->page_title($vars->{title}); | |||||
284 | 0 | $ctx->response->breadcrumb($vars->{breadcrumb}); | |||||
285 | |||||||
286 | 0 | $self->page_navigation($ctx->response, 'history', $vars); | |||||
287 | |||||||
288 | 0 | my $i = 0; | |||||
289 | return $self->render_page( | ||||||
290 | template => $self->history_template, | ||||||
291 | context => $ctx, | ||||||
292 | vars => { | ||||||
293 | 'form_action' => join('/', '/page/diff', $vars->{repository}, $vars->{page}), | ||||||
294 | 'revisions' => $vars->{revisions}, | ||||||
295 | }, | ||||||
296 | 0 | ); | |||||
297 | } | ||||||
298 | |||||||
299 - 303 | =head2 diff Display a diff for a file. =cut | ||||||
304 | |||||||
305 | sub diff { | ||||||
306 | 0 | 1 | my ($self, $ctx, $vars) = @_; | ||||
307 | 0 | my $file = $vars->{file}; | |||||
308 | |||||||
309 | 0 | $ctx->response->page_title($vars->{title}); | |||||
310 | 0 | $ctx->response->breadcrumb($vars->{breadcrumb}); | |||||
311 | |||||||
312 | 0 | $self->page_navigation($ctx->response, 'diff', $vars); | |||||
313 | |||||||
314 | 0 | my $html = $file->fetch_formatted($ctx); | |||||
315 | |||||||
316 | 0 | return $self->render_page( | |||||
317 | template => $self->diff_template, | ||||||
318 | context => $ctx, | ||||||
319 | vars => { | ||||||
320 | html => $html, | ||||||
321 | }, | ||||||
322 | ); | ||||||
323 | } | ||||||
324 | |||||||
325 - 329 | =head2 edit Renders the editor for a page. =cut | ||||||
330 | |||||||
331 | sub edit { | ||||||
332 | 0 | 1 | my ($self, $ctx, $vars) = @_; | ||||
333 | 0 | my $file = $vars->{file}; | |||||
334 | |||||||
335 | 0 | $ctx->response->page_title($vars->{title}); | |||||
336 | 0 | $ctx->response->breadcrumb($vars->{breadcrumb}); | |||||
337 | |||||||
338 | 0 | my $html = $file->fetch_formatted($ctx, $vars->{position}); | |||||
339 | |||||||
340 | 0 | $self->page_navigation($ctx->response, 'edit', $vars); | |||||
341 | |||||||
342 | return $self->render_page( | ||||||
343 | template => $self->edit_template, | ||||||
344 | context => $ctx, | ||||||
345 | vars => { | ||||||
346 | page => $vars->{page}, | ||||||
347 | source => scalar $vars->{file}->fetch // '', | ||||||
348 | position => $vars->{position}, | ||||||
349 | html => $html, | ||||||
350 | 0 | attachments => $self->attachments($ctx, $vars->{attachments}), | |||||
351 | }, | ||||||
352 | ); | ||||||
353 | } | ||||||
354 | |||||||
355 - 359 | =head2 rename Renders the rename form for a page. =cut | ||||||
360 | |||||||
361 | sub rename { | ||||||
362 | 0 | 1 | my ($self, $ctx, $vars) = @_; | ||||
363 | 0 | my $file = $vars->{file}; | |||||
364 | |||||||
365 | 0 | $ctx->response->page_title($vars->{title}); | |||||
366 | 0 | $ctx->response->breadcrumb($vars->{breadcrumb}); | |||||
367 | |||||||
368 | $self->page_navigation($ctx->response, 'rename', $vars) | ||||||
369 | 0 | unless $ctx->request->path_parameters->{file}; | |||||
370 | |||||||
371 | return $self->render_page( | ||||||
372 | template => $self->rename_template, | ||||||
373 | context => $ctx, | ||||||
374 | vars => { | ||||||
375 | page => $vars->{page}, | ||||||
376 | }, | ||||||
377 | 0 | ); | |||||
378 | } | ||||||
379 | |||||||
380 - 384 | =head2 remove Renders the remove confirmation page. =cut | ||||||
385 | |||||||
386 | sub remove { | ||||||
387 | 0 | 1 | my ($self, $ctx, $vars) = @_; | ||||
388 | 0 | my $file = $vars->{file}; | |||||
389 | |||||||
390 | 0 | $ctx->response->page_title($vars->{title}); | |||||
391 | 0 | $ctx->response->breadcrumb($vars->{breadcrumb}); | |||||
392 | |||||||
393 | $self->page_navigation($ctx->response, 'remove', $vars) | ||||||
394 | 0 | unless $ctx->request->path_parameters->{file}; | |||||
395 | |||||||
396 | return $self->render_page( | ||||||
397 | template => $self->remove_template, | ||||||
398 | context => $ctx, | ||||||
399 | vars => { | ||||||
400 | page => $vars->{page}, | ||||||
401 | return_link => $vars->{return_link}, | ||||||
402 | }, | ||||||
403 | 0 | ); | |||||
404 | } | ||||||
405 | |||||||
406 - 410 | =head2 attachments Renders the attachments table. =cut | ||||||
411 | |||||||
412 | sub attachments { | ||||||
413 | 0 | 1 | my ($self, $ctx, $attachments) = @_; | ||||
414 | |||||||
415 | return $self->render( | ||||||
416 | template => $self->attachments_template, | ||||||
417 | context => $ctx, | ||||||
418 | vars => { | ||||||
419 | files => @$attachments ? [ map { | ||||||
420 | 0 0 | my @links = $self->attachment_links($ctx, $_); | |||||
421 | |||||||
422 | 0 0 | my %primary_link = %{ $links[0] }; | |||||
423 | 0 | $primary_link{label} = $_->file_name; | |||||
424 | |||||||
425 | 0 | my $file_name = $self->render_links( | |||||
426 | context => $ctx, | ||||||
427 | links => [ \%primary_link ], | ||||||
428 | ); | ||||||
429 | |||||||
430 | { | ||||||
431 | 0 | file_id => $_->file_id, | |||||
432 | file_name => $file_name, | ||||||
433 | file_size => $_->formatted_file_size, | ||||||
434 | action => $self->render_attachment_links($ctx, \@links), | ||||||
435 | } | ||||||
436 | } @$attachments ] : undef, | ||||||
437 | }, | ||||||
438 | ); | ||||||
439 | } | ||||||
440 | |||||||
441 - 443 | =head2 attachment_links =cut | ||||||
444 | |||||||
445 | sub attachment_links { | ||||||
446 | 0 | 1 | my ($self, $ctx, $attachment) = @_; | ||||
447 | |||||||
448 | 0 | my @links; | |||||
449 | |||||||
450 | 0 | if ($attachment->has_format) { | |||||
451 | 0 | push @links, { | |||||
452 | label => 'View', | ||||||
453 | href => join('/', 'page', 'view', | ||||||
454 | $attachment->repository_name, | ||||||
455 | $attachment->full_path), | ||||||
456 | }; | ||||||
457 | } | ||||||
458 | else { | ||||||
459 | 0 | push @links, { | |||||
460 | label => 'View', | ||||||
461 | href => join('/', 'attachment', 'view', | ||||||
462 | $attachment->repository_name, | ||||||
463 | $attachment->full_path), | ||||||
464 | } if $attachment->media_type ne 'application/octet'; | ||||||
465 | |||||||
466 | 0 | push @links, { | |||||
467 | label => 'Download', | ||||||
468 | href => join('/', 'attachment', 'download', | ||||||
469 | $attachment->repository_name, | ||||||
470 | $attachment->full_path), | ||||||
471 | }; | ||||||
472 | } | ||||||
473 | |||||||
474 | 0 | push @links, { | |||||
475 | label => 'Rename', | ||||||
476 | href => join('/', 'attachment', 'rename', | ||||||
477 | $attachment->repository_name, | ||||||
478 | $attachment->full_path), | ||||||
479 | }; | ||||||
480 | |||||||
481 | 0 | push @links, { | |||||
482 | label => 'Remove', | ||||||
483 | href => join('/', 'attachment', 'remove', | ||||||
484 | $attachment->repository_name, | ||||||
485 | $attachment->full_path), | ||||||
486 | }; | ||||||
487 | |||||||
488 | 0 | return @links; | |||||
489 | } | ||||||
490 | |||||||
491 - 495 | =head2 render_attachment_links Renders the links listed in the action column of the attachments table. =cut | ||||||
496 | |||||||
497 | sub render_attachment_links { | ||||||
498 | 0 | 1 | my ($self, $ctx, $links) = @_; | ||||
499 | 0 | return $self->render_links(context => $ctx, links => $links); | |||||
500 | } | ||||||
501 | |||||||
502 - 506 | =head2 preview Renders a preview of an edit in progress. =cut | ||||||
507 | |||||||
508 | sub preview { | ||||||
509 | 0 | 1 | my ($self, $ctx, $vars) = @_; | ||||
510 | 0 | my $file = $vars->{file}; | |||||
511 | |||||||
512 | 0 | my $html = $file->fetch_formatted($ctx); | |||||
513 | |||||||
514 | 0 | return $html; | |||||
515 | } | ||||||
516 | |||||||
517 | 1; |