From 55b0cad8a34b8df3ba9b7588b9c7588d0494a467 Mon Sep 17 00:00:00 2001 From: hylom Date: Sat, 22 Oct 2016 03:09:04 +0900 Subject: [PATCH] Model::Stories: add 'create' method --- src/newslash_web/lib/Newslash/Model/Stories.pm | 457 +++++++++++++++++++++++++ 1 file changed, 457 insertions(+) diff --git a/src/newslash_web/lib/Newslash/Model/Stories.pm b/src/newslash_web/lib/Newslash/Model/Stories.pm index f0fe75d0..1dc4b5ed 100644 --- a/src/newslash_web/lib/Newslash/Model/Stories.pm +++ b/src/newslash_web/lib/Newslash/Model/Stories.pm @@ -187,6 +187,463 @@ EOSQL return $story; } +=head2 create(\%params, $uid) + +create a story. + +=over 4 + +=item Parameters + +=over 4 + +=item \%params + +parameters + +=item $uid + +author's uid + +=back + +=item Return value + +stoid + +=back + +=cut + +sub create { + my ($self, $params, $uid) = @_; + #createStory + +} + +sub createSid { + my ($self, $bogus_sid) = @_; + # yes, this format is correct, don't change it :-) + my $sidformat = '%02d/%02d/%02d/%02d%0d2%02d'; + # Create a sid based on the current time. + my @lt; + my $start_time = time; + if ($bogus_sid) { + # If we were called being told that there's at + # least one sid that is invalid (already taken), + # then look backwards in time until we find it, + # then go one second further. + my $loops = 1000; + while (--$loops) { + $start_time--; + @lt = localtime($start_time); + $lt[5] %= 100; $lt[4]++; # year and month + last if $bogus_sid eq sprintf($sidformat, @lt[reverse 0..5]); + } + if ($loops) { + # Found the bogus sid by looking + # backwards. Go one second further. + $start_time--; + } else { + # Something's wrong. Skip ahead in + # time instead of back (not sure what + # else to do). + $start_time = time + 1; + } + } + @lt = localtime($start_time); + $lt[5] %= 100; $lt[4]++; # year and month + return sprintf($sidformat, @lt[reverse 0..5]); +} + +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}); +} + +######################################################## +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 })) { + $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; +} + + + + + + + + + + =head2 related_link($stoid) get related links. -- 2.11.0