root/branches/release-33/lib/MT/Blog.pm @ 1759

Revision 1759, 38.0 kB (checked in by mpaschal, 20 months ago)

Don't use MD5 to make these names; the leading letters should be good enough
(revert changeset 1627)
BugzID: 75137

  • 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::Blog;
8
9use strict;
10use base qw( MT::Object );
11
12use MT::FileMgr;
13use MT::Util;
14
15__PACKAGE__->install_properties({
16    column_defs => {
17        'id' => 'integer not null auto_increment',
18        'name' => 'string(255) not null',
19        'description' => 'text',
20        'archive_type' => 'string(255)',
21        'archive_type_preferred' => 'string(25)',
22        'site_path' => 'string(255)',
23        'site_url' => 'string(255)',
24        'days_on_index' => 'integer',
25        'entries_on_index' => 'integer',
26        'file_extension' => 'string(10)',
27        'email_new_comments' => 'boolean',
28        'allow_comment_html' => 'boolean',
29        'autolink_urls' => 'boolean',
30        'sort_order_posts' => 'string(8)',
31        'sort_order_comments' => 'string(8)',
32        'allow_comments_default' => 'boolean',
33        'server_offset' => 'float',
34        'convert_paras' => 'string(30)',
35        'convert_paras_comments' => 'string(30)',
36        'allow_pings_default' => 'boolean',
37        'status_default' => 'smallint',
38        'allow_anon_comments' => 'boolean',
39        'words_in_excerpt' => 'smallint',
40        'moderate_unreg_comments' => 'boolean',
41        'moderate_pings' => 'boolean',
42        'allow_unreg_comments' => 'boolean',
43        'allow_reg_comments' => 'boolean',
44        'allow_pings' => 'boolean',
45        'manual_approve_commenters' => 'boolean',
46        'require_comment_emails' => 'boolean',
47        'junk_folder_expiry' => 'integer',
48        'ping_weblogs' => 'boolean',
49        'mt_update_key' => 'string(30)',
50        'language' => 'string(5)',
51        'welcome_msg' => 'text',
52        'google_api_key' => 'string(32)',
53        'email_new_pings' => 'boolean',
54        'ping_blogs' => 'boolean',
55        'ping_technorati' => 'boolean',
56        'ping_google' => 'boolean',
57        'ping_others' => 'text',
58        'autodiscover_links' => 'boolean',
59        'sanitize_spec' => 'string(255)',
60        'cc_license' => 'string(255)',
61        'is_dynamic' => 'boolean',
62        'remote_auth_token' => 'string(50)',
63        'children_modified_on' => 'datetime',
64        'custom_dynamic_templates' => 'string(25)',
65        'junk_score_threshold' => 'float',
66        'internal_autodiscovery' => 'boolean',
67        'basename_limit' => 'smallint',
68        'use_comment_confirmation' => 'boolean',
69        'allow_commenter_regist' => 'boolean',
70        ## Have to keep these around for use in mt-upgrade.cgi.
71        'archive_url' => 'string(255)',
72        'archive_path' => 'string(255)',
73        'old_style_archive_links' => 'boolean',
74        'archive_tmpl_daily' => 'string(255)',
75        'archive_tmpl_weekly' => 'string(255)',
76        'archive_tmpl_monthly' => 'string(255)',
77        'archive_tmpl_category' => 'string(255)',
78        'archive_tmpl_individual' => 'string(255)',
79    },
80    meta => 1,
81    audit => 1,
82    indexes => {
83        name => 1,
84    },
85    defaults => {
86        'custom_dynamic_templates' => 'none',
87    },
88    child_classes => ['MT::Entry', 'MT::Page', 'MT::Template', 'MT::Asset',
89                      'MT::Category', 'MT::Folder', 'MT::Notification', 'MT::Log',
90                      'MT::ObjectTag', 'MT::Association', 'MT::Comment',
91                      'MT::TBPing', 'MT::Trackback', 'MT::TemplateMap',
92                      'MT::Touch'],
93    datasource => 'blog',
94    primary_key => 'id',
95});
96__PACKAGE__->install_meta({
97    columns => [
98        'image_default_wrap_text',
99        'image_default_align',
100        'image_default_thumb',
101        'image_default_width',
102        'image_default_wunits',
103        'image_default_constrain',
104        'image_default_popup',
105        'commenter_authenticators',
106        'require_typekey_emails',
107        'nofollow_urls',
108        'follow_auth_links',
109        'update_pings',
110        'captcha_provider',
111        'publish_queue',
112        'nwc_smart_replace',
113        'nwc_replace_field',
114        'template_set',
115        'page_layout',
116        'include_system',
117        'include_cache',
118    ],
119});
120
121# Image upload defaults.
122sub ALIGN () { 'none' }
123sub UNITS () { 'pixels' }
124
125sub class_label {
126    MT->translate("Blog");
127}
128
129sub class_label_plural {
130    MT->translate("Blogs");
131}
132
133{
134my $default_text_format;
135sub set_defaults {
136    my $blog = shift;
137    unless ($default_text_format) {
138        if (my $allowed = MT->config('AllowedTextFilters')) {
139            $allowed =~ s/\s*,.*//;
140            $default_text_format = $allowed; # choose first allowed format
141        } else {
142            $default_text_format = 'richtext'; # MT system default
143        }
144        my $filters = MT->registry("text_filters");
145        # If the 'richtext' filter exists,
146        # and is uncondition or it meets the condition, use
147        # it as the blog default text format.
148        if (!($filters->{$default_text_format} && (!$filters->{$default_text_format}{condition} || $filters->{$default_text_format}{condition}->('blog')))) {
149            $default_text_format = '__default__';
150        }
151    }
152    $blog->set_values_internal({
153        days_on_index => 0,
154        entries_on_index => 10,
155        words_in_excerpt => 40,
156        sort_order_posts => 'descend',
157        language => MT->config('DefaultLanguage'),
158        sort_order_comments => 'ascend',
159        file_extension => 'html',
160        convert_paras => $default_text_format,
161        allow_unreg_comments => 0,
162        allow_reg_comments => 1,
163        allow_pings => 1,
164        moderate_unreg_comments => MT::Blog::MODERATE_UNTRSTD(),
165        moderate_pings => 1,
166        require_comment_emails => 1,
167        allow_comments_default => 1,
168        allow_comment_html => 1,
169        autolink_urls => 1, 
170        allow_pings_default => 1,
171        require_comment_emails => 0,
172        convert_paras_comments => 1,
173        email_new_pings => 1,
174        email_new_comments => 1,
175        allow_commenter_regist => 1,
176        use_comment_confirmation => 1,
177        sanitize_spec => 0,
178        ping_weblogs => 0,
179        ping_blogs => 0,
180        ping_technorati => 0,
181        ping_google => 0,
182        archive_type => 'Individual,Monthly,Category,Category-Monthly,Page',
183        archive_type_preferred => 'Individual',
184        status_default => 2,
185        junk_score_threshold => 0,
186        junk_folder_expiry => 14, # 14 days
187        custom_dynamic_templates => 'none',
188        internal_autodiscovery => 0,
189        basename_limit => 30,
190        server_offset => MT->config('DefaultTimezone') || 0,
191        # something far in the future to force dynamic side to read it.
192        children_modified_on => '20101231120000',
193    });
194    return $blog;
195}
196}
197
198sub create_default_blog {
199    my $class = shift;
200    my ($blog_name, $blog_template) = @_;
201    $blog_name ||= MT->translate("First Blog");
202    $class = ref $class if ref $class;
203
204    my $blog = new $class;
205    $blog->name($blog_name);
206
207    # Enable nofollow options
208    $blog->nofollow_urls(1);
209    $blog->follow_auth_links(1);
210   
211    # Enable default commenter authentication
212    $blog->commenter_authenticators(MT->config('DefaultCommenterAuth'));
213
214    # set default page layout
215    $blog->page_layout('layout-wtt');
216
217    $blog->save or return $class->error($blog->errstr);
218    $blog->create_default_templates($blog_template || 'mt_blog')
219        or return $class->error($blog->errstr);
220    return $blog;
221}
222
223sub create_default_templates {
224    my $blog = shift;
225
226    require MT::DefaultTemplates;
227    my $tmpl_list = MT::DefaultTemplates->templates( @_ );
228    return $blog->error(MT->translate("No default templates were found."))
229        if !$tmpl_list || (ref($tmpl_list) ne 'ARRAY') || (!@$tmpl_list);
230
231    require MT::Template;
232    my @arch_tmpl;
233    for my $val (@$tmpl_list) {
234        next if $val->{global};
235
236        my $obj = MT::Template->new;
237        my $p = $val->{plugin} || 'MT'; # component and/or MT package for translate
238        local $val->{name} = $val->{name}; # name field is translated in "templates" call
239        local $val->{text} = $p->translate_templatized($val->{text});
240        $obj->build_dynamic(0);
241        foreach my $v (keys %$val) {
242            $obj->column($v, $val->{$v}) if $obj->has_column($v);
243        }
244        $obj->blog_id($blog->id);
245        if (my $pub_opts = $val->{publishing}) {
246            $obj->include_with_ssi(1) if $pub_opts->{include_with_ssi};
247        }
248        $obj->save;
249        if ($val->{mappings}) {
250            push @arch_tmpl, {
251                template => $obj,
252                mappings => $val->{mappings},
253                exists($val->{preferred}) ? (preferred => $val->{preferred}) : ()
254            };
255        }
256    }
257
258    if (@arch_tmpl) {
259        require MT::TemplateMap;
260        for my $map_set (@arch_tmpl) {
261            my $tmpl = $map_set->{template};
262            my $mappings = $map_set->{mappings};
263            foreach my $map_key (keys %$mappings) {
264                my $m = $mappings->{$map_key};
265                my $at = $m->{archive_type};
266                # my $preferred = $mappings->{$map_key}{preferred};
267                my $map = MT::TemplateMap->new;
268                $map->archive_type($at);
269                if ( exists $m->{preferred} ) {
270                    $map->is_preferred($m->{preferred});
271                }
272                else {
273                    $map->is_preferred(1);
274                }
275                $map->template_id($tmpl->id);
276                $map->file_template($m->{file_template}) if $m->{file_template};
277                $map->blog_id($tmpl->blog_id);
278                $map->save;
279            }
280        }
281    }
282
283    MT->run_callbacks(
284        ref($blog). '::post_create_default_templates',
285        $blog, 
286        $tmpl_list
287    );
288
289    return $blog;
290}
291
292# As of MT 4, we always manage fileinfo records.
293sub needs_fileinfo {
294    return 1;
295}
296
297sub current_timestamp {
298    my $blog = shift;
299    require MT::Util;
300    my @ts = MT::Util::offset_time_list(time, $blog->id);
301    return sprintf '%04d%02d%02d%02d%02d%02d',
302        $ts[5]+1900, $ts[4]+1, @ts[3,2,1,0];
303}
304
305sub site_url {
306    my $blog = shift;
307    if (!@_ && $blog->is_dynamic) {
308        my $cfg = MT->config;
309        my $path = $cfg->CGIPath;
310        $path .= '/' unless $path =~ m!/$!;
311        return $path . $cfg->ViewScript . '/' . $blog->id;
312    } else {
313        return $blog->SUPER::site_url(@_);
314    }
315}
316
317sub archive_url {
318    my $blog = shift;
319    if (!@_ && $blog->is_dynamic) {
320        $blog->site_url;
321    } else {
322        $blog->SUPER::archive_url(@_) || $blog->site_url;
323    }
324}
325
326sub archive_path {
327    my $blog = shift;
328    $blog->SUPER::archive_path(@_) || $blog->site_path;
329}
330
331sub comment_text_filters {
332    my $blog = shift;
333    my $filters = $blog->convert_paras_comments;
334    return [] unless $filters;
335    if ($filters eq '1') {
336        return [ '__default__' ];
337    } else {
338        return [ split /\s*,\s*/, $filters ];
339    }
340}
341
342sub cc_license_url {
343    my $cc = $_[0]->cc_license or return '';
344    MT::Util::cc_url($cc);
345}
346
347sub email_all_comments {
348    return $_[0]->email_new_comments == 1;
349}
350
351sub email_attn_reqd_comments {
352    return $_[0]->email_new_comments == 2;
353}
354
355sub email_all_pings {
356    return $_[0]->email_new_pings == 1;
357}
358
359sub email_attn_reqd_pings {
360    return $_[0]->email_new_pings == 2;
361}
362
363sub MODERATE_NONE ()    { 0 }
364sub MODERATE_ALL ()     { 1 }
365sub MODERATE_UNTRSTD () { 2 }
366sub MODERATE_UNAUTHD () { 3 }
367
368sub publish_trusted_commenters {
369    !($_[0]->moderate_unreg_comments == MODERATE_ALL);
370}
371
372sub publish_authd_untrusted_commenters {
373    return $_[0]->moderate_unreg_comments == MODERATE_UNAUTHD
374        || $_[0]->moderate_unreg_comments == MODERATE_NONE;
375}
376
377sub publish_unauthd_commenters {
378    $_[0]->moderate_unreg_comments == MODERATE_NONE;
379}
380
381sub include_path_parts {
382    my $blog = shift;
383    my ($name) = @_;
384
385    my $filestem = MT::Util::dirify($name);
386    my $filename = join q{.}, $filestem, $blog->file_extension;
387    return (MT->config('IncludesDir'), substr($filestem, 0, 3), $filename);
388}
389
390sub include_path {
391    my $blog = shift;
392    my ($name) = @_;
393
394    my @parts = $blog->include_path_parts($name);
395    my $filename = pop @parts;
396    my $path = File::Spec->catdir($blog->site_path, @parts);
397    my $file_path = File::Spec->catfile($path, $filename);
398    return wantarray ? ($path, $file_path) : $file_path;
399}
400
401sub include_url {
402    my $blog = shift;
403    my ($name) = @_;
404
405    my @parts = $blog->include_path_parts();
406    my $url = join q{/}, $blog->site_url, @parts;
407    return $url;
408}
409
410sub include_statement {
411    my $blog = shift;
412    my ($name) = @_;
413
414    my $system = $blog->include_system or return;
415
416    my ($statement, $include);
417    if ($system eq 'shtml') {
418        $statement = q{<!--#include virtual="%s" -->};
419
420        my $site_url = $blog->site_url;
421        $site_url =~ s{ \A \w+ :// [^/]+ }{}xms;
422        $site_url =~ s{ / \z }{}xms;
423        $include = join q{/}, $site_url, $blog->include_path_parts($name);
424    }
425    else {
426        $include = $blog->include_path($name);
427        $statement = $system eq 'php'   ? q{<?php include("%s") ?>}
428                   : $system eq 'jsp'   ? q{<%@ include file="%s" %>}
429                   : $system eq 'asp'   ? '<!--#include file="%s" -->'
430                   :                      return
431                   ;
432    }
433    return sprintf $statement, MT::Util::encode_php($include, q{qq});
434}
435
436sub file_mgr {
437    my $blog = shift;
438    unless (exists $blog->{__file_mgr}) {
439## xxx need to add remote_host, remote_user, remote_pwd fields
440## then pull params from there; if remote_host is defined, we
441## assume we are using FTP?
442        $blog->{__file_mgr} = MT::FileMgr->new('Local');
443    }
444    $blog->{__file_mgr};
445}
446
447sub remove {
448    my $blog = shift;
449    $blog->remove_children({ key => 'blog_id'});
450    my $res = $blog->SUPER::remove(@_);
451    if ((ref $blog) && $res) {
452        require MT::Permission;
453        MT::Permission->remove({ blog_id => $blog->id });
454    }
455    $res;
456}
457
458# deprecated: use $blog->remote_auth_token instead
459sub effective_remote_auth_token {
460    my $blog = shift;
461    if (scalar @_) {
462        return $blog->remote_auth_token(@_);
463    }
464    if ($blog->remote_auth_token()) {
465        return $blog->remote_auth_token();
466    }
467    undef;
468}
469
470sub has_archive_type {
471    my $blog = shift;
472    my ($type) = @_;
473    my %at = map { lc $_ => 1 } split(/,/, $blog->archive_type);
474    return exists $at{lc $type} ? 1 : 0;
475}
476
477sub accepts_registered_comments {
478    $_[0]->allow_reg_comments && $_[0]->commenter_authenticators;
479}
480
481sub accepts_comments {
482    $_[0]->accepts_registered_comments || $_[0]->allow_unreg_comments;
483}
484
485sub count_static_templates {
486    my $blog = shift;
487    my ($archive_type) = @_;
488    my $result = 0;
489    require MT::TemplateMap;
490    my @maps = MT::TemplateMap->load({blog_id => $blog->id,
491                                      archive_type => $archive_type});
492    return 0 unless @maps;
493    require MT::Template;
494    foreach my $map (@maps) { 
495        my $tmpl = MT::Template->load($map->template_id);
496        $result++ if !$tmpl->build_dynamic;
497    }
498    #$result ||= 1 if ($blog->custom_dynamic_templates || '') ne 'custom';
499    return $result;
500}
501
502sub touch {
503    my $blog = shift;
504    my ( @types ) = @_;
505    my ($s,$m,$h,$d,$mo,$y) = localtime(time);
506    my $mod_time = sprintf("%04d%02d%02d%02d%02d%02d",
507                           1900+$y, $mo+1, $d, $h, $m, $s);
508    require MT::Touch;
509    MT::Touch->touch( $blog->id, @types );
510    $blog->children_modified_on($mod_time);
511    $mod_time;
512}
513
514sub clone {
515    my $blog = shift;
516    my ($param) = @_;
517    if ($param && $param->{Children}) {
518        $blog->clone_with_children(@_);
519    } else {
520        $blog->SUPER::clone(@_);
521    }
522}
523
524sub clone_with_children {
525    my $blog = shift;
526    my ($params) = @_;
527    my $callback = $params->{Callback} || sub {};
528    my $classes = $params->{Classes};
529    my $blog_name = $params->{BlogName};
530    delete $$params{Children} if ($params->{Children});
531    my $old_blog_id = $blog->id;
532
533    # we must clone:
534    #    Blog record
535    #    Entry records
536    #       - Comment records
537    #       - TrackBack records
538    #       - TBPing records
539    #       - ObjectTag records (if running 3.3)
540    #    Category records
541    #    Placement records
542    #    Template records
543    #    Permission records
544    #    IPBanList records???
545    #    Notification records???
546
547    my $new_blog_id;
548    my (%entry_map, %cat_map, %tb_map, %tmpl_map, $counter, $iter);
549
550    # Cloning blog
551    my $new_blog = $blog->clone($params);
552    $new_blog->name(MT->translate($blog_name ? $blog_name : "Clone of [_1]", $blog->name));
553    delete $new_blog->{column_values}->{id};
554    delete $new_blog->{changed_cols}->{id};
555    $new_blog->save or die $new_blog->errstr;
556    $new_blog_id = $new_blog->id;
557    $callback->(MT->translate("Cloned blog... new id is [_1].",
558        $new_blog_id));
559
560    if ((!exists $classes->{'MT::Permission'}) || $classes->{'MT::Permission'}) {
561        # Cloning PERMISSIONS records
562        $counter = 0;
563        my $state = MT->translate("Cloning permissions for blog:");
564        $callback->($state, "perms");
565        require MT::Permission;
566        $iter = MT::Permission->load_iter({blog_id => $old_blog_id});
567        while (my $perm = $iter->()) {
568            $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'perms')
569                if $counter && ($counter % 100 == 0);
570            $counter++;
571            delete $perm->{column_values}->{id};
572            delete $perm->{changed_cols}->{id};
573            $perm->blog_id($new_blog_id);
574            $perm->save or die $perm->errstr;
575        }
576        $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'perms');
577    }
578
579    if ((!exists $classes->{'MT::Association'}) || $classes->{'MT::Association'}) {
580        # Cloning association records
581        $counter = 0;
582        my $state = MT->translate("Cloning associations for blog:");
583        $callback->($state, "assoc");
584        require MT::Association;
585        $iter = MT::Association->load_iter({blog_id => $old_blog_id});
586        while (my $assoc = $iter->()) {
587            $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'assoc')
588                if $counter && ($counter % 100 == 0);
589            $counter++;
590            delete $assoc->{column_values}->{id};
591            delete $assoc->{changed_cols}->{id};
592            $assoc->blog_id($new_blog_id);
593            $assoc->save or die $assoc->errstr;
594        }
595        $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'assoc');
596    }
597
598    # include/exclude class logic
599    # if user has not specified 'Classes' element, clone everything
600    # if user has specified Classes, but a particular class is not
601    # identified, clone it (forward compatibility). if a class is
602    # specified and the flag is '1', clone it. if a class is specified
603    # but the flag is '0', skip it.
604
605    # MT::Entry -> MT::Category, MT::Comment, MT::Tracback, MT::TBPing
606    # MT::Page -> MT::Folder, MT::Comment, MT::Trackback, MT::TBPing
607
608    if ((!exists $classes->{'MT::Entry'}) || $classes->{'MT::Entry'}) {
609        # Cloning ENTRY records
610        my $state = MT->translate("Cloning entries for blog...");
611        $callback->($state, "entries");
612        $counter = 0;
613        require MT::Entry;
614        $iter = MT::Entry->load_iter({blog_id => $old_blog_id, class => '*'});
615        while (my $entry = $iter->()) {
616            $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'entries')
617                if $counter && ($counter % 100 == 0);
618            $counter++;
619            my $entry_id = $entry->id;
620            delete $entry->{column_values}->{id};
621            delete $entry->{changed_cols}->{id};
622            $entry->blog_id($new_blog_id);
623            $entry->save or die $entry->errstr;
624            $entry_map{$entry_id} = $entry->id;
625        }
626        $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'entries');
627
628        if ((!exists $classes->{'MT::Category'}) || $classes->{'MT::Category'}) {
629            # Cloning CATEGORY records
630            my $state = MT->translate("Cloning categories for blog...");
631            $callback->($state, "cats");
632            $counter = 0;
633            require MT::Category;
634            $iter = MT::Category->load_iter({ blog_id => $old_blog_id, class => '*' });
635            my %cat_parents;
636            while (my $cat = $iter->()) {
637                $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'cats')
638                    if $counter && ($counter % 100 == 0);
639                $counter++;
640                my $cat_id = $cat->id;
641                my $old_parent = $cat->parent;
642                delete $cat->{column_values}->{id};
643                delete $cat->{changed_cols}->{id};
644                $cat->blog_id($new_blog_id);
645                # temporarily wipe the parent association
646                # to avoid constraint issues.
647                $cat->parent(0);
648                $cat->save or die $cat->errstr;
649                $cat_map{$cat_id} = $cat->id;
650                if ($old_parent) {
651                    $cat_parents{$cat->id} = $old_parent;
652                }
653            }
654            # reassign the new category parents
655            foreach (keys %cat_parents) {
656                my $cat = MT::Category->load($_);
657                if ($cat) {
658                    $cat->parent($cat_map{$cat_parents{$cat->id}});
659                    $cat->save or die $cat->errstr;
660                }
661            }
662            $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'cats');
663
664            # Placements are automatically cloned if categories are
665            # cloned.
666            $state = MT->translate("Cloning entry placements for blog...");
667            $callback->($state, "places");
668            require MT::Placement;
669            $iter = MT::Placement->load_iter({ blog_id => $old_blog_id });
670            $counter = 0;
671            while (my $place = $iter->()) {
672                $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'places')
673                    if $counter && ($counter % 100 == 0);
674                $counter++;
675                delete $place->{column_values}->{id};
676                delete $place->{changed_cols}->{id};
677                $place->blog_id($new_blog_id);
678                $place->category_id($cat_map{$place->category_id});
679                $place->entry_id($entry_map{$place->entry_id});
680                $place->save or die $place->errstr;
681            }
682            $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'places');
683        }
684
685        if ((!exists $classes->{'MT::Comment'}) || $classes->{'MT::Comment'}) {
686            # Comments can only be cloned if entries are cloned.
687            my $state = MT->translate("Cloning comments for blog...");
688            $callback->($state, "comments");
689            require MT::Comment;
690            $iter = MT::Comment->load_iter({ blog_id => $old_blog_id });
691            $counter = 0;
692            while (my $comment = $iter->()) {
693                $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'comments')
694                    if $counter && ($counter % 100 == 0);
695                $counter++;
696                delete $comment->{column_values}->{id};
697                delete $comment->{changed_cols}->{id};
698                $comment->entry_id($entry_map{$comment->entry_id});
699                $comment->blog_id($new_blog_id);
700                $comment->save or die $comment->errstr;
701            }
702            $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'comments');
703        }
704
705        if ((!exists $classes->{'MT::ObjectTag'}) || $classes->{'MT::ObjectTag'}) {
706            # conditionally do MT::ObjectTag since it is only
707            # available with MT 3.3.
708            if ($MT::VERSION >= 3.3) {
709                my $state = MT->translate("Cloning entry tags for blog...");
710                $callback->($state, "tags");
711                require MT::ObjectTag;
712                $iter = MT::ObjectTag->load_iter({ blog_id => $old_blog_id, object_datasource => 'entry' });
713                $counter = 0;
714                while (my $entry_tag = $iter->()) {
715                    $callback->($state . " " . MT->translate("[_1] records processed...", $counter), "tags")
716                        if $counter && ($counter % 100 == 0);
717                    $counter++;
718                    delete $entry_tag->{column_values}->{id};
719                    delete $entry_tag->{changed_cols}->{id};
720                    $entry_tag->blog_id($new_blog_id);
721                    $entry_tag->object_id($entry_map{$entry_tag->object_id});
722                    $entry_tag->save or die $entry_tag->errstr;
723                }
724                $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'tags');
725            }
726        }
727    }
728
729    if ((!exists $classes->{'MT::Trackback'}) || $classes->{'MT::Trackback'}) {
730        my $state = MT->translate("Cloning TrackBacks for blog...");
731        $callback->($state, "tbs");
732        require MT::Trackback;
733        $iter = MT::Trackback->load_iter({ blog_id => $old_blog_id });
734        $counter = 0;
735        while (my $tb = $iter->()) {
736            next unless ($tb->entry_id && $entry_map{$tb->entry_id}) ||
737                ($tb->category_id && $cat_map{$tb->category_id});
738
739            $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'tbs')
740                if $counter && ($counter % 100 == 0);
741            $counter++;
742            my $tb_id = $tb->id;
743            delete $tb->{column_values}->{id};
744            delete $tb->{changed_cols}->{id};
745
746            if ($tb->category_id) {
747                if (my $cid = $cat_map{$tb->category_id}) {
748                    my $cat_tb = MT::Trackback->load(
749                        { category_id => $cid }
750                    );
751                    if ($cat_tb) {
752                        my $changed;
753                        if ($tb->passphrase) {
754                            $cat_tb->passphrase($tb->passphrase);
755                            $changed = 1;
756                        }
757                        if ($tb->is_disabled) {
758                            $cat_tb->is_disabled(1);
759                            $changed = 1;
760                        }
761                        $cat_tb->save if $changed;
762                        $tb_map{$tb_id} = $cat_tb->id;
763                        next;
764                    }
765                }
766            }
767            elsif ($tb->entry_id) {
768                if (my $eid = $entry_map{$tb->entry_id}) {
769                    my $entry_tb = MT::Entry->load($eid)->trackback;
770                    if ($entry_tb) {
771                        my $changed;
772                        if ($tb->passphrase) {
773                            $entry_tb->passphrase($tb->passphrase);
774                            $changed = 1;
775                        }
776                        if ($tb->is_disabled) {
777                            $entry_tb->is_disabled(1);
778                            $changed = 1;
779                        }
780                        $entry_tb->save if $changed;
781                        $tb_map{$tb_id} = $entry_tb->id;
782                        next;
783                    }
784                }
785            }
786
787            # A trackback wasn't created when saving the entry/category,
788            # (perhaps trackbacks are now disabled for the entry/category?)
789            # so create one now
790            $tb->entry_id($entry_map{$tb->entry_id})
791                if $tb->entry_id && $entry_map{$tb->entry_id};
792            $tb->category_id($cat_map{$tb->category_id})
793                if $tb->category_id && $cat_map{$tb->category_id};
794            $tb->blog_id($new_blog_id);
795            $tb->save or die $tb->errstr;
796            $tb_map{$tb_id} = $tb->id;
797        }
798        $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'tbs');
799
800        if ((!exists $classes->{'MT::TBPing'}) || $classes->{'MT::TBPing'}) {
801            my $state = MT->translate("Cloning TrackBack pings for blog...");
802            $callback->($state, "pings");
803            require MT::TBPing;
804            $iter = MT::TBPing->load_iter({ blog_id => $old_blog_id });
805            $counter = 0;
806            while (my $ping = $iter->()) {
807                next unless $tb_map{$ping->tb_id};
808                $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'pings')
809                    if $counter && ($counter % 100 == 0);
810                $counter++;
811                delete $ping->{column_values}->{id};
812                delete $ping->{changed_cols}->{id};
813                $ping->tb_id($tb_map{$ping->tb_id});
814                $ping->blog_id($new_blog_id);
815                $ping->save or die $ping->errstr;
816            }
817            $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'pings');
818        }
819    }
820
821    if ((!exists $classes->{'MT::Template'}) || $classes->{'MT::Template'}) {
822        my $state = MT->translate("Cloning templates for blog...");
823        $callback->($state, "tmpls");
824        require MT::Template;
825        $iter = MT::Template->load_iter({ blog_id => $old_blog_id });
826        $counter = 0;
827        while (my $tmpl = $iter->()) {
828            $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'tmpls')
829                if $counter && ($counter % 100 == 0);
830            my $tmpl_id = $tmpl->id;
831            $counter++;
832            delete $tmpl->{column_values}->{id};
833            delete $tmpl->{changed_cols}->{id};
834            # linked_file won't be cloned for now because
835            # new blog does not have site_path - breaks relative path
836            delete $tmpl->{column_values}->{linked_file};
837            delete $tmpl->{column_values}->{linked_file_mtime};
838            delete $tmpl->{column_values}->{linked_file_size};
839            $tmpl->blog_id($new_blog_id);
840            $tmpl->save or die $tmpl->errstr;
841            $tmpl_map{$tmpl_id} = $tmpl->id;
842        }
843        $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'tmpls');
844
845        $state = MT->translate("Cloning template maps for blog...");
846        $callback->($state, "tmplmaps");
847        require MT::TemplateMap;
848        $iter = MT::TemplateMap->load_iter({ blog_id => $old_blog_id });
849        $counter = 0;
850        while (my $map = $iter->()) {
851            $callback->($state . " " . MT->translate("[_1] records processed...", $counter), 'tmplmaps')
852                if $counter && ($counter % 100 == 0);
853            $counter++;
854            delete $map->{column_values}->{id};
855            delete $map->{changed_cols}->{id};
856            $map->template_id($tmpl_map{$map->template_id});
857            $map->blog_id($new_blog_id);
858            $map->save or die $map->errstr;
859        }
860        $callback->($state . " " . MT->translate("[_1] records processed.", $counter), 'tmplmaps');
861    }
862
863    MT->run_callbacks(ref($blog). '::post_clone',
864        OldBlogId => $blog->id, old_blog_id => $blog->id,
865        NewBlogId => $new_blog->id, new_blog_id => $new_blog->id,
866        OldObject => $blog, old_object => $blog,
867        NewObject => $new_blog, new_object => $new_blog,
868        EntryMap => \%entry_map, entry_map => \%entry_map,
869        CategoryMap => \%cat_map, category_map => \%cat_map,
870        TrackbackMap => \%tb_map, trackback_map => \%tb_map,
871        TemplateMap => \%tmpl_map, template_map => \%tmpl_map,
872        Callback => $callback, callback => $callback,
873    );
874    $new_blog;
875}
876
877sub smart_replace {
878    my $blog = shift;
879    if (@_) {
880        $blog->nwc_smart_replace(@_);
881        return;
882    }
883    my $val = $blog->nwc_smart_replace;
884    return defined($val) ? $val : MT->config->NwcSmartReplace;
885}
886
887sub smart_replace_fields {
888    my $blog = shift;
889    if (@_) {
890        $blog->nwc_replace_field(@_);
891        return;
892    }
893    my $val = $blog->nwc_replace_field;
894    return defined($val) ? $val : MT->config->NwcReplaceField;
895}
896
897#trans('blog')
898#trans('blogs')
899
9001;
901__END__
902
903=head1 NAME
904
905MT::Blog - Movable Type blog record
906
907=head1 SYNOPSIS
908
909    use MT::Blog;
910    my $blog = MT::Blog->load($blog_id);
911    $blog->name('Some new name');
912    $blog->save
913        or die $blog->errstr;
914
915=head1 DESCRIPTION
916
917An I<MT::Blog> object represents a blog in the Movable Type system. It
918contains all of the settings, preferences, and configuration for a particular
919blog. It does not contain any per-author permissions settings--for those,
920look at the I<MT::Permission> object.
921
922=head1 USAGE
923
924As a subclass of I<MT::Object>, I<MT::Blog> inherits all of the
925data-management and -storage methods from that class; thus you should look
926at the I<MT::Object> documentation for details about creating a new object,
927loading an existing object, saving an object, etc.
928
929The following methods are unique to the I<MT::Blog> interface:
930
931=head2 $blog->file_mgr
932
933Returns the I<MT::FileMgr> object specific to this particular blog.
934
935=head1 DATA ACCESS METHODS
936
937The I<MT::Blog> object holds the following pieces of data. These fields can
938be accessed and set using the standard data access methods described in the
939I<MT::Object> documentation.
940
941=over 4
942
943=item * id
944
945The numeric ID of the blog.
946
947=item * name
948
949The name of the blog.
950
951=item * description
952
953The blog description.
954
955=item * site_path
956
957The path to the directory containing the blog's output index templates.
958
959=item * site_url
960
961The URL corresponding to the I<site_path>.
962
963=item * archive_path
964
965The path to the directory where the blog's archives are stored.
966
967=item * archive_url
968
969The URL corresponding to the I<archive_path>.
970
971=item * server_offset
972
973A slight misnomer, this is actually the timezone that the B<user> has
974selected; the value is the offset from GMT.
975
976=item * archive_type
977
978A comma-separated list of archive types used in this particular blog, where
979an archive type is one of the following: C<Individual>, C<Daily>, C<Weekly>,
980C<Monthly>, or C<Category>. For example, a blog's I<archive_type> would be
981C<Individual,Monthly> if the blog were using C<Individual> and C<Monthly>
982archives.
983
984=item * archive_type_preferred
985
986The "preferred" archive type, which is used when constructing a link to the
987archive page for a particular archive--if multiple archive types are selected,
988for example, the link can only point to one of those archives. The preferred
989archive type (which should be one of the archive types set in I<archive_type>,
990above) specifies to which archive this link should point (among other things).
991
992=item * days_on_index
993
994The number of days to be displayed on the index.
995
996=item * file_extension
997
998The file extension to be used for archive pages.
999
1000=item * email_new_comments
1001
1002A boolean flag specifying whether authors should be notified of new comments
1003posted on entries they have written.
1004
1005=item * allow_comment_html
1006
1007A boolean flag specifying whether HTML should be allowed in comments. If it
1008is not allowed, it is automatically stripped before building the page (note
1009that the content stored in the database is B<not> stripped).
1010
1011=item * autolink_urls
1012
1013A boolean flag specifying whether URLs in comments should be turned into
1014links. Note that this setting is only taken into account if
1015I<allow_comment_html> is turned off.
1016
1017=item * sort_order_posts
1018
1019The default sort order for entries. Valid values are either C<ascend> or
1020C<descend>.
1021
1022=item * sort_order_comments
1023
1024The default sort order for comments. Valid values are either C<ascend> or
1025C<descend>.
1026
1027=item * allow_comments_default
1028
1029The default value for the I<allow_comments> field in the I<MT::Entry> object.
1030
1031=item * convert_paras
1032
1033A comma-separated list of text filters to apply to each entry when it
1034is built.
1035
1036=item * convert_paras_comments
1037
1038A comma-separated list of text filters to apply to each comment when it
1039is built.
1040
1041=item * status_default
1042
1043The default value for the I<status> field in the I<MT::Entry> object.
1044
1045=item * allow_anon_comments
1046
1047A boolean flag specifying whether anonymous comments (those posted without
1048a name or an email address) are allowed.
1049
1050=item * allow_unreg_comments
1051
1052A boolean flag specifying whether unregistered comments (those posted
1053without a validated email/password pair) are allowed.
1054
1055=item * words_in_excerpt
1056
1057The number of words in an auto-generated excerpt.
1058
1059=item * ping_weblogs
1060
1061A boolean flag specifying whether the system should send an XML-RPC ping to
1062I<weblogs.com> after an entry is saved.
1063
1064=item * mt_update_key
1065
1066The Movable Type Recently Updated Key to be sent to I<movabletype.org> after
1067an entry is saved.
1068
1069=item * language
1070
1071The language for date and time display for this particular blog.
1072
1073=item * welcome_msg
1074
1075The welcome message to be displayed on the main Editing Menu for this blog.
1076Should contain all desired HTML formatting.
1077
1078=back
1079
1080=head1 METHODS
1081
1082=over 4
1083
1084=item * clone( [ \%parameters ] )
1085
1086MT::Blog provides a clone method that supports cloning of all known child
1087records related to the MT::Blog object. To invoke this behavior, you
1088simply specify a parameter hash with a 'Children' key set.
1089
1090    # Clones blog and all data related to this blog within the database.
1091    my $new_blog = $original_blog->clone({ Children => 1 });
1092
1093You may further specify what kind of records are cloned in the process
1094of cloning child objects. Use the 'Classes' parameter to specifically
1095exclude particular classes:
1096
1097    # Clones everything except comments and trackback pings
1098    my $new_blog = $original_blog->clone({
1099        Children => 1,
1100        Classes => { 'MT::Comments' => 0, 'MT::TBPing' => 0 }
1101    });
1102
1103Note: Certain exclusions will prevent the clone process from including
1104other classes. For instance, if you exclude MT::Trackback, all MT::TBPing
1105objects are automatically excluded.
1106
1107=back
1108
1109=head1 DATA LOOKUP
1110
1111In addition to numeric ID lookup, you can look up or sort records by any
1112combination of the following fields. See the I<load> documentation in
1113I<MT::Object> for more information.
1114
1115=over 4
1116
1117=item * name
1118
1119=back
1120
1121=head1 NOTES
1122
1123=over 4
1124
1125=item *
1126
1127When you remove a blog using I<MT::Blog::remove>, in addition to removing the
1128blog record, all of the entries, notifications, permissions, comments,
1129templates, and categories in that blog will also be removed.
1130
1131=item *
1132
1133Because the system needs to load I<MT::Blog> objects from disk relatively
1134often during the duration of one request, I<MT::Blog> objects are cached by
1135the I<MT::Blog::load> object so that each blog only need be loaded once. The
1136I<MT::Blog> objects are cached in the I<MT::Request> singleton object; note
1137that this caching B<only occurs> if the blogs are loaded by numeric ID.
1138
1139=back
1140
1141=head1 AUTHOR & COPYRIGHTS
1142
1143Please see the I<MT> manpage for author, copyright, and license information.
1144
1145=cut
Note: See TracBrowser for help on using the browser.