use Data::Dumper;
use Mojo::Log;
+use bytes qw();
+use Digest::MD5 qw(md5_hex);
+
#========================================================================
=head2 select($query_type, $value)
return $root;
}
+sub generate_signature {
+ my ($self, $comment) = @_;
+ return md5_hex(encode_utf8($comment);
+}
+
+#========================================================================
+
+=head2 create(\%params, \%options)
+
+create comment
+
+=over 4
+
+=item Parameters
+
+=over 4
+
+=item \%options
+
+comment parameters
+
+ * sid
+ * pid
+ * title
+ * comment
+ * tags
+
+=item \%options
+
+options for submission
+
+=back
+
+=item Return value
+
+ARRAY of story contents
+
+=back
+
+=cut
+
+sub create {
+ my ($self, $params, $user, $extra_params, $opts) = @_;
+ if (!$params || !$user) {
+ return undef;
+ }
+ $opts ||= {};
+ $extra_params ||= {};
+
+ my $subject_orig = 'yes';
+
+ # if pid given, validate them
+ if ($params->{pid}) {
+ my $parent = $self->select(cid => $params->{pid});
+ if (!$parent) {
+ # ERROR: pid not exists
+ }
+ if ($parent->{sid} != $params->{sid}) {
+ # ERROR: pid is other story's comment
+ }
+ # TODO: check visibility
+ # check title is reply?
+ if ($params->{title} =~ /$parent->{subject}/) {
+ $subject_orig = 'no';
+ }
+ }
+ my $subject_max_length = 50;
+ my $subject = substr $params->{subject}, 0, $subject_max_length;
+ my $signature = $self->generate_signature($params->{comment});
+
+ my $sql = <<"EOSQL";
+INSERT INTO comments
+ (sid, pid, date, ipid, subnetid, subject, subject_orig, uid, points, pointsorig, pointsmax,
+ signature, len, karma_bonus, karma, karma_abs)
+ VALUE
+ (?, ?, NOW(),?, ?, ?, ?, ?, ?, ?, ?,
+ ?, ?, ?, ?, ?)
+EOSQL
+
+ my $dbh = $self->connect_db({AutoCommit => 0,});
+ $rs = $dbh->do($sql, undef,
+ $param->{sid},
+ $param->{pid},
+ $user->{ipid},
+ $user->{subnetid},
+ $subject, #subject
+ $subject_orig, #subject_orig
+ $user->{uid},
+ $param->{points} || 0,
+ $param->{points} || 0, #pointsorig
+ $param->{points} || 0, #pointsmax
+ $signature, #signature,
+ bytes::length $param->{comment}, #len
+ $param->{karma_bonus} || 'no',
+ $param->{karma} || 0,
+ $param->{karma_abs} || 0
+ );
+ if (!$rs) {
+ # error occured
+ $self->set_errorno($dbh->{mysql_errno});
+ $self->set_error("mysql_error");
+ $dbh->rollback;
+ $dbh->disconnect;
+ return undef;
+ }
+ my $cid = $dbh->last_insert_id(undef, undef, undef, undef);
+ $sql = "INSERT INTO comment_text (cid, comment) VALUE (?, ?)";
+ $rs = $dbh->do($sql, undef, $cid, $params->{comment});
+ if (!$rs) {
+ # error occured
+ $self->set_errorno($dbh->{mysql_errno});
+ $self->set_error("mysql_error");
+ $dbh->rollback;
+ $dbh->disconnect;
+ return undef;
+ }
+
+ # update discussion
+ $sql = "UPDATE discussions SET commentcount = commentcount + 1 WHERE id = ?";
+ $rs = $dbh->do($sql, undef, $sid);
+ if (!$rs) {
+ # error occured
+ $self->set_errorno($dbh->{mysql_errno});
+ $self->set_error("mysql_error");
+ $dbh->rollback;
+ $dbh->disconnect;
+ return undef;
+ }
+
+ # create firehose item
+ $self->firehose_createItemFromComment($cid);
+
+ $dbh->commit;
+ $dbh->disconnect;
+ return $cid;
+}
+
+
+sub firehose_createItemFromComment {
+ my($self, $cid) = @_;
+ my $comment = $self->getComment($cid);
+ my $text = $self->getCommentText($cid);
+ my $globjid = $self->getGlobjidCreate("comments", $cid);
+
+ # Set initial popularity scores -- we'll be forcing a quick
+ # recalculation of them so these scores don't much matter.
+ my($popularity, $editorpop, $neediness);
+ $popularity = $self->getEntryPopularityForColorLevel(7);
+ $editorpop = $self->getEntryPopularityForColorLevel(7);
+ $neediness = $self->getEntryPopularityForColorLevel(6);
+
+ # recalculation of them so these scores don't much matter.
+ my($popularity, $editorpop, $neediness);
+ $popularity = $self->getEntryPopularityForColorLevel(7);
+ $editorpop = $self->getEntryPopularityForColorLevel(7);
+ $neediness = $self->getEntryPopularityForColorLevel(6);
+
+ my $data = {
+ uid => $comment->{uid},
+ public => "yes",
+ title => $comment->{subject},
+ introtext => $text,
+ ipid => $comment->{ipid},
+ subnetid => $comment->{subnetid},
+ type => "comment",
+ srcid => $comment->{cid},
+ popularity => $popularity,
+ editorpop => $editorpop,
+ globjid => $globjid,
+ discussion => $comment->{sid},
+ createtime => $comment->{date},
+ };
+ my $fhid = $self->createFireHose($data);
+
+ if (!isAnon($comment->{uid})) {
+ my $constants = getCurrentStatic();
+ my $tags = getObject('Slash::Tags');
+ $tags->createTag({
+ uid => $comment->{uid},
+ name => $constants->{tags_upvote_tagname},
+ globjid => $globjid,
+ private => 1,
+ });
+ }
+
+ my $tagboxdb = getObject('Slash::Tagbox');
+ if ($tagboxdb) {
+ for my $tbname (qw( FireHoseScores FHEditorPop CommentScoreReason )) {
+ my $tagbox = $tagboxdb->getTagboxes($tbname);
+ next unless $tagbox;
+ $tagbox->{object}->forceFeederRecalc($globjid);
+ }
+ }
+
+ return $fhid;
+}
+
+
+
+sub saveComment {
+ #my($comm, $comment, $user, $discussion, $error_message, $submit_type) = @_; # probably $comm = $form
+ #my $slashdb = getCurrentDB();
+ #my $constants = getCurrentStatic();
+ #my $reader = getObject('Slash::DB', { db_type => 'reader' });
+
+ #$submit_type ||= "none";
+
+ $comm->{nobonus} = $user->{nobonus} unless $comm->{nobonus_present};
+ $comm->{postanon} = $user->{postanon} unless $comm->{postanon_present};
+ $comm->{nosubscriberbonus} = $user->{nosubscriberbonus}
+ unless $comm->{nosubscriberbonus_present};
+
+#print STDERR scalar(localtime) . " $$ E header_emitted=$header_emitted do_emit_html=$do_emit_html redirect_to=" . (defined($redirect_to) ? $redirect_to : "undef") . "\n";
+
+ # Set starting points to the AC's starting points, by default.
+ # If the user is posting under their own name, we'll reset this
+ # value (and add other modifiers) in a moment.
+ my $pts = getCurrentAnonymousCoward('defaultpoints');
+ my $karma_bonus = 0;
+ my $subscriber_bonus = 0;
+ my $tweak = 0;
+
+ if (!$comm->{anon}) {
+ $pts = $user->{defaultpoints};
+
+ if ($constants->{karma_posting_penalty_style} == 0) {
+ $pts-- if $user->{karma} < 0;
+ $pts-- if $user->{karma} < $constants->{badkarma};
+ } else {
+ $tweak-- if $user->{karma} < 0;
+ $tweak-- if $user->{karma} < $constants->{badkarma};
+ }
+ # Enforce proper ranges on comment points.
+ my($minScore, $maxScore) =
+ ($constants->{comment_minscore}, $constants->{comment_maxscore});
+ $pts = $minScore if $pts < $minScore;
+ $pts = $maxScore if $pts > $maxScore;
+ $karma_bonus = 1 if $pts >= 1 && $user->{karma} > $constants->{goodkarma}
+ && !$comm->{nobonus};
+ $subscriber_bonus = 1 if $constants->{plugin}{Subscribe}
+ && $user->{is_subscriber}
+ && (!$comm->{nosubscriberbonus} || $comm->{nosubscriberbonus} ne 'on');
+ }
+
+#print STDERR scalar(localtime) . " $$ F header_emitted=$header_emitted do_emit_html=$do_emit_html\n";
+
+ my $clean_comment = {
+ subject => $comment->{subject},
+ comment => $comment->{comment},
+ sid => $comment->{sid},
+ pid => $comment->{pid},
+ ipid => $user->{ipid},
+ subnetid => $user->{subnetid},
+ uid => $comment->{uid},
+ points => $pts,
+ tweak => $tweak,
+ tweak_orig => $tweak,
+ karma_bonus => $karma_bonus ? 'yes' : 'no',
+ };
+
+ if ($constants->{plugin}{Subscribe}) {
+ $clean_comment->{subscriber_bonus} = $subscriber_bonus ? 'yes' : 'no';
+ }
+
+ my $maxCid = $slashdb->createComment($clean_comment);
+ $slashdb->saveCommentInfo($maxCid, $submit_type);
+ if ($constants->{comment_karma_disable_and_log}) {
+ my $post_str = "";
+ $post_str .= "NO_ANON " if $user->{state}{commentkarma_no_anon};
+ $post_str .= "NO_POST " if $user->{state}{commentkarma_no_post};
+ if (isAnon($comment->{uid}) && $user->{state}{commentkarma_no_anon}) {
+ $slashdb->createCommentLog({
+ cid => $maxCid,
+ logtext => "COMMENTKARMA ANON: $post_str"
+ });
+ } elsif (!isAnon($comment->{uid}) && $user->{state}{commentkarma_no_post}) {
+ $slashdb->createCommentLog({
+ cid => $maxCid,
+ logtext => "COMMENTKARMA USER: $post_str"
+ });
+ }
+ }
+ if ($constants->{comment_is_troll_disable_and_log}) {
+ $slashdb->createCommentLog({
+ cid => $maxCid,
+ logtext => "ISTROLL"
+ });
+ }
+
+#print STDERR scalar(localtime) . " $$ G maxCid=$maxCid\n";
+
+ # make the formkeys happy
+ $comm->{maxCid} = $maxCid;
+
+ $slashdb->setUser($user->{uid}, {
+ '-expiry_comm' => 'expiry_comm-1',
+ }) if allowExpiry();
+
+ if ($maxCid == -1) {
+ $$error_message = getError('submission error');
+ return -1;
+
+ } elsif (!$maxCid) {
+ # This site has more than 2**32 comments? Wow.
+ $$error_message = getError('maxcid exceeded');
+ return -1;
+ }
+
+
+ my $saved_comment = $slashdb->getComment($maxCid);
+ slashHook('comment_save_success', { comment => $saved_comment });
+
+ my $moddb = getObject("Slash::$constants->{m1_pluginname}");
+ if ($moddb) {
+ my $text = $moddb->checkDiscussionForUndoModeration($comm->{sid});
+ print $text if $text && !discussion2($user);
+ }
+
+ my $tc = $slashdb->getVar('totalComments', 'value', 1);
+ $slashdb->setVar('totalComments', ++$tc);
+
+
+ if ($discussion->{sid}) {
+ $slashdb->setStory($discussion->{sid}, { writestatus => 'dirty' });
+ }
+
+ $slashdb->setUser($clean_comment->{uid}, {
+ -totalcomments => 'totalcomments+1',
+ }) if !isAnon($clean_comment->{uid});
+
+ my($messages, $reply, %users);
+ my $kinds = $reader->getDescriptions('discussion_kinds');
+ if ($comm->{pid}
+ || $kinds->{ $discussion->{dkid} } =~ /^journal/
+ || $constants->{commentnew_msg}) {
+ $messages = getObject('Slash::Messages');
+ $reply = $slashdb->getCommentReply($comm->{sid}, $maxCid);
+ }
+
+ $clean_comment->{pointsorig} = $clean_comment->{points};
+
+ # reply to comment
+ if ($messages && $comm->{pid}) {
+ my $parent = $slashdb->getCommentReply($comm->{sid}, $comm->{pid});
+ my $users = $messages->checkMessageCodes(MSG_CODE_COMMENT_REPLY, [$parent->{uid}]);
+ if (_send_comment_msg($users->[0], \%users, $pts, $clean_comment)) {
+ my $data = {
+ template_name => 'reply_msg',
+ template_page => 'comments',
+ subject => {
+ template_name => 'reply_msg_subj',
+ template_page => 'comments',
+ },
+ reply => $reply,
+ parent => $parent,
+ discussion => $discussion,
+ };
+ $messages->create($users->[0], MSG_CODE_COMMENT_REPLY, $data);
+ $users{$users->[0]}++;
+ }
+ }
+
+ # reply to journal
+ if ($messages && ($kinds->{ $discussion->{dkid} } eq "journal" || $kinds->{ $discussion->{dkid} } =~ /^journal/ && $constants->{messages_send_journal_story_comments})) {
+ my $users = $messages->checkMessageCodes(MSG_CODE_JOURNAL_REPLY, [$discussion->{uid}]);
+ if (_send_comment_msg($users->[0], \%users, $pts, $clean_comment)) {
+ my $data = {
+ template_name => 'journrep',
+ template_page => 'comments',
+ subject => {
+ template_name => 'journrep_subj',
+ template_page => 'comments',
+ },
+ reply => $reply,
+ discussion => $discussion,
+ };
+
+ $messages->create($users->[0], MSG_CODE_JOURNAL_REPLY, $data);
+ $users{$users->[0]}++;
+ }
+ }
+
+ # comment posted
+ if ($messages && $constants->{commentnew_msg}) {
+ my $users = $messages->getMessageUsers(MSG_CODE_NEW_COMMENT);
+
+ my $data = {
+ template_name => 'commnew',
+ template_page => 'comments',
+ subject => {
+ template_name => 'commnew_subj',
+ template_page => 'comments',
+ },
+ reply => $reply,
+ discussion => $discussion,
+ };
+
+ my @users_send;
+ for my $usera (@$users) {
+ next if $users{$usera};
+ push @users_send, $usera;
+ $users{$usera}++;
+ }
+ $messages->create(\@users_send, MSG_CODE_NEW_COMMENT, $data) if @users_send;
+ }
+
+ my $achievements = getObject('Slash::Achievements');
+ if ($achievements) {
+ $achievements->setUserAchievement('comment_posted', $user->{uid});
+ }
+
+ my $dynamic_blocks = getObject('Slash::DynamicBlocks');
+ if ($dynamic_blocks) {
+ $dynamic_blocks->setUserBlock('comments', $user->{uid});
+ }
+
+ if ($constants->{validate_html}) {
+ my $validator = getObject('Slash::Validator');
+ my $test = parseDomainTags($comment->{comment});
+ $validator->isValid($test, {
+ data_type => 'comment',
+ data_id => $maxCid,
+ message => 1
+ }) if $validator;
+ }
+
+ return $saved_comment;
+}
+
1;