BusyBird
view release on metacpan or search on metacpan
lib/BusyBird/Main/PSGI/View.pm view on Meta::CPAN
package BusyBird::Main::PSGI::View;
use v5.8.0;
use strict;
use warnings;
use BusyBird::Util qw(set_param split_with_entities);
use Carp;
use Try::Tiny;
use Scalar::Util qw(weaken);
use JSON qw(to_json);
use Text::Xslate qw(html_builder html_escape);
use File::Spec;
use Encode ();
use JavaScript::Value::Escape ();
use DateTime::TimeZone;
use BusyBird::DateTime::Format;
use BusyBird::Log qw(bblog);
use BusyBird::SafeData qw(safed);
use Cache::Memory::Simple;
use Plack::Util ();
use Tie::IxHash;
sub new {
my ($class, %args) = @_;
my $self = bless {
main_obj => undef,
renderer => undef,
}, $class;
$self->set_param(\%args, "main_obj", undef, 1);
$self->set_param(\%args, "script_name", undef, 1);
my $sharedir = $self->{main_obj}->get_config('sharedir_path');
$sharedir =~ s{/+$}{};
$self->{renderer} = Text::Xslate->new(
path => [ File::Spec->catdir($sharedir, 'www', 'templates') ],
cache_dir => File::Spec->tmpdir,
syntax => 'Kolon',
function => $self->template_functions(),
warn_handler => sub {
bblog("warn", @_);
},
## we don't use die_handler because (1) it is called for every
## death even when it's in eval() scope, (2) exceptions are
## caught by Xslate and passed to warn_handler anyway.
);
return $self;
}
sub response_notfound {
my ($self, $message) = @_;
$message ||= 'Not Found';
return ['404',
['Content-Type' => 'text/plain',
'Content-Length' => length($message)],
[$message]];
}
sub response_error_html {
my ($self, $http_code, $message) = @_;
return $self->_response_template(
template => 'error.tx', args => {error => $message},
code => $http_code
);
}
sub response_json {
my ($self, $res_code, $response_object) = @_;
my $message = try {
die "response must not be undef" if not defined $response_object;
lib/BusyBird/Main/PSGI/View.pm view on Meta::CPAN
my $attr_str = _html_attributes_string(['href'], @attr);
return qq{<a $attr_str>$escaped_text</a>};
}catch {
return $escaped_text;
};
}
sub _html_link_status_text {
my ($text, $url) = @_;
return _html_link($text, href => $url, target => "_blank");
}
sub _make_path {
my ($script_name, $given_path) = @_;
if(substr($given_path, 0, 1) eq "/") {
return "$script_name$given_path";
}else {
return $given_path;
}
}
sub template_functions {
my $script_name = $_[0]->{script_name};
return {
js => \&JavaScript::Value::Escape::js,
link => html_builder(\&_html_link),
image => html_builder {
my (@attr) = @_;
return try {
my $attr_str = _html_attributes_string(['src'], @attr);
return qq{<img $attr_str />}
}catch {
return "";
};
},
bb_level => sub {
my $level = shift;
$level = 0 if not defined $level;
return $level;
},
path => sub { _make_path($script_name, $_[0]) },
script_name => sub { $script_name },
};
}
sub _render_text_segment {
my ($self, $timeline_name, $segment, $status) = @_;
return undef if !defined($segment->{entity}) || !defined($segment->{type});
my $url_builder = $self->{main_obj}->get_timeline_config($timeline_name, "$segment->{type}_entity_url_builder");
my $text_builder = $self->{main_obj}->get_timeline_config($timeline_name, "$segment->{type}_entity_text_builder");
return undef if !defined($url_builder) || !defined($text_builder);
my $url_str = $url_builder->($segment->{text}, $segment->{entity}, $status);
return undef if !_is_valid_link_url($url_str);
my $text_str = $text_builder->($segment->{text}, $segment->{entity}, $status);
$text_str = "" if not defined $text_str;
return _html_link_status_text($text_str, $url_str);
}
sub template_functions_for_timeline {
my ($self, $timeline_name) = @_;
weaken $self; ## in case the functions are kept by $self
return {
bb_timestamp => sub {
my ($timestamp_string) = @_;
return "" if !$timestamp_string;
my $timezone = $self->_get_timezone($self->{main_obj}->get_timeline_config($timeline_name, "time_zone"));
my $dt = BusyBird::DateTime::Format->parse_datetime($timestamp_string);
return "" if !defined($dt);
$dt->set_time_zone($timezone);
$dt->set_locale($self->{main_obj}->get_timeline_config($timeline_name, "time_locale"));
return $dt->strftime($self->{main_obj}->get_timeline_config($timeline_name, "time_format"));
},
bb_status_permalink => sub {
my ($status) = @_;
my $builder = $self->{main_obj}->get_timeline_config($timeline_name, "status_permalink_builder");
my $url = try {
$builder->($status);
}catch {
my ($e) = @_;
bblog("error", "Error in status_permalink_builder: $e");
undef;
};
return (_is_valid_link_url($url) ? $url : "");
},
bb_text => html_builder {
my $status = shift;
return "" if not defined $status->{text};
my $segments_ref = split_with_entities($status->{text}, $status->{entities});
my $result_text = "";
foreach my $segment (@$segments_ref) {
my $rendered_text = try {
$self->_render_text_segment($timeline_name, $segment, $status)
}catch {
my ($e) = @_;
bblog("error", "Error while rendering text: $e");
undef;
};
$result_text .= defined($rendered_text) ? $rendered_text : _escape_and_linkify_status_text($segment->{text});
}
return $result_text;
},
bb_attached_image_urls => sub {
my ($status) = @_;
my $urls_builder = $self->{main_obj}->get_timeline_config($timeline_name, "attached_image_urls_builder");
my @image_urls = try {
$urls_builder->($status);
}catch {
my ($e) = @_;
bblog("error", "Error in attached_image_urls_builder: $e");
();
};
return [grep { _is_valid_link_url($_) } @image_urls ];
},
};
}
{
my $timezone_cache = Cache::Memory::Simple->new();
my $CACHE_EXPIRATION_TIME = 3600 * 24;
my $CACHE_SIZE_LIMIT = 100;
sub _get_timezone {
( run in 2.423 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )