root/branches/release-38/lib/MT/Blog.pm @ 2351

Revision 2351, 39.6 kB (checked in by fumiakiy, 19 months ago)

Modify ids of widget templates in the meta field of widgetsets when they are cloned. BugId:79748

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