root/branches/release-36/lib/MT/Blog.pm @ 2051

Revision 2051, 38.6 kB (checked in by fumiakiy, 19 months ago)

Fixed to create required files for dynamic publishing when an individual template is set to publish dynamically. BugId:79337

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