LJSUP-17669: Login.bml form refactoring
[livejournal.git] / cgi-bin / LJ / Event / JournalNewComment.pm
blob14fadec78825f7ffc090760fc704a53dd7b31515
1 package LJ::Event::JournalNewComment;
2 use strict;
3 use Scalar::Util qw(blessed);
4 use Class::Autouse qw(LJ::Comment LJ::HTML::Template);
5 use Carp qw(croak);
6 use LJ::Client::BitLy;
7 use LJ::AntiSpam;
8 use base 'LJ::Event';
10 # we don't allow subscriptions to comments on friends' journals, so
11 # setting undef on this skips some nasty queries
12 sub zero_journalid_subs_means { undef }
14 sub new {
15 my ($class, $comment) = @_;
16 croak 'Not an LJ::Comment' unless blessed $comment && $comment->isa("LJ::Comment");
17 return $class->SUPER::new($comment->journal, $comment->jtalkid);
20 sub priority { 5 }
21 sub is_common { 1 }
23 my @_ml_strings_en = (
24 'esn.mail_comments.fromname.user', # "[[user]] - [[sitenameabbrev]] Comment",
25 'esn.mail_comments.fromname.anonymous', # "[[sitenameshort]] Comment",
26 'esn.mail_comments.subject.edit_reply_to_your_comment', # "Edited reply to your comment...",
27 'esn.mail_comments.subject.reply_to_your_comment', # "Reply to your comment...",
28 'esn.mail_comments.subject.edit_reply_to_your_entry', # "Edited reply to your entry...",
29 'esn.mail_comments.subject.reply_to_your_entry', # "Reply to your entry...",
30 'esn.mail_comments.subject.edit_reply_to_an_entry', # "Edited reply to an entry...",
31 'esn.mail_comments.subject.reply_to_an_entry', # "Reply to an entry...",
32 'esn.mail_comments.subject.edit_reply_to_a_comment', # "Edited reply to a comment...",
33 'esn.mail_comments.subject.reply_to_a_comment', # "Reply to a comment...",
34 'esn.mail_comments.subject.comment_you_posted', # "Comment you posted...",
35 'esn.mail_comments.subject.comment_you_edited', # "Comment you edited...",
36 # as_im
37 'esn.mail_comments.alert.user_edited_reply_to_your_comment', #[[user]] edited a reply to your comment.
38 'esn.mail_comments.alert.user_edited_reply_to_a_comment', #[[user]] edited a reply to a comment.
39 'esn.mail_comments.alert.user_reply_to_your_comment', #[[user]] replied to your comment.
40 'esn.mail_comments.alert.user_reply_to_a_comment', #[[user]] replied to a comment.
41 'esn.mail_comments.alert.user_edited_reply_to_your_post', #[[user]] edited a reply to your post.
42 'esn.mail_comments.alert.user_edited_reply_to_a_post', #[[user]] edited a reply to a post.
43 'esn.mail_comments.alert.user_reply_to_your_post', #[[user]] replied to your post.
44 'esn.mail_comments.alert.user_reply_to_a_post', #[[user]] replied to a post.
45 'esn.mail_comments.alert.anonymous_edited_reply_to_your_comment', #Anonymous user edited a reply to your comment.
46 'esn.mail_comments.alert.anonymous_edited_reply_to_a_comment', #Anonymous user edited a reply to a comment.
47 'esn.mail_comments.alert.anonymous_reply_to_your_comment', #Anonymous user replied to your comment.
48 'esn.mail_comments.alert.anonymous_reply_to_a_comment', #Anonymous user replied to a comment.
49 'esn.mail_comments.alert.anonymous_edited_reply_to_your_post', #Anonymous user edited a reply to your post.
50 'esn.mail_comments.alert.anonymous_edited_reply_to_a_post', #Anonymous user edited a reply to a post.
51 'esn.mail_comments.alert.anonymous_reply_to_your_post', #Anonymous user replied to your post.
52 'esn.mail_comments.alert.anonymous_reply_to_a_post', #Anonymous user replied to a post.
54 'esn.journal_new_comment.actions.link', # Link
55 'esn.journal_new_comment.actions.reply', # Reply
56 'esn.journal_new_comment.comment.params.body', # New <a href="[[url]]">comment</a> [[subject]] [[poster]] on [[in_text]] in [[journal]].
57 'esn.journal_new_comment.comment.params.subject', # Someone has posted new comment
58 'esn.journal_new_comment.del.comment.params.body', # (Deleted comment in [[journal]])
59 'esn.journal_new_comment.del.comment.params.subject', # (Deleted comment)
60 'esn.journal_new_comment.del.entry.params.body', # (Comment on a deleted entry in [[journal]])
61 'esn.journal_new_comment.del.entry.params.subject', # (Comment on a deleted entry)
62 'esn.journal_new_comment.edited.params.body', # Edited <a href="[[url]]">comment</a> [[subject]] [[poster]] on [[in_text]] in [[journal]].
63 'esn.journal_new_comment.edited.params.subject', # Someone has edited comment
64 'esn.journal_new_comment.noauth.params.body', # (You are not authorized to view this comment)
65 'esn.journal_new_comment.noauth.params.subject', # (You are not authorized to view this comment)
68 sub as_email_from_name {
69 my ($self, $u) = @_;
71 my $lang = $u->prop('browselang');
73 my $vars = {
74 user => $self->comment->poster ? $self->comment->poster->display_username : '',
75 sitenameabbrev => $LJ::SITENAMEABBREV,
76 sitenameshort => $LJ::SITENAMESHORT,
79 my $key = 'esn.mail_comments.fromname.';
80 if($self->comment->poster) {
81 $key .= 'user';
82 } else {
83 $key .= 'anonymous';
86 return LJ::Lang::get_text($lang, $key, undef, $vars);
89 sub as_email_headers {
90 my ($self, $u) = @_;
92 my $this_msgid = $self->comment->email_messageid;
93 my $top_msgid = $self->comment->entry->email_messageid;
95 my $par_msgid;
96 if ($self->comment->parent) { # a reply to a comment
97 $par_msgid = $self->comment->parent->email_messageid;
98 } else { # reply to an entry
99 $par_msgid = $top_msgid;
100 $top_msgid = ""; # so it's not duplicated
103 my $journalu = $self->comment->entry->journal;
104 my $headers = {
105 'Message-ID' => $this_msgid,
106 'In-Reply-To' => $par_msgid,
107 'References' => "$top_msgid $par_msgid",
108 'X-LJ-Journal' => $journalu->user,
111 return $headers;
115 sub as_email_subject {
116 my ($self, $u) = @_;
118 my $edited = $self->comment->is_edited;
119 my $lang = $u->prop('browselang');
121 my $filename = $self->template_file_for(section => 'subject', lang => $lang);
122 if ($filename) {
123 # Load template file into template processor
124 my $t = LJ::HTML::Template->new(filename => $filename);
125 $t->param(subject => $self->comment->subject_html);
126 return $t->output;
129 my $key = 'esn.mail_comments.subject.';
130 if ( my $comment_subject = $self->comment->subject_orig ) {
131 return LJ::strip_html($comment_subject);
132 } elsif (LJ::u_equals($self->comment->poster, $u)) {
133 $key .= $edited ? 'comment_you_edited' : 'comment_you_posted';
134 } elsif ($self->comment->parent) {
135 if ($edited) {
136 $key .= LJ::u_equals($self->comment->parent->poster, $u) ? 'edit_reply_to_your_comment' : 'edit_reply_to_a_comment';
137 } else {
138 $key .= LJ::u_equals($self->comment->parent->poster, $u) ? 'reply_to_your_comment' : 'reply_to_a_comment';
140 } else {
141 if ($edited) {
142 $key .= LJ::u_equals($self->comment->entry->poster, $u) ? 'edit_reply_to_your_entry' : 'edit_reply_to_an_entry';
143 } else {
144 $key .= LJ::u_equals($self->comment->entry->poster, $u) ? 'reply_to_your_entry' : 'reply_to_an_entry';
148 my $ml_params = {};
149 if ( my $entry_subject = $self->comment->entry->subject_raw ) {
150 $key .= '.entry_subject';
151 $ml_params->{'subject'} = LJ::strip_html($entry_subject);
154 return LJ::Lang::get_text( $lang, $key, undef, $ml_params );
157 sub as_email_string {
158 my ($self, $u) = @_;
159 my $comment = $self->comment or return "(Invalid comment)";
161 my $filename = $self->template_file_for(section => 'body_text', lang => $u->prop('browselang'));
162 if ($filename) {
163 # Load template file into template processor
164 my $t = LJ::HTML::Template->new(filename => $filename);
166 return $comment->format_template_text_mail($u, $t) if $t;
169 return $comment->format_text_mail($u);
172 sub as_email_html {
173 my ($self, $u) = @_;
174 my $comment = $self->comment or return "(Invalid comment)";
176 my $filename = $self->template_file_for(section => 'body_html', lang => $u->prop('browselang'));
177 if ($filename) {
178 # Load template file into template processor
179 my $t = LJ::HTML::Template->new(filename => $filename);
181 return $comment->format_template_html_mail($u, $t) if $t;
184 return $comment->format_html_mail($u);
187 sub as_string {
188 my ($self, $u) = @_;
189 my $comment = $self->comment;
190 my $journal = $comment->entry->journal->user;
192 return "There is a new anonymous comment in $journal at " . $comment->url
193 unless $comment->poster;
195 my $poster = $comment->poster->display_username;
196 if ($self->comment->is_edited) {
197 return "$poster has edited a comment in $journal at " . $comment->url;
198 } else {
199 return "$poster has posted a new comment in $journal at " . $comment->url;
203 # 'esn.mail_comments.alert.user_edited_reply_to_your_comment', #[[user]] edited a reply to your comment.
204 # 'esn.mail_comments.alert.user_edited_reply_to_a_comment', #[[user]] edited a reply to a comment.
205 # 'esn.mail_comments.alert.user_reply_to_your_comment', #[[user]] replied to your comment.
206 # 'esn.mail_comments.alert.user_reply_to_a_comment', #[[user]] replied to a comment.
207 # 'esn.mail_comments.alert.user_edited_reply_to_your_post', #[[user]] edited a reply to your post.
208 # 'esn.mail_comments.alert.user_edited_reply_to_a_post', #[[user]] edited a reply to a post.
209 # 'esn.mail_comments.alert.user_reply_to_your_post', #[[user]] replied to your post.
210 # 'esn.mail_comments.alert.user_reply_to_a_post', #[[user]] replied to a post.
211 # 'esn.mail_comments.alert.anonymous_edited_reply_to_your_comment', #Anonymous user edited a reply to your comment.
212 # 'esn.mail_comments.alert.anonymous_edited_reply_to_a_comment', #Anonymous user edited a reply to a comment.
213 # 'esn.mail_comments.alert.anonymous_reply_to_your_comment', #Anonymous user replied to your comment.
214 # 'esn.mail_comments.alert.anonymous_reply_to_a_comment', #Anonymous user replied to a comment.
215 # 'esn.mail_comments.alert.anonymous_edited_reply_to_your_post', #Anonymous user edited a reply to your post.
216 # 'esn.mail_comments.alert.anonymous_edited_reply_to_a_post', #Anonymous user edited a reply to a post.
217 # 'esn.mail_comments.alert.anonymous_reply_to_your_post', #Anonymous user replied to your post.
218 # 'esn.mail_comments.alert.anonymous_reply_to_a_post', #Anonymous user replied to a post.
220 sub as_alert {
221 my $self = shift;
222 my $u = shift;
224 # TODO: [[post]] [[reply]] etc
225 my $comment = $self->comment;
226 my $user = $comment->poster ? $comment->poster->ljuser_display() : '(Anonymous user)';
227 my $edited = $comment->is_edited;
229 return LJ::Lang::get_text($u->prop('browselang'),
230 'esn.mail_comments.alert.' .
231 ($comment->poster ? 'user' : 'anonymous') . '_' .
232 ($edited ? 'edited_' : '') . 'reply_' .
233 (LJ::u_equals($comment->entry->poster, $u) ? 'to_your' : 'to_a') . '_' .
234 ($comment->parent ? 'comment' : 'post'), undef,
236 user => $user,
237 openlink => '<a href="' . $comment->url . '">',
238 closelink => '</a>',
242 sub as_sms {
243 my ($self, $u, $opt) = @_;
245 my $user = $self->comment->poster ? $self->comment->poster->display_username(1) : '(Anonymous user)';
246 my $edited = $self->comment->is_edited;
248 my $parent = $self->comment->parent;
249 my $entry = $self->comment->entry;
250 my $lang = $u->prop('browselang') || $LJ::DEFAULT_LANG;
252 my ($ml_key, $ml_params);
253 if ($self->event_journal->journaltype eq 'C') {
254 if ($parent) {
255 if ($edited) {
256 $ml_key = LJ::u_equals($parent->poster, $u) ?
257 'sms.communityentryreply.edit_reply_your_comment' : 'sms.communityentryreply.edit_reply_a_comment';
258 } else {
259 $ml_key = LJ::u_equals($parent->poster, $u) ?
260 'sms.communityentryreply.replied_your_comment' : 'sms.communityentryreply.replied_a_comment';
262 } else {
263 if ($edited) {
264 $ml_key = LJ::u_equals($entry->poster, $u) ?
265 'sms.communityentryreply.edit_reply_your_post' : 'sms.communityentryreply.edit_reply_a_post';
266 } else {
267 $ml_key = LJ::u_equals($entry->poster, $u) ?
268 'sms.communityentryreply.replied_your_post' : 'sms.communityentryreply.replied_a_post';
271 $ml_params = { user => $user, community => $self->event_journal->user };
272 } else {
273 if ($parent) {
274 if ($edited) {
275 $ml_key = LJ::u_equals($parent->poster, $u)
276 ? 'sms.journalnewcomment.edit_reply_your_comment' : 'sms.journalnewcomment.edit_reply_a_comment';
277 } else {
278 $ml_key = LJ::u_equals($parent->poster, $u)
279 ? 'sms.journalnewcomment.replied_your_comment' : 'sms.journalnewcomment.replied_a_comment';
281 } else {
282 if ($edited) {
283 $ml_key = LJ::u_equals($entry->poster, $u)
284 ? 'sms.journalnewcomment.edit_reply_your_post' : 'sms.journalnewcomment.edit_reply_a_post';
285 } else {
286 $ml_key = LJ::u_equals($entry->poster, $u)
287 ? 'sms.journalnewcomment.replied_your_post' : 'sms.journalnewcomment.replied_a_post';
290 $ml_params = { user => $user };
293 my $msg = LJ::Lang::get_text($lang, $ml_key, undef, $ml_params);
294 #/read/user/%username%/%post_ID%/comments/%comment_ID%#comments
295 my $tinyurl = "http://m.livejournal.com/read/user/".$self->event_journal->user."/".$entry->ditemid."/comments/".$self->comment->dtalkid;
296 my $mparms = $opt->{mobile_url_extra_params};
297 $tinyurl .= '?' . join('&', map {$_ . '=' . $mparms->{$_}} keys %$mparms) if $mparms;
298 $tinyurl .= "#comments";
299 $tinyurl = LJ::Client::BitLy->shorten($tinyurl);
300 undef $tinyurl if $tinyurl =~ /^500/;
301 return $msg . " " . $tinyurl;
304 sub content {
305 my ($self, $target) = @_;
307 my $comment = $self->comment;
309 return undef unless $comment && $comment->valid;
310 return undef unless $comment->entry && $comment->entry->valid;
311 return undef unless $comment->visible_to($target);
312 return undef if $comment->is_deleted;
314 LJ::need_res('js/commentmanage.js');
316 my $comment_body = $comment->body_html;
317 my $buttons = $comment->manage_buttons;
318 my $dtalkid = $comment->dtalkid;
320 $comment_body =~ s/\n/<br \/>/g;
322 my $ret = qq {
323 <div id="ljcmt$dtalkid" class="JournalNewComment">
324 <div class="ManageButtons">$buttons</div>
325 <div class="Body">$comment_body</div>
326 </div>
329 my $cmt_info = $comment->info;
330 my $cmt_info_js = LJ::js_dumper($cmt_info) || '{}';
332 my $posterusername = $self->comment->poster ? $self->comment->poster->{user} : "";
334 $ret .= qq {
335 <script language="JavaScript">
338 while (my ($k, $v) = each %$cmt_info) {
339 $k = LJ::ejs($k);
340 $v = LJ::ejs($v);
341 $ret .= "LJ_cmtinfo['$k'] = '$v';\n";
344 my $dtid_cmt_info = {u => $posterusername, rc => []};
346 $ret .= "LJ_cmtinfo['$dtalkid'] = " . LJ::js_dumper($dtid_cmt_info) . "\n";
348 $ret .= qq {
349 </script>
351 $ret .= $self->as_html_actions;
353 return $ret;
356 sub as_html {
357 my ($self, $target) = @_;
359 my $comment = $self->comment;
360 my $journal = $self->u;
362 return sprintf("(Deleted comment in %s)", $journal->ljuser_display)
363 unless $comment && $comment->valid && !$comment->is_deleted;
365 my $entry = $comment->entry;
366 return sprintf("(Comment on a deleted entry in %s)", $journal->ljuser_display)
367 unless $entry && $entry->valid;
369 return "(You are not authorized to view this comment)" unless $comment->visible_to($target);
371 my $ju = LJ::ljuser($journal);
372 my $pu = LJ::ljuser($comment->poster);
373 my $url = $comment->url;
375 my $in_text = '<a href="' . $entry->url . '">an entry</a>';
376 my $subject = $comment->subject_text ? ' "' . $comment->subject_text . '"' : '';
378 my $poster = $comment->poster ? "by $pu" : '';
379 if ($comment->is_edited) {
380 return "Edited <a href=\"$url\">comment</a> $subject $poster on $in_text in $ju.";
381 } else {
382 return "New <a href=\"$url\">comment</a> $subject $poster on $in_text in $ju.";
386 sub tmpl_params {
387 my ($self, $target) = @_;
389 my $comment = $self->comment;
390 my $journal = $self->u;
392 my $lang = $target->prop('browselang') || $LJ::DEFAULT_LANG;
394 return {
395 body => LJ::Lang::get_text($lang, 'esn.journal_new_comment.del.comment.params.body', undef, { journal => $journal->ljuser_display } ),
396 subject => LJ::Lang::get_text($lang, 'esn.journal_new_comment.del.comment.params.subject'),
397 } unless $comment && $comment->valid && !$comment->is_deleted;
399 my $entry = $comment->entry;
400 return {
401 body => LJ::Lang::get_text($lang, 'esn.journal_new_comment.del.entry.params.body', undef, { journal => $journal->ljuser_display } ),
402 subject => LJ::Lang::get_text($lang, 'esn.journal_new_comment.del.entry.params.subject'),
403 } unless $entry && $entry->valid;
405 return {
406 body => LJ::Lang::get_text($lang, 'esn.journal_new_comment.noauth.params.body'),
407 subject => LJ::Lang::get_text($lang, 'esn.journal_new_comment.noauth.params.subject'),
408 } unless $comment->visible_to($target);
410 my $ju = LJ::ljuser($journal);
411 my $pu = LJ::ljuser($comment->poster);
412 my $url = $comment->url;
413 my $reply_url = $comment->reply_url;
415 my $in_text = '<a href="' . $entry->url . '">an entry</a>';
416 my $subject = $comment->subject_text ? ' "' . $comment->subject_text . '"' : '';
417 my $comment_body = $comment->body_html;
418 $comment_body =~ s/\n/<br \/>/g;
420 my $poster = $comment->poster ? "by $pu" : '';
421 if ($comment->is_edited) {
422 return {
423 body => LJ::Lang::get_text($lang, 'esn.journal_new_comment.edited.params.body', undef,
425 url => $url,
426 subject => $subject,
427 poster => $poster,
428 in_text => $in_text,
429 journal => $ju
430 } ),
431 subject => LJ::Lang::get_text($lang, 'esn.journal_new_comment.edited.params.subject'),
432 content => $comment_body,
433 userpic => $comment->poster && $comment->poster->userpic ? $comment->poster->userpic->url : '',
434 actions => [{
435 action_url => $reply_url,
436 action => LJ::Lang::get_text($lang, 'esn.journal_new_comment.actions.reply'),
438 action_url => $url,
439 action => LJ::Lang::get_text($lang, 'esn.journal_new_comment.actions.link'),
442 } else {
443 return {
444 body => LJ::Lang::get_text($lang, 'esn.journal_new_comment.comment.params.body', undef,
446 url => $url,
447 subject => $subject,
448 poster => $poster,
449 in_text => $in_text,
450 journal => $ju
451 } ),
452 subject => LJ::Lang::get_text($lang, 'esn.journal_new_comment.comment.params.subject'),,
453 content => $comment_body,
454 userpic => $comment->poster && $comment->poster->userpic ? $comment->poster->userpic->url : '',
455 actions => [{
456 action_url => $reply_url,
457 action => LJ::Lang::get_text($lang, 'esn.journal_new_comment.actions.reply'),
459 action_url => $url,
460 action => LJ::Lang::get_text($lang, 'esn.journal_new_comment.actions.link'),
466 sub as_html_actions {
467 my ($self) = @_;
469 my $comment = $self->comment;
470 my $url = $comment->url;
471 my $reply_url = $comment->reply_url;
473 my $ret .= "<div class='actions'>";
474 $ret .= " <a href='$reply_url'>Reply</a>";
475 $ret .= " <a href='$url'>Link</a>";
476 $ret .= "</div>";
478 return $ret;
481 # ML-keys and contents of all items used in this subroutine:
482 # 01 event.journal_new_comment.friend=Someone comments in any journal on my friends page
483 # 02 event.journal_new_comment.my_journal=Someone comments in my journal, on any entry
484 # 03 event.journal_new_comment.user_journal=Someone comments in [[user]], on any entry
485 # 04 event.journal_new_comment.user_journal.deleted=Someone comments on a deleted entry in [[user]]
486 # 05 event.journal_new_comment.my_journal.deleted=Someone comments on a deleted entry in my journal
487 # 06 event.journal_new_comment.user_journal.titled_entry=Someone comments on <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
488 # 07 event.journal_new_comment.user_journal.untitled_entry=Someone comments on <a href='[[entryurl]]'>en entry</a> in [[user]]
489 # 08 event.journal_new_comment.my_journal.titled_entry=Someone comments on <a href='[[entryurl]]'>[[entrydesc]]</a> my journal
490 # 09 event.journal_new_comment.my_journal.untitled_entry=Someone comments on <a href='[[entryurl]]'>en entry</a> my journal
491 # 10 event.journal_new_comment.my_journal.titled_entry.titled_thread.user=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by [[posteruser]] in <a href='[[entryurl]]'>[[entrydesc]]</a> on my journal
492 # 11 event.journal_new_comment.my_journal.titled_entry.untitled_thread.user=Someone comments under <a href='[[threadurl]]'>the thread</a> by [[posteruser]] in <a href='[[entryurl]]'>[[entrydesc]]</a> on my journal
493 # 12 event.journal_new_comment.my_journal.titled_entry.titled_thread.me=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by me in <a href='[[entryurl]]'>[[entrydesc]]</a> on my journal
494 # 13 event.journal_new_comment.my_journal.titled_entry.untitled_thread.me=Someone comments under <a href='[[threadurl]]'>the thread</a> by me in <a href='[[entryurl]]'>[[entrydesc]]</a> on my journal
495 # 14 event.journal_new_comment.my_journal.titled_entry.titled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by (Anonymous) in <a href='[[entryurl]]'>[[entrydesc]]</a> on my journal
496 # 15 event.journal_new_comment.my_journal.titled_entry.untitled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>the thread</a> by (Anonymous) in <a href='[[entryurl]]'>[[entrydesc]]</a> on my journal
497 # 16 event.journal_new_comment.my_journal.untitled_entry.titled_thread.user=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by [[posteruser]] in <a href='[[entryurl]]'>en entry</a> on my journal
498 # 17 event.journal_new_comment.my_journal.untitled_entry.untitled_thread.user=Someone comments under <a href='[[threadurl]]'>the thread</a> by [[posteruser]] in <a href='[[entryurl]]'>en entry</a> on my journal
499 # 18 event.journal_new_comment.my_journal.untitled_entry.titled_thread.me=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by me in <a href='[[entryurl]]'>en entry</a> on my journal
500 # 19 event.journal_new_comment.my_journal.untitled_entry.untitled_thread.me=Someone comments under <a href='[[threadurl]]'>the thread</a> by me in <a href='[[entryurl]]'>en entry</a> on my journal
501 # 20 event.journal_new_comment.my_journal.untitled_entry.titled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by (Anonymous) in <a href='[[entryurl]]'>en entry</a> on my journal
502 # 21 event.journal_new_comment.my_journal.untitled_entry.untitled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>the thread</a> by (Anonymous) in <a href='[[entryurl]]'>en entry</a> on my journal
503 # 22 event.journal_new_comment.user_journal.titled_entry.titled_thread.user=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by [[posteruser]] in <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
504 # 23 event.journal_new_comment.user_journal.titled_entry.untitled_thread.user=Someone comments under <a href='[[threadurl]]'>the thread</a> by [[posteruser]] in <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
505 # 24 event.journal_new_comment.user_journal.titled_entry.titled_thread.me=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by me in <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
506 # 25 event.journal_new_comment.user_journal.titled_entry.untitled_thread.me=Someone comments under <a href='[[threadurl]]'>the thread</a> by me in <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
507 # 26 event.journal_new_comment.user_journal.titled_entry.titled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by (Anonymous) in <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
508 # 27 event.journal_new_comment.user_journal.titled_entry.untitled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>the thread</a> by (Anonymous) in <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
509 # 28 event.journal_new_comment.user_journal.untitled_entry.titled_thread.user=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by [[posteruser]] in <a href='[[entryurl]]'>en entry</a> in [[user]]
510 # 29 event.journal_new_comment.user_journal.untitled_entry.untitled_thread.user=Someone comments under <a href='[[threadurl]]'>the thread</a> by [[posteruser]] in <a href='[[entryurl]]'>en entry</a> in [[user]]
511 # 30 event.journal_new_comment.user_journal.untitled_entry.titled_thread.me=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by me in <a href='[[entryurl]]'>en entry</a> in [[user]]
512 # 31 event.journal_new_comment.user_journal.untitled_entry.untitled_thread.me=Someone comments under <a href='[[threadurl]]'>the thread</a> by me in <a href='[[entryurl]]'>en entry</a> in [[user]]
513 # 32 event.journal_new_comment.user_journal.untitled_entry.titled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>[[thread_desc]]</a> by (Anonymous) in <a href='[[entryurl]]'>en entry</a> in [[user]]
514 # 33 event.journal_new_comment.user_journal.untitled_entry.untitled_thread.anonymous=Someone comments under <a href='[[threadurl]]'>the thread</a> by (Anonymous) in <a href='[[entryurl]]'>en entry</a> in [[user]]
515 # -- now, let's begin.
516 sub subscription_as_html {
517 my ($class, $subscr) = @_;
519 my $arg1 = $subscr->arg1;
520 my $arg2 = $subscr->arg2;
521 my $journal = $subscr->journal;
523 my $key = 'event.journal_new_comment';
525 if (!$journal) {
526 ### 01 event.journal_new_comment.friend=Someone comments in any journal on my friends page
527 return LJ::Lang::ml($key . '.friend');
530 my ($user, $journal_is_owner);
531 if (LJ::u_equals($journal, $subscr->owner)) {
532 $user = 'my journal';
533 $key .= '.my_journal';
534 my $journal_is_owner = 1;
535 } else {
536 $user = LJ::ljuser($journal);
537 $key .= '.user_journal';
538 my $journal_is_owner = 0;
541 if ($arg1 == 0 && $arg2 == 0) {
542 ### 02 event.journal_new_comment.my_journal=Someone comments in my journal, on any entry
543 ### 03 event.journal_new_comment.user_journal=Someone comments in [[user]], on any entry
544 return LJ::Lang::ml($key, { user => $user });
547 # load ditemid from jtalkid if no ditemid
548 my $comment;
549 if ($arg2) {
550 $comment = LJ::Comment->new($journal, jtalkid => $arg2);
551 return "(Invalid comment)" unless $comment && $comment->valid;
552 $arg1 = eval { $comment->entry->ditemid } unless $arg1;
553 return "(Invalid entry [$arg1:$arg2])" if $@;
556 my $entry = LJ::Entry->new($journal, ditemid => $arg1);
557 ### 04 event.journal_new_comment.user_journal.deleted=Someone comments on a deleted entry in [[user]]
558 ### 05 event.journal_new_comment.my_journal.deleted=Someone comments on a deleted entry in my journal
559 return LJ::Lang::ml($key . '.deleted', { user => $user }) unless $entry && $entry->valid;
561 my $entrydesc = $entry->subject_text;
562 if ($entrydesc) {
563 $entrydesc = "\"$entrydesc\"";
564 $key .= '.titled_entry';
565 } else {
566 $entrydesc = "an entry";
567 $key .= '.untitled_entry';
570 my $entryurl = $entry->url;
571 ### 06 event.journal_new_comment.user_journal.titled_entry=Someone comments on <a href='[[entryurl]]'>[[entrydesc]]</a> in [[user]]
572 ### 07 event.journal_new_comment.user_journal.untitled_entry=Someone comments on <a href='[[entryurl]]'>en entry</a> in [[user]]
573 ### 08 event.journal_new_comment.my_journal.titled_entry=Someone comments on <a href='[[entryurl]]'>[[entrydesc]]</a> my journal
574 ### 09 event.journal_new_comment.my_journal.untitled_entry=Someone comments on <a href='[[entryurl]]'>en entry</a> my journal
575 return LJ::Lang::ml($key,
577 user => $user,
578 entryurl => $entryurl,
579 entrydesc => $entrydesc,
580 }) if $arg2 == 0;
582 my $posteru = $comment->poster;
583 my $posteruser;
585 my $threadurl = $comment->url;
586 my $thread_desc = $comment->subject_text;
587 if ($thread_desc) {
588 $thread_desc = "\"$thread_desc\"";
589 $key .= '.titled_thread';
590 } else {
591 $thread_desc = "the thread";
592 $key .= '.untitled_thread';
595 if ($posteru) {
596 if ($journal_is_owner) {
597 $posteruser = LJ::ljuser($posteru);
598 $key .= '.me';
599 } else {
600 $posteruser = LJ::ljuser($posteru);
601 $key .= '.user';
603 } else {
604 $posteruser = "(Anonymous)";
605 $key .= '.anonymous';
608 if ($comment->state eq 'B') {
609 $key .= '.spam';
612 ### 10 ... 33
613 return LJ::Lang::ml($key,
615 user => $user,
616 threadurl => $threadurl,
617 thread_desc => $thread_desc,
618 posteruser => $posteruser,
619 entryurl => $entryurl,
620 entrydesc => $entrydesc,
624 sub matches_filter {
625 my ($self, $subscr) = @_;
627 return 1 if
628 LJ::Event->class($subscr->etypeid) ne __PACKAGE__ ||
629 !$subscr->id;
631 my $sjid = $subscr->journalid;
632 my $ejid = $self->event_journal->{userid};
634 # if subscription is for a specific journal (not a wildcard like 0
635 # for all friends) then it must match the event's journal exactly.
636 return 0 if $sjid && $sjid != $ejid;
638 my ($earg1, $earg2) = ($self->arg1, $self->arg2);
639 my ($sarg1, $sarg2) = ($subscr->arg1, $subscr->arg2);
641 my $comment = $self->comment;
642 my $parent_comment = $comment->parent;
643 my $parent_comment_author = $parent_comment ?
644 $parent_comment->poster : undef;
646 my $entry = $comment->entry;
648 my $watcher = $subscr->owner;
650 return 0 unless
651 $comment->visible_to($watcher) ||
652 LJ::u_equals($parent_comment_author, $watcher);
654 # not a match if this user posted the comment and they don't
655 # want to be notified of their own posts
656 # moreover, getselfemail only applies to email, so if it's not an email
657 # notification, it's not a match either
658 if (LJ::u_equals($comment->poster, $watcher)) {
659 return 0
660 unless $watcher->get_cap('getselfemail')
661 && $watcher->prop('opt_getselfemail')
662 && $subscr->ntypeid == LJ::NotificationMethod::Email->ntypeid;
665 # watching a specific journal
666 if ($sarg1 == 0 && $sarg2 == 0) {
667 # if this is a community, maintainer gets notified no matter
668 # what entry settings are
669 return 1 unless LJ::u_equals($entry->journal, $entry->poster);
671 # if this is their own journal and they selected not to be notified
672 # of comments to this specific entry, well, don't notify them
673 return 0 if $entry->prop('opt_noemail');
675 return 1;
678 my $wanted_ditemid = $sarg1;
679 # a (journal, dtalkid) pair identifies a comment uniquely, as does
680 # a (journal, ditemid, dtalkid pair). So ditemid is optional. If we have
681 # it, though, it needs to be correct.
682 return 0 if $wanted_ditemid && $entry->ditemid != $wanted_ditemid;
684 # watching a post
685 return 1 if $sarg2 == 0;
687 # watching a thread
688 my $wanted_jtalkid = $sarg2;
689 while ($comment) {
690 return 1 if $comment->jtalkid == $wanted_jtalkid;
691 $comment = $comment->parent;
693 return 0;
696 sub jtalkid {
697 my $self = shift;
698 return $self->arg1;
701 # when was this comment posted or edited?
702 sub eventtime_unix {
703 my $self = shift;
704 my $cmt = $self->comment;
706 my $time = $cmt->is_edited ? $cmt->edit_time : $cmt->unixtime;
707 return $cmt ? $time : $self->SUPER::eventtime_unix;
710 sub comment {
711 my $self = shift;
712 return LJ::Comment->new($self->event_journal, jtalkid => $self->jtalkid);
715 sub available_for_user {
716 my ($self, $u) = @_;
718 my $journal = $self->event_journal;
719 my ($arg1, $arg2) = ($self->arg1, $self->arg2);
721 # user can always track all comments to their own journal
722 if (LJ::u_equals($journal, $u) && !$arg1 && !$arg2) {
723 return 1;
726 # user does not receive notification if entry is not visible to user
727 if (my $comment = $self->comment) {
728 if (my $entry = $comment->entry) {
729 unless ($entry->visible_to($u)) {
730 return 0;
735 # user can always track comments to a specific entry
736 if ($arg1) {
737 return 1;
740 # user can track comments left to a thread if and only if they have a paid
741 # account
742 if ($arg2) {
743 return $u->get_cap('track_thread') ? 1 : 0;
746 # user can track all comments to their community journal, provided
747 # that the community is paid
748 if ($u && $u->can_manage($journal)) {
749 return $journal->get_cap('maintainer_track_comments') ? 1 : 0;
752 return 0;
755 sub is_subscription_visible_to { 1 }
757 sub get_disabled_pic {
758 my ($self, $u) = @_;
760 my $journal = $self->event_journal;
762 return LJ::run_hook('esn_community_comments_track_upgrade', $u, $journal) || ''
763 unless ref $self ne 'LJ::Event::JournalNewComment' ||
764 $self->arg1 || $self->arg2 || LJ::u_equals($u, $journal);
766 return $self->SUPER::get_disabled_pic($u);
769 # return detailed data for XMLRPC::getinbox
770 sub raw_info {
771 my ($self, $target, $flags) = @_;
772 my $extended = ($flags and $flags->{extended}) ? 1 : 0; # add comments body
774 my $res = $self->SUPER::raw_info;
776 my $comment = $self->comment;
777 my $journal = $self->u;
779 $res->{journal} = $journal->user;
781 return { %$res, action => 'deleted' }
782 unless $comment && $comment->valid && !$comment->is_deleted;
784 my $entry = $comment->entry;
785 return { %$res, action => 'comment_deleted' }
786 unless $entry && $entry->valid;
788 return { %$res, visibility => 'no' } unless $comment->visible_to($target);
790 $res->{entry} = $entry->url;
791 $res->{comment} = $comment->url;
792 if (my $poster = $comment->poster) {
793 $res->{poster} = $poster->user;
794 if($poster->is_identity){
795 my $i = $poster->identity;
796 $res->{'poster_identity_type'} = $i->pretty_type;
797 $res->{'poster_identity_value'} = $i->value;
798 $res->{'poster_identity_url'} = $i->url($poster);
799 $res->{'poster_identity_display'} = $poster->display_name;
802 $res->{subject} = $comment->subject_text;
804 if ($extended){
805 $res->{extended}->{subject_raw} = $comment->subject_raw;
806 $res->{extended}->{body} = $comment->body_raw;
807 $res->{extended}->{dtalkid} = $comment->dtalkid;
810 if ($comment->is_edited) {
811 return { %$res, action => 'edited' };
812 } else {
813 return { %$res, action => 'new' };
817 sub subscriptions {
818 my ($self, %args) = @_;
819 my $cid = delete $args{'cluster'}; # optional
820 my $limit = int delete $args{'limit'}; # optional
821 my $original_limit = int $limit;
823 croak("Unknown options: " . join(', ', keys %args)) if %args;
824 croak("Can't call in web context") if LJ::is_web_context();
826 my $comment = $self->comment;
827 my $parent_comment = $comment->parent;
828 my $entry = $comment->entry;
830 my $comment_author = $comment->poster;
831 my $parent_comment_author = $parent_comment ?
832 $parent_comment->poster :
833 undef;
834 my $entry_author = $entry->poster;
835 if (!$entry_author) {
836 warn "No entry author for entry " . $entry->url;
837 return;
840 my $entry_journal = $entry->journal;
842 my @subs;
844 if ($comment_author && $comment->state eq 'B' && $entry_journal->is_personal && $entry_journal->in_class('paid')) {
846 ## Get
847 my @ids = $comment_author->friend_uids;
848 my %is_friend = map { $_ => 1 } @ids; # uid -> 1
850 require LJ::M::ProfilePage;
851 my $pm = LJ::M::ProfilePage->new($comment_author);
852 require LJ::M::FriendsOf;
853 my $fro_m = LJ::M::FriendsOf->new($comment_author,
854 sloppy => 1, # approximate if no summary info
855 mutuals_separate => 0,
856 # TODO: lame that we have to pass this in, but currently
857 # it's not cached on the $u singleton
858 friends => \%is_friend,
859 hide_test_cb => sub {
860 return $pm->should_hide_friendof($_[0]);
863 my $friend_ofs_count = $fro_m->friend_ofs;
865 return if $friend_ofs_count <= $LJ::SPAM_MAX_FRIEND_OFS &&
866 (time() - $comment_author->timecreate) / 86400 <= $LJ::SPAM_MAX_DAYS_CREATED;
868 return if LJ::AntiSpam->is_spam_in_friends_journals($entry_journal, $comment_author);
871 my $acquire_sub_slot = sub {
872 my ($how_much) = @_;
873 $how_much ||= 1;
875 return $how_much unless $original_limit;
877 $how_much = $limit if $limit < $how_much;
879 $limit -= $how_much;
880 return $how_much;
883 croak("Unknown options: " . join(', ', keys %args)) if %args;
884 croak("Can't call in web context") if LJ::is_web_context();
886 my $email_ntypeid = LJ::NotificationMethod::Email->ntypeid;
888 # own comments are deliberately sent to email only
889 if (
890 $comment_author &&
891 (!$cid || $comment_author->clusterid == $cid) &&
892 $comment_author->prop('opt_getselfemail') &&
893 $comment_author->get_cap('getselfemail') &&
894 $acquire_sub_slot->()
896 push @subs, LJ::Subscription->new_from_row({
897 'etypeid' => LJ::Event::JournalNewComment->etypeid,
898 'userid' => $comment_author->id,
899 'ntypeid' => $email_ntypeid,
903 # send a notification to the author of the "parent" comment, if they
904 # want to get it
905 if (
906 $parent_comment && $parent_comment_author &&
907 (!$cid || $parent_comment_author->clusterid == $cid) &&
909 # if they are responding to themselves and wish to get that, we've
910 # already handled it above
911 !LJ::u_equals($comment_author, $parent_comment_author) &&
913 # if parent_comment_author is also the author of the container entry,
914 # we should respect their choice to not get this comment, as set
915 # in the entry properties
916 (!LJ::u_equals($parent_comment_author, $entry_author) ||
917 !$entry->prop('opt_noemail'))
919 my @subs2 = LJ::Subscription->find($parent_comment_author,
920 'event' => 'CommentReply',
921 'require_active' => 1,
924 push @subs2, LJ::Subscription->new_from_row({
925 'etypeid' => LJ::Event::CommentReply->etypeid,
926 'userid' => $parent_comment_author->id,
927 'ntypeid' => $email_ntypeid,
928 }) if $parent_comment_author->{'opt_gettalkemail'} eq 'Y';
930 my $count = scalar(@subs2);
931 if ($count && ($count = $acquire_sub_slot->($count))) {
932 $#subs2 = $count - 1;
933 push @subs, @subs2;
937 # send a notification to the author of the entry, if they
938 # want to get it
939 if (
940 # if they are responding to themselves and wish to get that, we've
941 # already handled it above
942 !LJ::u_equals($comment_author, $entry_author) &&
943 (!$cid || $entry_author->clusterid == $cid) &&
945 !$entry->prop('opt_noemail')
948 if (!LJ::u_equals($entry_author, $entry_journal)) {
949 # community journal
950 my @subs2 = LJ::Subscription->find($entry_author,
951 'event' => 'CommunityEntryReply',
952 'require_active' => 1,
955 my $count = scalar(@subs2);
956 if ($count && ($count = $acquire_sub_slot->($count))) {
957 $#subs2 = $count - 1;
958 push @subs, @subs2;
962 push @subs, LJ::Subscription->new_from_row({
963 'etypeid' => LJ::Event::JournalNewComment->etypeid,
964 'userid' => $entry_author->id,
965 'ntypeid' => $email_ntypeid,
966 }) if
967 $entry_author->{'opt_gettalkemail'} eq 'Y' && $acquire_sub_slot->();
970 return @subs unless ($limit || !$original_limit);
972 # handle tracks as usual
973 push @subs, $self->SUPER::subscriptions(
974 cluster => $cid,
975 limit => $limit
978 return @subs;
981 sub is_tracking {
982 my ($self, $ownerid) = @_;
984 return 1 if $self->arg1 || $self->arg2;
985 return 1 unless $self->event_journal->id == $ownerid;
987 return 0;
991 sub as_push {
992 my $self = shift;
993 my $u = shift;
994 my $lang = shift;
995 my %opts = @_;
997 my $parent = $self->comment->parent;
998 my $entry = $self->comment->entry;
1000 my $subject;
1001 if($subject = $entry->subject_text) {
1003 $subject = (substr $subject, 0, $opts{cut})."..."
1004 if $opts{cut} && length($subject) > $opts{cut};
1006 } else {
1007 $subject = LJ::Lang::get_text($lang, "widget.officialjournals.nosubject")
1010 # tracking event
1011 unless($u->equals($self->event_journal)) {
1013 if($self->event_journal->journaltype eq 'C') {
1015 if($self->comment->parent) {
1016 return LJ::Lang::get_text($lang, "esn.push.notification.eventtrackcommetstreadinentrytitle", 1, {
1017 user => $self->comment->poster->user,
1018 subject => $subject,
1019 poster => $self->comment->parent->poster->user,
1020 journal => $self->event_journal->user,
1023 } else {
1024 return LJ::Lang::get_text($lang, "esn.push.notification.eventtrackcommetsonentrytitle", 1, {
1025 user => $self->comment->poster->user,
1026 subject => $subject,
1027 journal => $self->event_journal->user,
1030 } else {
1031 return LJ::Lang::get_text($lang, "esn.push.notification.eventtrackcommetsonentrytitle", 1, {
1032 user => $self->comment->poster->user,
1033 subject => $subject,
1034 journal => $self->event_journal->user,
1038 } else {
1040 if($parent && LJ::u_equals($parent->poster, $u)) {
1041 return LJ::Lang::get_text($lang, "esn.push.notification.commentreply", 1, {
1042 user => $self->comment->poster->user,
1043 journal => $self->event_journal->user,
1046 } elsif($self->event_journal->journaltype eq 'C') {
1047 return LJ::Lang::get_text($lang, "esn.push.notification.communityentryreply", 1, {
1048 user => $self->comment->poster->user,
1049 community => $self->event_journal->user,
1052 } else {
1053 return LJ::Lang::get_text($lang, "esn.push.notification.journalnewcomment", 1, {
1054 user => $self->comment->poster->user,
1060 sub as_push_payload {
1061 my $self = shift;
1062 my $u = shift;
1063 my $lang = shift;
1065 my $entry = $self->comment->entry;
1066 my $parent = $self->comment->parent;
1068 my $payload = { 'p' => $entry->ditemid,
1069 'c' => $self->comment->dtalkid,
1073 unless($u->equals($self->event_journal)) {
1075 if($self->event_journal->journaltype eq 'C') {
1076 if($parent) {
1077 $payload->{'t'} = 26;
1078 $payload->{'j'} = $self->event_journal->user;
1079 $payload->{'r'} = $self->comment->parent->dtalkid;
1080 return $payload;
1082 } else {
1083 $payload->{'t'} = 25;
1084 $payload->{'j'} = $self->event_journal->user;
1085 return $payload;
1088 } else {
1089 $payload->{'j'} = $self->event_journal->user;
1091 if($parent && LJ::u_equals($parent->poster, $u)) {
1092 $payload->{'t'} = 5;
1093 return $payload;
1094 } elsif($self->event_journal->journaltype eq 'C') {
1095 $payload->{'t'} = 4;
1096 return $payload;
1097 } else {
1098 $payload->{'t'} = 3;
1099 return $payload;
1104 sub update_events_counter {
1105 my $self = shift;
1107 my $comment = $self->comment;
1108 my $journalu = $self->event_journal;
1110 return unless $comment && $comment->valid;
1111 return unless $journalu;
1113 return if $comment->is_deleted || $comment->is_spam;
1115 my $entry = $comment->entry;
1116 my $parent = $comment->parent;
1117 my $jtalkid = $comment->jtalkid;
1118 my $poster = $comment->poster;
1120 return unless $entry && $entry->valid;
1122 return if $poster && ($poster->is_suspended || $poster->is_expunged);
1124 my $jitemid = $entry->jitemid;
1125 my $user = $entry->poster;
1127 my $journalid = $journalu->userid;
1129 return unless $user;
1131 my $pposter = $parent && $parent->poster;
1133 unless ( $user->equals($poster) ) {
1134 LJ::Widget::HomePage::CommentsCounter->add_comment($user, pack("NNN", $journalid, $jitemid, $jtalkid));
1137 if ( $pposter && !$pposter->equals($poster) && !$pposter->equals($user) ) {
1138 LJ::Widget::HomePage::CommentsCounter->add_comment($pposter, pack("NNN", $journalid, $jitemid, $jtalkid));
1141 return;