use Newslash::Model::SlashDB;
-use Data::Dumper;
use DateTime;
+use DateTime::Format::MySQL;
+use DateTime::Format::ISO8601;
-#========================================================================
-
-=head2 latest(\%options)
-
-get latest stories.
-
-=over 4
-
-=item Parameters
-
-=over 4
-
-=item \%options
-
-options for query.
-
-$options->{show_future}: when 1, return feature stories. default is 0.
-$options->{limit}: number of stories. default is 10.
-
-=back
-
-=item Return value
-
-ARRAY of story contents
-
-=back
-
-=cut
-
-sub latest {
- my ($self, $options) = @_;
- $options ||= {};
-
- my $show_future = $options->{show_future} || 0;
- my $limit = $options->{limit} || 10;
-
- my $dbh = $self->connect_db;
-
- # get stories
- my $where_clause = 'WHERE stories.time <= NOW()';;
- if ($show_future) {
- $where_clause = '';
- }
-
- my $sql = <<"EOSQL";
-SELECT latest.*, story_text.*, users.nickname as author
- FROM (SELECT * from stories $where_clause ORDER BY time DESC LIMIT ?) AS latest
- LEFT JOIN story_text ON latest.stoid = story_text.stoid
- LEFT JOIN users ON latest.uid = users.uid
-EOSQL
-
- my $sth = $dbh->prepare($sql);
-
- $sth->execute($limit);
- my $rs = $sth->fetchall_arrayref(+{});
-
- if (@$rs == 0) {
- $dbh->disconnect();
- return [];
- }
-
- # get tags
- $sql = <<"EOSQL";
-SELECT story_topics_rendered.*, story_topics_chosen.weight, topics.*
- FROM (SELECT stoid FROM stories $where_clause ORDER BY time DESC LIMIT ?) AS latest
- INNER JOIN story_topics_rendered ON latest.stoid = story_topics_rendered.stoid
- LEFT JOIN story_topics_chosen ON story_topics_rendered.stoid = story_topics_chosen.stoid
- AND story_topics_rendered.tid = story_topics_chosen.tid
- LEFT JOIN topics ON story_topics_rendered.tid = topics.tid
-EOSQL
-
- $sth = $dbh->prepare($sql);
+use Data::Dumper;
+use DateTime;
- $sth->execute($limit);
- my $topics_table = $sth->fetchall_arrayref(+{});
- $sth->finish;
- $dbh->disconnect();
- my $topics = {};
- for my $topic (@$topics_table) {
- if (!$topic->{stoid}) {
- $topics->{$topic->{stoid}} = [];
- }
- push @{$topics->{$topic->{stoid}}}, $topic;
- }
+sub key_definition {
+ return {
+ table => "stories",
+ primary => "stoid",
+ unique => [qw(sid)],
+ datetime => [qw(time last_update day_published
+ stuckendtime archive_last_update
+ )],
+ other => [qw(uid dept hits discussion primaryskid
+ tid submitter commentcount hitparade
+ is_archived in_trash
+ qid body_length word_count sponsor
+ stuck stuckpos fakeemail homepage
+ )],
+ aliases => { user_id => "uid",
+ id => "stoid",
+ create_time => time,
+ }
+ };
+}
- for my $story (@$rs) {
- my $stoid = $story->{stoid};
- $story->{topics} = $topics->{$stoid};
- $self->_generalize($story);
- }
+use constant FACULTIES => { 1000 => [qw(hits hitparade)] };
- return $rs;
+sub count {
+ my $self = shift;
+ my $join = 'LEFT JOIN firehose ON (stories.stoid = firehose.srcid AND firehose.type = "story")';
+ my $where = 'firehose.public != "no"';
+ return $self->generic_count(table => "stories",
+ target => "stoid",
+ timestamp => "time",
+ join => $join,
+ where => $where,
+ @_);
}
+##### sub models
+sub text { return shift->new_instance_of('::Stories::Text'); }
+
#========================================================================
sub select {
my $self = shift;
my $params = {@_};
- my $query_type;
- my $value;
- my $return_single = 0;
-
- for my $k (qw(sid stoid)) {
- if ($params->{$k}) {
- $query_type = $k;
- $value = $params->{$k};
- $return_single = 1;
- }
- }
- my @where_clauses;
- my @query_param;
- if ($query_type && $value) {
- push @where_clauses, "stories.$query_type = ?";
- push @query_param, $value;
+ my $unique_keys = { id => "stories.stoid",
+ story_id => "stories.stoid",
+ stoid => "stories.stoid",
+ sid => "stories.sid",
+ };
+ my $keys = { user_id => "stories.uid",
+ uid => "stories.uid",
+ topic_id => "stories.tid",
+ tid => "stories.tid",
+ discussion_id => "stories.discussion",
+ commentcount => "stories.commentcount",
+ hits => "stories.hits",
+ submitter => "stories.submitter",
+ create_time => "stories.time",
+ update_time => "stories.last_update",
+ public => "firehose.public",
+ };
+ my $datetime_keys = { create_time => "stories.time",
+ update_time => "stories.last_update",
+ };
+ my $timestamp = "stories.time";
+
+ my ($where_clause, $where_values, $unique) = $self->build_where_clause(unique_keys => $unique_keys,
+ keys => $keys,
+ datetime_keys => $datetime_keys,
+ timestamp => $timestamp,
+ params => $params);
+ my ($limit_clause, $limit_values) = $self->build_limit_clause(params => $params);
+ my ($orderby_clause, $orderby_values) = $self->build_order_by_clause(keys => $keys,
+ params => $params);
+
+ # TODO: give reasonable LIMIT Value...
+ $limit_clause = "LIMIT 50" if !$limit_clause;
+
+ # hide future story?
+ my @where_clauses;
+ if ($params->{hide_future}) {
+ push @where_clauses, "stories.time <= NOW()";
}
- # show future story?
- if (!$params->{show_future}) {
- push @where_clauses, "stories.time <= NOW()";
+ # hide non-public story?
+ if ($params->{public_only}) {
+ push @where_clauses, "firehose.public != 'no'";
}
- # target period
- my $date_limit = "";
- for my $term (qw(years months weeks days hours minutes)) {
- if (defined $params->{$term}) {
- my $dt = DateTime->now();
- $dt->subtract($term => $params->{$term});
- $date_limit = $dt->ymd("-") . " " . $dt->hms(":");
+ if (@where_clauses) {
+ if ($where_clause) {
+ $where_clause = $where_clause . " AND ";
}
- }
- if (length $date_limit) {
- push @where_clauses, "stories.time > ?";
- push @query_param, $date_limit;
- }
-
- my @order_clauses;
- # set ORDER BY clause
- my @safe_params = qw(commentcount hits);
- if (defined $params->{order_by}) {
- # check order_by's value
- my $k = $params->{order_by};
- if (grep {$_ eq $k} @safe_params) {
- my $order = "DESC";
- if (defined $params->{order} && $params->{order} eq "ASC") {
- $order = "ASC";
- }
- push @order_clauses, "ORDER BY $k $order";
+ else {
+ $where_clause = "WHERE ";
}
+ $where_clause = $where_clause . join(" AND ", @where_clauses);
}
- # set LIMIT clause
- my $limit_clause = "";
- if (defined $params->{limit}) {
- push @order_clauses, "LIMIT ?";
- push @query_param, $params->{limit};
- }
+ my @attrs;
+ push @attrs, @$where_values, @$limit_values, @$orderby_values;
- my $where_clause = "WHERE " . join("\n AND ", @where_clauses) . "\n";
- my $sub_clauses = $where_clause . join("\n", @order_clauses);
my $dbh = $self->connect_db;
my $sql = <<"EOSQL";
-SELECT stories.*, story_text.*, users.nickname as author
+SELECT stories.*, story_text.*, users.nickname as author, firehose.public,
+ discussions.type AS discussion_type, discussions.commentcount AS comment_count
FROM stories
LEFT JOIN story_text ON stories.stoid = story_text.stoid
LEFT JOIN users ON stories.uid = users.uid
- $sub_clauses
+ LEFT JOIN firehose
+ ON (stories.stoid = firehose.srcid AND firehose.type = "story")
+ LEFT JOIN discussions ON firehose.discussion = discussions.id
+ $where_clause
+ $orderby_clause
+ $limit_clause
EOSQL
+ #warn($sql);
+ #warn(Dumper(@attrs));
+
my $sth = $dbh->prepare($sql);
- $sth->execute(@query_param);
- my $rs = $sth->fetchall_arrayref({});
+ $sth->execute(@attrs);
+ my $stories = $sth->fetchall_arrayref({});
- if (!$rs) {
- $dbh->disconnect();
+ if (!$stories) {
+ $self->disconnect_db();
return;
}
- if (@$rs == 0) {
- $dbh->disconnect();
- return;
+ if (@$stories == 0) {
+ $self->disconnect_db();
+ return $unique ? undef : [];
}
# get tags
$sql = <<"EOSQL";
+SELECT tags.*, tagnames.tagname, target.stoid
+ FROM (SELECT stories.stoid FROM stories
+ LEFT JOIN firehose ON (stories.stoid = firehose.srcid AND firehose.type = "story")
+ $where_clause $orderby_clause $limit_clause) AS target
+ LEFT JOIN globjs
+ ON target.stoid = globjs.target_id
+ LEFT JOIN tags
+ ON globjs.globjid = tags.globjid
+ LEFT JOIN tagnames
+ ON tags.tagnameid = tagnames.tagnameid
+ WHERE globjs.gtid = 1
+EOSQL
+
+ $sth = $dbh->prepare($sql);
+ $sth->execute(@attrs);
+ my $tags_table = $sth->fetchall_arrayref({});
+
+ # get topics
+ $sql = <<"EOSQL";
SELECT story_topics_rendered.*, story_topics_chosen.weight, topics.*
- FROM (SELECT stoid FROM stories $sub_clauses) AS latest
- INNER JOIN story_topics_rendered ON latest.stoid = story_topics_rendered.stoid
- LEFT JOIN story_topics_chosen ON story_topics_rendered.stoid = story_topics_chosen.stoid
- AND story_topics_rendered.tid = story_topics_chosen.tid
- LEFT JOIN topics ON story_topics_rendered.tid = topics.tid
+ FROM (SELECT stories.stoid FROM stories
+ LEFT JOIN firehose ON (stories.stoid = firehose.srcid AND firehose.type = "story")
+ $where_clause $orderby_clause $limit_clause) AS target
+ LEFT JOIN story_topics_rendered
+ ON target.stoid = story_topics_rendered.stoid
+ LEFT JOIN story_topics_chosen
+ ON story_topics_rendered.stoid = story_topics_chosen.stoid
+ AND story_topics_rendered.tid = story_topics_chosen.tid
+ LEFT JOIN topics
+ ON story_topics_rendered.tid = topics.tid
+EOSQL
+
+ $sth = $dbh->prepare($sql);
+ $sth->execute(@attrs);
+ my $topics_table = $sth->fetchall_arrayref({});
+
+ # get params
+ $sql = <<"EOSQL";
+SELECT story_param.*
+ FROM (SELECT stories.stoid FROM stories
+ LEFT JOIN firehose ON (stories.stoid = firehose.srcid AND firehose.type = "story")
+ $where_clause $orderby_clause $limit_clause) AS target
+ LEFT JOIN story_param
+ ON target.stoid = story_param.stoid
EOSQL
$sth = $dbh->prepare($sql);
- $sth->execute(@query_param);
- my $topics_table = $sth->fetchall_arrayref(+{});
- $dbh->disconnect();
+ $sth->execute(@attrs);
+ my $params_table = $sth->fetchall_arrayref({});
+
+ # done
+ $self->disconnect_db();
+
+
+ my $tags = {};
+ for my $tag (@$tags_table) {
+ my $stoid = $tag->{stoid};
+ if (!$tags->{$stoid}) {
+ $tags->{$stoid} = [];
+ }
+ push @{$tags->{$stoid}}, $tag;
+ }
my $topics = {};
for my $topic (@$topics_table) {
- if (!$topic->{stoid}) {
- $topics->{$topic->{stoid}} = [];
+ my $stoid = $topic->{stoid};
+ if (!$topics->{$stoid}) {
+ $topics->{$stoid} = [];
+ }
+ push @{$topics->{$stoid}}, $topic;
+ }
+
+ my $params = {};
+ for my $param (@$params_table) {
+ my $stoid = $param->{stoid};
+ if (!$params->{$stoid}) {
+ $params->{$stoid} = [];
}
- push @{$topics->{$topic->{stoid}}}, $topic;
+ push @{$params->{$stoid}}, $param;
}
- for my $story (@$rs) {
+ for my $story (@$stories) {
my $stoid = $story->{stoid};
- $story->{topics} = $topics->{$stoid};
- $self->_generalize($story);
+ $story->{tags} = $tags->{$stoid} if $tags->{$stoid};
+ $story->{topics} = $topics->{$stoid} if $topics->{$stoid};
+ if ($params->{$stoid}) {
+ for my $param (@{$params->{$stoid}}) {
+ $story->{$param->{name}} = $param->{value};
+ }
+ }
+ $self->_generalize($story, $params);
+ }
+
+ return $stories->[0] if $unique;
+ return $stories;
+}
+
+sub _check_and_regularize_params {
+ my ($self, $params) = @_;
+ my $msg;
+
+ if (defined $params->{title}) {
+ if (length($params->{title}) > $self->{options}->{Story}->{title_max_byte}) {
+ $msg = "title too long. max: $self->{options}->{Story}->{title_max_byte} bytes";
+ $self->set_error($msg, -1);
+ return;
+ }
+ }
+
+ $params->{commentstatus} = $params->{commentstatus} || $params->{comment_status} || "enabled";
+ if (defined $params->{commentstatus}) {
+ if (!grep /\A$params->{commentstatus}\z/, qw(disabled
+ enabled
+ friends_only
+ friends_fof_only
+ no_foe
+ no_foe_eof
+ logged_in)) {
+ $msg = "invalid comment_status";
+ $self->set_error($msg, -1);
+ return;
+ }
}
- return $rs->[0] if $return_single;
- return $rs;
+ # check timestamp. use ISO8601 style timestamp like: 2006-08-14T02:34:56-0600
+ if ($params->{time}) {
+ my $rex_timestamp = qr/
+ ^(\d+)-(\d+)-(\d+)\D+(\d+):(\d+):(\d+(?:\.\d+)?) # datetime
+ (?:Z|([+-])(\d+):(\d+))?$ # tz
+ /xi;
+ if ($params->{time} =~ $rex_timestamp) {
+ $params->{time} = "$1-$2-$3 $4:$5:$6";
+ }
+ }
+
+ return 1;
+}
+
+sub _set_tags_from_topics {
+ my ($self, $user, $stoid, $topics) = @_;
+
+ return if !$stoid;
+ return if !$topics;
+
+ my $globjs = $self->new_instance_of("Newslash::Model::Globjs");
+ my $globj_id = $globjs->getGlobjidFromTargetIfExists("stories", $stoid);
+ if ($globj_id) {
+ # set tags
+ my $tags = $self->new_instance_of("Tags");
+ for my $tid (keys %$topics) {
+ 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
+ }
+ }
+ return $stoid;
}
+=head2 update
+
+this implementation uses old slash's updateStory($sid, $data),
+$sid is takable sid or stoid.
+
+=cut
+
+sub update {
+ #my ($self, $params, $user, $extra_params, $opts) = @_;
+ my $self = shift;
+ my $params = {@_};
+
+ # check id
+ my $id = $params->{stoid} || $params->{story_id} || $params->{id};
+ if (!$id) {
+ $self->set_error("story id not 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;
+
+ $self->_set_tags_from_topics($params->{user}, $stoid, $params->{topics_chosen});
+ return $stoid;
+}
+
+sub update2 {
+ my $self = shift;
+ my $params = {@_};
+ return $self->generic_update(params => $params);
+}
+
+
=head2 create(\%params, $uid)
create a story.
=cut
sub create {
- my ($self, $params, $user, $extra_params, $opts) = @_;
+ #my ($self, $params, $user, $extra_params, $opts) = @_;
+ my $self = shift;
+ return if $self->check_readonly;
+
+ my $params = {@_};
+ my $user = $params->{user};
# check parameters
my $msg = "";
- $msg = "no title" if !$params->{title};
- $msg = "no title" if !$params->{title};
- $msg = "no uid" if !$params->{uid};
- $msg = "no topics" if !defined $params->{topics_chosen};
+ $msg = "no_title" if !$params->{title};
+ $msg = "no_introtext" if !$params->{introtext} || $params->{intro_text};
+ $msg = "no_topics" if !defined $params->{topics_chosen};
+ $msg = "invalid_user" if !defined $user->{uid};
if (length($params->{title}) > $self->{options}->{Story}->{title_max_byte}) {
$msg = "title too long. max: $self->{options}->{Story}->{title_max_byte} bytes";
}
- $params->{commentstatus} ||= "enabled";
+ $params->{commentstatus} = $params->{commentstatus} || $params->{comment_status} || "enabled";
if (!grep /\A$params->{commentstatus}\z/, qw(disabled enabled friends_only friends_fof_only no_foe no_foe_eof logged_in)) {
- $msg = "invalid commentstatus";
+ $msg = "invalid comment_status";
}
# check timestamp. use ISO8601 style timestamp like: 2006-08-14T02:34:56-0600
- my $rex_timestamp = qr/
- ^(\d+)-(\d+)-(\d+)\D+(\d+):(\d+):(\d+(?:\.\d+)?) # datetime
- (?:Z|([+-])(\d+):(\d+))?$ # tz
- /xi;
- if ($params->{time} =~ $rex_timestamp) {
- $params->{time} = "$1-$2-$3 $4:$5:$6";
+ if ($params->{time}) {
+ my $rex_timestamp = qr/
+ ^(\d+)-(\d+)-(\d+)[^ 0-9]+(\d+):(\d+):(\d+(?:\.\d+)?) # datetime
+ (?:Z|([+-])(\d+):(\d+))?$ # tz
+ /xi;
+ if ($params->{time} =~ $rex_timestamp) {
+ my $dt = DateTime::Format::ISO8601->parse_datetime($params->{time});
+ $params->{time} = DateTime::Format::MySQL->format_datetime($dt);
+ }
}
# check parameters finish
$self->set_error($msg, -1);
return;
}
-
$params->{neverdisplay} ||= 0;
+ $params->{submitter} ||= $user->{uid};
+ $params->{uid} = $user->{uid};
- # createStory deletes topics_chosen, so save before.
+ # createStory() deletes topics_chosen, so need to save here.
my $topics_chosen = $params->{topics_chosen};
my $slash_db = Newslash::Model::SlashDB->new($self->{options});
- my $sid = $slash_db->createStory($params);
+ my ($sid, $stoid);
+ if ($params->{update}) {
+ $stoid = $params->{stoid} || $params->{story_id} || $params->{id};
+ $sid = $slash_db->updateStory($stoid, $params);
+ $self->set_error("updateStory failed");
+ return if !$sid;
+ }
+ else {
+ ($sid, $stoid) = $slash_db->createStory($params);
+ }
my $globjs = $self->new_instance_of("Newslash::Model::Globjs");
my $globj_id = $globjs->getGlobjidFromTargetIfExists("stories", $params->{stoid});
if ($globj_id) {
# set tags
- use Newslash::Model::Tags;
- my $tags = $self->new_instance_of("Newslash::Model::Tags");
+ my $tags = $self->new_instance_of("Tags");
for my $tid (keys %$topics_chosen) {
- my $ret = $tags->set_tag(uid => $user->{uid},
+ 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
+ #warn "set_tag fault..." if !$ret
}
}
- return $sid;
+ return wantarray ? ($sid, $stoid) : $stoid;
}
+sub create2 {
+ my $self = shift;
+ my $params = {@_};
+ return $self->generic_insert(params => $params);
+}
+
+sub allocate_sid {
+ my ($self, @params) = @_;
+ my $params = {@params};
+ my $dt = $params->{base_datetime} || DateTime->now;
+
+ # create sid from timestamp
+ # my $sid_format = '%02d/%02d/%02d/%02d%0d2%02d';
+ my $sid_format = '%y/%m/%d/%H%M%S';
+ my $sid = $dt->strftime($sid_format);
+
+ # insert blank story with given sid
+ my $dbh = $self->connect_db;
+ my $sql = "INSERT INTO stories (sid) VALUES (?)";
+
+ my $n = 100; # retry 100 times
+ while (--$n) {
+ my $rs = $dbh->do($sql, undef, $sid);
+ if (!defined $rs) {
+ $self->set_error("sid_insert_error", -1);
+ return;
+ }
+ if ($rs) {
+ my $stoid = $dbh->last_insert_id(undef, undef, undef, undef);
+ $self->disconnect_db;
+ return ($sid, $stoid);
+ }
+
+ # allocate failed, so recreate sid
+ $dt->subtract( seconds => 1 );
+ $sid = $dt->strftime($sid_format);
+ }
+ $self->set_error("sid_allocate_failed", -1);
+ $self->disconnect_db;
+ return;
+}
+
+
+# Legacy API
sub createSid {
my ($self, $bogus_sid) = @_;
# yes, this format is correct, don't change it :-)
return sprintf($sidformat, @lt[reverse 0..5]);
}
-=pod
-
-sub grantStorySubmissionKarma {
- my($self, $story) = @_;
- #my $constants = getCurrentStatic();
- my $db = $self->new_instance_of('LegacyDB');
- #if ($constants->{plugin}{FireHose}) {
- {
- my $fhid;
- #my $firehose = getObject("Slash::FireHose");
- if ($story->{fhid}) {
- $fhid = $story->{fhid};
- } elsif ($story->{subid}) {
- #my $subid_q = $self->sqlQuote($story->{subid});
- #($fhid) = $self->sqlSelect('id', 'firehose', "type='submission' and srcid=$subid_q");
- my $subid_q = $db->sqlQuote($story->{subid});
- ($fhid) = $db->sqlSelect('id', 'firehose', "type='submission' and srcid=$subid_q");
- }
- #$firehose->setFireHose($fhid, { accepted => "yes" }) if $fhid;
- $self->setFireHose($fhid, { accepted => "yes" }) if $fhid;
- }
- return 0 unless $story->{subid};
- #my($submitter_uid) = $self->sqlSelect(
- my($submitter_uid) = $db->sqlSelect(
- 'uid', 'submissions',
- # 'subid=' . $self->sqlQuote($story->{subid})
- 'subid=' . $db->sqlQuote($story->{subid})
- );
-
- if (!isAnon($submitter_uid)) {
- #my $constants = getCurrentStatic();
- my $maxkarma = 50;
- #$self->sqlUpdate('users_info',
- $db->sqlUpdate('users_info',
- { -karma => "LEAST(karma + $constants->{submission_bonus}, $maxkarma)" },
- "uid=$submitter_uid");
-
- #$self->clearRookie($submitter_uid);
- my $users = $self->new_instance_of('Users');
- $users->clearRookie($submitter_uid);
-
- #$self->validateSubmitter($submitter_uid);
- $users->update(target => 'class',
- field => 'validated_submitter',
- now => 1);
- #$self->setUser_delete_memcached($submitter_uid);
- }
-
- #my $submission_info = { del => 2 };
- #$submission_info->{stoid} = $story->{stoid} if $story->{stoid};
- #$submission_info->{sid} = $story->{sid} if $story->{sid};
- #$self->setSubmission($story->{subid}, $submission_info);
- my $submissions = $self->new_instance_of('Submissions');
- $submissions->update(subid => $story->{subid},
- field => 'del',
- value => 2);
- $submissions->upsert_param(params => {
- stoid => $story->{stoid},
- sid => $story->{sid},
- },
- subid => $story->{subid});
-}
+=head2 get_histories
=cut
-########################################################
-
-=pod
-
-sub createStory {
- my($self, $story) = @_;
-
- #my $constants = getCurrentStatic();
- my $db = $self->new_instance_of('LegacyDB');
- #$self->sqlDo("SET AUTOCOMMIT=0");
- $db->sqlDo("SET AUTOCOMMIT=0");
-
- my $error = "";
-
- $story->{submitter} = $story->{submitter} ? $story->{submitter} : $story->{uid};
- $story->{is_dirty} = 1;
-
- if (!defined($story->{title})) {
- $error = "createStory needs a defined title";
- } else {
- # Rather than call truncateStringForCharColumn() here,
- # we prefer to throw an error. Unlike createComment,
- # we would prefer that overlong subjects not be silently
- # chopped off. Consider the consequences of saving a
- # story with the headline "Chris Nandor is a Freakishly
- # Ugly Twisted Criminal, Claims National Enquirer" and
- # later realizing it had been truncated after 50 chars.
- #my $title_len = $self->sqlGetCharColumnLength('story_text', 'title');
- my $title_len = $db->sqlGetCharColumnLength('story_text', 'title');
- if ($title_len && length($story->{title}) > $title_len) {
- $error = "createStory title too long: " . length($story->{title}) . " > $title_len";
- }
- }
-
- my $stoid;
- if (!$error) {
- #$story->{sid} = createSid();
- $story->{sid} = $self->createSid;
- my $sid_ok = 0;
- while ($sid_ok == 0) {
- # we rely on logic in setStory() later to properly
- # set up the data for a story, so we can't someday
- # just change this to do an insert of all the story
- # data, we do need to continue pass it through
- # setStory()
- #$sid_ok = $self->sqlInsert('stories',
- $sid_ok = $db->sqlInsert('stories',
- { sid => $story->{sid} },
- { ignore => 1 } ); # don't need error messages
- if ($sid_ok == 0) { # returns 0E0 on collision, which == 0
- # Keep looking...
- $story->{sid} = $self->createSid($story->{sid});
- }
- }
- # If this came from a submission, update submission and grant
- # karma to the user
- $stoid = $self->getLastInsertId({ table => 'stories', prime => 'stoid' });
- $story->{stoid} = $stoid;
- $self->grantStorySubmissionKarma($story);
- }
-
- if (!$error) {
- #if (! $self->sqlInsert('story_text', { stoid => $stoid })) {
- if (! $db->sqlInsert('story_text', { stoid => $stoid })) {
- $error = "sqlInsert failed for story_text: " . $self->sqlError();
- }
- }
-
- # Write the chosen topics into story_topics_chosen. We do this
- # here because it returns the primaryskid and we will write that
- # into the stories table with setStory in just a moment.
- my($primaryskid, $tids);
- if (!$error) {
- my $success = $self->setStoryTopicsChosen($stoid, $story->{topics_chosen});
- $error = "Failed to set chosen topics for story '$stoid'\n" if !$success;
- }
- if (!$error) {
- my $info_hr = { };
- $info_hr->{neverdisplay} = 1 if $story->{neverdisplay};
- ($primaryskid, $tids) = $self->setStoryRenderedFromChosen($stoid,
- $story->{topics_chosen}, $info_hr);
- $error = "Failed to set rendered topics for story '$stoid'\n" if !defined($primaryskid);
- }
- delete $story->{topics_chosen};
- my $commentstatus = delete $story->{commentstatus};
-
- if (!$error) {
- if ($story->{fhid} && $constants->{plugin}{FireHose}) {
- my $firehose = getObject("Slash::FireHose");
- my $item = $firehose->getFireHose($story->{fhid});
- $firehose->setFireHose($story->{fhid}, { stoid => $stoid });
- if ($item && $item->{type} eq "journal") {
- $story->{discussion} = $item->{discussion};
- $story->{journal_id} = $item->{srcid};
-
- if ($story->{journal_id}) {
- if (!$self->sqlCount("journal_transfer", "id = ".$self->sqlQuote($story->{journal_id}))) {
- $self->sqlInsert("journal_transfer", {
- id => $story->{journal_id}
- });
- }
- }
- }
-
- } elsif ($story->{subid}) {
- if ($self->sqlSelect('id', 'journal_transfer',
- 'subid=' . $self->sqlQuote($story->{subid})
- )) {
- my $sub = $self->getSubmission($story->{subid});
- if ($sub) {
- for (qw(discussion journal_id by by_url)) {
- $story->{$_} = $sub->{$_};
- }
- }
- }
- }
-
- $story->{body_length} = defined($story->{bodytext}) ? length($story->{bodytext}) : 0;
- $story->{word_count} = countWords($story->{introtext}) + countWords($story->{bodytext});
- $story->{primaryskid} = $primaryskid;
- $story->{tid} = $tids->[0];
-
- if (! $self->setStory($stoid, $story)) {
- $error = "setStory failed after creation: " . $self->sqlError();
- }
- }
- if (!$error) {
- my $rootdir;
- if ($story->{primaryskid}) {
-
- my $storyskin = $self->getSkin($story->{primaryskid});
- $rootdir = $storyskin->{rootdir};
- } else {
- # The story is set never-display so its discussion's rootdir
- # probably doesn't matter. Just go with the default.
- my $storyskin = $self->getSkin($constants->{mainpage_skid});
- $rootdir = $storyskin->{rootdir};
- }
- my $comment_codes = $self->getDescriptions('commentcodes_extended');
-
- my $discussion = {
- kind => 'story',
- uid => $story->{uid},
- title => $story->{title},
- primaryskid => $primaryskid,
- topic => $tids->[0],
- url => $self->getUrlFromSid(
- $story->{sid},
- $story->{primaryskid},
- $tids->[0],
- $story->{title}
- ),
- stoid => $stoid,
- sid => $story->{sid},
- commentstatus => $comment_codes->{$commentstatus}
- ? $commentstatus
- : $constants->{defaultcommentstatus},
- ts => $story->{'time'}
- };
-
- my $id;
- if ($story->{discussion} && $story->{journal_id}) {
- # updating now for journals tips off users that this will
- # be a story soon, esp. ts, url, title, kind ... i don't
- # care personally, does it matter? if so we can task some
- # of these changes, if we need to make them -- pudge
-
- # update later in task
- delete @{$discussion}{qw(title url ts)};
- delete $discussion->{uid}; # leave it "owned" by poster
-
- $id = $story->{discussion};
- $discussion->{kind} = 'journal-story';
- $discussion->{type} = 'open'; # should be already
- $discussion->{archivable} = 'yes'; # for good measure
-
- if (!$self->setDiscussion($id, $discussion)) {
- $error = "Failed to set discussion data for story\n";
-
- } elsif ($story->{journal_id}) {
- $self->sqlUpdate('journal_transfer', {
- stoid => $stoid,
- updated => 0,
- }, 'id=' . $self->sqlQuote($story->{journal_id}));
- }
-
- } else {
- $id = $self->createDiscussion($discussion);
- if (!$id) {
- $error = "Failed to create discussion for story";
- }
- }
- if (!$error && !$self->setStory($stoid, { discussion => $id })) {
- $error = "Failed to set discussion '$id' for story '$stoid'\n";
- }
- }
-
- if ($error) {
- # Rollback doesn't even work in 4.0.x, since some tables
- # are non-transactional...
- $self->sqlDo("ROLLBACK");
- $self->sqlDo("SET AUTOCOMMIT=1");
- chomp $error;
- print STDERR scalar(localtime) . " createStory error: $error\n";
- return "";
- }
-
- $self->sqlDo("COMMIT");
- $self->sqlDo("SET AUTOCOMMIT=1");
-
- if ($constants->{plugin}{FireHose}) {
- my $firehose = getObject("Slash::FireHose");
- $firehose->createItemFromStory($stoid);
- }
-
- return $story->{sid};
-}
-
-sub editCreateStory {
- my($self, $preview, $fhitem) = @_;
- my $constants = getCurrentStatic();
- my $user = getCurrentUser();
- my $form = getCurrentForm();
- my $data;
-
- my $tagsdb = getObject("Slash::Tags");
- my $admindb = getObject("Slash::Admin");
-
- my @topics;
- push @topics, $fhitem->{tid} if $fhitem->{tid};
-
- my $chosen_hr = $tagsdb->extractChosenFromTags($fhitem->{globjid}, 'admin');
-
- my $save_extras = $self->getExtrasToSaveForChosen($chosen_hr, $preview);
-
- my $is_sectiononly = $tagsdb->isAdminTagged($fhitem->{globjid}, 'sectiononly');
- $save_extras->{offmainpage} = 1 if $is_sectiononly;
-
- $data = {
- uid => $fhitem->{uid},
- #sid
- title => $preview->{title},
- #section
- submitter => $preview->{submitter},
- topics_chosen => $chosen_hr,
- dept => $fhitem->{dept},
- 'time' => $admindb->findTheTime($fhitem->{createtime}, $preview->{fastforward}),
- bodytext => $preview->{bodytext},
- introtext => $preview->{introtext},
- #relatedtext
- media => $fhitem->{media},
- commentstatus => $preview->{commentstatus},
- thumb => $fhitem->{thumb},
- -rendered => 'NULL',
- neverdisplay => $preview->{neverdisplay},
- sponsor => $preview->{sponsor},
- stuck => $preview->{stuck},
- stuckpos => $preview->{stuckpos},
- stuckendtime => $preview->{stuckendtime},
- };
-
- foreach my $key (keys %$save_extras) {
- $data->{$key} = $save_extras->{$key};
- }
-
- $data->{subid} = $preview->{subid} if $preview->{subid};
- $data->{fhid} = $preview->{fhid} if $preview->{fhid};
-
- for (qw(dept bodytext relatedtext)) {
- $data->{$_} = '' unless defined $data->{$_}; # allow to blank out
- }
-
- for my $field (qw( introtext bodytext media )) {
- local $Slash::Utility::Data::approveTag::admin = 2;
-
- # XXXEdit check this
- $data->{$field} = $self->autoUrl($form->{section}, $data->{$field});
- $data->{$field} = cleanSlashTags($data->{$field});
- $data->{$field} = strip_html($data->{$field});
- $data->{$field} = slashizeLinks($data->{$field});
- $data->{$field} = parseSlashizedLinks($data->{$field});
- $data->{$field} = balanceTags($data->{$field});
- $data->{$field} = adjustStoryTags($data->{$field});
- }
-
- for (qw(dept bodytext relatedtext)) {
- $data->{$_} = '' unless defined $data->{$_}; # allow to blank out
- }
-
- my $sid = $self->createStory($data);
-
- if ($sid) {
- my $st = $self->getStory($sid);
- $self->setRelated($sid);
- slashHook('admin_save_story_success', { story => $data });
- my $stoid = $st->{stoid};
- my $story_globjid = $self->getGlobjidCreate('stories', $stoid);
-
- # XXXEdit Do we have to worry about user editing vs author uid on transfer
- $tagsdb->transferTags($fhitem->{globjid}, $story_globjid);
-
- #Don't automatically signoff with new editor, this makes it automatically disapper for an admin on first refresh
- $self->createSignoff($st->{stoid}, $user->{uid}, "created", { no_filter => 1});
-
- #XXXEdit Tags Auto save?
- my $admindb = getObject("Slash::Admin");
- if ($admindb) {
- $admindb->grantStoryPostingAchievements($data->{uid}, $data->{submitter});
- $admindb->addSpriteForSid($sid);
- }
-
- #XXX Move this to Slash::DB
- my $sfids = $self->sqlSelect('value', 'preview_param', "name = 'sfid' and preview_id = " . $preview->{preview_id});
- if ($sfids && $stoid) {
- $self->sqlUpdate('static_files', { stoid => $stoid, fhid => 0 }, 'fhid = ' . $preview->{preview_fhid});
- }
- }
-
- return $sid;
+sub get_histories {
}
-
-
-
-=cut
-
-
-
-
-
-=head2 related_link($stoid)
+=head2 get_related_items($stoid)
get related links.
=cut
-sub related {
- my ($self, $stoid) = @_;
+sub get_related_items {
+ my $self = shift;
+ my $params = {@_};
+ my $stoid = $params->{stoid} || $params->{story_id} || $params->{id};
+ return if !$stoid;
my $dbh = $self->connect_db;
my $sql = <<"EOSQL";
-SELECT related.*, story_text.title as title2, firehose.srcid
+SELECT related.*,
+ story_text.title as title2,
+ firehose.*,
+ stories.*,
+ topics.*
FROM (
SELECT * FROM related_stories
WHERE stoid = ?
ORDER BY ordernum ASC
) AS related
- LEFT JOIN story_text ON related.rel_stoid = story_text.stoid
+ LEFT JOIN story_text ON story_text.stoid = related.rel_stoid
LEFT JOIN firehose ON firehose.id = related.fhid
+ LEFT JOIN stories ON stories.sid = related.rel_sid
+ LEFT JOIN topics ON topics.tid = stories.tid
EOSQL
my $sth = $dbh->prepare($sql);
$sth->execute($stoid);
my $related = $sth->fetchall_arrayref({});
- $sth->finish;
- $dbh->disconnect();
+ $self->disconnect_db();
for my $r (@$related) {
+ $r->{create_time} = $r->{time};
$r->{title} = $r->{title2} unless $r->{title};
if ($r->{rel_sid}) {
$r->{type} = "story";
$r->{type} = "submission";
$r->{key_id} = $r->{srcid};
}
+ $r->{primary_topic} = {};
+ $r->{primary_topic}->{tid} = $r->{tid};
+ for my $k (qw{keyword textname series image width height
+ submittable searchable storypickable usesprite}) {
+ next if !$r->{$k};
+ $r->{primary_topic}->{$k} = $r->{$k};
+ delete $r->{$k};
+ }
}
return $related;
$sth->finish;
}
- $dbh->disconnect();
+ $self->disconnect_db();
return $params;
}
sub set_dirty {
my ($self, $key, $id) = @_;
+ return if $self->check_readonly;
my $stoid;
if ($key eq 'stoid') {
}
sub _generalize {
- my ($self, $story) = @_;
+ my ($self, $story, $params) = @_;
+ $params ||= {};
- $story->{content_type} = "story";
+ # NTO-nized
+ $story->{id} = $story->{stoid};
+ $story->{story_id} = $story->{stoid};
+ $story->{create_time} = $story->{time};
+ $story->{update_time} = $story->{last_update};
- #my $max_weight = 0;
for my $t (@{$story->{topics}}) {
- if ($t->{tid} == $story->{tid}) {
+ if ($t->{tid} && $t->{tid} == $story->{tid}) {
$story->{primary_topic} = $t;
}
- #if ($t->{weight} && $t->{weight} > $max_weight) {
- # $max_weight = $t->{weight};
- #}
}
+
+ $story->{content_type} = "story";
+ $story->{intro_text} = $story->{introtext};
+ $story->{bodytext} ||= "";
+ $story->{body_text} = $story->{bodytext};
+ if ($story->{body_text}) {
+ $story->{full_text} = join("\n", $story->{intro_text}, $story->{body_text});
+ }
+ else {
+ $story->{full_text} = $story->{intro_text};
+ }
+ $story->{fulltext} = $story->{full_text};
+
+ $story->{discussion_id} = $story->{discussion};
+
+ # no public flag given, public is 'yes'
+ $story->{public} = 'yes' if !$story->{public};
+
+}
+
+# delete story from database
+# this method is for test purpose only.
+sub hard_delete {
+ my $self = shift;
+ return if $self->check_readonly;
+ my $params = {@_};
+
+ my $stoid = $params->{story_id};
+ return if !$stoid;
+
+ my $error = 0;
+ my $sql;
+
+ #my $dbh = $self->connect_db({AutoCommit => 0,});
+ my $dbh = $self->start_transaction;
+ for my $table (qw(stories story_param story_text story_topics_chosen story_topics_rendered)) {
+ my $sql = "DELETE FROM $table WHERE stoid = ?";
+ my $rs = $dbh->do($sql, undef, $stoid);
+ if (!defined $rs || $rs == 0) {
+ Mojo::Log->new->warn("DELETE FROM $table failed. stoid is $stoid.");
+ $error = 1;
+ }
+ }
+
+ # delete from firehose
+ my $firehose = $self->new_instance_of("Firehose");
+ $firehose->hard_delete("story", $stoid);
+
+ $self->commit;
+ return !$error;
+
+ # delete globjs
+ # delete tags
+
+ return;
}
1;