OSDN Git Service

implement /topics/
authorhylom <hylom@users.sourceforge.jp>
Mon, 1 Apr 2019 13:17:01 +0000 (22:17 +0900)
committerhylom <hylom@users.sourceforge.jp>
Mon, 1 Apr 2019 13:17:01 +0000 (22:17 +0900)
src/newslash_web/css/newslash.less
src/newslash_web/css/newslash/topics.less [new file with mode: 0644]
src/newslash_web/lib/Newslash/Plugin/Topics.pm [new file with mode: 0644]
src/newslash_web/lib/Newslash/Web.pm
src/newslash_web/lib/Newslash/Web/Controller/Topics.pm [new file with mode: 0644]
src/newslash_web/templates/common/footer.html.tt2
src/newslash_web/templates/topics/topics.html.tt2 [new file with mode: 0644]

index f04f38c..6fcd3b5 100644 (file)
@@ -31,6 +31,7 @@
 @import "newslash/timeline.less";
 @import "newslash/authors.less";
 @import "newslash/hof.less";
+@import "newslash/topics.less";
 
 @import "newslash/ads.less";
 @import "newslash/system_error.less";
diff --git a/src/newslash_web/css/newslash/topics.less b/src/newslash_web/css/newslash/topics.less
new file mode 100644 (file)
index 0000000..50d3bd1
--- /dev/null
@@ -0,0 +1,15 @@
+.topics {
+    .topic-item {
+        display: inline-block;
+        padding: 20px 12px;
+        text-align: center;
+        img {
+            display: block;
+            margin: 0 auto;
+        }
+
+        &:hover {
+            background-color: @background-color-weak;
+        }
+    }
+}
diff --git a/src/newslash_web/lib/Newslash/Plugin/Topics.pm b/src/newslash_web/lib/Newslash/Plugin/Topics.pm
new file mode 100644 (file)
index 0000000..9516705
--- /dev/null
@@ -0,0 +1,431 @@
+package Newslash::Plugin::Topics;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use Newslash::Util::TextFormatter;
+
+=encoding utf8
+
+=head1 NAME
+
+Newslash::Plugin::NewslashHelper - Newslash helpers plugin
+
+=head1 SYNOPSIS
+
+  # Mojolicious
+  $app->plugin('Newslash::Plugin::NewslashHelpers');
+
+=head1 DESCRIPTION
+
+L<Newslash::Plugin::NewslashHelpers> is collection of helpers for L<Newslash>.
+
+
+=head1 METHODS
+
+=head2 register
+
+  $plugin->register(Mojolicious->new);
+
+Register helpers in L<Mojolicious> application.
+
+=cut
+
+sub register {
+    my ($self, $app, $conf) = @_;
+    $self->{app} = $app;
+
+    for my $k (qw[boxes format_timestamp page_type content_type site_config
+                  declare insert_code
+                  the_path
+                  get_timestamp_by_id
+                  tidy_html clean_html escape_html format_htmltext escape_plaintext strip_by_mode
+                  get_authors
+                ]) {
+        $app->helper($k => $self->can("_$k"))
+    }
+}
+
+=head1 HELPERS
+
+L<Mojolicious::Plugin::NewslashHelpers> implements the following helpers.
+
+=head2 tidy_html($html)
+
+  tidy HTML, then returns result.
+
+=cut
+
+sub _tidy_html {
+    my ($c, $html) = @_;
+    return Newslash::Util::TextFormatter::tidy_html($html);
+}
+
+=head2 escape_html($html)
+
+  escape HTML, then returns result.
+
+=cut
+
+sub _escape_html {
+    my ($c, $html) = @_;
+    my $allowed = $c->app->config->{Editor}->{allowed_tags};
+    return Newslash::Util::TextFormatter::escape_html($allowed, $html);
+}
+
+=head2 clean_html($html, $type)
+
+  escape and tidy HTML, then returns result.
+
+=cut
+
+sub _clean_html {
+    my ($c, $html) = @_;
+    my $allowed = $c->app->config->{Editor}->{allowed_tags};
+    return Newslash::Util::TextFormatter::clean_html($allowed, $html);
+}
+
+=head2 escape_plaintext($text)
+
+  escape plaintext, then returns result.
+
+=cut
+
+sub _escape_plaintext {
+    my ($c, $text) = @_;
+    return Newslash::Util::TextFormatter::escape_plaintext($text);
+}
+
+=head2 format_htmltext($text)
+
+  format html/text, then returns result.
+
+=cut
+
+sub _format_htmltext {
+    my ($c, $text, $type) = @_;
+    my $allowed;
+
+    if ($type eq "title") {
+        return Newslash::Util::TextFormatter::escape_plaintext($text);
+    }
+    elsif ($type eq "story") {
+        return Newslash::Util::TextFormatter::tidy_html($text);
+    }
+    elsif ($type eq "comment") {
+        $allowed = $c->app->config->{Editor}->{allowed_tags};
+    }
+    elsif ($type eq "journal") {
+        $allowed = $c->app->config->{Editor}->{allowed_tags};
+    }
+    elsif ($type eq "submission") {
+        $allowed = $c->app->config->{Editor}->{allowed_tags};
+    }
+    else {
+        $c->app->log->warn("NewslashHelper::_format_htmltext: invalid type - '$type'");
+        $allowed = $c->app->config->{Editor}->{allowed_tags};
+    }
+    return Newslash::Util::TextFormatter::escape_html($allowed, $text);
+}
+
+=head2 strip_by_mode($html, $post_type)
+
+  strip HTML, then returns result.
+
+=cut
+
+sub _strip_by_mode {
+    my ($c, $html, $post_type) = @_;
+    my $allowed = $c->app->config->{Editor}->{allowed_tags};
+    return Newslash::Util::TextFormatter::strip_by_mode($html, $post_type, $allowed);
+}
+
+=head2 the_path()
+
+  returns current path
+
+=cut
+
+sub _the_path {
+    my ($c, $html, $post_type) = @_;
+    return $c->req->url->to_abs->path;
+}
+
+=head2 get_timestamp_by_id($content_type, $id)
+
+  get given content's timestamp
+
+=cut
+
+sub _get_timestamp_by_id {
+    my ($c, $content_type, $id) = @_;
+}
+
+=head2 get_authors()
+
+  get authors
+
+=cut
+
+sub _get_authors {
+    my ($c) = @_;
+    my $users = $c->model('users');
+    my $authors = $users->select(author => { gt => 0 });
+    return $authors;
+}
+
+
+######################################################################
+# 
+
+use constant MARKERS => qw(begin_footer);
+my $marker_contents = {};
+
+sub _insert_code {
+    my ($c, $marker, $content) = @_;
+    if(!$marker_contents->{$marker}) {
+        $marker_contents->{$marker} = [];
+    }
+    push @{$marker_contents->{$marker}}, $content;
+}
+
+sub _declare {
+    my ($c, $name) = @_;
+    my $contents = $marker_contents->{$name} || [];
+
+    return join("\n", @$contents);
+}
+
+
+sub _generate_site_config {
+    my $c = shift;
+    my $epoch = $c->stash('epoch');
+
+    my $reasons = $c->model('moderations')->reasons;
+    my $topics = $c->model('topics')->select;
+    my @topic_texts = map { $_->{textname} } @$topics;
+
+    my $mod_reasons = {};
+    for my $k (keys %$reasons) {
+        $mod_reasons->{$k} = $reasons->{$k}->{name};
+    }
+
+    # my $keywords = {};
+    # for my $topic (@$topics) {
+    #     my $lc_keyword = lc($topic->{keyword});
+    #     my $lc_textname = lc($topic->{textname});
+    #     $keywords->{$lc_keyword} = {keyword => $topic->{keyword},
+    #                                 textname => $topic->{textname},
+    #                                 image => $topic->{image}};
+    #     if ($lc_keyword ne $lc_textname) {
+    #         $keywords->{$lc_textname} = $keywords->{$lc_keyword};
+    #     }
+    # }
+
+    my $value = {
+                 modReasons => $mod_reasons,
+                 siteInfo => { topic_icon_base_url => $c->config->{Site}->{topic_icon_base_url} },
+                 #topics => $keywords,
+                 topics => \@topic_texts,
+                 editorConfig => $c->config->{Editor},
+                 acl2Types => {},
+                };
+
+    return $value;
+}
+
+
+sub _site_config {
+    my $c = shift;
+
+    my $epoch = $c->epoch->get;
+    my $cache_key = "siteconfig";
+    my $config = { memory_expire => 300, # 5 minutes
+                   kbs_expire => 86400 }; # 1 day
+
+    my $value = $c->ccache->get($cache_key, $epoch);
+    if (!defined $value) {
+        $value = _generate_site_config($c);
+        $c->ccache->set($cache_key, $epoch, $value);
+    }
+    my $json = to_json($value);
+    return "var siteConfig = $json;";
+}
+
+sub _page_type {
+    my $c = shift;
+    if ($c->stash("page")) {
+        return $c->stash("page")->{type};
+    }
+
+    my $cls = ref($c);
+    if ($cls =~ /^Newslash::Web::Controller::/) {
+        $cls =~ s/^Newslash::Web::Controller:://;
+        $cls = decamelize($cls);
+        return $cls;
+    }
+    return;
+}
+
+sub _content_type {
+    my $c = shift;
+    if ($c->stash("page")) {
+        return $c->stash("page")->{content_type};
+    }
+    return;
+}
+
+=head2 boxes
+
+  [% helpers.boxes() %]
+
+Fetch box contents for current user and returns them.
+
+=cut
+
+sub _boxes {
+    my $c = shift;
+    my $user = $c->stash("user");
+
+    my $value = $c->ccache->get("user-boxes", $user->{uid});
+    if (!defined $value) {
+        $value = _get_sidebar_item($c);
+        $c->ccache->set("user-boxes", $user->{uid}, $value);
+    }
+    return $value;
+}
+
+sub _get_sidebar_item {
+    my $c = shift;
+
+    my $users = $c->model('users');
+    my $user = $c->stash("user");
+    my $user_boxes = $users->sidebar->get_sidebar_contents(uid => $user->{uid});
+
+    my $items = [];
+    my $tt = Template->new();
+
+    for my $box (@$user_boxes) {
+        #$c->app->log->debug("load box $box->{name}");
+        my $q_params = {};
+        if ($box->{query_params}) {
+            $q_params = from_json($box->{query_params});
+        }
+        else {
+            if (!$box->{type} || $box->{type} ne "custom") {
+                push @$items, { type => $box->{type}, name => $box->{name}, contents => $box->{template} };
+                next;
+            }
+        }
+        if (!$q_params) {
+            $c->app->log->warn("Sidebar: invalid query_params in $box->{name}");
+            next;
+        }
+        $q_params->{limit} = $box->{limit} || 20;
+
+        # regularize query parameters
+        my @keys = sort { $a cmp $b } keys %$q_params;
+        my @params;
+        for my $k (@keys) {
+            push @params, $k;
+            push @params, $q_params->{$k};
+        }
+
+        my $rs;
+        if ($box->{type} eq "custom") {
+            my $custom_boxes = $c->app->custom_boxes;
+            if ($custom_boxes) {
+                push @params, "_user";
+                push @params, $user;
+                $rs = $custom_boxes->query($box->{model}, $c, @params);
+            }
+        }
+        else {
+            my $model = $c->ccache->model($box->{model});
+            $rs = $model->select(@params);
+        }
+
+        my $result = '';
+        my $template = $box->{template};
+        next if !$rs || !$template;
+
+        if ($tt->process(\$template, {items => $rs}, \$result)) {
+            push @$items, { type => $box->{type}, name => $box->{name}, contents => $result };
+        }
+    }
+    return $items;
+
+}
+
+=head2 format_timestamp
+
+  $c->format_timestamp(user => $user, epoch => $epoch, format => "user")
+  $c->format_timestamp(datetime => $dt, format => "simple")
+  $c->format_timestamp(user => $user, mysql => $mysql_timestamp_string, format => "datetime");
+
+Return formated string.
+
+=cut
+
+sub _format_timestamp {
+    my $c = shift;
+    my $params = {};
+    my $user = $params->{user} || $c->stash('user');
+
+    if (!ref($_[0])) {
+        $params = {@_};
+    } else {
+        $params = shift;
+    }
+
+    my $offset_sec = 0;
+    if ($user && $user->{config}->{ui}->{offset_sec}) {
+        $offset_sec = $user->{config}->{ui}->{offset_sec};
+    }
+
+    my $format = $params->{format} || ($params->{user} ? "user" : "simple");
+
+    my $dt;
+    if ($params->{from_epoch}) {
+        $dt = DateTime->from_epoch(epoch => $params->{from_epoch});
+    } elsif ($params->{datetime}) {
+        $dt = $params->{datetime}
+    } elsif ($params->{mysql}) {
+        $dt = DateTime::Format::MySQL->parse_datetime($params->{mysql});
+    } elsif ($params->{now}) {
+        $dt = DateTime->now();
+        $format = $params->{now};
+    } else {
+        return;
+    }
+
+    if ($offset_sec) {
+        $dt->add(seconds => $offset_sec);
+    }
+
+    if ($format eq "simple") {
+        return $dt->strftime('%Y-%m-%d %H:%M:%S');
+    }
+    elsif ($format eq "iso8601") {
+        return $dt->strftime('%Y-%m-%dT%H:%M:%S+00:00');
+    }
+    elsif ($format eq "user" && $user) {
+        return $dt->strftime($user->{config}->{ui}->{time_format});
+    }
+    elsif ($format eq "ja") {
+        $dt->set_time_zone('UTC');
+        $dt->set_time_zone('Asia/Tokyo');
+        return $dt->strftime('%Y年%m月%e日 %k時%M分');
+    }
+    elsif ($format eq "datetime") {
+        return $dt;
+    }
+
+    return;
+}
+
+1;
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
+
+=cut
index 9e92171..521fe8f 100644 (file)
@@ -351,6 +351,10 @@ sub startup {
     $r->get('/faq/tech')->to('faq#tech');
     $r->get('/faq/')->to('faq#faq');
 
+    # topics
+    $r->get('/topics.pl')->to('topics#topics');
+    $r->get('/topics')->to('topics#topics');
+
     # authors
     $r->get('/authors')->to('authors#authors');
 
diff --git a/src/newslash_web/lib/Newslash/Web/Controller/Topics.pm b/src/newslash_web/lib/Newslash/Web/Controller/Topics.pm
new file mode 100644 (file)
index 0000000..466689b
--- /dev/null
@@ -0,0 +1,15 @@
+package Newslash::Web::Controller::Topics;
+use Mojo::Base 'Mojolicious::Controller';
+use Data::Dumper;
+
+sub topics {
+    my $c = shift;
+    my $topics = $c->model('topics')->select;
+
+    $c->render(page => { type => "topics" },
+               topics => $topics);
+}
+
+
+1;
+
index 1b43430..4eae03b 100644 (file)
@@ -13,7 +13,7 @@
       <li><a href="/hof/">殿堂入り</a></li>
       <li><a href="https://osdn.net/ticket/newticket.php?group_id=10768">バグ報告</a></li>
       <li><a href="/authors/">編集者紹介</a></li>
-      <li><a href="https://srad.jp/topics.pl">トピック</a></li>
+      <li><a href="/topics/">トピック</a></li>
       <li><a href="http://osdn.co.jp/advertise/">広告掲載</a></li>
       <li><a href="http://osdn.co.jp/terms.shtml" rel="nofollow">利用規約</a></li>
       <li><a href="http://osdn.co.jp/privacy.shtml" rel="nofollow">プライバシー</a></li>
diff --git a/src/newslash_web/templates/topics/topics.html.tt2 b/src/newslash_web/templates/topics/topics.html.tt2
new file mode 100644 (file)
index 0000000..8adb6a7
--- /dev/null
@@ -0,0 +1,33 @@
+[%- WRAPPER common/layout title="トピック一覧" -%]
+
+<div class="sidebar-wrapper">
+  <div class="index main-contents topics">
+
+    <article>
+      <header>
+        <h1>トピック一覧</h1>
+      </header>
+
+      <div class="topics-list">
+        [%- FOREACH topic = topics %]
+        [%- IF topic.searchable == "yes" %]
+        <div class="topic-item">
+          <a href="/stories/[% topic.keyword %]/">
+            [%- IF topic.image %]
+            <img src="[% Site.topic_icon_base_url %]/[% topic.image %]" />
+            [%- ELSE %]
+            <img src="[% Site.topic_icon_base_url %]/meta_64.png" />
+            [%- END %]
+            [% topic.textname %]
+          </a>
+        </div>
+        [%- END %]
+        [%- END %]
+      </div>
+    </article>
+
+  </div>
+  [%- INCLUDE common/sidebar -%]
+</div>
+
+[% END %]