root/branches/release-40/lib/MT/CMS/Comment.pm @ 2642

Revision 2642, 64.6 kB (checked in by auno, 17 months ago)

Fixed to change Spam to Unapproved status properly and also fixed status change error. BugzID:80324

  • Property svn:keywords set to Id Revision
Line 
1package MT::CMS::Comment;
2
3use strict;
4use MT::Util qw( remove_html format_ts relative_date encode_html );
5use MT::I18N qw( const break_up_text substr_text length_text );
6
7sub edit {
8    my $cb = shift;
9    my ($app, $id, $obj, $param) = @_;
10
11    my $q = $app->param;
12    my $blog_id = $q->param('blog_id');
13    my $perms = $app->permissions;
14    my $blog = $app->blog;
15    my $type = $q->param('_type');
16    use Data::Dumper;
17    print STDERR "########".Dumper(\$param);
18    if ($id) {
19        $param->{nav_comments} = 1;
20        $app->add_breadcrumb(
21            $app->translate('Comments'),
22            $app->uri(
23                'mode' => 'list_comment',
24                args   => { blog_id => $blog_id }
25            )
26        );
27        $app->add_breadcrumb( $app->translate('Edit Comment') );
28        $param->{has_publish_access} = 1 if $app->user->is_superuser;
29        $param->{has_publish_access} = (
30            ( $perms->can_manage_feedback || $perms->can_edit_all_posts )
31            ? 1
32            : 0
33        ) unless $app->user->is_superuser;
34        if ( my $entry = $obj->entry ) {
35            my $title_max_len = const('DISPLAY_LENGTH_EDIT_COMMENT_TITLE');
36            $param->{entry_title} =
37              ( !defined( $entry->title ) || $entry->title eq '' )
38              ? $app->translate('(untitled)')
39              : $entry->title;
40            $param->{entry_title} =
41              substr_text( $param->{entry_title}, 0, $title_max_len ) . '...'
42              if $param->{entry_title}
43              && length_text( $param->{entry_title} ) > $title_max_len;
44            $param->{entry_permalink} = $entry->permalink;
45            unless ( $param->{has_publish_access} ) {
46                $param->{has_publish_access} =
47                  ( $perms->can_publish_post
48                      && ( $app->user->id == $entry->author_id ) ) ? 1 : 0;
49            }
50        }
51        else {
52            $param->{no_entry} = 1;
53        }
54        $param->{comment_approved} = $obj->is_published
55          or $param->{comment_pending} = $obj->is_moderated
56          or $param->{is_junk}         = $obj->is_junk;
57
58        $param->{created_on_time_formatted} =
59          format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $obj->created_on(), $blog, $app->user ? $app->user->preferred_language : undef );
60        $param->{created_on_day_formatted} =
61          format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $obj->created_on(), $blog, $app->user ? $app->user->preferred_language : undef );
62
63        $param->{approved}   = $app->param('approved');
64        $param->{unapproved} = $app->param('unapproved');
65        $param->{is_junk}    = $obj->is_junk;
66
67        $param->{entry_class_label} = $obj->entry->class_label;
68        $param->{entry_class} = $obj->entry->class;
69
70        ## Load next and previous entries for next/previous links
71        if ( my $next = $obj->next ) {
72            $param->{next_comment_id} = $next->id;
73        }
74        if ( my $prev = $obj->previous ) {
75            $param->{previous_comment_id} = $prev->id;
76        }
77        if ( $obj->junk_log ) {
78            require MT::CMS::Comment;
79            MT::CMS::Comment::build_junk_table( $app, param => $param, object => $obj );
80        }
81
82        if ( my $cmtr_id = $obj->commenter_id ) {
83            my $cmtr = $app->model('author')->load($cmtr_id);
84            if ($cmtr) {
85                $param->{is_mine} = 1 if $cmtr_id == $app->user->id;
86                $param->{commenter_approved} =
87                  $cmtr->commenter_status( $obj->blog_id ) ==
88                  MT::Author::APPROVED();
89                $param->{commenter_banned} =
90                  $cmtr->commenter_status( $obj->blog_id ) ==
91                  MT::Author::BANNED();
92                $param->{type_author} = 1
93                  if MT::Author::AUTHOR() == $cmtr->type;
94                $param->{commenter_url} = $app->uri(
95                    mode => 'view',
96                    args => { '_type' => 'author', 'id' => $cmtr->id, }
97                  )
98                  if ( MT::Author::AUTHOR() == $cmtr->type )
99                  && $app->user->is_superuser;
100            }
101            if ( $obj->email !~ m/@/ ) {    # no email for this commenter
102                $param->{email_withheld} = 1;
103            }
104        }
105        $param->{invisible_unregistered} = !$obj->visible
106          && !$obj->commenter_id;
107
108        $param->{search_label} = $app->translate('Comments');
109        $param->{object_type}  = 'comment';
110
111        my $children =
112          build_comment_table( $app, load_args =>
113              [ { parent_id => $obj->id }, { direction => 'descend' } ] );
114        $param->{object_loop} = $children if @$children;
115
116        $app->load_list_actions( $type, $param );
117    }
118    1;
119}
120
121sub edit_commenter {
122    my $cb = shift;
123    my ($app, $id, $obj, $param) = @_;
124
125    # We never create commenters through the UI, so this
126    # is really the only valid condition
127    if ($id) {
128        my $q = $app->param;
129        my $blog_id = $q->param('blog_id');
130        my $perms = $app->permissions;
131        my $type = $q->param('_type');
132
133        $param->{is_email_hidden} = $obj->is_email_hidden;
134        $param->{status}          = {
135            MT::Author::PENDING()  => "pending",
136            MT::Author::APPROVED() => "approved",
137            MT::Author::BANNED()   => "banned"
138        }->{ $obj->commenter_status($blog_id) };
139        $param->{"commenter_" . $param->{status}} = 1;
140        $param->{commenter_url} = $obj->url if $obj->url;
141
142        if ( $app->user->is_superuser()
143          || ($perms && $perms->can_administer_blog ) ) {
144            $param->{search_type}   = 'author';
145            $param->{search_label}  = $app->translate('Users');
146        }
147
148        $param->{is_me} = 1 if $obj->id == $app->user->id;
149        $param->{type_author} = 1
150          if MT::Author::AUTHOR() == $obj->type;
151    }
152
153    1;
154}
155
156sub list {
157    my $app = shift;
158
159    my $trim_length =
160      $app->config('ShowIPInformation')
161      ? const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_SHORT')
162      : const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_LONG');
163    my $author_max_len = const('DISPLAY_LENGTH_EDIT_COMMENT_AUTHOR');
164    my $comment_short_len =
165      const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_BREAK_UP_SHORT');
166    my $comment_long_len =
167      const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_BREAK_UP_LONG');
168    my $title_max_len = const('DISPLAY_LENGTH_EDIT_COMMENT_TITLE');
169
170    my ( %entries, %blogs, %cmntrs );
171    my $perms = $app->permissions;
172    my $user  = $app->user;
173    my $admin = $user->is_superuser
174      || ( $perms && $perms->can_administer_blog );
175    my $can_empty_junk = $admin
176      || ( $perms && $perms->can_manage_feedback )
177      ? 1 : 0;
178    my $state_editable = $admin
179      || ( $perms
180        && ( $perms->can_edit_all_posts || $perms->can_manage_feedback ) )
181      ? 1 : 0;
182    my $state_entry_editable = $admin
183      || ( $perms && $perms->can_edit_all_posts )
184      ? 1 : 0;
185    my $state_commenter_editable = $perms
186      && ( $perms->can_publish_post
187        || $perms->can_edit_all_posts || $perms->can_manage_feedback )
188      ? 1 : 0;
189    my $entry_pkg = $app->model('entry');
190    my $code      = sub {
191        my ( $obj, $row ) = @_;
192
193        # Comment column
194        $row->{comment_short} =
195          ( substr_text( $obj->text(), 0, $trim_length )
196              . ( length_text( $obj->text ) > $trim_length ? "..." : "" ) );
197        $row->{comment_short} =
198          break_up_text( $row->{comment_short}, $comment_short_len )
199          ;    # break up really long strings
200        $row->{comment_long} = remove_html( $obj->text );
201        $row->{comment_long} =
202          break_up_text( $row->{comment_long}, $comment_long_len )
203          ;    # break up really long strings
204
205        # Commenter name column
206        $row->{author_display} = $row->{author};
207        $row->{author_display} =
208          substr_text( $row->{author_display}, 0, $author_max_len ) . '...'
209          if $row->{author_display}
210          && length_text( $row->{author_display} ) > $author_max_len;
211        if ( $row->{commenter_id} ) {
212            my $cmntr = $cmntrs{ $row->{commenter_id} } ||=
213              MT::Author->load( { id => $row->{commenter_id} } );
214            if ($cmntr) {
215                $row->{email_hidden} = $cmntr && $cmntr->is_email_hidden();
216                $row->{auth_icon_url} = $cmntr->auth_icon_url;
217
218                my $status = $cmntr->commenter_status( $obj->blog_id );
219                $row->{commenter_approved} =
220                  ( $cmntr->commenter_status( $obj->blog_id ) ==
221                      MT::Author::APPROVED() );
222                $row->{commenter_banned} =
223                  ( $cmntr->commenter_status( $obj->blog_id ) ==
224                      MT::Author::BANNED() );
225            }
226        }
227
228        # Entry column
229        my $entry = $entries{ $obj->entry_id } ||=
230          $entry_pkg->load( $obj->entry_id );
231        unless ($entry) {
232            $entry = $entry_pkg->new;
233            $entry->title( '* ' . $app->translate('Orphaned comment') . ' *' );
234        }
235        $row->{entry_class} = $entry->class;
236        $row->{entry_class_label} = $entry->class_label;
237        $row->{entry_title} = (
238              defined( $entry->title ) ? $entry->title
239            : defined( $entry->text )  ? $entry->text
240            : ''
241        );
242        $row->{entry_title} = $app->translate('(untitled)')
243          if $row->{entry_title} eq '';
244        $row->{entry_title} =
245          substr_text( $row->{entry_title}, 0, $title_max_len ) . '...'
246          if $row->{entry_title}
247          && length_text( $row->{entry_title} ) > $title_max_len;
248
249        # Date column
250        my $blog = $blogs{ $obj->blog_id } ||= $obj->blog;
251        if ( my $ts = $obj->created_on ) {
252            $row->{created_on_time_formatted} =
253              format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
254            $row->{created_on_formatted} =
255              format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
256
257            $row->{created_on_relative} = relative_date( $ts, time, $blog );
258        }
259
260        # Permissions
261        $row->{has_edit_access} = $state_editable
262          || ( $entry && ( $user->id == $entry->author_id ) );
263        $row->{can_edit_entry} = $state_entry_editable
264          || ( $entry && ($user->id == $entry->author_id ) );
265        $row->{can_edit_commenter} = $user->is_superuser ? 1 : 0;
266        if ( !$row->{can_edit_commenter} && $row->{commenter_id} ) {
267            my $cmntr = $cmntrs{ $row->{commenter_id} };
268            if ($cmntr) {
269                $row->{can_edit_commenter} = $cmntr->type eq MT::Author::COMMENTER()
270                  && $state_commenter_editable
271                  ? 1 : 0;
272            }
273        }
274
275        # Blog column
276        if ($blog) {
277            $row->{weblog_id}   = $blog->id;
278            $row->{weblog_name} = $blog->name;
279        }
280        else {
281            $row->{weblog_name} =
282              '* ' . $app->translate('Orphaned comment') . ' *';
283        }
284
285        $row->{reply_count} =
286          $app->model('comment')->count( { parent_id => $obj->id } );
287    };
288
289    my %terms;
290    my $filter_col = $app->param('filter');
291    if ( $filter_col && ( my $val = $app->param('filter_val') ) ) {
292        if ( $filter_col eq 'status' ) {
293            if ( $val eq 'approved' ) {
294                $terms{visible} = 1;
295            }
296            elsif ( $val eq 'pending' ) {
297                $terms{visible} = 0;
298            }
299            elsif ( $val eq 'junk' ) {
300                $terms{junk_status} = MT::Comment::JUNK();
301            }
302            else {
303                $terms{junk_status} = MT::Comment::NOT_JUNK();
304            }
305        }
306    }
307
308    my %param;
309    my $blog_id = $app->param('blog_id');
310    $param{feed_name} = $app->translate("Comments Activity Feed");
311    $param{feed_url} =
312      $app->make_feed_link( 'comment',
313        $blog_id ? { blog_id => $blog_id } : undef );
314    $param{filter_spam} =
315      ( $app->param('filter_key') && $app->param('filter_key') eq 'spam' );
316    $param{has_expanded_mode} = 1;
317    $param{object_type}       = 'comment';
318    $param{screen_id}         = 'list-comment';
319    $param{screen_class}      = 'list-comment';
320    $param{search_label}      = $app->translate('Comments');
321    $param{state_editable}    = $state_editable;
322    $param{can_empty_junk}    = $can_empty_junk;
323    return $app->listing(
324        {
325            type   => 'comment',
326            code   => $code,
327            args   => { sort => 'created_on', direction => 'descend' },
328            params => \%param,
329            terms    => \%terms,
330        }
331    );
332}
333
334# list of all users, regardless of commenter/author on a blog
335sub list_member {
336    my $app = shift;
337
338    my $blog_id = $app->param('blog_id');
339    $app->return_to_dashboard( redirect => 1 )
340      unless $blog_id;
341
342    my $blog  = $app->blog;
343    my $user  = $app->user;
344    my $perms = $app->permissions;
345    return $app->return_to_dashboard( permission => 1 )
346      unless $user->is_superuser() || ($perms && $perms->can_administer_blog());
347
348    my $super_user = 1 if $user->is_superuser();
349    my $args       = {};
350    my $terms      = {};
351    my $param = { list_noncron => 1 };
352    $args->{join} =
353      MT::Permission->join_on( 'author_id', { blog_id => $blog_id, } );
354
355    $args->{sort_order} = 'created_on';
356    $args->{direction}  = 'descend';
357
358    $param->{saved} = 1 if $app->param('saved');
359    $param->{search_label} = $app->translate('Users');
360    $param->{object_type} = 'author';
361
362    my $all_perms;
363    my @all_perms = @{ MT::Permission->perms() };
364    $all_perms = [@all_perms];
365    foreach (@$all_perms) {
366        $_->[1] = $app->translate( $_->[1] );
367    }
368
369    require MT::Association;
370    require MT::Role;
371    my @all_roles = MT::Role->load( undef, { sort => 'name' });
372
373    my $sel_role = 0;
374    my $filter = $app->param('filter') || '';
375    if ($filter eq 'role') {
376        my $val = scalar $app->param('filter_val');
377        if ($val) {
378            $sel_role = $val;
379            $args->{join} = MT::Association->join_on('author_id', { blog_id => $blog_id, role_id => $val });
380        }
381    }
382    elsif ($filter eq 'status') {
383        my $val = $app->param('filter_val');
384        if ($val eq 'disabled') {
385            $terms->{status} = 2;
386        }
387        elsif ($val eq 'pending') {
388            $terms->{status} = 3;
389        }
390        else {
391            $terms->{status} = 1;
392        }
393    }
394
395    my @role_loop;
396    foreach my $r (@all_roles) {
397        push @role_loop, { role_id => $r->id, role_name => $r->name, selected => $r->id == $sel_role };
398    }
399    $param->{role_loop} = \@role_loop;
400    my $hasher = sub {
401        my ( $obj, $row ) = @_;
402        if ( ( $row->{email} || '' ) !~ m/@/ ) {
403            $row->{email} = '';
404        }
405        if ( $row->{created_by} ) {
406            if ( my $created_by = MT::Author->load( $row->{created_by} ) ) {
407                $row->{created_by} = $created_by->name;
408            }
409            else {
410                $row->{created_by} = $app->translate('*User deleted*');
411            }
412        }
413        $row->{is_me}           = $row->{id} == $user->id;
414        $row->{has_edit_access} = 1 if $super_user;
415        $row->{usertype_author} = 1 if $obj->type == MT::Author::AUTHOR();
416        if ( $obj->type == MT::Author::COMMENTER() ) {
417            $row->{usertype_commenter} = 1;
418            $row->{status_trusted} = 1 if $obj->is_trusted($blog_id);
419            if ($row->{name} =~ m/^[a-f0-9]{32}$/) {
420                $row->{name} = $row->{nickname} || $row->{url};
421            }
422        }
423        $row->{status_enabled} = 1 if $obj->status == 1;
424        my @roles = MT::Role->load(undef, { join => MT::Association->join_on('role_id', { author_id => $row->{id}, blog_id => $blog_id }, { unique => 1 })});
425        my @role_loop;
426        foreach my $role (@roles) {
427            my @perms;
428            foreach (@$all_perms) {
429                next unless length( $_->[1] || '' );
430                push @perms, $app->translate( $_->[1] )
431                  if $role->has( $_->[0] );
432            }
433            my $role_perms = join(", ", @perms);
434            push @role_loop, { role_name => $role->name, role_id => $role->id, role_perms => $role_perms };
435        }
436        $row->{role_loop} = \@role_loop;
437        $row->{auth_icon_url} = $obj->auth_icon_url;
438    };
439    $param->{screen_id} = "list-member";
440
441    return $app->listing(
442        {
443            type     => 'user',
444            template => 'list_member.tmpl',
445            terms    => $terms,
446            params   => $param,
447            args     => $args,
448            code     => $hasher,
449        }
450    );
451}
452
453sub list_commenter {
454    my $app = shift;
455    unless ( $app->user_can_admin_commenters ) {
456        return $app->error( $app->translate("Permission denied.") );
457    }
458
459    my $q         = $app->param;
460    my $list_pref = $app->list_pref('commenter');
461    my $blog_id   = $q->param('blog_id');
462    my %param     = %$list_pref;
463    my %terms     = ( type => MT::Author::COMMENTER() );
464    my %terms2    = ();
465    my $limit     = $list_pref->{rows};
466    my $offset    = $q->param('offset') || 0;
467    my %arg;
468    require MT::Comment;
469    $arg{'join'} = MT::Comment->join_on(
470        'commenter_id',
471        { ( $blog_id ? ( blog_id => $blog_id ) : () ) },
472        {
473            'sort'    => 'created_on',
474            direction => 'descend',
475            unique    => 1
476        }
477    );
478    my ( $filter_col, $val );
479    $param{filter_args} = "";
480
481    if (   ( $filter_col = $q->param('filter') )
482        && ( $val = $q->param('filter_val') ) )
483    {
484        if ( !exists( $terms{$filter_col} ) ) {
485            if ( $filter_col eq 'status' ) {
486                my ($perm) = (
487                      $val eq 'neutral'  ? undef
488                    : $val eq 'approved' ? 'comment'
489                    : $val eq 'banned'   ? 'not_comment'
490                    : undef
491                );
492                $arg{join} = MT::Permission->join_on(
493                    'author_id',
494                    {
495                        blog_id => $blog_id,
496                        ( defined $perm ) ? ( permissions => $perm ) : ()
497                    }
498                );
499            }
500            else {
501                $terms{$filter_col} = $val;
502            }
503
504            $param{filter}     = $filter_col;
505            $param{filter_val} = $val;
506            $param{filter_args} = "&filter=" . encode_url($filter_col) . "&filter_val=" . encode_url($val);
507        }
508    }
509    $arg{'offset'} = $offset if $offset;
510    $arg{'limit'} = $limit + 1;
511    my $terms = \%terms;
512    my $arg   = \%arg;
513    my $iter  = MT::Author->load_iter( $terms, $arg );
514
515    my $data = build_commenter_table( $app, iter => $iter, param => \%param );
516    if ( @$data > $limit ) {
517        pop @$data;
518        $param{next_offset}     = 1;
519        $param{next_offset_val} = $offset + $limit;
520    }
521    if ( $offset > 0 ) {
522        $param{prev_offset}     = 1;
523        $param{prev_offset_val} = $offset - $limit;
524        $param{prev_offset_val} = 0 if $param{prev_offset_val} < 0;
525    }
526
527    $param{object_type}  = 'commenter';
528    $param{search_label} = $app->translate('Commenters');
529    $param{list_start}   = $offset + 1;
530    $param{list_end}     = $offset + scalar @$data;
531    $param{offset}       = $offset;
532    $param{limit}        = $limit;
533    delete $arg->{limit};
534    delete $arg->{offset};
535    $param{list_total} = MT::Author->count( $terms, $arg );
536
537    if ( $param{list_total} ) {
538        $param{next_max} = $param{list_total} - $limit;
539        $param{next_max} = 0 if ( $param{next_max} || 0 ) < $offset + 1;
540    }
541    $app->add_breadcrumb( $app->translate('Authenticated Commenters') );
542    $param{nav_commenters} = 1;
543    for my $msg (qw(trusted untrusted banned unbanned)) {
544        $param{$msg} = 1 if $app->param($msg);
545    }
546    return $app->load_tmpl( 'list_commenter.tmpl', \%param );
547}
548
549sub build_commenter_table {
550    my ($app, %args) = @_;
551
552    my $param = $args{param};
553    my $iter;
554    if ( $args{load_args} ) {
555        my $class = $app->model('commenter');
556        $iter = $class->load_iter( @{ $args{load_args} } );
557    }
558    elsif ( $args{iter} ) {
559        $iter = $args{iter};
560    }
561    elsif ( $args{items} ) {
562        $iter = sub { pop @{ $args{items} } };
563    }
564    return [] unless $iter;
565
566    my $app_user  = $app->user;
567    my $user_perm = $app->permissions;
568    my $blog_id   = $app->param('blog_id');
569
570    my @data;
571    require MT::Blog;
572    my $blog = MT::Blog->load($blog_id);
573    while ( my $cmtr = $iter->() ) {
574        require MT::Comment;
575        my $cmt_count = MT::Comment->count(
576            {
577                commenter_id => $cmtr->id,
578                blog_id      => $blog_id
579            }
580        );
581        my $most_recent = MT::Comment->load(
582            {
583                commenter_id => $cmtr->id,
584                blog_id      => $blog_id
585            },
586            {
587                'sort'    => 'created_on',
588                direction => 'descend'
589            }
590        ) if $cmt_count > 0;
591
592        my $blog_connection = MT::Permission->load(
593            {
594                author_id => $cmtr->id,
595                blog_id   => $blog_id
596            }
597        );
598
599        # Tells us whether the commenter is associated with this
600        # blog. the role flags are not important
601        next if ( !$cmt_count && !$blog_connection );
602
603        my $row = {};
604        $row->{author_id}      = $cmtr->id();
605        $row->{author}         = $cmtr->name();
606        $row->{author_display} = $cmtr->nickname();
607        $row->{email}          = $cmtr->email();
608        $row->{url}            = $cmtr->url();
609        $row->{email_hidden}   = $cmtr->is_email_hidden();
610        $row->{comment_count}  = $cmt_count;
611        if ($most_recent) {
612
613            if ( my $ts = $most_recent->created_on ) {
614                $row->{most_recent_time_formatted} =
615                  format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
616                $row->{most_recent_formatted} =
617                  format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
618                $row->{most_recent_relative} =
619                  relative_date( $ts, time, $blog );
620            }
621        }
622        $row->{status} = {
623            PENDING  => "neutral",
624            APPROVED => "approved",
625            BANNED   => "banned"
626        }->{ $cmtr->commenter_status($blog_id) };
627        $row->{commenter_approved} =
628          $cmtr->commenter_status($blog_id) == MT::Author::APPROVED();
629        $row->{commenter_banned} =
630          $cmtr->commenter_status($blog_id) == MT::Author::BANNED();
631        $row->{commenter_url}   = $cmtr->url if $cmtr->url;
632        $row->{has_edit_access} = $app->user_can_admin_commenters;
633        $row->{object}          = $cmtr;
634        push @data, $row;
635    }
636    return [] unless @data;
637
638    $param->{commenter_table}[0]{object_loop} = \@data if @data;
639    $app->load_list_actions( 'commenter', $param->{commenter_table}[0] );
640    $param->{commenter_table}[0]{page_actions} =
641      $app->page_actions('list_commenter');
642    $param->{object_loop} = $param->{commenter_table}[0]{object_loop};
643    \@data;
644}
645
646sub save_commenter_perm {
647    my $app      = shift;
648    my ($params) = @_;
649    my $q        = $app->param;
650    my $action   = $q->param('action');
651
652    $app->validate_magic() or return;
653
654    my $acted_on;
655    my %rebuild_set;
656    my @ids     = $params ? @$params : $app->param('commenter_id');
657    my $blog_id = $q->param('blog_id');
658    my $author  = $app->user;
659
660    foreach my $id (@ids) {
661        ( $id, $blog_id ) = @$id if ref $id eq 'ARRAY';
662
663        my $cmntr = MT::Author->load($id)
664          or return $app->errtrans( "No such commenter [_1].", $id );
665        my $old_status = $cmntr->commenter_status($blog_id);
666
667        if (   $action eq 'trust'
668            && $cmntr->commenter_status($blog_id) != MT::Author::APPROVED() )
669        {
670            $cmntr->approve($blog_id) or return $app->error( $cmntr->errstr );
671            $app->log(
672                $app->translate(
673                    "User '[_1]' trusted commenter '[_2]'.", $author->name,
674                    $cmntr->name
675                )
676            );
677            $acted_on++;
678        }
679        elsif ($action eq 'ban'
680            && $cmntr->commenter_status($blog_id) != MT::Author::BANNED() )
681        {
682            $cmntr->ban($blog_id) or return $app->error( $cmntr->errstr );
683            $app->log(
684                $app->translate(
685                    "User '[_1]' banned commenter '[_2]'.", $author->name,
686                    $cmntr->name
687                )
688            );
689            $acted_on++;
690        }
691        elsif ($action eq 'unban'
692            && $cmntr->commenter_status($blog_id) == MT::Author::BANNED() )
693        {
694            $cmntr->pending($blog_id) or return $app->error( $cmntr->errstr );
695            $app->log(
696                $app->translate(
697                    "User '[_1]' unbanned commenter '[_2]'.", $author->name,
698                    $cmntr->name
699                )
700            );
701            $acted_on++;
702        }
703        elsif ($action eq 'untrust'
704            && $cmntr->commenter_status($blog_id) == MT::Author::APPROVED() )
705        {
706            $cmntr->pending($blog_id) or return $app->error( $cmntr->errstr );
707            $app->log(
708                $app->translate(
709                    "User '[_1]' untrusted commenter '[_2]'.", $author->name,
710                    $cmntr->name
711                )
712            );
713            $acted_on++;
714        }
715
716        require MT::Entry;
717        require MT::Comment;
718        my $iter = MT::Entry->load_iter(
719            undef,
720            {
721                join => MT::Comment->join_on(
722                    'entry_id', { commenter_id => $cmntr->id }
723                )
724            }
725        );
726        my $e;
727        while ( $e = $iter->() ) {
728            $rebuild_set{$id} = $e;
729        }
730    }
731    if ($acted_on) {
732        my %msgs = (
733            trust   => 'trusted',
734            ban     => 'banned',
735            unban   => 'unbanned',
736            untrust => 'untrusted'
737        );
738        $app->add_return_arg( $msgs{$action} => 1 );
739    }
740    $app->call_return;
741}
742
743sub trust_commenter_by_comment {
744    my $app        = shift;
745    my @comments   = $app->param('id');
746    my @commenters = map_comment_to_commenter( $app, \@comments );
747    $app->param( 'action', 'trust' );
748    save_commenter_perm( $app, \@commenters );
749}
750
751sub untrust_commenter_by_comment {
752    my $app        = shift;
753    my @comments   = $app->param('id');
754    my @commenters = map_comment_to_commenter( $app, \@comments );
755    $app->param( 'action', 'untrust' );
756    save_commenter_perm( $app, \@commenters );
757}
758
759sub ban_commenter_by_comment {
760    my $app        = shift;
761    my @comments   = $app->param('id');
762    my @commenters = map_comment_to_commenter( $app, \@comments );
763    $app->param( 'action', 'ban' );
764    save_commenter_perm( $app, \@commenters );
765}
766
767sub unban_commenter_by_comment {
768    my $app        = shift;
769    my @comments   = $app->param('id');
770    my @commenters = map_comment_to_commenter( $app, \@comments );
771    $app->param( 'action', 'unban' );
772    save_commenter_perm( $app, \@commenters );
773}
774
775sub trust_commenter {
776    my $app        = shift;
777    my @commenters = $app->param('id');
778    $app->param( 'action', 'trust' );
779    save_commenter_perm( $app, \@commenters );
780}
781
782sub ban_commenter {
783    my $app        = shift;
784    my @commenters = $app->param('id');
785    $app->param( 'action', 'ban' );
786    save_commenter_perm( $app, \@commenters );
787}
788
789sub unban_commenter {
790    my $app        = shift;
791    my @commenters = $app->param('id');
792    $app->param( 'action', 'unban' );
793    save_commenter_perm( $app, \@commenters );
794}
795
796sub untrust_commenter {
797    my $app        = shift;
798    my @commenters = $app->param('id');
799    $app->param( 'action', 'untrust' );
800    save_commenter_perm( $app, \@commenters );
801}
802
803sub approve_item {
804    my $app   = shift;
805    my $perms = $app->permissions;
806    $app->param( 'approve', 1 );
807    set_item_visible($app);
808}
809
810sub unapprove_item {
811    my $app   = shift;
812    my $perms = $app->permissions;
813    $app->param( 'unapprove', 1 );
814    set_item_visible($app);
815}
816
817sub cfg_comments {
818    my $app     = shift;
819    my $q       = $app->param;
820    my $blog_id = scalar $q->param('blog_id');
821    return $app->return_to_dashboard( redirect => 1 )
822      unless $blog_id;
823    $q->param( '_type', 'blog' );
824    $q->param( 'id',    scalar $q->param('blog_id') );
825    $app->forward( "view",
826        {
827            output         => 'cfg_comments.tmpl',
828            screen_class   => 'settings-screen',
829            screen_id      => 'comment-settings',
830        }
831    );
832}
833
834sub cfg_registration {
835    my $app     = shift;
836    my $q       = $app->param;
837    my $blog_id = scalar $q->param('blog_id');
838    return $app->return_to_dashboard( redirect => 1 )
839      unless $blog_id;
840    $q->param( '_type', 'blog' );
841    $q->param( 'id',    scalar $q->param('blog_id') );
842    eval { require Digest::SHA1; };
843    my $openid_available = $@ ? 0 : 1;
844    $app->forward( "view",
845        {
846            output       => 'cfg_registration.tmpl',
847            screen_class => 'settings-screen registration-screen',
848            openid_enabled => $openid_available
849        }
850    );
851}
852
853sub cfg_spam {
854    my $app = shift;
855    my $q   = $app->param;
856    $q->param( '_type', 'blog' );
857    $q->param( 'id',    scalar $q->param('blog_id') );
858
859    my $plugin_config_html;
860    my $plugin_name;
861    my $plugin;
862    if ( my $p = $q->param('plugin') ) {
863        $plugin = $MT::Plugins{$p};
864        if ($plugin) {
865            $plugin = $plugin->{object};
866        }
867        else {
868            $plugin = MT->component($p);
869        }
870        return $app->errtrans("Invalid request.") unless $plugin;
871
872        my $scope;
873        if ( $q->param('blog_id') ) {
874            $scope = 'blog:' . $q->param('blog_id');
875        }
876        else {
877            $scope = 'system';
878        }
879        $plugin_name = $plugin->name;
880        my %plugin_param;
881        $plugin->load_config( \%plugin_param, $scope );
882        my $snip_tmpl = $plugin->config_template( \%plugin_param, $scope );
883        my $tmpl;
884        if ( ref $snip_tmpl ne 'MT::Template' ) {
885            require MT::Template;
886            $tmpl = MT::Template->new(
887                type   => 'scalarref',
888                source => ref $snip_tmpl
889                ? $snip_tmpl
890                : \$snip_tmpl
891            );
892        }
893        else {
894            $tmpl = $snip_tmpl;
895        }
896
897        # Process template independent of $app to avoid premature
898        # localization (give plugin a chance to do L10N first).
899        $tmpl->param( \%plugin_param );
900        $plugin_config_html = $tmpl->output();
901        $plugin_config_html =
902          $plugin->translate_templatized($plugin_config_html)
903          if $plugin_config_html =~ m/<(?:__trans|mt_trans) /i;
904    }
905    my $filters = MT::Component->registry('junk_filters') || [];
906    my %plugins;
907    foreach my $set (@$filters) {
908        foreach my $f ( values %$set ) {
909            $plugins{ $f->{plugin} } = $f->{plugin};
910        }
911    }
912    my @plugins = values %plugins;
913    my $loop    = [];
914    foreach my $p (@plugins) {
915        push @$loop,
916          {
917            name   => $p->name,
918            plugin => $p->id,
919            active => ( $plugin && ( $p->id eq $plugin->id ) ? 1 : 0 ),
920          },
921          ;
922    }
923    @$loop = sort { $a->{name} cmp $b->{name} } @$loop;
924
925    $app->forward( "view",
926        {
927            plugin_config_html => $plugin_config_html,
928            plugin_name        => $plugin_name,
929            junk_filter_loop   => $loop,
930            output             => 'cfg_spam.tmpl',
931            screen_class       => 'settings-screen spam-screen'
932        }
933    );
934}
935
936sub empty_junk {
937    my $app     = shift;
938    my $perms   = $app->permissions;
939    my $user    = $app->user;
940    my $blog_id = $app->param('blog_id');
941    return $app->errtrans("Permission denied.")
942      if ( !$blog_id && !$user->is_superuser() )
943      || (
944        $perms
945        && !(
946               $perms->can_administer_blog
947            || $perms->can_edit_all_posts
948            || $perms->can_manage_feedback
949        )
950      );
951
952    my $type  = $app->param('_type');
953    my $class = $app->model($type);
954    my $arg   = {};
955    require MT::Comment;
956    $arg->{junk_status} = MT::Comment::JUNK();
957    $arg->{blog_id} = $blog_id if $blog_id;
958    $class->remove($arg);
959    $app->add_return_arg( 'emptied' => 1 );
960    $app->call_return;
961}
962
963sub handle_junk {
964    my $app   = shift;
965    my @ids   = $app->param("id");
966    my $type  = $app->param("_type");
967    my $class = $app->model($type);
968    my @item_loop;
969    my $i       = 0;
970    my $blog_id = $app->param('blog_id');
971    my ( %rebuild_entries, %rebuild_categories );
972
973    my @obj_ids = $app->param('id');
974    if ( my $req_nonce = $app->param('nonce') ) {
975        if ( scalar @obj_ids == 1 ) {
976            my $cmt_id = $obj_ids[0];
977            if ( my $obj = $class->load($cmt_id) ) {
978                my $nonce =
979                  MT::Util::perl_sha1_digest_hex( $obj->id
980                      . $obj->created_on
981                      . $obj->blog_id
982                      . $app->config->SecretToken );
983                return $app->errtrans("Invalid request.")
984                  unless $nonce eq $req_nonce;
985                my $return_args = $app->uri_params(
986                    mode => 'view',
987                    args => {
988                        '_type' => $type,
989                        id      => $cmt_id,
990                        blog_id => $obj->blog_id
991                    }
992                );
993                $return_args =~ s!^\?!!;
994                $app->return_args($return_args);
995            }
996            else {
997                return $app->errtrans("Invalid request.");
998            }
999        }
1000        else {
1001            return $app->errtrans("Invalid request.");
1002        }
1003    }
1004    else {
1005        $app->validate_magic() or return;
1006    }
1007
1008    my $perms = $app->permissions;
1009    my $perm_checked = ( $app->user->is_superuser()
1010      || (
1011        $app->param('blog_id')
1012        && (   $perms->can_manage_feedback
1013            || $perms->can_edit_all_posts )
1014      ) ) ? 1 : 0;
1015
1016    foreach my $id (@ids) {
1017        next unless $id;
1018
1019        my $obj = $class->load($id) or die "No $class $id";
1020        my $old_visible = $obj->visible || 0;
1021        unless ($perm_checked) {
1022            if ( $obj->isa('MT::TBPing') && $obj->parent->isa('MT::Entry') ) {
1023                next if $obj->parent->author_id != $app->user->id;
1024            }
1025            elsif ( $obj->isa('MT::Comment') ) {
1026                next if $obj->entry->author_id != $app->user->id;
1027            }
1028            next unless $perms->can_publish_post;
1029        }
1030        $obj->junk;
1031        $app->run_callbacks( 'handle_spam', $app, $obj )
1032          ;            # mv this into blk below?
1033        $obj->save;    # (so that each cb doesn't have to save indiv'ly)
1034        next if $old_visible == $obj->visible;
1035        if ( $obj->isa('MT::TBPing') ) {
1036            my ( $parent_type, $parent_id ) = $obj->parent_id();
1037            if ( $parent_type eq 'MT::Entry' ) {
1038                $rebuild_entries{$parent_id} = 1;
1039            }
1040            else {
1041                $rebuild_categories{ $obj->category_id } = 1;
1042
1043                # TODO: do something with this list.
1044            }
1045        }
1046        else {
1047            $rebuild_entries{ $obj->entry_id } = 1;
1048        }
1049    }
1050    $app->add_return_arg( 'junked' => 1 );
1051    if (%rebuild_entries) {
1052        $app->rebuild_these( \%rebuild_entries, how => MT::App::CMS::NEW_PHASE() );
1053    }
1054    else {
1055        $app->call_return;
1056    }
1057}
1058
1059sub not_junk {
1060    my $app = shift;
1061
1062    my $perms = $app->permissions;
1063
1064    my @ids = $app->param("id");
1065    my @item_loop;
1066    my $i     = 0;
1067    my $type  = $app->param('_type');
1068    my $class = $app->model($type);
1069    my %rebuild_set;
1070
1071    my $perm_checked = ( $app->user->is_superuser()
1072      || (
1073        $app->param('blog_id')
1074        && (   $perms->can_manage_feedback
1075            || $perms->can_edit_all_posts )
1076      ) ) ? 1 : 0;
1077
1078    foreach my $id (@ids) {
1079        next unless $id;
1080        my $obj = $class->load($id)
1081            or next;
1082        unless ($perm_checked) {
1083            if ( $obj->isa('MT::TBPing') && $obj->parent->isa('MT::Entry') ) {
1084                next if $obj->parent->author_id != $app->user->id;
1085            }
1086            elsif ( $obj->isa('MT::Comment') ) {
1087                next if $obj->entry->author_id != $app->user->id;
1088            }
1089            next unless $perms->can_publish_post;
1090        }
1091        $obj->approve;
1092        $app->run_callbacks( 'handle_ham', $app, $obj );
1093        if ( $obj->isa('MT::TBPing') ) {
1094            my ( $parent_type, $parent_id ) = $obj->parent_id();
1095            if ( $parent_type eq 'MT::Entry' ) {
1096                $rebuild_set{$parent_id} = 1;
1097            }
1098            else {
1099            }
1100        }
1101        else {
1102            $rebuild_set{ $obj->entry_id } = 1;
1103        }
1104        $obj->save();
1105    }
1106    $app->param( 'approve', 1 );
1107
1108    $app->add_return_arg( 'unjunked' => 1 );
1109
1110    $app->rebuild_these( \%rebuild_set, how => MT::App::CMS::NEW_PHASE() );
1111}
1112
1113sub cfg_system_feedback {
1114    my $app = shift;
1115    my %param;
1116    return $app->redirect(
1117        $app->uri(
1118            mode => 'cfg_comments',
1119            args => { blog_id => $app->param('blog_id') }
1120        )
1121    ) if $app->param('blog_id');
1122
1123    return $app->errtrans("Permission denied.")
1124      unless $app->user->is_superuser();
1125
1126    my $cfg = $app->config;
1127    $param{nav_config} = 1;
1128    $app->add_breadcrumb( $app->translate('Feedback Settings') );
1129    $param{nav_settings}         = 1;
1130    $param{comment_disable}      = $cfg->AllowComments ? 0 : 1;
1131    $param{ping_disable}         = $cfg->AllowPings ? 0 : 1;
1132    $param{disabled_notify_ping} = $cfg->DisableNotificationPings ? 1 : 0;
1133    $param{system_no_email}      = 1 unless $cfg->EmailAddressMain;
1134    my $send = $cfg->OutboundTrackbackLimit || 'any';
1135
1136    if ( $send =~ m/^(any|off|selected|local)$/ ) {
1137        $param{ "trackback_send_" . $cfg->OutboundTrackbackLimit } = 1;
1138        if ( $send eq 'selected' ) {
1139            my @domains = $cfg->OutboundTrackbackDomains;
1140            my $domains = join "\n", @domains;
1141            $param{trackback_send_domains} = $domains;
1142        }
1143    }
1144    else {
1145        $param{"trackback_send_any"} = 1;
1146    }
1147    $param{saved}        = $app->param('saved');
1148    $param{screen_class} = "settings-screen system-feedback-settings";
1149    $app->load_tmpl( 'cfg_system_feedback.tmpl', \%param );
1150}
1151
1152sub save_cfg_system_feedback {
1153    my $app = shift;
1154    return $app->errtrans("Permission denied.")
1155      unless $app->user->is_superuser();
1156
1157    $app->validate_magic or return;
1158    my $cfg = $app->config;
1159    $cfg->AllowComments( ( $app->param('comment_disable') ? 0 : 1 ), 1 );
1160    $cfg->AllowPings(    ( $app->param('ping_disable')    ? 0 : 1 ), 1 );
1161    $cfg->DisableNotificationPings(
1162        ( $app->param('disable_notify_ping') ? 1 : 0 ), 1 );
1163    my $send = $app->param('trackback_send') || 'any';
1164    if ( $send =~ m/^(any|off|selected|local)$/ ) {
1165        $cfg->OutboundTrackbackLimit( $send, 1 );
1166        if ( $send eq 'selected' ) {
1167            my $domains = $app->param('trackback_send_domains') || '';
1168            $domains =~ s/[\r\n]+/ /gs;
1169            $domains =~ s/\s{2,}/ /gs;
1170            my @domains = split /\s/, $domains;
1171            $cfg->OutboundTrackbackDomains( \@domains, 1 );
1172        }
1173    }
1174
1175    $cfg->save_config();
1176    $app->redirect(
1177        $app->uri(
1178            'mode' => 'cfg_system_feedback',
1179            args   => { saved => 1 }
1180        )
1181    );
1182}
1183
1184sub reply {
1185    my $app = shift;
1186    my $q   = $app->param;
1187
1188    my $reply_to    = encode_html( $q->param('reply_to') );
1189    my $magic_token = encode_html( $q->param('magic_token') );
1190    my $blog_id     = encode_html( $q->param('blog_id') );
1191    my $return_url  = encode_html( $q->param('return_url') );
1192    my $text        = encode_html( $q->param('text') );
1193    my $indicator   = $app->static_path . 'images/indicator.gif';
1194    my $url         = $app->uri;
1195    <<SPINNER;
1196<html><head><style type="text/css">
1197#dialog-indicator {position: relative;top: 200px;
1198background: url($indicator)
1199no-repeat;width: 66px;height: 66px;margin: 0 auto;
1200}</style><script type="text/javascript">
1201function init() { var f = document.getElementById('f'); f.submit(); }
1202window.setTimeout("init()", 1500);
1203</script></head><body>
1204<div align="center"><div id="dialog-indicator"></div>
1205<form id="f" method="post" action="$url">
1206<input type="hidden" name="__mode" value="do_reply" />
1207<input type="hidden" name="reply_to" value="$reply_to" />
1208<input type="hidden" name="magic_token" value="$magic_token" />
1209<input type="hidden" name="blog_id" value="$blog_id" />
1210<input type="hidden" name="return_url" value="$return_url" />
1211<input type="hidden" name="text" value="$text" />
1212</form></div></body></html>
1213SPINNER
1214}
1215
1216sub do_reply {
1217    my $app = shift;
1218    my $q   = $app->param;
1219
1220    my $param = {
1221        reply_to    => $q->param('reply_to'),
1222        magic_token => $q->param('magic_token'),
1223        blog_id     => $q->param('blog_id'),
1224    };
1225
1226    my ( $comment, $parent, $entry ) = _prepare_reply($app);
1227
1228    $param->{commenter_name} = $parent->author;
1229    $param->{entry_title}    = $entry->title;
1230    $param->{comment_created_on} =
1231      format_ts( "%Y-%m-%d %H:%M:%S", $parent->created_on, undef, $app->user ? $app->user->preferred_language : undef );
1232    $param->{comment_text} = $parent->text;
1233
1234    return $app->build_page( 'dialog/comment_reply.tmpl',
1235        { %$param, error => $app->errstr } )
1236      unless $comment;
1237
1238    $comment->parent_id( $param->{reply_to} );
1239    $comment->approve;
1240    return $app->handle_error(
1241        $app->translate( "An error occurred: [_1]", $comment->errstr() ) )
1242      unless $comment->save;
1243
1244    MT::Util::start_background_task(
1245        sub {
1246            $app->rebuild_entry( Entry => $parent->entry_id, BuildDependencies => 1 )
1247              or return $app->publish_error( "Publish failed: [_1]", $app->errstr );
1248            $app->_send_comment_notification( $comment, q(), $entry,
1249                $app->model('blog')->load( $param->{blog_id} ), $app->user );
1250        }
1251    );
1252    return $app->build_page( 'dialog/comment_reply.tmpl',
1253        { closing => 1, return_url => $q->param('return_url') } );
1254}
1255
1256sub reply_preview {
1257    my $app = shift;
1258    my $q   = $app->param;
1259    my $cfg = $app->config;
1260
1261    my $param = {
1262        reply_to    => $q->param('reply_to'),
1263        magic_token => $q->param('magic_token'),
1264        blog_id     => $q->param('blog_id'),
1265    };
1266    my ( $comment, $parent, $entry ) = _prepare_reply($app);
1267
1268    my $blog = $parent->blog
1269            || $app->model('blog')->load($q->param('blog_id'));
1270    return $app->error($app->translate('Can\'t load blog #[_1].', $q->param('blog_id'))) unless $blog;
1271
1272    require MT::Sanitize;
1273    my $spec = $blog->sanitize_spec
1274            || $app->config->GlobalSanitizeSpec;
1275    $param->{commenter_name} = MT::Sanitize->sanitize($parent->author, $spec);
1276    $param->{entry_title}    = $entry->title;
1277    $param->{comment_created_on} =
1278      format_ts( "%Y-%m-%d %H:%M:%S", $parent->created_on, undef, $app->user ? $app->user->preferred_language : undef );
1279    $param->{comment_text} = MT::Sanitize->sanitize($parent->text, $spec);
1280
1281    return $app->build_page( 'dialog/comment_reply.tmpl',
1282        { %$param, error => $app->errstr } )
1283      unless $comment;
1284
1285    ## Set timestamp as we would usually do in ObjectDriver.
1286    my @ts = MT::Util::offset_time_list( time, $entry->blog_id );
1287    my $ts = sprintf "%04d%02d%02d%02d%02d%02d", $ts[5] + 1900, $ts[4] + 1,
1288      @ts[ 3, 2, 1, 0 ];
1289    $comment->created_on($ts);
1290    $comment->commenter_id( $app->user->id );
1291    $param->{'comment'} = $comment;
1292
1293    require MT::Serialize;
1294    my $ser   = MT::Serialize->new( $cfg->Serializer );
1295    my $state = $comment->column_values;
1296    $state->{static} = $q->param('static');
1297    $param->{'comment_state'} = unpack 'H*', $ser->serialize( \$state );
1298    $param->{'comment_is_static'} = 1;
1299    $param->{'entry'} = $entry;
1300    $param->{'current_timestamp'} = $ts;
1301    $param->{'commenter'} = $app->user;
1302    $param->{'blog_id'} = $parent->blog_id;
1303    $param->{'blog'} = $parent->blog;
1304
1305    return $app->build_page( 'dialog/comment_reply.tmpl',
1306        { %$param, text => $q->param('text') } );
1307}
1308
1309sub dialog_post_comment {
1310    my $app = shift;
1311    $app->validate_magic or return;
1312
1313    my $user      = $app->user;
1314    my $parent_id = $app->param('reply_to');
1315
1316    return $app->errtrans('Parent comment id was not specified.')
1317      unless $parent_id;
1318
1319    my $comment_class = $app->model('comment');
1320    my $parent        = $comment_class->load($parent_id)
1321      or return $app->errtrans('Parent comment was not found.');
1322    return $app->errtrans("You can't reply to unapproved comment.")
1323      unless $parent->is_published;
1324
1325    my $perms = $app->{perms};
1326    return unless $perms;
1327
1328    my $entry_class = $app->_load_driver_for('entry');
1329    my $entry       = $entry_class->load( $parent->entry_id );
1330
1331    unless ( $user->is_superuser()
1332        || $perms->can_edit_all_posts
1333        || $perms->can_manage_feedback )
1334    {
1335        return $app->errtrans("Permission denied.")
1336          unless $perms->can_edit_entry( $entry, $user, 1 )
1337          ;    # check for publish_post
1338    }
1339
1340    my $blog = $parent->blog
1341            || $app->model('blog')->load($app->param('blog_id'));
1342    return $app->error($app->translate('Can\'t load blog #[_1].', $app->param('blog_id'))) unless $blog;
1343
1344    require MT::Sanitize;
1345    my $spec = $blog->sanitize_spec
1346            || $app->config->GlobalSanitizeSpec;
1347    my $param = {
1348        reply_to       => $parent_id,
1349        commenter_name => MT::Sanitize->sanitize($parent->author, $spec),
1350        entry_title    => $entry->title,
1351        comment_created_on =>
1352          format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $parent->created_on, $blog, $app->user ? $app->user->preferred_language : undef ),
1353        comment_text       => MT::Sanitize->sanitize($parent->text, $spec),
1354        comment_script_url => $app->config('CGIPath')
1355          . $app->config('CommentScript'),
1356        return_url => $app->base
1357          . $app->mt_uri . '?'
1358          . $app->param('return_args'),
1359    };
1360
1361    $app->load_tmpl( 'dialog/comment_reply.tmpl', $param );
1362}
1363
1364sub can_view {
1365    my $eh = shift;
1366    my ( $app, $id, $objp ) = @_;
1367    return 0 unless ($id);
1368    my $obj = $objp->force() or return 0;
1369    require MT::Entry;
1370    my $entry = MT::Entry->load( $obj->entry_id )
1371      or return 0;
1372    my $perms = $app->permissions;
1373    if (
1374        !(
1375               $entry->author_id == $app->user->id
1376            || $perms->can_edit_all_posts
1377            || $perms->can_manage_feedback
1378        )
1379      )
1380    {
1381        return 0;
1382    }
1383    1;
1384}
1385
1386sub can_save {
1387    my ( $eh, $app, $id ) = @_;
1388    return 0 unless $id;    # Can't create new comments here
1389    return 1 if $app->user->is_superuser();
1390
1391    my $perms = $app->permissions;
1392    return 1
1393      if $perms
1394      && ( $perms->can_edit_all_posts
1395        || $perms->can_manage_feedback );
1396
1397    my $c = MT::Comment->load($id)
1398        or return 0;
1399    if ( $perms && $perms->can_create_post && $perms->can_publish_post ) {
1400        return $c->entry->author_id == $app->user->id;
1401    }
1402    elsif ( $perms && $perms->can_create_post ) {
1403        return ( $c->entry->author_id == $app->user->id )
1404          && ( ( $c->is_junk && ( 'junk' eq $app->param('status') ) )
1405            || ( $c->is_moderated && ( 'moderate' eq $app->param('status') ) )
1406            || ( $c->is_published && ( 'publish' eq $app->param('status') ) ) );
1407    }
1408    elsif ( $perms && $perms->can_publish_post ) {
1409        return 0 unless $c->entry->author_id == $app->user->id;
1410        return 0
1411          unless ( $c->text eq $app->param('text') )
1412          && ( $c->author eq $app->param('author') )
1413          && ( $c->email  eq $app->param('email') )
1414          && ( $c->url    eq $app->param('url') );
1415    }
1416    else {
1417        return 0;
1418    }
1419}
1420
1421sub can_delete {
1422    my ( $eh, $app, $obj ) = @_;
1423    my $author = $app->user;
1424    return 1 if $author->is_superuser();
1425    my $perms = $app->permissions;
1426    require MT::Entry;
1427    my $entry = MT::Entry->load( $obj->entry_id )
1428        or return 0;
1429    if ( !$perms || $perms->blog_id != $entry->blog_id ) {
1430        $perms ||= $author->permissions( $entry->blog_id );
1431    }
1432
1433    # publish_post allows entry author to delete comment.
1434    return 1
1435      if $perms->can_edit_all_posts
1436      || $perms->can_manage_feedback
1437      || $perms->can_edit_entry( $entry, $author, 1 );
1438    return 0 if $obj->visible;    # otherwise, visible comment can't be deleted.
1439    return $perms && $perms->can_edit_entry( $entry, $author );
1440}
1441
1442sub pre_save {
1443    my $eh = shift;
1444    my ( $app, $obj, $original ) = @_;
1445    my $perms = $app->permissions;
1446    return 1
1447      unless $perms->can_publish_post
1448      || $perms->can_edit_all_posts
1449      || $perms->can_manage_feedback;
1450
1451    unless ( $perms->can_edit_all_posts || $perms->can_manage_feedback ) {
1452        return 1 unless $perms->can_publish_post;
1453        require MT::Entry;
1454        my $entry = MT::Entry->load( $obj->entry_id )
1455          or return 1;
1456        return 1 unless $entry->author_id == $app->user->id;
1457    }
1458
1459    my $status = $app->param('status');
1460    if ( $status eq 'publish' ) {
1461        $obj->approve;
1462        if ( $original->junk_status != $obj->junk_status ) {
1463            $app->run_callbacks( 'handle_ham', $app, $obj );
1464        }
1465    }
1466    elsif ( $status eq 'moderate' ) {
1467        $obj->moderate;
1468    }
1469    elsif ( $status eq 'junk' ) {
1470        $obj->junk;
1471        if ( $original->junk_status != $obj->junk_status ) {
1472            $app->run_callbacks( 'handle_spam', $app, $obj );
1473        }
1474    }
1475    return 1;
1476}
1477
1478sub post_save {
1479    my $eh = shift;
1480    my ( $app, $obj, $original ) = @_;
1481
1482    if ( $obj->visible
1483        || ( ( $obj->visible || 0 ) != ( $original->visible || 0 ) ) )
1484    {
1485        return MT::Util::start_background_task(
1486            sub {
1487                my $app = MT->instance;
1488                if ( !$app->rebuild_entry( Entry => $obj->entry_id, BuildIndexes => 1 ) ) {
1489                    $app->publish_error(); # logs error as well.
1490                    return $eh->error( MT->translate( "Publish failed: [_1]", $app->errstr ) );
1491                }
1492                1;
1493            }
1494        );
1495    }
1496    1;
1497}
1498
1499sub post_delete {
1500    my ( $eh, $app, $obj ) = @_;
1501
1502    require MT::Entry;
1503    my $title = '';
1504    if ( my $entry = MT::Entry->load( $obj->entry_id ) ) {
1505        $title = $entry->title;
1506    }
1507
1508    $app->log(
1509        {
1510            message => $app->translate(
1511"Comment (ID:[_1]) by '[_2]' deleted by '[_3]' from entry '[_4]'",
1512                $obj->id, $obj->author, $app->user->name, $title
1513            ),
1514            level    => MT::Log::INFO(),
1515            class    => 'system',
1516            category => 'delete'
1517        }
1518    );
1519}
1520
1521sub can_view_commenter {
1522    my $eh = shift;
1523    my ( $app, $id ) = @_;
1524    my $auth = MT::Author->load(
1525        {
1526            id   => $id,
1527            type => MT::Author::COMMENTER()
1528        }
1529    );
1530    $auth ? 1 : 0;
1531}
1532
1533sub can_delete_commenter {
1534    my ( $eh, $app, $obj ) = @_;
1535    my $author = $app->user;
1536    return 1 if $author->is_superuser();
1537    my $perms = $author->permissions( $obj->blog_id );
1538    ( $perms && $perms->can_administer_blog );
1539}
1540
1541sub build_junk_table {
1542    my $app = shift;
1543    my (%args) = @_;
1544
1545    my $param = $args{param};
1546    my $obj   = $args{object};
1547
1548    if ( defined $obj->junk_score ) {
1549        $param->{junk_score} =
1550          ( $obj->junk_score > 0 ? '+' : '' ) . $obj->junk_score;
1551    }
1552    my $log = $obj->junk_log || '';
1553    my @log = split /\r?\n/, $log;
1554    my @junk;
1555    for ( my $i = 0 ; $i < scalar(@log) ; $i++ ) {
1556        my $line = $log[$i];
1557        $line =~ s/(^\s+|\s+$)//g;
1558        next unless $line;
1559        last if $line =~ m/^--->/;
1560        my ( $test, $score, $log );
1561        ($test) = $line =~ m/^([^:]+?):/;
1562        if ( defined $test ) {
1563            ($score) = $test =~ m/\(([+-]?\d+?(?:\.\d*?)?)\)/;
1564            $test =~ s/\(.+\)//;
1565        }
1566        if ( defined $score ) {
1567            $score =~ s/\+//;
1568            $score .= '.0' unless $score =~ m/\./;
1569            $score = ( $score > 0 ? '+' : '' ) . $score;
1570        }
1571        $log = $line;
1572        $log =~ s/^[^:]+:\s*//;
1573        $log = encode_html($log);
1574        for ( my $j = $i + 1 ; $j < scalar(@log) ; $j++ ) {
1575            my $line = encode_html( $log[$j] );
1576            if ( $line =~ m/^\t+(.*)$/s ) {
1577                $i = $j;
1578                $log .= "<br />" . $1;
1579            }
1580            else {
1581                last;
1582            }
1583        }
1584        push @junk, { test => $test, score => $score, log => $log };
1585    }
1586    $param->{junk_log_loop} = \@junk;
1587    \@junk;
1588}
1589
1590sub set_item_visible {
1591    my $app    = shift;
1592    my $perms  = $app->permissions;
1593    my $author = $app->user;
1594
1595    my $type    = $app->param('_type');
1596    my $class   = $app->model($type);
1597    my @obj_ids = $app->param('id');
1598
1599    if ( my $req_nonce = $app->param('nonce') ) {
1600        if ( scalar @obj_ids == 1 ) {
1601            my $cmt_id = $obj_ids[0];
1602            if ( my $obj = $class->load($cmt_id) ) {
1603                my $nonce =
1604                  MT::Util::perl_sha1_digest_hex( $obj->id
1605                      . $obj->created_on
1606                      . $obj->blog_id
1607                      . $app->config->SecretToken );
1608                return $app->errtrans("Invalid request.")
1609                  unless $nonce eq $req_nonce;
1610                my $return_args = $app->uri_params(
1611                    mode => 'view',
1612                    args => {
1613                        '_type' => $type,
1614                        id      => $cmt_id,
1615                        blog_id => $obj->blog_id
1616                    }
1617                );
1618                $return_args =~ s!^\?!!;
1619                $app->return_args($return_args);
1620            }
1621            else {
1622                return $app->errtrans("Invalid request.");
1623            }
1624        }
1625        else {
1626            return $app->errtrans("Invalid request.");
1627        }
1628    }
1629    else {
1630        $app->validate_magic() or return;
1631    }
1632
1633    my $new_visible;
1634    if ( $app->param('approve') ) {
1635        $new_visible = 1;
1636    }
1637    elsif ( $app->param('unapprove') ) {
1638        $new_visible = 0;
1639    }
1640
1641    my %rebuild_set = ();
1642    require MT::Entry;
1643    foreach my $id (@obj_ids) {
1644        my $obj = $class->load($id)
1645            or next;
1646        my $old_visible = $obj->visible || 0;
1647        if ( $old_visible != $new_visible ) {
1648            if ( $obj->isa('MT::TBPing') ) {
1649                my $obj_parent = $obj->parent();
1650                if ( $obj_parent->isa('MT::Category') ) {
1651                    my $blog = MT::Blog->load( $obj_parent->blog_id );
1652                    next unless $blog;
1653                    $app->publisher->_rebuild_entry_archive_type(
1654                        Entry       => undef,
1655                        Blog        => $blog,
1656                        Category    => $obj_parent,
1657                        ArchiveType => 'Category'
1658                    );
1659                }
1660                else {
1661                    if ( !$author->is_superuser ) {
1662                        if ( !$perms || $perms->blog_id != $obj->blog_id ) {
1663                            $perms = $author->permissions( $obj->blog_id );
1664                        }
1665                        unless ($perms) {
1666                            return $app->errtrans(
1667"You don't have permission to approve this comment."
1668                            );
1669                        }
1670                        unless (
1671                            $perms->can_manage_feedback
1672                            || ( $perms->can_publish_post
1673                                && ( $obj_parent->author_id == $author->id ) )
1674                          )
1675                        {
1676                            return $app->errtrans(
1677"You don't have permission to approve this comment."
1678                            );
1679                        }
1680                    }
1681                    $rebuild_set{ $obj_parent->id } = $obj_parent;
1682                }
1683            }
1684            elsif ( $obj->entry_id ) {
1685
1686                # TODO: Factor out permissions checking
1687                my $entry = MT::Entry->load( $obj->entry_id )
1688                  || return $app->error(
1689                    $app->translate("Comment on missing entry!") );
1690                if ( !$author->is_superuser ) {
1691                    if ( !$perms || $perms->blog_id != $obj->blog_id ) {
1692                        $perms = $author->permissions( $obj->blog_id );
1693                    }
1694                    unless ($perms) {
1695                        return $app->errtrans(
1696                            "You don't have permission to approve this comment."
1697                        );
1698                    }
1699                    unless (
1700                        $perms->can_manage_feedback
1701                        || ( $perms->can_publish_post
1702                            && ( $entry->author_id == $author->id ) )
1703                      )
1704                    {
1705                        return $app->errtrans(
1706                            "You don't have permission to approve this comment."
1707                        );
1708                    }
1709                }
1710                $rebuild_set{ $obj->entry_id } = $entry;
1711            }
1712            $obj->visible($new_visible);
1713            $obj->save();
1714        }
1715    }
1716    my $approved_flag = ( $new_visible ? '' : 'un' ) . 'approved';
1717    $app->add_return_arg( $approved_flag => 1 );
1718    return $app->rebuild_these( \%rebuild_set, how => MT::App::CMS::NEW_PHASE() );
1719}
1720
1721sub map_comment_to_commenter {
1722    my $app = shift;
1723    my ($comments) = @_;
1724    my %commenters;
1725    require MT::Comment;
1726    for my $id (@$comments) {
1727        my $cmt = MT::Comment->load($id);
1728        if ( $cmt && $cmt->commenter_id ) {
1729            $commenters{ $cmt->commenter_id . ':' . $cmt->blog_id } =
1730              [ $cmt->commenter_id, $cmt->blog_id ];
1731        }
1732        else {
1733            $app->add_return_arg( 'unauth', 1 );
1734        }
1735    }
1736    return values %commenters;
1737}
1738
1739sub _prepare_reply {
1740    my $app = shift;
1741    my $q   = $app->param;
1742
1743    my $comment_class = $app->model('comment');
1744    my $parent        = $comment_class->load( $q->param('reply_to') );
1745    my $entry         = $app->model('entry')->load( $parent->entry_id );
1746
1747    if ( !$parent || !$parent->is_published ) {
1748        $app->error(
1749            $app->translate("You can't reply to unpublished comment.") );
1750        return ( undef, $parent, $entry );
1751    }
1752
1753    unless ( $app->validate_magic ) {
1754        $app->error( $app->translate("Invalid request.") );
1755        return ( undef, $parent, $entry );
1756    }
1757
1758    my $nick = $app->user->nickname || $app->translate('Registered User');
1759
1760    my $comment = $comment_class->new;
1761
1762    ## Strip linefeed characters.
1763    my $text = $q->param('text');
1764    $text = '' unless defined $text;
1765    $text =~ tr/\r//d;
1766    $comment->ip( $app->remote_ip );
1767    $comment->commenter_id( $app->user->id );
1768    $comment->blog_id( $entry->blog_id );
1769    $comment->entry_id( $entry->id );
1770    $comment->author( remove_html($nick) );
1771    $comment->email( remove_html( $app->user->email ) );
1772    $comment->text($text);
1773    if (my $url = $app->user->url ) {
1774        $comment->url($url);
1775    }
1776
1777    $comment->visible(1);    # leave as undefined
1778    $comment->is_junk(0);
1779
1780    # strip of any null characters (done after junk checks so they can
1781    # monitor for that kind of activity)
1782    for my $field (qw(author email text)) {
1783        my $val = $comment->column($field);
1784        if ( $val =~ m/\x00/ ) {
1785            $val =~ tr/\x00//d;
1786            $comment->column( $field, $val );
1787        }
1788    }
1789
1790    ( $comment, $parent, $entry );
1791}
1792
1793sub build_comment_table {
1794    my $app = shift;
1795    my (%args) = @_;
1796
1797    my $author    = $app->user;
1798    my $class     = $app->model('comment');
1799    my $list_pref = $app->list_pref('comment');
1800    my $entry_pkg = $app->model('entry');
1801    my $iter;
1802    if ( $args{load_args} ) {
1803        $iter = $class->load_iter( @{ $args{load_args} } );
1804    }
1805    elsif ( $args{iter} ) {
1806        $iter = $args{iter};
1807    }
1808    elsif ( $args{items} ) {
1809        $iter = sub { pop @{ $args{items} } };
1810    }
1811    return [] unless $iter;
1812    my $limit = $args{limit};
1813    my $param = $args{param} || {};
1814
1815    my @data;
1816    my $i;
1817    $i = 1;
1818    my ( %blogs, %entries, %perms, %cmntrs );
1819    my $trim_length =
1820      $app->config('ShowIPInformation')
1821      ? const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_SHORT')
1822      : const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_LONG');
1823    my $author_max_len = const('DISPLAY_LENGTH_EDIT_COMMENT_AUTHOR');
1824    my $comment_short_len =
1825      const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_BREAK_UP_SHORT');
1826    my $comment_long_len =
1827      const('DISPLAY_LENGTH_EDIT_COMMENT_TEXT_BREAK_UP_LONG');
1828    my $title_max_len = const('DISPLAY_LENGTH_EDIT_COMMENT_TITLE');
1829
1830    while ( my $obj = $iter->() ) {
1831        my $row = $obj->column_values;
1832        $row->{author_display} = $row->{author};
1833        $row->{author_display} =
1834          substr_text( $row->{author_display}, 0, $author_max_len ) . '...'
1835          if $row->{author_display}
1836          && length_text( $row->{author_display} ) > $author_max_len;
1837        $row->{comment_short} =
1838          ( substr_text( $obj->text(), 0, $trim_length )
1839              . ( length_text( $obj->text ) > $trim_length ? "..." : "" ) );
1840        $row->{comment_short} =
1841          break_up_text( $row->{comment_short}, $comment_short_len )
1842          ;    # break up really long strings
1843        $row->{comment_long} = remove_html( $obj->text );
1844        $row->{comment_long} =
1845          break_up_text( $row->{comment_long}, $comment_long_len )
1846          ;    # break up really long strings
1847
1848        $row->{visible}  = $obj->visible();
1849        $row->{entry_id} = $obj->entry_id();
1850        my $blog = $blogs{ $obj->blog_id } ||= $obj->blog;
1851        my $entry = $entries{ $obj->entry_id } ||=
1852          $entry_pkg->load( $obj->entry_id );
1853        unless ($entry) {
1854            $entry = $entry_pkg->new;
1855            $entry->title( '* ' . $app->translate('Orphaned comment') . ' *' );
1856        }
1857        $row->{entry_class} = $entry->class;
1858        $row->{entry_class_label} = $entry->class_label;
1859        $row->{entry_title} = (
1860              defined( $entry->title ) ? $entry->title
1861            : defined( $entry->text )  ? $entry->text
1862            : ''
1863        );
1864        $row->{entry_title} = $app->translate('(untitled)')
1865          if $row->{entry_title} eq '';
1866        $row->{entry_title} =
1867          substr_text( $row->{entry_title}, 0, $title_max_len ) . '...'
1868          if $row->{entry_title}
1869          && length_text( $row->{entry_title} ) > $title_max_len;
1870        $row->{commenter_id} = $obj->commenter_id() if $obj->commenter_id();
1871        my $cmntr;
1872        if ( $obj->commenter_id ) {
1873            $cmntr = $cmntrs{ $obj->commenter_id } ||= MT::Author->load(
1874                {
1875                    id   => $obj->commenter_id(),
1876                }
1877            );
1878        }
1879        if ($cmntr) {
1880            $row->{email_hidden} = $cmntr && $cmntr->is_email_hidden();
1881            $row->{auth_icon_url} = $cmntr->auth_icon_url;
1882
1883            my $status = $cmntr->commenter_status( $obj->blog_id );
1884            $row->{commenter_approved} =
1885              ( $cmntr->commenter_status( $obj->blog_id ) ==
1886                  MT::Author::APPROVED() );
1887            $row->{commenter_banned} =
1888              ( $cmntr->commenter_status( $obj->blog_id ) ==
1889                  MT::Author::BANNED() );
1890        }
1891        if ( my $ts = $obj->created_on ) {
1892            $row->{created_on_time_formatted} =
1893              format_ts( MT::App::CMS::LISTING_DATETIME_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
1894            $row->{created_on_formatted} =
1895              format_ts( MT::App::CMS::LISTING_DATE_FORMAT(), $ts, $blog, $app->user ? $app->user->preferred_language : undef );
1896
1897            $row->{created_on_relative} = relative_date( $ts, time, $blog );
1898        }
1899        if ( $author->is_superuser() ) {
1900            $row->{has_edit_access} = 1;
1901            $row->{has_bulk_access} = 1;
1902        }
1903        else {
1904            my $perms = $perms{ $obj->blog_id } ||=
1905              $author->permissions( $obj->blog_id );
1906            $row->{has_bulk_access} = (
1907                $perms && ( $perms->can_edit_all_posts
1908                    || $perms->can_manage_feedback )
1909                  || ( ( $perms->can_publish_post )
1910                    && ( $author->id == $entry->author_id ) )
1911            );
1912            $row->{has_edit_access} = (
1913                $perms && ( $perms->can_edit_all_posts
1914                    || $perms->can_manage_feedback )
1915                  || ( ( $perms->can_create_post )
1916                    && ( $author->id == $entry->author_id ) )
1917            );
1918        }
1919        if ($blog) {
1920            $row->{weblog_id}   = $blog->id;
1921            $row->{weblog_name} = $blog->name;
1922        }
1923        else {
1924            $row->{weblog_name} =
1925              '* ' . $app->translate('Orphaned comment') . ' *';
1926        }
1927        $row->{reply_count} = $class->count( { parent_id => $obj->id } );
1928        $row->{object} = $obj;
1929        push @data, $row;
1930        last if $limit and @data > $limit;
1931    }
1932    return [] unless @data;
1933
1934    my $junk_tab = ( $app->param('tab') || '' ) eq 'junk';
1935    $param->{comment_table}[0]              = {%$list_pref};
1936    $param->{comment_table}[0]{object_loop} = \@data;
1937    $param->{comment_table}[0]{object_type} = 'comment';
1938    $param->{object_loop} = $param->{comment_table}[0]{object_loop}
1939      unless exists $param->{object_loop};
1940
1941    $app->load_list_actions( 'comment', \%$param );
1942    \@data;
1943}
1944
19451;
Note: See TracBrowser for help on using the browser.