root/branches/release-34/lib/MT/Comment.pm @ 1866

Revision 1866, 10.5 kB (checked in by bchoate, 20 months ago)

Changes to store binary state to junk_status column. BugId:79280

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