root/branches/release-38/lib/MT/Comment.pm @ 2365

Revision 2365, 10.9 kB (checked in by bchoate, 19 months ago)

Revised commenter sessions to include user id (as we do with authors) so we can load by id rather than by name. BugId:79253

  • 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' ],
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}
196
197sub approve {
198    my ($comment) = @_;
199    $comment->visible(1);
200    $comment->junk_status(NOT_JUNK);
201}
202
203*publish = \&approve;
204
205sub author {
206    my $comment = shift;
207    if (!@_ && $comment->commenter_id) {
208        require MT::Author;
209        if (my $auth = MT::Author->load($comment->commenter_id)) {
210            return $auth->nickname;
211        }
212    }
213    return $comment->column('author', @_);
214}
215
216sub all_text {
217    my $this = shift;
218    my $text = $this->column('author') || '';
219    $text .= "\n" . ($this->column('email') || '');
220    $text .= "\n" . ($this->column('url') || '');
221    $text .= "\n" . ($this->column('text') || '');
222    $text;
223}
224
225sub is_published {
226    return $_[0]->visible && !$_[0]->is_junk;
227}
228
229sub is_moderated {
230    return !$_[0]->visible() && !$_[0]->is_junk();
231}
232
233sub log {
234    # TBD: pre-load __junk_log when loading the comment
235    my $comment = shift;
236    push @{$comment->{__junk_log}}, @_;
237}
238
239sub save {
240    my $comment = shift;
241    $comment->junk_log(join "\n", @{$comment->{__junk_log}})
242        if ref $comment->{__junk_log} eq 'ARRAY';
243    my $ret = $comment->SUPER::save();
244    delete $comment->{__changed}{visibility} if $ret;
245    return $ret;
246}
247
248sub to_hash {
249    my $cmt = shift;
250    my $hash = $cmt->SUPER::to_hash();
251
252    $hash->{'comment.created_on_iso'} = sub { MT::Util::ts2iso($cmt->blog, $cmt->created_on) };
253    $hash->{'comment.modified_on_iso'} = sub { MT::Util::ts2iso($cmt->blog, $cmt->modified_on) };
254    if (my $blog = $cmt->blog) {
255        $hash->{'comment.text_html'} = sub {
256            my $txt = defined $cmt->text ? $cmt->text : '';
257            require MT::Util;
258            $txt = MT::Util::munge_comment($txt, $blog);
259            my $convert_breaks = $blog->convert_paras_comments;
260            $txt = $convert_breaks ?
261                MT->apply_text_filters($txt, $blog->comment_text_filters) :
262                $txt;
263            my $sanitize_spec = $blog->sanitize_spec ||
264                MT->config->GlobalSanitizeSpec;
265            require MT::Sanitize;
266            MT::Sanitize->sanitize($txt, $sanitize_spec);
267        }
268    }
269    if (my $entry = $cmt->entry) {
270        my $entry_hash = $entry->to_hash;
271        $hash->{"comment.$_"} = $entry_hash->{$_} foreach keys %$entry_hash;
272    }
273    if ($cmt->commenter_id) {
274        # commenter record exists... populate it
275        require MT::Author;
276        if (my $auth = MT::Author->load($cmt->commenter_id)) {
277            my $auth_hash = $auth->to_hash;
278            $hash->{"comment.$_"} = $auth_hash->{$_} foreach keys %$auth_hash;
279        }
280    }
281
282    $hash;
283}
284
285sub visible {
286    my $comment = shift;
287    return $comment->SUPER::visible unless @_;
288
289    ## Note transitions in visibility in the object, so that
290    ## other methods can act appropriately.
291    my $was_visible = $comment->SUPER::visible || 0;
292    my $is_visible = shift || 0;
293
294    my $vis_delta = 0;
295    if (!$was_visible && $is_visible) {
296        $vis_delta = 1;
297    } elsif ($was_visible && !$is_visible) {
298        $vis_delta = -1;
299    }
300    $comment->{__changed}{visibility} ||= 0;
301    $comment->{__changed}{visibility} += $vis_delta;
302
303    return $comment->SUPER::visible($is_visible);
304}
305
306sub parent {
307    my $comment = shift;
308    $comment->cache_property('parent', sub {
309        if ($comment->parent_id) {
310            return MT::Comment->load($comment->parent_id);
311        }
312    });
313}
314
3151;
316__END__
317
318=head1 NAME
319
320MT::Comment - Movable Type comment record
321
322=head1 SYNOPSIS
323
324    use MT::Comment;
325    my $comment = MT::Comment->new;
326    $comment->blog_id($entry->blog_id);
327    $comment->entry_id($entry->id);
328    $comment->author('Foo');
329    $comment->text('This is a comment.');
330    $comment->save
331        or die $comment->errstr;
332
333=head1 DESCRIPTION
334
335An I<MT::Comment> object represents a comment in the Movable Type system. It
336contains all of the metadata about the comment (author name, email address,
337homepage URL, IP address, etc.), as well as the actual body of the comment.
338
339=head1 USAGE
340
341As a subclass of I<MT::Object>, I<MT::Comment> inherits all of the
342data-management and -storage methods from that class; thus you should look
343at the I<MT::Object> documentation for details about creating a new object,
344loading an existing object, saving an object, etc.
345
346=head1 DATA ACCESS METHODS
347
348The I<MT::Comment> object holds the following pieces of data. These fields can
349be accessed and set using the standard data access methods described in the
350I<MT::Object> documentation.
351
352=over 4
353
354=item * id
355
356The numeric ID of the comment.
357
358=item * blog_id
359
360The numeric ID of the blog in which the comment is found.
361
362=item * entry_id
363
364The numeric ID of the entry on which the comment has been made.
365
366=item * author
367
368The name of the author of the comment.
369
370=item * commenter_id
371
372The author_id for the commenter; this will only be defined if the
373commenter is registered, which is only required if the blog config
374option allow_unreg_comments is false.
375
376=item * ip
377
378The IP address of the author of the comment.
379
380=item * email
381
382The email address of the author of the comment.
383
384=item * url
385
386The URL of the author of the comment.
387
388=item * text
389
390The body of the comment.
391
392=item * visible
393
394Returns a true value if the comment should be displayed. Comments can
395be hidden if comment registration is required and the commenter is
396pending approval.
397
398=item * created_on
399
400The timestamp denoting when the comment record was created, in the format
401C<YYYYMMDDHHMMSS>. Note that the timestamp has already been adjusted for the
402selected timezone.
403
404=item * modified_on
405
406The timestamp denoting when the comment record was last modified, in the
407format C<YYYYMMDDHHMMSS>. Note that the timestamp has already been adjusted
408for the selected timezone.
409
410=back
411
412=head1 DATA LOOKUP
413
414In addition to numeric ID lookup, you can look up or sort records by any
415combination of the following fields. See the I<load> documentation in
416I<MT::Object> for more information.
417
418=over 4
419
420=item * created_on
421
422=item * entry_id
423
424=item * blog_id
425
426=back
427
428=head1 AUTHOR & COPYRIGHTS
429
430Please see the I<MT> manpage for author, copyright, and license information.
431
432=cut
Note: See TracBrowser for help on using the browser.