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

Revision 2642, 11.0 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 Author Date Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::Comment;
8
9use strict;
10use base qw( MT::Object MT::Scorable );
11use MT::Util qw( weaken );
12
13sub JUNK ()     { -1 }
14sub NOT_JUNK () { 1 }
15
16__PACKAGE__->install_properties({
17    column_defs => {
18        'id' => 'integer not null auto_increment',
19        'blog_id' => 'integer not null',
20        'entry_id' => 'integer not null',
21        'author' => 'string(100)',
22        'commenter_id' => 'integer',
23        'visible' => 'boolean',
24        'junk_status' => 'smallint',
25        'email' => 'string(75)',
26        'url' => 'string(255)',
27        'text' => 'text',
28        'ip' => 'string(16)',
29        'last_moved_on' => 'datetime not null',
30        'junk_score' => 'float',
31        'junk_log' => 'text',
32        'parent_id' => 'integer',
33    },
34    indexes => {
35        created_on => 1,
36        entry_visible => {
37            columns => [ 'entry_id', 'visible', 'created_on' ],
38        },
39        email => 1,
40        commenter_id => 1,
41        parent_id => 1,
42        last_moved_on => 1, # used for junk expiration
43        # For comment throttle check
44        blog_ip_date => {
45            columns => [ 'blog_id', 'ip', 'created_on' ],
46        },
47        # For URL lookups to aid spam filtering
48        blog_url => {
49            columns => [ 'blog_id', 'visible', 'url' ],
50        },
51        blog_stat => {
52            columns => [ 'blog_id', 'junk_status', 'created_on' ],
53        },
54        blog_visible => {
55            columns => [ 'blog_id', 'visible', 'created_on', 'id' ],
56        },
57        visible_date => {
58            columns => [ 'visible', 'created_on' ],
59        },
60        junk_date => {
61            columns => [ 'junk_status', 'created_on' ],
62        },
63    },
64    meta => 1,
65    defaults => {
66        junk_status => NOT_JUNK,
67        last_moved_on => '20000101000000',
68    },
69    audit => 1,
70    datasource => 'comment',
71    primary_key => 'id',
72});
73
74my %blocklists = ();
75
76sub class_label {
77    return MT->translate("Comment");
78}
79
80sub class_label_plural {
81    return MT->translate("Comments");
82}
83
84sub is_junk {
85    $_[0]->junk_status == JUNK;
86}
87
88sub is_not_junk {
89    $_[0]->junk_status != JUNK;
90}
91
92sub is_not_blocked { 
93    my ($eh, $cmt) = @_;
94   
95    my($host, @hosts);
96    # other URI schemes?
97    require MT::Util;
98    @hosts = MT::Util::extract_urls($cmt->text);
99   
100    my $not_blocked = 1;
101    my $blog_id = $cmt->blog_id;
102    if (!$blocklists{$blog_id}) {
103        require MT::Blocklist;
104        my @blocks = MT::Blocklist->load( { blog_id => $blog_id } );
105        $blocklists{$blog_id} = [ @blocks ];
106    }
107    if (@{$blocklists{$blog_id}}) {
108        for my $h (@hosts) {
109            for my $b (@{$blocklists{$blog_id}}) {
110                $not_blocked = 0 if ($h eq $b->text);
111            }
112        }
113    }
114    $not_blocked;
115}
116
117sub next {
118    my $comment = shift;
119    my($publish_only) = @_;
120    $publish_only = $publish_only ? {'visible' => 1} : {};
121    $comment->_nextprev('next', $publish_only);
122}
123
124sub previous {
125    my $comment = shift;
126    my($publish_only) = @_;
127    $publish_only = $publish_only ? {'visible' => 1} : {};
128    $comment->_nextprev('previous', $publish_only);
129}
130
131sub _nextprev {
132    my $obj = shift;
133    my $class = ref($obj);
134    my ($direction, $terms) = @_;
135    return undef unless ($direction eq 'next' || $direction eq 'previous');
136    my $next = $direction eq 'next';
137
138    my $label = '__' . $direction;
139    return $obj->{$label} if $obj->{$label};
140
141    my $o = $obj->nextprev(
142        direction => $direction,
143        terms     => { blog_id => $obj->blog_id, %$terms },
144        by        => 'created_on',
145    );
146    weaken($o->{$label} = $o) if $o;
147    return $o;
148}
149
150sub entry {
151    my ($comment) = @_;
152    my $entry = $comment->{__entry};
153    unless ($entry) {
154        my $entry_id = $comment->entry_id;
155        return undef unless $entry_id;
156        require MT::Entry;
157        $entry = MT::Entry->load($entry_id) or
158            return $comment->error(MT->translate(
159            "Load of entry '[_1]' failed: [_2]", $entry_id, MT::Entry->errstr));
160        $comment->{__entry} = $entry;
161    }
162    return $entry;
163}
164
165sub blog {
166    my ($comment) = @_;
167    my $blog = $comment->{__blog};
168    unless ($blog) {
169        my $blog_id = $comment->blog_id;
170        require MT::Blog;
171        $blog = MT::Blog->load($blog_id) or
172            return $comment->error(MT->translate(
173            "Load of blog '[_1]' failed: [_2]", $blog_id, MT::Blog->errstr));
174        $comment->{__blog} = $blog;
175    }
176    return $blog;
177}
178
179sub junk {
180    my ($comment) = @_;
181    if (($comment->junk_status || 0) != JUNK) {
182        require MT::Util;
183        my @ts = MT::Util::offset_time_list(time, $comment->blog_id);
184        my $ts = sprintf("%04d%02d%02d%02d%02d%02d",
185                         $ts[5]+1900, $ts[4]+1, @ts[3,2,1,0]);
186        $comment->last_moved_on($ts);
187    }
188    $comment->junk_status(JUNK);
189    $comment->visible(0);
190}
191
192sub moderate {
193    my ($comment) = @_;
194    $comment->visible(0);
195    $comment->junk_status(NOT_JUNK);
196}
197
198sub approve {
199    my ($comment) = @_;
200    $comment->visible(1);
201    $comment->junk_status(NOT_JUNK);
202}
203
204*publish = \&approve;
205
206sub author {
207    my $comment = shift;
208    if (!@_ && $comment->commenter_id) {
209        require MT::Author;
210        if (my $auth = MT::Author->load($comment->commenter_id)) {
211            return $auth->nickname;
212        }
213    }
214    return $comment->column('author', @_);
215}
216
217sub all_text {
218    my $this = shift;
219    my $text = $this->column('author') || '';
220    $text .= "\n" . ($this->column('email') || '');
221    $text .= "\n" . ($this->column('url') || '');
222    $text .= "\n" . ($this->column('text') || '');
223    $text;
224}
225
226sub is_published {
227    return $_[0]->visible && !$_[0]->is_junk;
228}
229
230sub is_moderated {
231    return !$_[0]->visible() && !$_[0]->is_junk();
232}
233
234sub log {
235    # TBD: pre-load __junk_log when loading the comment
236    my $comment = shift;
237    push @{$comment->{__junk_log}}, @_;
238}
239
240sub save {
241    my $comment = shift;
242    $comment->junk_log(join "\n", @{$comment->{__junk_log}})
243        if ref $comment->{__junk_log} eq 'ARRAY';
244    my $ret = $comment->SUPER::save();
245    delete $comment->{__changed}{visibility} if $ret;
246    return $ret;
247}
248
249sub to_hash {
250    my $cmt = shift;
251    my $hash = $cmt->SUPER::to_hash();
252
253    $hash->{'comment.created_on_iso'} = sub { MT::Util::ts2iso($cmt->blog, $cmt->created_on) };
254    $hash->{'comment.modified_on_iso'} = sub { MT::Util::ts2iso($cmt->blog, $cmt->modified_on) };
255    if (my $blog = $cmt->blog) {
256        $hash->{'comment.text_html'} = sub {
257            my $txt = defined $cmt->text ? $cmt->text : '';
258            require MT::Util;
259            $txt = MT::Util::munge_comment($txt, $blog);
260            my $convert_breaks = $blog->convert_paras_comments;
261            $txt = $convert_breaks ?
262                MT->apply_text_filters($txt, $blog->comment_text_filters) :
263                $txt;
264            my $sanitize_spec = $blog->sanitize_spec ||
265                MT->config->GlobalSanitizeSpec;
266            require MT::Sanitize;
267            MT::Sanitize->sanitize($txt, $sanitize_spec);
268        }
269    }
270    if (my $entry = $cmt->entry) {
271        my $entry_hash = $entry->to_hash;
272        $hash->{"comment.$_"} = $entry_hash->{$_} foreach keys %$entry_hash;
273    }
274    if ($cmt->commenter_id) {
275        # commenter record exists... populate it
276        require MT::Author;
277        if (my $auth = MT::Author->load($cmt->commenter_id)) {
278            my $auth_hash = $auth->to_hash;
279            $hash->{"comment.$_"} = $auth_hash->{$_} foreach keys %$auth_hash;
280        }
281    }
282
283    $hash;
284}
285
286sub visible {
287    my $comment = shift;
288    return $comment->SUPER::visible unless @_;
289
290    ## Note transitions in visibility in the object, so that
291    ## other methods can act appropriately.
292    my $was_visible = $comment->SUPER::visible || 0;
293    my $is_visible = shift || 0;
294
295    my $vis_delta = 0;
296    if (!$was_visible && $is_visible) {
297        $vis_delta = 1;
298    } elsif ($was_visible && !$is_visible) {
299        $vis_delta = -1;
300    }
301    $comment->{__changed}{visibility} ||= 0;
302    $comment->{__changed}{visibility} += $vis_delta;
303
304    return $comment->SUPER::visible($is_visible);
305}
306
307sub parent {
308    my $comment = shift;
309    $comment->cache_property('parent', sub {
310        if ($comment->parent_id) {
311            return MT::Comment->load($comment->parent_id);
312        }
313    });
314}
315
3161;
317__END__
318
319=head1 NAME
320
321MT::Comment - Movable Type comment record
322
323=head1 SYNOPSIS
324
325    use MT::Comment;
326    my $comment = MT::Comment->new;
327    $comment->blog_id($entry->blog_id);
328    $comment->entry_id($entry->id);
329    $comment->author('Foo');
330    $comment->text('This is a comment.');
331    $comment->save
332        or die $comment->errstr;
333
334=head1 DESCRIPTION
335
336An I<MT::Comment> object represents a comment in the Movable Type system. It
337contains all of the metadata about the comment (author name, email address,
338homepage URL, IP address, etc.), as well as the actual body of the comment.
339
340=head1 USAGE
341
342As a subclass of I<MT::Object>, I<MT::Comment> inherits all of the
343data-management and -storage methods from that class; thus you should look
344at the I<MT::Object> documentation for details about creating a new object,
345loading an existing object, saving an object, etc.
346
347=head1 DATA ACCESS METHODS
348
349The I<MT::Comment> object holds the following pieces of data. These fields can
350be accessed and set using the standard data access methods described in the
351I<MT::Object> documentation.
352
353=over 4
354
355=item * id
356
357The numeric ID of the comment.
358
359=item * blog_id
360
361The numeric ID of the blog in which the comment is found.
362
363=item * entry_id
364
365The numeric ID of the entry on which the comment has been made.
366
367=item * author
368
369The name of the author of the comment.
370
371=item * commenter_id
372
373The author_id for the commenter; this will only be defined if the
374commenter is registered, which is only required if the blog config
375option allow_unreg_comments is false.
376
377=item * ip
378
379The IP address of the author of the comment.
380
381=item * email
382
383The email address of the author of the comment.
384
385=item * url
386
387The URL of the author of the comment.
388
389=item * text
390
391The body of the comment.
392
393=item * visible
394
395Returns a true value if the comment should be displayed. Comments can
396be hidden if comment registration is required and the commenter is
397pending approval.
398
399=item * created_on
400
401The timestamp denoting when the comment record was created, in the format
402C<YYYYMMDDHHMMSS>. Note that the timestamp has already been adjusted for the
403selected timezone.
404
405=item * modified_on
406
407The timestamp denoting when the comment record was last modified, in the
408format C<YYYYMMDDHHMMSS>. Note that the timestamp has already been adjusted
409for the selected timezone.
410
411=back
412
413=head1 DATA LOOKUP
414
415In addition to numeric ID lookup, you can look up or sort records by any
416combination of the following fields. See the I<load> documentation in
417I<MT::Object> for more information.
418
419=over 4
420
421=item * created_on
422
423=item * entry_id
424
425=item * blog_id
426
427=back
428
429=head1 AUTHOR & COPYRIGHTS
430
431Please see the I<MT> manpage for author, copyright, and license information.
432
433=cut
Note: See TracBrowser for help on using the browser.