410 lines
10 KiB
Perl
410 lines
10 KiB
Perl
#!/usr/bin/perl
|
|
#
|
|
# DW::Template
|
|
#
|
|
# Template Toolkit helpers for Apache2.
|
|
#
|
|
# Authors:
|
|
# Andrea Nall <anall@andreanall.com>
|
|
#
|
|
# Copyright (c) 2009-2013 by Dreamwidth Studios, LLC.
|
|
#
|
|
# This program is free software; you may redistribute it and/or modify it under
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
|
#
|
|
|
|
package DW::Template;
|
|
use strict;
|
|
use Template;
|
|
use Template::Plugins;
|
|
use Template::Namespace::Constants;
|
|
use DW::FragmentCache;
|
|
use DW::Request;
|
|
use LJ::Directories;
|
|
|
|
=head1 NAME
|
|
|
|
DW::Template - Template Toolkit helpers for Apache2.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
=cut
|
|
|
|
# setting this to false -- have to explicitly specify which plugins we want.
|
|
$Template::Plugins::PLUGIN_BASE = '';
|
|
|
|
my $site_constants = Template::Namespace::Constants->new(
|
|
{
|
|
name => $LJ::SITENAME,
|
|
nameshort => $LJ::SITENAMESHORT,
|
|
nameabbrev => $LJ::SITENAMEABBREV,
|
|
|
|
company => $LJ::SITECOMPANY,
|
|
address => $LJ::SITEADDRESS,
|
|
addressline => $LJ::SITEADDRESSLINE,
|
|
|
|
domain => $LJ::DOMAIN,
|
|
domainweb => $LJ::DOMAIN_WEB,
|
|
|
|
help => \%LJ::HELPURL,
|
|
|
|
email => {
|
|
abuse => $LJ::ABUSE_EMAIL,
|
|
coppa => $LJ::COPPA_EMAIL,
|
|
privacy => $LJ::PRIVACY_EMAIL,
|
|
},
|
|
|
|
maxlength_user => $LJ::USERNAME_MAXLENGTH,
|
|
maxlength_pass => $LJ::PASSWORD_MAXLENGTH,
|
|
}
|
|
);
|
|
|
|
# precreating this
|
|
my $view_engine = Template->new(
|
|
{
|
|
INCLUDE_PATH => join( ':', LJ::get_all_directories('views') ),
|
|
NAMESPACE => {
|
|
site => $site_constants,
|
|
},
|
|
CACHE_SIZE =>
|
|
$LJ::TEMPLATE_CACHE_SIZE, # this can be undef, and that means cache everything.
|
|
STAT_TTL => $LJ::IS_DEV_SERVER ? 1 : 3600,
|
|
RECURSION => 1,
|
|
PLUGINS => {
|
|
autoformat => 'Template::Plugin::Autoformat',
|
|
date => 'Template::Plugin::Date',
|
|
url => 'Template::Plugin::URL',
|
|
dw => 'DW::Template::Plugin',
|
|
form => 'DW::Template::Plugin::FormHTML',
|
|
},
|
|
PRE_PROCESS => '_init.tt',
|
|
}
|
|
);
|
|
|
|
my $scheme_engine = Template->new(
|
|
{
|
|
INCLUDE_PATH => join( ':', LJ::get_all_directories('schemes') ),
|
|
NAMESPACE => {
|
|
site => $site_constants,
|
|
},
|
|
CACHE_SIZE =>
|
|
$LJ::TEMPLATE_CACHE_SIZE, # this can be undef, and that means cache everything.
|
|
STAT_TTL => $LJ::IS_DEV_SERVER ? 1 : 3600,
|
|
PLUGINS => {
|
|
dw => 'DW::Template::Plugin',
|
|
dw_scheme => 'DW::Template::Plugin::SiteScheme',
|
|
form => 'DW::Template::Plugin::FormHTML',
|
|
},
|
|
}
|
|
);
|
|
|
|
=head1 API
|
|
|
|
=head2 C<< $class->template_string( $filename, $opts, $extra ) >>
|
|
|
|
Render a template to a string.
|
|
|
|
=cut
|
|
|
|
sub template_string {
|
|
my ( $class, $filename, $opts, $extra ) = @_;
|
|
my $r = DW::Request->get;
|
|
|
|
$opts->{sections} = $extra;
|
|
$opts->{sections}->{errors} = $opts->{errors};
|
|
|
|
# now we have to save the scope and update it for this rendering
|
|
my $oldscope = $r->note('ml_scope');
|
|
$r->note( ml_scope => ( $extra->{ml_scope} || "/$filename" ) );
|
|
|
|
my $out;
|
|
$view_engine->process( $filename, $opts, \$out )
|
|
or die $view_engine->error->as_string;
|
|
|
|
# now revert the scope if we had one
|
|
$r->note( ml_scope => $oldscope ) if $oldscope;
|
|
|
|
return $out;
|
|
}
|
|
|
|
=head2 C<< $class->cached_template_string( $key, $filename, $opts_subref, $cache_opts, $extra ) >>
|
|
|
|
Render a template to a string -- optionally fragment caching it.
|
|
$opts_subref returns the options for template_string.
|
|
|
|
fragment opts:
|
|
|
|
=over
|
|
|
|
=item B< lock_failed > - The text returned by this subref is returned if the lock is failed and the grace period is up.
|
|
|
|
=item B< expire > - Number of seconds the fragment is valid for
|
|
|
|
=item B< grace_period > - Number of seconds that an expired fragment could still be served if the lock is in place
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub cached_template_string {
|
|
my ( $class, $key, $filename, $opts_subref, $cache_opts, $extra ) = @_;
|
|
|
|
return DW::FragmentCache->get(
|
|
$key,
|
|
{
|
|
lock_failed => $cache_opts->{lock_failed},
|
|
expire => $cache_opts->{expire},
|
|
grace_period => $cache_opts->{grace_period},
|
|
render => sub {
|
|
return $class->template_string( $filename, $opts_subref->( $_[0] ), $extra );
|
|
}
|
|
},
|
|
$extra
|
|
);
|
|
}
|
|
|
|
=head2 C<< $class->render_cached_template( $key, $filename, $subref, $extra ) >>
|
|
|
|
Render a template inside the sitescheme or alone.
|
|
|
|
See render_template, except note that the opts hash is returned by opts_subref if it's needed.
|
|
|
|
$cache_opts can contain:
|
|
|
|
=over
|
|
|
|
=item B< no_sitescheme > == render alone
|
|
|
|
=item B< title / windowtitle / head / bodyopts / ... > == text to get thrown in the section if inside sitescheme
|
|
|
|
=item B< lock_failed > = subref for lock failed.
|
|
|
|
=item B< expire > - Number of seconds the fragment is valid for
|
|
|
|
=item B< grace_period > - Number of seconds that an expired fragment could still be served if the lock is in place
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub render_cached_template {
|
|
my ( $class, $key, $filename, $opts_subref, $cache_opts, $extra ) = @_;
|
|
|
|
$extra ||= {};
|
|
|
|
my $out = $class->cached_template_string( $key, $filename, $opts_subref, $cache_opts, $extra );
|
|
|
|
return $class->render_string( $out, $extra );
|
|
}
|
|
|
|
=head2 C<< $class->render_template( $filename, $opts, $extra ) >>
|
|
|
|
Render a template inside the sitescheme or alone.
|
|
|
|
$extra can contain:
|
|
|
|
=over
|
|
|
|
=item B< no_sitescheme > == render alone
|
|
|
|
=item B< title / windowtitle / head / bodyopts / ... > == text to get thrown in the section if inside sitescheme
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub render_template {
|
|
my ( $class, $filename, $opts, $extra ) = @_;
|
|
|
|
$extra ||= {};
|
|
my $out = $class->template_string( $filename, $opts, $extra );
|
|
|
|
return $class->render_string( $out, $extra );
|
|
}
|
|
|
|
=head2 C<< $class->render_template_misc( $filename, $opts, $extra ) >>
|
|
|
|
Render a template inside the sitescheme or alone.
|
|
This can also be safely called ( with some work on the other side )
|
|
from a BML context and still spit the content where required.
|
|
( Note, the "alone" bit will be ignored from BML contexts )
|
|
|
|
Can safely directly return this from either trans/Controller, internal journal page generation or (most) BML contexts.
|
|
|
|
$extra can contain:
|
|
|
|
=over
|
|
|
|
=item B< no_sitescheme > == render alone
|
|
|
|
=over
|
|
|
|
This will be ignored for 'bml' scopes.
|
|
|
|
=back
|
|
|
|
=item B< title / windowtitle / head / bodyopts / ... > == text to get thrown in the section if inside sitescheme
|
|
|
|
=item B< scope > = Scope, accepts nothing, 'bml', or 'journal'
|
|
|
|
=item B< scope_data > = Depends on B< scope >
|
|
|
|
=over
|
|
|
|
=item B< bml > Hashref of scalar-refs of where to throw the sections
|
|
|
|
=item B< journal > $opts hashref passed into LJ::make_journal and beyond.
|
|
|
|
=back
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
# FIXME(dre): Remove this method when BML is completely dead
|
|
# and refactor the journal scope bits up into render_template or render_string.
|
|
sub render_template_misc {
|
|
my ( $class, $filename, $opts, $extra ) = @_;
|
|
|
|
$extra ||= {};
|
|
my $out = $class->template_string( $filename, $opts, $extra );
|
|
|
|
my $scope = $extra->{scope} // '';
|
|
|
|
if ( $scope eq 'bml' ) {
|
|
my $r = DW::Request->get;
|
|
my $bml = $extra->{scope_data};
|
|
|
|
for my $item (qw(title windowtitle head bodyopts)) {
|
|
${ $bml->{$item} } = $extra->{$item} || "";
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
my $rv = $class->render_string( $out, $extra );
|
|
if ( $scope eq 'journal' ) {
|
|
$extra->{scope_data}->{handler_return} = $rv;
|
|
return;
|
|
}
|
|
else {
|
|
return $rv;
|
|
}
|
|
}
|
|
|
|
=head2 C<< $class->render_string( $string, $extra ) >>
|
|
|
|
Render a string inside the sitescheme or alone.
|
|
|
|
$extra can contain:
|
|
|
|
=over
|
|
|
|
=item B< no_sitescheme > == render alone
|
|
|
|
=over
|
|
|
|
If you are just printing text or other data, do not call DW::Template->render_string and instead just
|
|
$r->print and return $r->OK.
|
|
|
|
This is mostly for being used from render_template.
|
|
|
|
=back
|
|
|
|
=item B< title / windowtitle / head / bodyopts / ... > == text to get thrown in the section if inside sitescheme
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub render_string {
|
|
my ( $class, $out, $extra ) = @_;
|
|
|
|
my $r = DW::Request->get;
|
|
|
|
my $scheme = DW::SiteScheme->get;
|
|
|
|
if ( $extra->{no_sitescheme} ) {
|
|
$r->print($out);
|
|
|
|
return $r->OK;
|
|
}
|
|
elsif ( $extra->{fragment} ) {
|
|
LJ::set_active_resource_group("fragment");
|
|
$out .= LJ::res_includes( nojs => 1, nolib => 1 );
|
|
$r->print($out);
|
|
|
|
return $r->OK;
|
|
}
|
|
elsif ( $scheme->supports_tt ) {
|
|
$r->content_type("text/html; charset=utf-8");
|
|
$r->print( $class->render_scheme( $scheme, $out, $extra ) );
|
|
|
|
return $r->OK;
|
|
}
|
|
else {
|
|
die "Can not use invalid/unknown engine "
|
|
. $scheme->engine
|
|
. " for scheme "
|
|
. $scheme->name;
|
|
}
|
|
}
|
|
|
|
=head2 C<< $class->render_scheme( $sitescheme, $body, $sections ) >>
|
|
|
|
Render the body and sections in a TT sitescheme
|
|
|
|
=over
|
|
|
|
=item B< sitescheme >
|
|
|
|
A DW::SiteScheme object
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub render_scheme {
|
|
my ( $class, $scheme, $body, $sections ) = @_;
|
|
my $r = DW::Request->get;
|
|
my $out;
|
|
|
|
my $args = $r->query_string;
|
|
my $baseuri = "$LJ::PROTOCOL://" . $r->host . $r->uri;
|
|
$baseuri .= $args ? "?$args" : "";
|
|
|
|
my $opts = $scheme->get_vars;
|
|
$opts->{sections} = $sections;
|
|
$opts->{inheritance} = [ map { "$_.tt" } reverse $scheme->inheritance ];
|
|
$opts->{content} = $body;
|
|
$opts->{get} = $r->get_args;
|
|
$opts->{resource_group} = $LJ::ACTIVE_RES_GROUP;
|
|
$opts->{msgs} = $r->msgs;
|
|
$opts->{returnto} = $baseuri;
|
|
|
|
$scheme_engine->process( "_init.tt", $opts, \$out )
|
|
or die $scheme_engine->error->as_string;
|
|
$r->clear_msgs;
|
|
|
|
return $out;
|
|
}
|
|
|
|
=head1 AUTHOR
|
|
|
|
=over
|
|
|
|
=item Andrea Nall <anall@andreanall.com>
|
|
|
|
=back
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
Copyright (c) 2009-2011 by Dreamwidth Studios, LLC.
|
|
|
|
This program is free software; you may redistribute it and/or modify it under
|
|
the same terms as Perl itself. For a copy of the license, please reference
|
|
'perldoc perlartistic' or 'perldoc perlgpl'.
|
|
|
|
=cut
|
|
|
|
1;
|