| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- package Trog::Renderer;
- use v5.36;
- use re '/aa';
- use Carp::Always;
- use Trog::Vars;
- use Trog::Log qw{:all};
- use Trog::Renderer::text;
- use Trog::Renderer::html;
- use Trog::Renderer::json;
- use Trog::Renderer::blob;
- use Trog::Renderer::css;
- use Trog::Renderer::email;
- =head1 Trog::Renderer
- Idea here is to have a renderer per known/supported content-type we need to output that is also theme-aware.
- We have an abstraction here, render() which you feed everything to.
- =cut;
- our %renderers = (
- text => \&Trog::Renderer::text::render,
- html => \&Trog::Renderer::html::render,
- json => \&Trog::Renderer::json::render,
- blob => \&Trog::Renderer::blob::render,
- xsl => \&Trog::Renderer::text::render,
- xml => \&Trog::Renderer::text::render,
- rss => \&Trog::Renderer::html::render,
- css => \&Trog::Renderer::css::render,
- email => \&Trog::Renderer::email::render,
- );
- =head2 Trog::Renderer->render(%options)
- Returns either the 3-arg arrayref suitable to emit at the end of a PSGI session or a response body if the component field of options is truthy.
- The idea is that components will be concatenated to other rendered templates until we finish having everything ready.
- =cut
- sub render ( $class, %options ) {
- local $@;
- my $renderer;
- return _yeet( $renderer, "Renderer requires a valid content type to be passed", %options ) unless $options{contenttype};
- my $rendertype = $Trog::Vars::byct{ $options{contenttype} };
- return _yeet( $renderer, "Renderer requires a known content type (used $options{contenttype}) to be passed", %options ) unless $rendertype;
- $renderer = $renderers{$rendertype};
- return _yeet( $renderer, "Renderer for $rendertype is not defined!", %options ) unless $renderer;
- return _yeet( $renderer, "Status code not provided", %options ) if !$options{code} && !$options{component};
- return _yeet( $renderer, "Template data not provided", %options ) unless $options{data};
- return _yeet( $renderer, "Template not provided", %options ) unless $options{template};
- #TODO future - save the components too and then compose them?
- my $data = $options{data};
- # $data->{nocache} as well as $options{nocache}: TCMS::build_routes puts a
- # route's nocache flag on the query, and exactly one caller (login) has ever
- # forwarded it here as an option. Every other nocache route was relying on
- # being auth-gated, so that the $data->{user} test below caught it -- which
- # says nothing about a route that is nocache and public, and there is now
- # one: a datasource whose posts go stale asks for it this way.
- my $skip_save =
- $options{component}
- || $options{nocache}
- || $data->{nocache}
- || !$data->{route}
- || $data->{has_query}
- || $data->{user}
- || ( $options{code} // 0 ) != 200
- || Trog::Log::is_debug();
- my $ret;
- local $@;
- eval {
- $ret = $renderer->(%options);
- if ( exists $data->{tpsgi} && !$skip_save ) {
- $data->{tpsgi}->add_post_close_callback(
- sub {
- # We invalidate these in the individual routes themselves.
- $data->{tpsgi}->save_render( $data->{route}, $rendertype, $ret->[2][0] );
- }
- );
- }
- 1;
- } or do {
- return _yeet( $renderer, $@, %options );
- };
- return $ret;
- }
- sub _yeet ( $renderer, $error, %options ) {
- WARN($error);
- # All-else fails error page
- my $ret;
- local $@;
- eval {
- $ret = $renderer->(
- code => 500,
- template => '500.tx',
- contenttype => 'text/html',
- data => { %options, content => "<h1>500 Internal Server Error</h1>$error" },
- );
- 1;
- } or do {
- my $msg = $error;
- $msg .= " and subsequently during render of error template, $@" if $renderer;
- #XXX bytes is probably not correct here
- INFO( "$options{data}{method} 500 " . length($msg) . " $options{data}{route}" );
- FATAL($msg);
- };
- return $ret;
- }
- 1;
|