root/branches/release-40/lib/MT/Blog.pm @ 2606

Revision 2606, 39.8 kB (checked in by takayama, 17 months ago)

Fixed BugId:80147
* The archive type without a mapping was disabled.

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