root/branches/release-35/lib/MT/CMS/Comment.pm @ 1919

Revision 1919, 63.5 kB (checked in by auno, 20 months ago)

Need $app parameter.

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