OSDN Git Service

Model::Stories: support tag and related stories setting in create()
authorhylom <hylom@users.sourceforge.jp>
Thu, 7 Feb 2019 13:25:38 +0000 (22:25 +0900)
committerhylom <hylom@users.sourceforge.jp>
Thu, 7 Feb 2019 13:25:38 +0000 (22:25 +0900)
src/newslash_web/lib/Newslash/Model/Stories.pm
src/newslash_web/t/api/story.t
src/newslash_web/t/models/stories.t

index 99ea3cd..f327710 100644 (file)
@@ -3,9 +3,13 @@ use Newslash::Model::Base -base;
 
 use Newslash::Model::SlashDB;
 
+use Newslash::Model::Stories::Text;
+use Newslash::Model::Stories::RelatedStories;
+
 use DateTime;
 use DateTime::Format::MySQL;
 use DateTime::Format::ISO8601;
+use List::Util q(any);
 
 use Data::Dumper;
 use DateTime;
@@ -47,8 +51,22 @@ sub count {
 }
 
 ##### sub models
-sub text { return shift->new_instance_of('::Stories::Text'); }
+sub text            { return shift->_get_model('::Stories::Text'); }
+sub related_stories { return shift->_get_model('::Stories::RelatedStories'); }
 
+sub _get_model {
+    my ($self, $class) = @_;
+    $self->{sub_models} ||= {};
+    if (!$self->{sub_models}->{$class}) {
+        $self->{sub_models}->{$class} = $self->new_instance_of($class);
+    }
+    else {
+        if ($self->transaction_mode && !$self->{sub_models}->{$class}->transaction_mode) {
+            $self->{sub_models}->{$class}->use_transaction($self->{_tr_dbh});
+        }
+    }
+    return $self->{sub_models}->{$class};
+}
 
 #========================================================================
 
@@ -259,8 +277,8 @@ EOSQL
 
     for my $story (@$stories) {
         my $stoid = $story->{stoid};
-        $story->{tags} = $tags->{$stoid} if $tags->{$stoid};
-        $story->{topics} = $topics->{$stoid} if $topics->{$stoid};
+        $story->{tags} = $tags->{$stoid} || [];
+        $story->{topics} = $topics->{$stoid} || [];
         if ($params->{$stoid}) {
             for my $param (@{$params->{$stoid}}) {
                 $story->{$param->{name}} = $param->{value};
@@ -348,24 +366,77 @@ sub update {
     #my ($self, $params, $user, $extra_params, $opts) = @_;
     my $self = shift;
     my $params = {@_};
+    my $user = $params->{user};
 
     # check id
-    my $id = $params->{stoid} || $params->{story_id} || $params->{id};
-    if (!$id) {
-        $self->set_error("story id not given");
+    my $story;
+    my $stoid = $params->{stoid} || $params->{story_id} || $params->{id};
+    my $sid = $params->{sid};
+    if (!$story) {
+        if ($stoid) {
+            $story = $self->select(stoid => $stoid);
+        }
+        elsif ($sid) {
+            $story = $self->select(sid => $sid);
+        }
+    }
+    if ($story) {
+        $sid = $story->{sid};
+        $stoid = $story->{stoid};
+    }
+
+    if (!$story) {
+        $self->last_error("no story given");
         return;
     }
 
     # check params
     return if !$self->_check_and_regularize_params($params);
 
-    my $stoid = $params->{stoid} || $params->{story_id} || $params->{id};
     my $slash_db = Newslash::Model::SlashDB->new($self->{options});
 
-    my $sid = $slash_db->updateStory($stoid, $params);
-    return if !$sid;
+    my $add_related = $params->{add_related};
+    delete $params->{add_related} if $add_related;
+    my $add_tags = $params->{add_tags};
+    delete $params->{add_tags} if $add_tags;
+
+    $stoid = $slash_db->updateStory($stoid, $params);
+    return if !$stoid;
 
     $self->_set_tags_from_topics($params->{user}, $stoid, $params->{topics_chosen});
+
+    # set tags
+    if ($add_tags) {
+        my $tags = $self->new_instance_of("Tags");
+        my $globjs = $self->new_instance_of("Newslash::Model::Globjs");
+        my $globj_id = $globjs->getGlobjidFromTargetIfExists("stories", $params->{stoid});
+        for my $tag (@$add_tags) {
+            my $rs = $tags->add(uid => $user->{uid} || $user->{user_id},
+                                globj_id => $globj_id,
+                                name => $tag);
+            if (!defined $rs) {
+                $self->logger->warn("Stories::update: tag $tag set failed: " . $tags->last_error);
+            }
+        }
+    }
+
+    # insert related story
+    if ($add_related) {
+        for my $related_sid (@$add_related) {
+            my $rs = $self->add_related_story(sid => $sid,
+                                              related_sid => $related_sid);
+            if (!defined $rs) {
+                $self->logger->warn("Stories::update: insert related story $related_sid to $sid failed: " . $self->last_error);
+            }
+
+            $rs = $self->add_related_story(sid => $related_sid,
+                                           related_sid => $sid);
+            if (!defined $rs) {
+                $self->logger->warn("Stories::update: insert related story $sid to $related_sid failed: " . $self->last_error);
+            }
+        }
+    }
+
     return $stoid;
 }
 
@@ -455,6 +526,11 @@ sub create {
     # createStory() deletes topics_chosen, so need to save here.
     my $topics_chosen = $params->{topics_chosen};
 
+    my $add_related = $params->{add_related};
+    delete $params->{add_related} if $add_related;
+    my $add_tags = $params->{add_tags};
+    delete $params->{add_tags} if $add_tags;
+
     my $slash_db = Newslash::Model::SlashDB->new($self->{options});
     my ($sid, $stoid);
     if ($params->{update}) {
@@ -469,18 +545,53 @@ sub create {
 
     my $globjs = $self->new_instance_of("Newslash::Model::Globjs");
     my $globj_id = $globjs->getGlobjidFromTargetIfExists("stories", $params->{stoid});
+
+    # set topics
+    my $tags = $self->new_instance_of("Tags");
     if ($globj_id) {
-        # set tags
-        my $tags = $self->new_instance_of("Tags");
         for my $tid (keys %$topics_chosen) {
-            my $ret = $tags->set_tag(uid => $user->{uid} || $user->{user_id},
-                                     tagname_id => $tid,
-                                     globj_id => $globj_id,
-                                     private => 0,
-                                    );
-            #warn "set_tag fault..." if !$ret
+            my $rs = $tags->add(uid => $user->{uid} || $user->{user_id},
+                                tagname_id => $tid,
+                                globj_id => $globj_id,
+                                private => 0,
+                               );
+            if (!defined $rs) {
+                $self->logger->warn("Stories::create: tag $tid set failed: " . $tags->last_error);
+            }
+        }
+    }
+
+    # set tags
+    if ($add_tags) {
+        for my $tag (@$add_tags) {
+            my $rs = $tags->add(uid => $user->{uid} || $user->{user_id},
+                                globj_id => $globj_id,
+                                name => $tag);
+            if (!defined $rs) {
+                $self->logger->warn("Stories::create: tag $tag set failed: " . $tags->last_error);
+            }
         }
     }
+
+    # insert related story
+    if ($add_related) {
+        for my $related_sid (@$add_related) {
+            my $rs = $self->add_related_story(sid => $sid,
+                                              related_sid => $related_sid);
+            if (!defined $rs) {
+                $self->logger->warn("insert related story failed: " . $self->last_error);
+            }
+
+            $rs = $self->add_related_story(sid => $related_sid,
+                                           related_sid => $sid);
+            if (!defined $rs) {
+                $self->logger->warn("Stories::create: insert related story failed: " . $self->last_error);
+            }
+        }
+    }
+
+    # TODO: add firehose item to related_story
+
     return wantarray ? ($sid, $stoid) : $stoid;
 }
 
@@ -648,6 +759,65 @@ EOSQL
     return $related;
 }
 
+sub add_related_story {
+    my $self = shift;
+    my $args = {@_};
+
+    my $story = $args->{story};
+    if (!$story) {
+        if ($args->{stoid}) {
+            $story = $self->select(stoid => $args->{stoid});
+        } elsif ($args->{sid}) {
+            $story = $self->select(sid => $args->{sid});
+        }
+    }
+
+    if (!$story || !$story->{stoid} || !$story->{sid}) {
+        $self->last_error("cannot get target story");
+        return;
+    }
+
+    my $related = $args->{related};
+    if (!$related) {
+        if ($args->{related_stoid}) {
+            $related = $self->select(stoid => $args->{related_stoid});
+        } elsif ($args->{related_sid}) {
+            $related = $self->select(sid => $args->{related_sid});
+        }
+    }
+
+    if (!$related || !$related->{stoid} || !$related->{sid} ) {
+        $self->last_error("cannot get related story");
+        return;
+    }
+
+    # get related item count
+    my $dbh = $self->start_transaction;
+    my $sql = "SELECT rel_stoid FROM related_stories WHERE stoid = ?";
+    my $stoids = $dbh->do($sql, undef, $story->{stoid});
+    $stoids = [] if ref($stoids) ne "ARRAY";
+
+    # check if related story is already set
+    if (any { $_ eq $related->{stoid} } @$stoids) {
+        return 0;
+    }
+
+    # insert
+    my $count = @$stoids;
+    my $rs = $self->related_stories->insert(stoid => $story->{stoid},
+                                            rel_stoid => $related->{stoid},
+                                            rel_sid => $related->{sid},
+                                            ordernum => $count + 1);
+
+    if (!defined $rs) {
+        $self->last_error("insert failed: " . $self->related_stories->last_error);
+        $dbh->rollback;
+        return;
+    }
+    $dbh->commit;
+    return 1;
+}
+
 =head2 parameters($stoid)
 
 get story parameters.
index fe3b4ed..f8a2f47 100644 (file)
@@ -13,9 +13,23 @@ use POSIX qw(strftime);
 my $t = Test::Mojo->new('Newslash::Web');
 my $test_man = Newslash::Util::TestMan->new($t);
 my $admin;
+my $test_story;
 
 if ($t->app->mode eq 'test') {
     $admin = $test_man->create_admin("story_test", "foobar");
+
+    $test_story = $test_man->create_story( user => $admin,
+                                           uid => $admin->{uid},
+                                           title => "test story for firehose",
+                                           dept => "test for firehose",
+                                           introtext => '<p>foo bar hoge</p>',
+                                           bodytext => '',
+                                           topics_chosen => {49 => 10, 2271 => 20},
+                                           commentstatus => '',
+                                           tid => 49,
+                                           time => "2020-11-11 10:00:00");
+    ok($test_story, "create story for test");
+
 }
 
 SKIP: {
@@ -25,19 +39,19 @@ SKIP: {
     subtest 'story post/update' => sub {
 
         my $test_title = "テストタイトル";
-        my $test_introtext = <<"EOT";
-hylom 曰く、<blockquote><div><p>英国のEU離脱問題についてEUは厳しい姿勢を見せており、離脱後は英市民や英企業が.euドメイン名を取得・更新できなくなる可能性があるそうだ(<a href="https://www.theregister.co.uk/2019/01/07/brit_eu_domain_owners/">The Registerの記事</a>、
-<a href="https://www.forbes.com/sites/annatobin/2019/01/09/brits-could-be-locked-out-of-their-eu-domain-names-after-brexit/">Forbesの記事</a>)。
-<br> <br>
+        my $test_introtext = <<EOT;
+<p>insiderman曰く、</p><blockquote>
 
-現在英国とEUは離脱条件について議論を進めているが、3月29日までに両者が同意せず「合意なしでの離脱」に至った場合、英市民や他のEU域内に拠点を持たない英企業は.euドメインの利用資格を失う。既存のドメインが即座に失効することはないようだが、ドメイン名の更新や新規ドメイン名登録はできなくなる。そのため、英国政府はそういった事態に備えて.comや.co.ukドメインを取得しておくようアドバイスを行っているという。
-<br> <br>
+<p>はやくも来年発表されるとみられているiPhoneの新モデルの噂が出始めている(<a href="https://www.theverge.com/circuitbreaker/2017/11/13/16644266/apple-iphone-x-plus-rumor-two-fullscreen-phones-2018">The Verge</a>、<a href="http://jbpress.ismedia.jp/articles/-/51619">JBPress</a>、<a href="https://hardware.slashdot.org/story/17/11/14/000224/apple-could-launch-two-new-full-screen-iphones-next-year">Slashdot</a>)。</p>
 
-英国内では.ukドメインの利用が多いものの、英市民・企業が登録した.euドメインは登録済み.euドメインの10%近くを占めるとのことで、影響は少なくないようだ。</p></div></blockquote>
+<p>KGI SecuritiesのアナリストMing-Chi Kuo氏による予想では、6.5インチ有機ELディスプレイを搭載したモデルがiPhone Xの新たなモデル(「Plus」バージョン)として出るのではないか、とされている。また、iPhone 8 Plusと同等のサイズでiPhone Xのようにフルスクリーン化した6.1インチ液晶ディスプレイ搭載モデルも出る可能性があるとも予測されている。これは、「ハイエンド市場」のさまざまな需要を狙ったものになるという。iPhone Xはその価格も話題となったが、液晶ディスプレイの採用で価格を下げることができるようだ。</p>
+</blockquote>
 EOT
-        my $tidyed_introtext = $t->app->clean_html($test_introtext);
+        my $related = "https://apple.srad.jp/story/" . $test_story->{sid} . "/";
+        my $tidyed_introtext = $t->app->format_htmltext($test_introtext, "story");
         my $test_dept = "テストdept";
         my $createtime = strftime('%FT%T', gmtime);
+        my $test_tags = "apple ios iphone hoge";
         my $test_data = {
                          action => 'preview',
                          item => {
@@ -47,11 +61,10 @@ EOT
                                   dept => $test_dept,
                                   introtext => $test_introtext,
                                   bodytext => "",
-                                  add_related => "",
-                                  url => "",
+                                  add_related => $related,
+                                  tags_string => $test_tags,
                                   mediaurl => "",
                                   mediatype => "",
-                                  email => "",
                                   commentstatus => "enabled",
                                   display => 1,
                                  },
@@ -82,8 +95,19 @@ EOT
           ->json_is('/item/stoid', $stoid)
           ->json_is('/item/title', $test_title)
           ->json_is('/item/intro_text', $tidyed_introtext)
+          ->json_has('/item/related')
+          ->json_is('/item/related/0/stoid', $test_story->{stoid})
+          ->json_has('/item/tags/0')
+          ->json_has('/item/tags/1')
+          ->json_has('/item/tags/2')
+          ->json_has('/item/tags/3')
+          ->json_like('/item/tags/0/tagname', qr/(hoge|iOS|iPhone|アップル)/)
+          ->json_like('/item/tags/1/tagname', qr/(hoge|iOS|iPhone|アップル)/)
+          ->json_like('/item/tags/2/tagname', qr/(hoge|iOS|iPhone|アップル)/)
+          ->json_like('/item/tags/3/tagname', qr/(hoge|iOS|iPhone|アップル)/)
           ->json_is('/item/dept', $test_dept);
         diag "message: " . $t->tx->res->json("/message") if $t->tx->res->code != 200;
+        #diag "result: " . Dumper $t->tx->res->json;
 
         # update
         my $new_title = "テストタイトル2テストテスト";
@@ -94,7 +118,7 @@ EOT
           ->status_is(200)
           ->json_has('/id')
           ->json_is('/type' => "story");
-        diag $t->tx->res->json->{message} if $t->tx->res->code != 200;
+        #diag $t->tx->res->json->{message} if $t->tx->res->code != 200;
 
         # get updated item
         $t->get_ok("/api/v1/story?stoid=$stoid")
index a46beca..4876af9 100644 (file)
@@ -67,6 +67,15 @@ subtest 'create/select/update/latest stories' => sub {
     ok($story, "select story by stoid");
     is($story->{introtext}, $new_text, "update story correctly");
 
+
+    # set/delete related
+    $rs = $stories->add_related_story(stoid => $stoid,
+                                      related_stoid => $stoid);
+    ok($rs, "add related story");
+    my $related = $stories->get_related_items(stoid => $stoid);
+    ok($related && @$related, "get related items");
+    is($related->[0]->{stoid}, $stoid, "get correct related items");
+
     my $latest = $stories->select(limit => 10, order_by => {create_time => "DESC"});
     ok($latest, "retrive latest stories");
     ok(@$latest > 0, "retrive 1 or more stories");