root/branches/release-36/lib/MT/TBPing.pm @ 2057

Revision 2057, 10.4 kB (checked in by bchoate, 19 months ago)

Adding meta support for comments/pings and creating a new index to aid selecting by parent object and visible status. Also, comment author method now returns MT::Author nickname when comment has an associated author. BugId:79475

  • 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::TBPing;
8
9use strict;
10use base qw( MT::Object MT::Scorable );
11
12sub JUNK()      { -1 }
13sub NOT_JUNK () {  1 }
14
15__PACKAGE__->install_properties({
16    column_defs => {
17        'id' => 'integer not null auto_increment',
18        'blog_id' => 'integer not null',
19        'tb_id' => 'integer not null',
20        'title' => 'string(255)',
21        'excerpt' => 'text',
22        'source_url' => 'string(255)',
23        'ip' => 'string(15) not null',
24        'blog_name' => 'string(255)',
25        'visible' => 'boolean',
26        'junk_status' => 'smallint not null',
27        'last_moved_on' => 'datetime not null',
28        'junk_score' => 'float',
29        'junk_log' => 'text',
30    },
31    indexes => {
32        created_on => 1,
33        tb_visible => {
34            columns => [ 'tb_id', 'visible' ],
35        },
36        ip => 1,
37        last_moved_on => 1, # used for junk expiration
38        # For URL lookups to aid spam filtering
39        blog_url => {
40            columns => [ 'blog_id', 'visible', 'source_url' ],
41        },
42        blog_stat => {
43            columns => ['blog_id', 'junk_status', 'created_on'],
44        },
45        blog_visible => {
46            columns => ['blog_id', 'visible', 'created_on', 'id'],
47        },
48        visible_date => {
49            columns => [ 'visible', 'created_on' ],
50        },
51        junk_date => {
52            columns => [ 'junk_status', 'created_on' ],
53        },
54    },
55    defaults => {
56        junk_status => NOT_JUNK,
57        last_moved_on => '20000101000000',
58    },
59    audit => 1,
60    meta => 1,
61    datasource => 'tbping',
62    primary_key => 'id',
63});
64
65sub class_label {
66    return MT->translate('TrackBack');
67}
68
69sub class_label_plural {
70    return MT->translate('TrackBacks');
71}
72
73sub is_junk {
74    $_[0]->junk_status == JUNK;
75}
76sub is_not_junk {
77    $_[0]->junk_status != JUNK;
78}
79
80sub is_published {
81    return $_[0]->visible && !$_[0]->is_junk;
82}
83
84sub is_moderated {
85    return !$_[0]->visible() && !$_[0]->is_junk();
86}
87
88sub blog {
89    my ($ping) = @_;
90    my $blog = $ping->{__blog};
91    unless ($blog) {
92        my $blog_id = $ping->blog_id;
93        my $blog_class = MT->model('blog');
94        $blog = $blog_class->load($blog_id) or
95            return $ping->error(MT->translate(
96                "Load of blog '[_1]' failed: [_2]", $blog_id, $blog_class->errstr));   
97        $ping->{__blog} = $blog;
98    }
99    return $blog;
100}
101
102sub parent {
103    my ($ping) = @_;
104    if (my $tb = MT->model('trackback')->load($ping->tb_id)) {
105        if ($tb->entry_id) {
106            return MT->model('entry')->load($tb->entry_id);
107        } else {
108            return MT->model('category')->load($tb->category_id);
109        }
110    }
111}
112
113sub parent_id {
114    my ($ping) = @_;
115    if (my $tb = MT->model('trackback')->load($ping->tb_id)) {
116        if ($tb->entry_id) {
117            return ('MT::Entry', $tb->entry_id);
118        } else {
119            return ('MT::Category', $tb->category_id);
120        }
121    }
122}
123
124sub next {
125    my $ping = shift;
126    my($publish_only) = @_;
127    $publish_only = $publish_only ? {'visible' => 1} : {};
128    $ping->_nextprev('next', $publish_only);
129}
130
131sub previous {
132    my $ping = shift;
133    my($publish_only) = @_;
134    $publish_only = $publish_only ? {'visible' => 1} : {};
135    $ping->_nextprev('previous', $publish_only);
136}
137
138sub _nextprev {
139    my $obj = shift;
140    my $class = ref($obj);
141    my ($direction, $publish_only) = @_;
142    return undef unless ($direction eq 'next' || $direction eq 'previous');
143    my $next = $direction eq 'next';
144
145    my $label = '__' . $direction;
146    return $obj->{$label} if $obj->{$label};
147
148    # Selecting the adjacent object can be tricky since timestamps
149    # are not necessarily unique for entries. If we find that the
150    # next/previous object has a matching timestamp, keep selecting entries
151    # to select all entries with the same timestamp, then compare them using
152    # id as a secondary sort column.
153
154    my ($id, $ts) = ($obj->id, $obj->created_on);
155    my $iter = $class->load_iter({
156        blog_id => $obj->blog_id,
157        created_on => ($next ? [ $ts, undef ] : [ undef, $ts ]),
158        %{$publish_only}
159    }, {
160        'sort' => 'created_on',
161        'direction' => $next ? 'ascend' : 'descend',
162        'range_incl' => { 'created_on' => 1 },
163    });
164
165    # This selection should always succeed, but handle situation if
166    # it fails by returning undef.
167    return unless $iter;
168
169    # The 'same' array will hold any entries that have matching
170    # timestamps; we will then sort those by id to find the correct
171    # adjacent object.
172    my @same;
173    while (my $e = $iter->()) {
174        # Don't consider the object that is 'current'
175        next if $e->id == $id;
176        my $e_ts = $e->created_on;
177        if ($e_ts eq $ts) {
178            # An object with the same timestamp should only be
179            # considered if the id is in the scope we're looking for
180            # (greater than for the 'next' object; less than for
181            # the 'previous' object).
182            push @same, $e
183                if $next && $e->id > $id or !$next && $e->id < $id;
184        } else {
185            # We found an object with a timestamp different than
186            # the 'current' object.
187            if (!@same) {
188                push @same, $e;
189                # We should check to see if this new timestamped object also
190                # has entries adjacent to _it_ that have the same timestamp.
191                while (my $e = $iter->()) {
192                    push(@same, $e), next if $e->created_on eq $e_ts;
193                    $iter->('finish'), last;
194                }
195            } else {
196                $iter->('finish');
197            }
198            return $obj->{$label} = $e unless @same;
199            last;
200        }
201    }
202    if (@same) {
203        # If we only have 1 element in @same, return that.
204        return $obj->{$label} = $same[0] if @same == 1;
205        # Sort remaining elements in @same by id.
206        @same = sort { $a->id <=> $b->id } @same;
207        # Return front of list (smallest id) if selecting 'next'
208        # object. Return tail of list (largest id) if selection 'previous'.
209        return $obj->{$label} = $same[$next ? 0 : $#same];
210    }
211    return;
212}
213
214sub junk {
215    my $ping = shift;
216    if (($ping->junk_status || 0) != JUNK) {
217        require MT::Util;
218        my @ts = MT::Util::offset_time_list(time, $ping->blog_id);
219        my $ts = sprintf("%04d%02d%02d%02d%02d%02d",
220                         $ts[5]+1900, $ts[4]+1, @ts[3,2,1,0]);
221        $ping->last_moved_on($ts);
222    }
223    $ping->junk_status(JUNK);
224    $ping->visible(0);
225}
226
227sub moderate {
228    my $ping = shift;
229    $ping->visible(0);
230}
231
232sub approve {
233    my $ping = shift;
234    $ping->visible(1);
235    $ping->junk_status(NOT_JUNK);
236}
237
238sub all_text {
239    my $this = shift;
240    my $text = $this->column('blog_name') || '';
241    $text .= "\n" . ($this->column('title') || '');
242    $text .= "\n" . ($this->column('source_url') || '');
243    $text .= "\n" . ($this->column('excerpt') || '');
244    $text;
245}
246
247sub to_hash {
248    my $ping = shift;
249    my $hash = $ping->SUPER::to_hash(@_);
250    require MT::Sanitize;
251    $hash->{'tbping.excerpt_html'} = MT::Sanitize->sanitize($ping->excerpt || '');
252    $hash->{'tbping.created_on_iso'} = sub { MT::Util::ts2iso($ping->blog_id, $ping->created_on) };
253    $hash->{'tbping.modified_on_iso'} = sub { MT::Util::ts2iso($ping->blog_id, $ping->modified_on) };
254
255    if (my $parent = $ping->parent) {
256        my $parent_hash = $parent->to_hash;
257        $hash->{"tbping.$_"} = $parent_hash->{$_} foreach keys %$parent_hash;
258    }
259
260    $hash;
261}
262
263sub visible {
264    my $ping = shift;
265    return $ping->SUPER::visible unless @_;
266       
267    ## Note transitions in visibility in the object, so that
268    ## other methods can act appropriately.
269    my $was_visible = $ping->SUPER::visible || 0;
270    my $is_visible = shift || 0;
271       
272    my $vis_delta = 0;
273    if (!$was_visible && $is_visible) {
274        $vis_delta = 1;
275    } elsif ($was_visible && !$is_visible) {
276        $vis_delta = -1;
277    }
278    $ping->{__changed}{visibility} = $vis_delta;
279
280    return $ping->SUPER::visible($is_visible);
281}
282
2831;
284__END__
285
286=head1 NAME
287
288MT::TBPing - Movable Type TrackBack Ping record
289
290=head1 SYNOPSIS
291
292    use MT::TBPing;
293    my $ping = MT::TBPing->new;
294    $ping->blog_id($tb->blog_id);
295    $ping->tb_id($tb->id);
296    $ping->title('Foo');
297    $ping->excerpt('This is from a TrackBack ping.');
298    $ping->source_url('http://www.foo.com/bar');
299    $ping->save
300        or die $ping->errstr;
301
302=head1 DESCRIPTION
303
304An I<MT::TBPing> object represents a TrackBack ping in the Movable Type system.
305It contains all of the metadata about the ping (title, excerpt, URL, etc).
306
307=head1 USAGE
308
309As a subclass of I<MT::Object>, I<MT::TBPing> inherits all of the
310data-management and -storage methods from that class; thus you should look
311at the I<MT::Object> documentation for details about creating a new object,
312loading an existing object, saving an object, etc.
313
314=head1 DATA ACCESS METHODS
315
316The I<MT::TBPing> object holds the following pieces of data. These fields can
317be accessed and set using the standard data access methods described in the
318I<MT::Object> documentation.
319
320=over 4
321
322=item * id
323
324The numeric ID of the ping.
325
326=item * blog_id
327
328The numeric ID of the blog in which the ping is found.
329
330=item * tb_id
331
332The numeric ID of the TrackBack record (I<MT::Trackback> object) to which
333the ping was sent.
334
335=item * title
336
337The title of the ping item.
338
339=item * ip
340
341The IP address of the server that sent the ping.
342
343=item * excerpt
344
345The excerpt of the ping item.
346
347=item * source_url
348
349The URL of the item pointed to by the ping.
350
351=item * blog_name
352
353The name of the blog on which the original item was posted.
354
355=item * created_on
356
357The timestamp denoting when the ping record was created, in the format
358C<YYYYMMDDHHMMSS>. Note that the timestamp has already been adjusted for the
359selected timezone.
360
361=item * modified_on
362
363The timestamp denoting when the ping record was last modified, in the
364format C<YYYYMMDDHHMMSS>. Note that the timestamp has already been adjusted
365for the selected timezone.
366
367=back
368
369=head1 DATA LOOKUP
370
371In addition to numeric ID lookup, you can look up or sort records by any
372combination of the following fields. See the I<load> documentation in
373I<MT::Object> for more information.
374
375=over 4
376
377=item * created_on
378
379=item * tb_id
380
381=item * blog_id
382
383=item * ip
384
385=back
386
387=head1 AUTHOR & COPYRIGHTS
388
389Please see the I<MT> manpage for author, copyright, and license information.
390
391=cut
Note: See TracBrowser for help on using the browser.