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 )