root/branches/release-41/plugins/WXRImporter/lib/WXRImporter/WXRHandler.pm @ 2731

Revision 2731, 26.8 kB (checked in by auno, 17 months ago)

Fixed to set tags properly for WXR format in 2.3.x. BugzID:80487

  • Property svn:keywords set to Author Date Id Revision
Line 
1# WXRImporter plugin for Movable Type
2# Author: Six Apart (http://www.sixapart.com)
3# Released under the Artistic License
4#
5# $Id$
6
7package WXRImporter::WXRHandler;
8
9use strict;
10use XML::SAX::Base;
11use Time::Local qw( timegm );
12use MT;
13use MT::Util qw( offset_time_list );
14
15use base qw(XML::SAX::Base);
16
17sub POST_SEPARATOR { '<!--more-->'; } # WordPress's separator string
18
19sub new {
20    my $class = shift;
21    my (%param) = @_;
22    my $self = bless \%param, $class;
23    return $self;
24}
25
26sub start_document {
27    my $self = shift;
28    my $data = shift;
29
30    $self->{start} = 1;
31    $self->{basename_limit} = 255; # max length of the column
32
33    1;
34}
35
36sub start_element {
37    my $self = shift;
38    my $data = shift;
39
40    my $name = $data->{LocalName};
41    my $prefix = $data->{Prefix};
42    my $attrs = $data->{Attributes};
43    my $ns = $data->{NamespaceURI};
44
45    if (exists $self->{in_wp_comment_content}) {
46        # wordpress's comment content consists of mixed contents (tags and texts)...
47        my $element_data = '<' . $data->{Name};
48        for my $attr (keys %$attrs) {
49            $element_data .= ' ' . $attrs->{$attr}->{Name} . '=' . $attrs->{$attr}->{Value};
50        }
51        $element_data .= '>';
52        $data->{Data} = $element_data;
53        $self->characters($data);
54        return;
55    }
56
57    if ($self->{start}) {
58        die MT->translate("File is not in WXR format.")
59            unless (('rss' eq $name) && ('2.0' eq $attrs->{'{}version'}->{Value})); ## FIXME: This is checking RSS2.
60        $self->{start} = 0;
61        $self->{'bucket'} = [];
62        return 1;
63    }
64
65    my %values = map { $attrs->{$_}->{LocalName} => 
66            MT::I18N::encode_text(MT::I18N::utf8_off($attrs->{$_}->{Value}), 'utf-8')
67        } keys(%$attrs);
68
69    $self->{in_wp_comment_content} = 1 if ('wp' eq $prefix) && ('comment_content' eq $name);
70
71    if ( scalar(%values) ) {
72        push @{ $self->{'bucket'} },
73          { $prefix . '_' . $name => undef, _a => \%values };
74    }
75    else {
76        push @{ $self->{'bucket'} }, $prefix . '_' . $name;
77    }
78    1;
79}
80
81sub characters {
82    my $self = shift;
83    my $data = shift;
84
85    return unless ($data->{Data} !~ /^\s+$/)
86        || (exists $self->{in_wp_comment_content}); # see if we need to process whitespaces
87
88    my $element = pop @{$self->{'bucket'}};
89    return unless $element;
90
91    my $chars = MT::I18N::utf8_off($data->{Data});
92    if ('HASH' eq ref($element)) {
93        my @hash_array = grep { $_ ne '_a' } keys %$element;
94        return unless $hash_array[0];
95        my $val = $element->{$hash_array[0]};
96        $val .= $chars;
97        $element->{$hash_array[0]} = $val;
98    } elsif ($element) {
99        $element = { $element => $chars };
100    }
101    push @{$self->{'bucket'}}, $element;
102    1;
103}
104
105sub end_element {
106    my $self = shift;
107    my $data = shift;
108
109    my $name = $data->{LocalName};
110    my $prefix = $data->{Prefix};
111
112    if ((exists $self->{in_wp_comment_content}) && ('wp:comment_content' ne $data->{Name})) {
113        # wordpress's comment content consists of mixed contents (tags and texts)...
114        my $element_data = '</' . $data->{Name} . '>';
115        $data->{Data} = $element_data;
116        $self->characters($data);
117        return;
118    }
119    my $element = pop @{$self->{'bucket'}};
120    if ('HASH' eq ref($element)) {
121        $element->{$prefix . '_' . $name} = 
122            MT::I18N::encode_text($element->{$prefix . '_' . $name}, 'utf-8')
123                if exists $element->{$prefix . '_' . $name};
124    }
125    push @{$self->{'bucket'}}, $element;
126    my $name_element = $prefix . '_' . $name;
127
128    if ('_item' eq $name_element) {
129        $self->_create_item($data);
130    } elsif ('wp_postmeta' eq $name_element) {
131        $self->_setup_metadata($data);
132    } elsif ('wp_category' eq $name_element) {
133        $self->_create_category($data);
134    } elsif ('wp_tag' eq $name_element) {
135        $self->_create_tag($data);
136    } elsif ('wp_comment' eq $name_element) {
137        $self->_create_feedback($data);
138    } elsif ('_channel' eq $name_element) {
139        $self->_update_blog($data);
140    } elsif ('wp_comment_content' eq $name_element) {
141        delete $self->{in_wp_comment_content};
142    }
143
144    1;
145}
146
147sub _setup_metadata {
148    my $self = shift;
149    my $data = shift;
150   
151    my $cb = $self->{callback};
152    my $blog = $self->{blog};
153
154    my $meta = {};
155    my $current_key;
156    my $current_value;
157    while (my $hash = pop @{$self->{'bucket'}}) {
158        last if 'wp_postmeta' eq $hash;
159        next if 'HASH' ne ref $hash;
160        my @hash_array = %$hash;
161        my $key = $hash_array[0];
162        my $value = $hash_array[1];
163        if ('wp_meta_key' eq $key) {
164            $current_key = $value;
165        } elsif ('wp_meta_value' eq $key) {
166            $current_value = $value;
167        }
168        if ($current_key && $current_value) {
169            $meta->{$current_key} = $current_value;
170            $current_key = $current_value = undef;
171        }
172    }
173    push @{$self->{'bucket'}}, { 'wp_postmeta' => $meta };
174}
175
176sub _update_blog {
177    my $self = shift;
178    my $data = shift;
179   
180    my $cb = $self->{callback};
181    my $blog = $self->{blog};
182
183    while (my $hash = pop @{$self->{'bucket'}}) {
184        last if '_channel' eq $hash;
185        next if 'HASH' ne ref $hash;
186        my @hash_array = %$hash;
187        my $key = $hash_array[0];
188        my $value = $hash_array[1];
189        if ('_description' eq $key) {
190            $blog->description($value) unless $blog->description;
191# Should we do these?
192#        } elsif ('_link' eq $key) {
193#            $blog->site_url($value) unless $blog->site_url;
194#        } elsif ('_title' eq $key) {
195#            $blog->name($value) if 'First Weblog' eq $blog->name;
196#        } elsif ('_language' eq $key) {
197#            $blog->language($value);
198        }
199    }
200    $blog->save;
201}
202
203sub _create_category {
204    my $self = shift;
205    my $data = shift;
206   
207    my $cb = $self->{callback};
208    my $blog = $self->{blog};
209
210    require MT::Category; ##FIXME: don't directly reference packages
211    my $cat = MT::Category->new;
212
213    while (my $hash = pop @{$self->{'bucket'}}) {
214        last if 'wp_category' eq $hash;
215        next if 'HASH' ne ref $hash;
216        my @hash_array = %$hash;
217        my $key = $hash_array[0];
218        my $value = $hash_array[1];
219        $cat->blog_id($blog->id);
220        if ('wp_category_nicename' eq $key) {
221            my $dash = MT->instance->config('CategoryNameNodash') ? '' : '-';
222            my $base = MT::Util::dirify(MT::Util::decode_url($value)) || ("cat" . $dash . $cat->id);
223            $base = substr($base, 0, $self->{basename_limit});
224            $base =~ s/_+$//;
225            $base = 'cat' if $base eq '';
226            my $i = 1;
227            my $base_copy = $base;
228            while (MT::Category->count({ blog_id => $blog->id,
229                                         basename => $base })) {
230                $base = $base_copy . '_' . $i++;
231            }
232            $cat->basename($base);
233        } elsif ('wp_category_parent' eq $key) {
234            my $parent = MT::Category->load(
235                { label => $value,
236                  blog_id => $self->{blog}->id });
237            $cat->parent($parent->id) if defined $parent;
238        } elsif ('wp_posts_private' eq $key) {
239            # skip
240        } elsif ('wp_posts_private' eq $key) {
241            # skip
242        } elsif ('wp_cat_name' eq $key) {
243            return if (MT::Category->load({ label => $value }));
244            $cat->label($value);
245        } elsif ('wp_category_description' eq $key) {
246            $cat->description($value);
247        }
248    }
249    if (defined $cat) {
250        if (exists $self->{author}) {
251            $cat->author_id($self->{author}->id);
252        } elsif (exists $self->{parent}) {
253            $cat->author_id($self->{parent}->id);
254        }
255        $cb->(MT->translate("Creating new category ('[_1]')...", $cat->label));
256        if ($cat->save) {
257            $cb->(MT->translate("ok") . "\n");
258        } else {
259            $cb->(MT->translate("failed") . "\n");
260            return die MT->translate(
261                 "Saving category failed: [_1]", $cat->errstr);
262        }
263    }
264}
265
266sub _create_tag {
267    my $self = shift;
268    my $data = shift;
269
270    my $cb   = $self->{callback};
271    my $blog = $self->{blog};
272
273    require MT::Tag;
274    my $tag = MT::Tag->new;
275    my $name;
276    while ( my $hash = pop @{ $self->{'bucket'} } ) {
277        last if 'wp_tag' eq $hash;
278        next if 'HASH' ne ref $hash;
279        my @hash_array = %$hash;
280        my $key        = $hash_array[0];
281        my $value      = $hash_array[1];
282        if ( 'wp_tag_slug' eq $key ) {
283            $name = MT::Util::decode_url($value);
284        }
285        if ( 'wp_tag_name' eq $key ) {
286            $name = $value;
287        }
288    }
289    if ($name) {
290        return if ( MT::Tag->load( { name => $name } ) );
291        $tag->name($name);
292        $cb->( MT->translate( "Creating new tag ('[_1]')...", $tag->name ) );
293        if ( $tag->save ) {
294            $cb->( MT->translate("ok") . "\n" );
295        }
296        else {
297            $cb->( MT->translate("failed") . "\n" );
298            return die MT->translate( "Saving tag failed: [_1]", $tag->errstr );
299        }
300    }
301}
302
303sub _create_feedback {
304    my $self = shift;
305    my $data = shift;
306   
307    my $cb = $self->{callback};
308
309    my $feedback_data = {};
310    my $type = 'comment';
311
312    while (my $hash = pop @{$self->{'bucket'}}) {
313        last if 'wp_comment' eq $hash;
314        next if 'HASH' ne ref $hash;
315        my @hash_array = %$hash;
316        my $key = $hash_array[0];
317        my $value = $hash_array[1];
318        if ('wp_comment_type' eq $key) {
319            $type = $value;
320        } else {
321            $feedback_data->{$key} = $value;
322        }
323    }
324    push @{$self->{'bucket'}}, { $type => $feedback_data };
325}
326
327sub _create_item {
328    my $self = shift;
329    my $data = shift;
330
331    my @hashes;
332    my $post_type = 'post';  ## TODO: default?
333    while (my $hash = pop @{$self->{'bucket'}}) {
334        last if '_item' eq $hash;
335        next if 'HASH' ne ref $hash;
336        $post_type = $hash->{'wp_post_type'}, next if exists $hash->{'wp_post_type'};
337        push @hashes, $hash if 'HASH' eq ref($hash);
338    }
339
340    my $blog = $self->{blog};
341    return unless $blog;
342
343    if ('post' eq $post_type) {
344        $self->_create_post('entry', \@hashes);
345    } elsif ('page' eq $post_type) {
346        $self->_create_post('page', \@hashes);
347    } elsif ('attachment') {
348        $self->_create_asset(\@hashes);
349    }
350    1;
351}
352
353sub _create_asset {
354    my $self = shift;
355    my ($hashes) = @_;
356
357    my $blog = $self->{blog};
358    my $cb = $self->{callback};
359
360    my @tags;
361    my %meta_hash;
362    my $asset_values = { 'blog_id' => $blog->id };
363    for my $hash (@$hashes) {
364        my @hash_array = %$hash;
365        my $key = $hash_array[0];
366        my $value = $hash_array[1];
367        if ('_title' eq $key) {
368            $asset_values->{'label'} = $value;
369        } elsif ('_link' eq $key) {
370            # skip
371        } elsif ('_pubDate' eq $key) {
372            # skip - we use post_date_gmt;
373        } elsif ('dc_creator' eq $key) {
374            $asset_values->{'created_by'} = $self->_get_author_id($cb, $value);
375        } elsif ('_category' eq $key) {
376            # TODO: is it ok to make it tags?
377            push @tags, $value;
378        } elsif ('_guid' eq $key) {
379            $asset_values->{'url'} = $value;
380        } elsif ('_description' eq $key) {
381            # skip
382        } elsif ('content_encoded' eq $key) {
383            $asset_values->{'description'} = $value;
384        } elsif ('wp_post_id' eq $key) {
385            # skip;
386        } elsif ('wp_post_date' eq $key) {
387            # skip;
388        } elsif ('wp_post_date_gmt' eq $key) {
389            $asset_values->{'created_on'} = $self->_gmt2blogtime($value, $blog);
390        } elsif ('wp_comment_status' eq $key) {
391            # skip
392        } elsif ('wp_ping_status' eq $key) {
393            # skip
394        } elsif ('wp_post_name' eq $key) {
395            # skip - we don't have an equivalent.
396        } elsif ('wp_status' eq $key) {
397            # skip possible values: inherit,
398        } elsif ('wp_post_parent' eq $key) {
399            # skip - entry association?
400        } elsif ('wp_postmeta' eq $key) {
401            for my $meta_key (keys %$value) {
402                if ('_wp_attached_file' eq $meta_key) {
403                    $asset_values->{'file_path'} = $value->{$meta_key};
404                } elsif ('_wp_attachment_metadata' eq $meta_key) {
405                    # only parse width and height
406                    my $serialized = $value->{$meta_key};
407                    if ($serialized =~ m!s:5:"width";i:(\d+);s:6:"height";i:(\d+);!i) {
408                        $asset_values->{'image_width'} = $1;
409                        $asset_values->{'image_height'} = $2;
410                    }
411                    $meta_hash{$meta_key} = $value->{$meta_key};
412                } else {
413                    $meta_hash{$meta_key} = $value->{$meta_key};
414                }
415            }
416        }
417    }
418
419    my $wp_path = $self->{'wp_path'};
420    my $mt_path = $self->{'mt_path'};
421    my $path = $asset_values->{'file_path'};
422    if ($wp_path && $mt_path) {
423        $path =~ s/^.*$wp_path(.+)$/$mt_path$1/i;
424        $path = File::Spec->canonpath($path);
425    }
426    $asset_values->{'file_path'} = $path;
427
428    my $mt_url = $self->{'mt_url'};
429    my $url = $asset_values->{'url'};
430    my $old_url = $url;
431    if ($mt_url) {
432        $url =~ s/^.*$wp_path(.+)$/$mt_url$1/i;
433    }
434    $asset_values->{'url'} = $url;
435
436    require MT::Asset;
437
438    # Check dupe
439    if ( MT::Asset->count(
440      {
441        blog_id => $asset_values->{blog_id},
442        label => $asset_values->{label},
443        file_path => $asset_values->{file_path},
444      }
445    ))
446    {
447        $cb->(MT->translate("Duplicate asset ('[_1]') found.  Skipping.", $asset_values->{label}));
448        $cb->("\n");
449        return 1;
450    }
451    require File::Basename;
452    my $local_basename = File::Basename::basename($path);
453    my $ext = (File::Basename::fileparse($path, qr/[A-Za-z]+$/))[2];
454
455    $asset_values->{'file_name'} = $local_basename;
456    $asset_values->{'file_ext'} = $ext;
457
458    # Now save the asset.
459    my $asset_pkg = MT::Asset->handler_for_file($local_basename);
460    my $asset = $asset_pkg->new();
461    my $w = delete $asset_values->{'image_width'};
462    my $h = delete $asset_values->{'image_height'};
463    $asset->set_values($asset_values);
464    if ( $h && $w ) {
465        $asset->image_width($w);
466        $asset->image_height($h);
467    }
468    $cb->(MT->translate("Saving asset ('[_1]')...", $asset->label));
469    $asset->add_tags(@tags) if 0 < scalar(@tags);
470    $cb->(MT->translate(" and asset will be tagged ('[_1]')...", join(',', @tags)));
471    if ($asset->save) {
472        $cb->(MT->translate("ok (ID [_1])", $asset->id) . "\n");
473        if ( exists($self->{'wp_download'}) && $self->{'wp_download'} ) {
474            _get_item_via_http($asset->id, $old_url);
475        }
476    } else {
477        $cb->(MT->translate("failed") . "\n");
478        die MT->translate(
479            "Saving entry failed: [_1]", $asset->errstr);
480    }
481}
482
483sub _create_post {
484    my $self = shift;
485    my ($class_type, $hashes) = @_;
486
487    my $blog = $self->{blog};
488    my $cb = $self->{callback};
489
490    my %cat_ids;
491    my $primary_cat_id;
492    my $feedbacks = {
493        'comments' => [],
494        'trackbacks' => [],
495    };
496    my %meta_hash;
497    my @tags;
498
499    my $class = MT->model($class_type);
500    require MT::Comment;
501    require MT::TBPing;
502    require MT::Trackback;
503
504    my $post = $class->new;
505    $post->blog_id($blog->id);
506    $post->convert_breaks($self->{convert_breaks});
507    $post->status($blog->status_default);
508    for my $hash (@$hashes) {
509        my @hash_array = grep { $_ ne '_a' } keys %$hash;
510        my $key = $hash_array[0];
511        my $value = $hash->{ $hash_array[0] };
512        if ('_title' eq $key) {
513            $post->title($value);
514        } elsif ('_link' eq $key) {
515            # skip;
516        } elsif ('_pubDate' eq $key) {
517            # skip - we use post_date_gmt;
518        } elsif ('dc_creator' eq $key) {
519            $post->author_id($self->_get_author_id($cb, $value));
520        } elsif ('_category' eq $key) {
521            if ( $hash->{_a} ) {
522                if ( $hash->{_a}->{domain} eq 'tag' ) {
523                    $value = MT::Util::decode_url( $hash->{_a}->{nicename} )
524                      if !$value;
525                    push @tags, $value if $value;
526                }
527            }
528            else {
529
530                # previous category definition
531                my $cat_class =
532                  MT->model( $class_type eq 'entry' ? 'category' : 'folder' );
533                my $cat = $cat_class->load(
534                    {
535                        label   => $value,
536                        blog_id => $self->{blog}->id
537                    }
538                );
539                if ( defined $cat ) {
540                    $cat_ids{ $cat->id } = 1;
541                    $primary_cat_id = $cat->id unless $primary_cat_id;
542                }
543            }
544        } elsif ('_guid' eq $key) {
545            # skip;
546        } elsif ('_description' eq $key) {
547            # skip;
548        } elsif ('content_encoded' eq $key) {
549            my $pos = index $value, POST_SEPARATOR();
550            if (-1 == $pos) {
551                $post->text($value);
552            } else {
553                $post->text(substr $value, 0, $pos);
554                $post->text_more(substr $value, $pos + length(POST_SEPARATOR()));
555            }
556        } elsif ('wp_post_id' eq $key) {
557            # skip;
558        } elsif ('wp_post_date' eq $key) {
559            # skip;
560        } elsif ('wp_post_date_gmt' eq $key) {
561            $post->authored_on($self->_gmt2blogtime($value, $blog));
562        } elsif ('wp_comment_status' eq $key) {
563            $post->allow_comments('open' eq $value ? 1 : 0);
564        } elsif ('wp_ping_status' eq $key) {
565            $post->allow_pings('open' eq $value ? 1 : 0);
566        } elsif ('wp_post_name' eq $key) {
567            my $base = MT::Util::decode_url($value);
568            $base = MT::Util::dirify($base) if $base ne $value;
569            $base = substr($base, 0, $self->{basename_limit});
570            $base =~ s/_+$//;
571            $base = 'post' if $base eq '';
572            my $i = 1;
573            my $base_copy = $base;
574            while ($class->count({ blog_id => $blog->id,
575                                      basename => $base })) {
576                $base = $base_copy . '_' . $i++;
577            }
578            $post->basename($base);
579        } elsif ('wp_status' eq $key) {
580            $post->status(MT::Entry::HOLD()) unless 'publish' eq $value;
581            $post->status(MT::Entry::RELEASE()) if 'publish' eq $value;
582        } elsif ('wp_post_parent' eq $key) {
583            # skip;
584        } elsif ('wp_postmeta' eq $key) {
585            for my $meta_key (keys %$value) {
586                $meta_hash{$meta_key} = $value->{$meta_key};
587            }
588            # TODO: how we should handle metadata is to be decided later
589        } elsif ('comment' eq $key) {
590            my $cmt = MT::Comment->new;
591            $cmt->blog_id($blog->id);
592            $cmt->author($value->{'wp_comment_author'}) if exists $value->{'wp_comment_author'};
593            $cmt->email($value->{'wp_comment_author_email'}) if exists $value->{'wp_comment_author_email'};
594            $cmt->url($value->{'wp_comment_author_url'}) if exists $value->{'wp_comment_author_url'};
595            $cmt->ip($value->{'wp_comment_author_IP'}) if exists $value->{'wp_comment_author_IP'};
596            my $date = $value->{'wp_comment_date_gmt'};
597            $cmt->created_on($self->_gmt2blogtime($date, $blog));
598            $cmt->text($value->{'wp_comment_content'}) if exists $value->{'wp_comment_content'};
599            my $status = $value->{'wp_comment_approved'};
600            if ($status eq '1') {
601                $cmt->approve;
602            } elsif ('spam' eq $status) {
603                $cmt->junk;
604            }
605            # skip wp:comment_id
606            # skip wp:comment_parent
607            push @{$feedbacks->{comments}}, $cmt;
608        } elsif (('trackback' eq $key) || ('pingback' eq $key)) {
609            # TODO: are trackback and pingback the same in its data structure?
610            my $ping = MT::TBPing->new;
611            $ping->blog_id($blog->id);
612            $ping->blog_name($value->{'wp_comment_author'}) if exists $value->{'wp_comment_author'};
613            $ping->source_url($value->{'wp_comment_author_url'}) if exists $value->{'wp_comment_author_url'};
614            $ping->ip($value->{'wp_comment_author_IP'}) if exists $value->{'wp_comment_author_IP'};
615            my $date = $value->{'wp_comment_date_gmt'};
616            $ping->created_on($self->_gmt2blogtime($date, $blog));
617            if (exists $value->{'wp_comment_content'}) {
618                my $content = $value->{'wp_comment_content'} ;
619                if ($content =~ m!^<strong>(.+)</strong>\n*(.+)$!m) {
620                    # this is exactly how wordpress stores trackbacks in its database as of v2.1.
621                    $ping->title($1);
622                    $ping->excerpt($2);
623                }
624            }
625            my $status = $value->{'wp_comment_approved'};
626            if ($status eq '1') {
627                $ping->approve;
628            } elsif ('spam' eq $status) {
629                $ping->junk;
630            }
631            push @{$feedbacks->{trackbacks}}, $ping;
632        }
633    }
634
635    # Check dupe
636    if ( $class->count(
637      {
638        class => $class_type,
639        blog_id => $post->blog_id,
640        title => $post->title,
641        authored_on => $post->authored_on
642      }
643    ))
644    {
645        $cb->(MT->translate("Duplicate entry ('[_1]') found.  Skipping.", $post->title));
646        $cb->("\n");
647        return 1;
648    }
649
650    # Associate tags to the entry.
651    if (@tags) {
652        $post->set_tags(@tags);
653    }
654
655    # Now save the entry/page.
656    if ('entry' eq $class_type) {
657        $cb->(MT->translate("Saving entry ('[_1]')...", $post->title));
658    } elsif ('page' eq $class_type) {
659        $cb->(MT->translate("Saving page ('[_1]')...", $post->title));
660    }
661    if ($post->save) {
662        $cb->(MT->translate("ok (ID [_1])", $post->id) . "\n");
663    } else {
664        $cb->(MT->translate("failed") . "\n");
665        die MT->translate(
666            "Save failed: [_1]", $post->errstr);
667    }
668
669    # Associate the entry to categories.
670    $primary_cat_id = $self->{def_cat_id} unless $primary_cat_id;
671    if ($primary_cat_id) {
672        my $place = MT::Placement->new;
673        $place->is_primary(1);
674        $place->entry_id($post->id);
675        $place->blog_id($self->{blog}->id);
676        $place->category_id($primary_cat_id);
677        $place->save
678            or die MT->translate(
679                "Saving placement failed: [_1]", $place->errstr);
680        delete $cat_ids{$primary_cat_id};
681    }
682
683    for my $cat_id (keys %cat_ids) {
684        my $place = MT::Placement->new;
685        $place->is_primary(0);
686        $place->entry_id($post->id);
687        $place->blog_id($self->{blog}->id);
688        $place->category_id($cat_id);
689        $place->save
690            or die MT->translate(
691                "Saving placement failed: [_1]", $place->errstr);
692    }
693
694    # Associate comments to the entry.
695    for my $comment (@{$feedbacks->{comments}}) {
696        $comment->entry_id($post->id);
697        $cb->(MT->translate("Creating new comment (from '[_1]')...", $comment->author));
698        if ($comment->save) {
699            $cb->(MT->translate("ok (ID [_1])", $comment->id) . "\n");
700        } else {
701            $cb->(MT->translate("failed") . "\n");
702            die MT->translate(
703                "Saving comment failed: [_1]", $comment->errstr);
704        }
705    }
706
707    # Associate trackbacks to the entry.
708    if (scalar @{$feedbacks->{trackbacks}}) {
709        my $tb = $post->trackback;
710        unless ($tb) {
711            $tb = MT::Trackback->new;
712            $tb->blog_id($post->blog_id);
713            $tb->entry_id($post->id);
714            $tb->category_id(0);   ## category_id can't be NULL
715        }
716        $tb->title($post->title);
717        $tb->description($post->get_excerpt);
718        $tb->url($post->permalink);
719        $tb->is_disabled($post->allow_pings);
720        $tb->save;
721        unless ($tb) {
722            die MT->translate("Entry has no MT::Trackback object!");
723        }
724        $post->trackback($tb);
725        for my $ping (@{$feedbacks->{trackbacks}}) {
726            $ping->tb_id($tb->id);
727            $cb->(MT->translate("Creating new ping ('[_1]')...", $ping->title));
728            if ($ping->save) {
729                $cb->(MT->translate("ok (ID [_1])", $ping->id) . "\n");
730            } else {
731                $cb->(MT->translate("failed") . "\n");
732                die MT->translate(
733                    "Saving ping failed: [_1]", $ping->errstr);
734            }
735        }
736    }
737    1;
738}
739
740sub _get_author_id {
741    my $self = shift;
742    my ($cb, $value) = @_;
743
744    my $author = $self->{author};
745    unless ($author) {
746        require MT::BasicAuthor;
747        $author = MT::BasicAuthor->load({ name => $value });
748        unless (defined $author) {
749            my $parent_author = $self->{parent};
750            my $pass = $self->{pass};
751            $author = MT::Author->new;
752            $author->created_by($parent_author->id) if defined $parent_author;
753            $author->name($value);
754            $author->email('');
755            $author->type(MT::Author::AUTHOR());
756            if ($pass) {
757                $author->set_password($pass);
758            } else {
759                $author->password('(none)');
760            }
761            $cb->(MT->translate("Creating new user ('[_1]')...", $value));
762            if ($author->save) {
763                $cb->(MT->translate("ok") . "\n");
764            } else {
765                $cb->(MT->translate("failed") . "\n");
766                die MT->translate(
767                    "Saving user failed: [_1]", $author->errstr);
768            }
769            $cb->(MT->translate("Assigning permissions for new user..."));
770            require MT::Role;
771            require MT::Association;
772            my $role = MT::Role->load_by_permission('post');
773            if ($role) {
774                my $assoc;
775                if ($assoc = MT::Association->link($author => $role => $self->{blog})) {
776                    $cb->(MT->translate("ok") . "\n");
777                } else {
778                    $cb->(MT->translate("failed") . "\n");
779                    die MT->translate(
780                         "Saving permission failed: [_1]", $assoc->errstr);
781                }
782            }
783        }
784    }
785    defined $author ? $author->id : undef;
786}
787
788sub _gmt2blogtime {
789    my $self = shift;
790    my ($datetime, $blog) = @_;
791    if ($datetime =~ /^(\d{4})-?(\d{2})-?(\d{2})\s?(\d{2}):(\d{2}):(\d{2})/) {
792        my($y, $mo, $d, $h, $m, $s) =
793            ($1, $2 || 1, $3 || 1, $4 || 0, $5 || 0, $6 || 0);
794        my $time = eval { timegm($s, $m, $h, $d, $mo-1, $y); } or return undef;
795        ($s, $m, $h, $d, $mo, $y) = offset_time_list($time, $blog);
796        $y += 1900;
797        $mo++;
798        return sprintf "%04d%02d%02d%02d%02d%02d", $y, $mo, $d, $h, $m, $s;
799    }
800    return undef;
801}
802
803sub _get_item_via_http {
804    my ($asset_id, $url) = @_;
805
806    require MT::TheSchwartz;
807    require TheSchwartz::Job;
808    my $job = TheSchwartz::Job->new();
809    $job->funcname('WXRImporter::Worker::Downloader');
810    $job->uniqkey( $asset_id );
811    $job->arg( { old_url => $url } );
812    $job->coalesce( $$ . ':' . ( time - ( time % 100 ) ) );
813    MT::TheSchwartz->insert($job);
814}
815
8161;
Note: See TracBrowser for help on using the browser.