root/trunk/lib/MT/Upgrade/v4.pm @ 3082

Revision 3082, 42.1 kB (checked in by bchoate, 14 months ago)

Merging fireball branch changes to-date to trunk: svn merge -r2974:3081 http://code.sixapart.com/svn/movabletype/branches/fireball .

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::Upgrade::v4;
8
9use strict;
10
11sub upgrade_functions {
12    return {
13        'core_init_comment_junk_status' => {
14            version_limit => 4.0053,
15            priority => 3.1,
16            updater => {
17                type => 'comment',
18                condition => sub { !$_[0]->junk_status },
19                code => sub { $_[0]->junk_status(1) },
20                label => 'Assigning junk status for comments...',
21                sql => 'update mt_comment set comment_junk_status = 1
22                        where comment_junk_status is null
23                           or comment_junk_status=0',
24            }
25        },
26        'core_init_tbping_junk_status' => {
27            version_limit => 4.0053,
28            priority => 3.1,
29            updater => {
30                type => 'tbping',
31                condition => sub { !$_[0]->junk_status },
32                code => sub { $_[0]->junk_status(1) },
33                label => 'Assigning junk status for TrackBacks...',
34                sql => 'update mt_tbping set tbping_junk_status = 1
35                        where tbping_junk_status is null
36                          or tbping_junk_status=0',
37            }
38        },
39        'core_deprecate_bitmask_permissions' => {
40            code => \&deprecate_bitmask_permissions,
41            version_limit => 4.0002,
42            priority => 3.3,
43        },
44        'core_migrate_system_privileges' => {
45            code => \&migrate_system_privileges,
46            version_limit => 4.0002,
47            priority => 3.3,
48        },
49        'core_populate_authored_on' => {
50            version_limit => 4.0014,
51            priority => 3.1,
52            updater => {
53                type => 'entry',
54                label => 'Populating authored and published dates for entries...',
55                condition => sub {
56                    !defined $_[0]->authored_on
57                },
58                code => sub {
59                    $_[0]->authored_on($_[0]->created_on)
60                        if !defined $_[0]->authored_on;
61                },
62                sql =>
63                    'update mt_entry set entry_authored_on = entry_created_on
64                        where entry_authored_on is null',
65            },
66        },
67        'core_update_3x_system_search_templates' => {
68            version_limit => 4.0017,
69            priority => 3.1,
70            code => \&update_3x_system_search_templates,
71        },
72        'core_rename_php_plugin_filenames' => {
73            version_limit => 4.0019,
74            priority => 3.1,
75            code => \&rename_php_plugin_filenames,
76        },
77        'core_migrate_nofollow_settings' => {
78            version_limit => 4.0020,
79            priority => 3.1,
80            code => \&migrate_nofollow_settings,
81        },
82        'core_update_widget_template' => {
83            version_limit => 4.0022,
84            priority => 3.1,
85            updater => {
86                type => 'template',
87                label => 'Updating widget template records...',
88                condition => sub {
89                    return 0 unless 'custom' eq $_[0]->type;
90                    my $name = $_[0]->name;
91                    if ($name =~ s/^(?:Widget|Sidebar): ?//) {
92                        $_[0]->name($name);
93                        $_[0]->type('widget');
94                        $_[0]->save;
95                    }
96                    0;
97                },
98                code => sub { 1; },
99            },
100        },
101        # This upgrade step is currently necessary for PostgreSQL
102        # which doesn't support adding a column, populating the existing
103        # records with a value.
104        'core_typify_category_records' => {
105            version_limit => 4.0023,
106            priority => 3.1,
107            updater => {
108                type => 'category',
109                label => 'Classifying category records...',
110                condition => sub {
111                    !$_[0]->column('class')
112                },
113                code => sub {
114                    $_[0]->class('category');
115                },
116                sql =>
117                    "update mt_category set category_class = 'category'
118                        where category_class is null",
119            },
120        },
121        'core_typify_entry_records' => {
122            version_limit => 4.0023,
123            priority => 3.1,
124            updater => {
125                type => 'entry',
126                label => 'Classifying entry records...',
127                condition => sub {
128                    !$_[0]->column('class')
129                },
130                code => sub {
131                    $_[0]->class('entry');
132                },
133                sql =>
134                    "update mt_entry set entry_class = 'entry'
135                        where entry_class is null",
136            },
137        },
138        'core_merge_comment_response_templates' => {
139            version_limit => 4.0023,
140            priority => 3.1,
141            updater => {
142                type => 'blog',
143                label => "Merging comment system templates...",
144                code => \&_merge_comment_response_templates_updater,
145            },
146        },
147        'core_populate_default_file_template' => {
148            version_limit => 4.0023,
149            priority => 3.1,
150            updater => {
151                type => 'templatemap',
152                label => 'Populating default file template for templatemaps...',
153                condition => sub {
154                    !defined $_[0]->file_template
155                },
156                code => sub {
157                    my %default_template = (
158                        Individual => '%y/%m/%f',
159                        Category => '%c/%i',
160                    );
161                    $_[0]->file_template($default_template{$_[0]->archive_type})
162                        if !defined $_[0]->file_template && exists($default_template{$_[0]->archive_type});
163                },
164            },
165        },
166        'core_remove_unused_templatemap' => {
167            version_limit => 4.0023,
168            priority => 3.0,
169            updater => {
170                type => 'blog',
171                label => 'Removing unused template maps...',
172                condition => sub {
173                    my $blog = shift;
174                    my @blog_at = split /,/, $blog->archive_type;
175                    require MT::TemplateMap;
176                    MT::TemplateMap->remove(
177                      { blog_id => $blog->id, archive_type => \@blog_at },
178                      { not => { archive_type => 1 } }
179                    ) if @blog_at;
180                    return 0;
181                },
182            },
183        },
184        'core_set_author_auth_type' => {
185            version_limit => 4.0024,
186            priority => 3.2,
187            updater => {
188                type => 'author',
189                label => 'Assigning user authentication type...',
190                condition => sub {
191                    !$_[0]->auth_type;
192                },
193                code => \&core_populate_author_auth_type,
194            },
195        },
196        'core_add_newfeature_widget' => {
197            version_limit => 4.0027,
198            priority => 3.4,
199            updater => {
200                type => 'author',
201                label => 'Adding new feature widget to dashboard...',
202                condition => sub {
203                    my $App = $MT::Upgrade::App;
204                    my ($user) = @_;
205                    if ( $user->type == MT::Author::AUTHOR() ) {
206                        return 1
207                            if $App && UNIVERSAL::isa( $App, 'MT::App' )
208                            && ( $user->id == $App->user->id );
209                    }
210                    return 0;
211                },
212                code => sub {
213                    my ($user) = @_;
214                    my $widget_store = $user->widgets();
215                    if ($widget_store && %$widget_store) {
216                        for my $set (keys %$widget_store) {
217                            $widget_store->{$set}->{new_version} = {
218                                template => 'widget/new_version.tmpl',
219                                set      => 'main',
220                                singular => 1,
221                                order    => -1,
222                            };
223                        }
224                    }
225                    else {
226                        # FIXME: copied from MT::CMS::Dashboard
227                        my %default_widgets = (
228                            'new_version'   => {
229                                order => -1,
230                                set => 'main',
231                                template => 'widget/new_version.tmpl',
232                                singular => 1
233                            },
234                            'blog_stats' => {
235                                param => { tab => 'entry' },
236                                order => 1,
237                                set => 'main'
238                            },
239                            'this_is_you-1' => { order => 1, set => 'sidebar' },
240                            'mt_shortcuts'  => { order => 2, set => 'sidebar' },
241                            'mt_news'       => { order => 3, set => 'sidebar' },
242                        );
243                        my $blog_iter = MT->model('blog')->load_iter(
244                            undef,
245                            { fetchonly => ['id'] }
246                        );
247                        while ( my $blog = $blog_iter->() ) {
248                            my $set = 'dashboard:blog:' . $blog->id;
249                            $widget_store->{$set} = \%default_widgets;
250                        }
251                        $widget_store->{'dashboard:system'} = \%default_widgets;
252                    }
253                    $user->widgets($widget_store);
254                },
255            },
256        },
257        'core_add_email_template' => {
258            version_limit => 4.0030,
259            priority => 9.3,
260            code => sub {
261                my $self = shift;
262                $self->add_step('core_upgrade_templates', Install => 1);
263            },
264        },
265        'core_replace_openid_username' => {
266            version_limit => 4.0033,
267            priority => 3.2,
268            updater => {
269                type => 'author',
270                label => 'Moving OpenID usernames to external_id fields...',
271                condition => sub {
272                    return 0 if 'MT' eq $_[0]->auth_type;
273                    my $auth = MT->commenter_authenticator($_[0]->auth_type);
274                    return 0 unless $auth && %$auth && exists($auth->{class});
275                    my $auth_class = $auth->{class};
276                    eval "require $auth_class;";
277                    return 0 if $@;
278                    return UNIVERSAL::isa($auth_class, 'MT::Auth::OpenID');
279                },
280                code => sub {
281                    return unless $_[0]->url;
282                    my $existing = MT::Author->load({ name => $_[0]->url, auth_type => 'OpenID' });
283                    unless ($existing) {
284                        $_[0]->external_id($_[0]->name);
285                        $_[0]->name($_[0]->url);
286                    }
287                },
288            },
289        },
290        'core_set_template_set' => {
291            version_limit => 4.0034,
292            priority => 3.2,
293            updater => {
294                type => 'blog',
295                label => 'Assigning blog template set...',
296                condition => sub {
297                    !$_[0]->template_set;
298                },
299                code => sub {
300                    $_[0]->template_set('mt_blog');
301                    MT->run_callbacks( 'blog_template_set_change', { blog => $_[0] } );
302                },
303            },
304        },
305        'core_set_page_layout' => {
306            version_limit => 4.0036,
307            priority => 3.2,
308            updater => {
309                type => 'blog',
310                label => 'Assigning blog page layout...',
311                condition => sub {
312                    !$_[0]->page_layout;
313                },
314                code => sub {
315                    my ($blog) = @_;
316                    my $layout = 'layout-wtt';
317                    require MT::Template;
318                    my $styles = MT::Template->load({ blog_id => $blog->id, identifier => 'styles' });
319                    if ($styles) {
320                        if ($styles->text =~ m{ <\$?mt:?setvar \s+ name="page_layout" \s+ value="([^"]+)"\$?> }xmsi) {
321                            $layout = $1;
322                        }
323                    }
324                    $blog->page_layout($layout);
325                },
326            },
327        },
328        'core_set_author_basename' => {
329            version_limit => 4.0037,
330            priority => 3.2,
331            updater => {
332                type => 'author',
333                label => 'Assigning author basename...',
334                condition => sub {
335                    $_[0]->type == 1;
336                },
337                code => sub {
338                    my ($author) = @_;
339                    my $basename = MT::Util::make_unique_author_basename($author);
340                    $author->basename($basename);
341                },
342            },
343        },
344        'core_remove_indexes' => {
345            version_limit => 4.0041,
346            priority => 3.2,
347            code => \&remove_indexes,
348        },
349        'core_set_count_columns' => {
350            version_limit => 4.0047,
351            priority      => 3.2,
352            code          => \&core_update_entry_counts,
353        },
354        'core_assign_object_embedded' => {
355            version_limit => 4.0052,
356            priority => 3.2,
357            updater => {
358                type => 'objectasset',
359                label => 'Assigning embedded flag to asset placements...',
360                code => sub {
361                    $_[0]->embedded(1);
362                },
363                sql => 'update mt_objectasset set objectasset_embedded=1',
364            },
365        },
366        'core_set_template_build_type' => {
367            version_limit => 4.0053,
368            priority => 3.2,
369            updater => {
370                type  => 'blog',
371                label => 'Updating template build types...',
372                code  => sub {
373                    my ($blog) = @_;
374                    require MT::CMS::Blog;
375                    my $App = $MT::Upgrade::App;
376                    MT::CMS::Blog::update_publishing_profile( $App, $blog );
377                    require MT::Template;
378                    require MT::PublishOption;
379                    my @tmpls = MT::Template->load( { blog_id => $blog->id } );
380                    foreach my $tmpl (@tmpls) {
381
382                        if ( $tmpl->build_dynamic ) {
383                            require MT::TemplateMap;
384                            $tmpl->build_type( MT::PublishOption::DYNAMIC() );
385                            $tmpl->save;
386                            my @maps = MT::TemplateMap->load(
387                                { template_id => $tmpl->id } );
388                            foreach my $map (@maps) {
389                                $map->build_type(
390                                    MT::PublishOption::DYNAMIC() );
391                                $map->save;
392                            }
393                        }
394                        if ( !$tmpl->rebuild_me && $tmpl->type eq 'index' ) {
395                            $tmpl->build_type(
396                                MT::PublishOption::MANUALLY() );
397                            $tmpl->save;
398                        }
399                    }
400                    return 0;
401                },
402            },
403        },
404        'core_enable_address_book' => {
405            version_limit => 4.0054,
406            priority => 3.2,
407            code => sub {
408                require MT::Notification;
409                if (MT::Notification->exist()) {
410                    my $cfg = MT->config;
411                    if (! $cfg->EnableAddressBook) {
412                        $cfg->EnableAddressBook(1, 1);
413                        $cfg->save;
414                    }
415                }
416                return 0;
417            },
418        },
419        'core_upgrade_meta' => {
420            version_limit => 4.0057,
421            priority => 3.2,
422            code => \&core_upgrade_meta,
423        },
424        'core_upgrade_category_meta' => {
425            version_limit => 4.0057,
426            priority => 3.2,
427            code => \&core_upgrade_category_meta,
428        },
429
430        # Helper upgrade routines for core_upgrade_meta
431        # and possibly other object types that require
432        # this migration; version_limit is unspecified, so
433        # these can only be invoked if another upgrade
434        # operation utilizes them.
435        'core_upgrade_meta_for_table' => {
436            priority => 1.5,
437            code => \&core_upgrade_meta_for_table,
438        },
439        'core_upgrade_plugindata_meta_for_table' => {
440            priority => 1.5,
441            code => \&core_upgrade_plugindata_meta_for_table,
442        },
443        'core_drop_meta_for_table' => {
444            priority => 3.4,
445            code => \&core_drop_meta_for_table,
446        },
447        'core_replace_file_template_format' => {
448            version_limit => 4.0058,
449            priority => 3.2,
450            updater => {
451                type => 'templatemap',
452                label => 'Replacing file formats to use CategoryLabel tag...',
453                condition => sub {
454                    ( $_[0]->file_template || '' ) =~ m/%-?C/;
455                },
456                code => sub {
457                    my ($map) = shift;
458                    my $file_template = $map->file_template();
459                    $file_template =~ s/%C/<MTCategoryLabel dirify="1">/g;
460                    $file_template =~ s/%-C/<MTCategoryLabel dirify="-">/g;
461                    $map->file_template($file_template);
462                },
463            },
464        },
465    };
466}
467
468### Subroutines
469
470sub _process_masks {
471    my ($perm) = @_;
472
473    my $mask = $perm->role_mask;
474    return unless $mask;
475    my @perms;
476    for my $key (keys %MT::Upgrade::LegacyPerms) {
477        if (int($mask) & int($key)) {
478            if (2 eq $key) { # post
479                push @perms, 'create_post', 'publish_post';
480            } elsif (64 eq $key) { #edit_config
481                push @perms, 'edit_config', 'set_publish_paths', 'manage_feedback';
482            } elsif (4096 eq $key) { #adminsiter_blog
483                push @perms, 'administer_blog', 'manage_pages';
484            } elsif (2048 eq $key) { #not_comment
485                $perm->restrictions("'comment'");
486            } else {
487                push @perms, $MT::Upgrade::LegacyPerms{$key};
488            }
489        }
490    }
491    my $perm_str = scalar(@perms) ? "'" . join("','", @perms) . "'" : q();
492    $perm->permissions($perm_str);
493    $perm->role_mask(0); ## remove legacy permissions
494    $perm;
495}
496
497sub deprecate_bitmask_permissions {
498    my $self = shift;
499   
500    require MT::Permission;
501    my $perm_iter = MT::Permission->load_iter;
502    $self->progress($self->translate_escape('Migrating permission records to new structure...'));
503    while (my $perm = $perm_iter->()) {
504        if (_process_masks($perm)) {
505            $perm->save;
506        }
507    }
508
509    require MT::Role;
510    my $role_iter = MT::Role->load_iter;
511    $self->progress($self->translate_escape('Migrating role records to new structure...'));
512    while (my $role = $role_iter->()) {
513        if (_process_masks($role)) {
514            # do not have to rebuild permissions here.
515            # "save" here causes segfault in sqlite.
516            $role->update;
517        }
518    }
519}
520
521sub migrate_system_privileges {
522    my $self = shift;
523
524    require MT::Permission;
525    my $author_iter = MT::Author->load_iter({ type => MT::Author::AUTHOR() });
526    $self->progress($self->translate_escape('Migrating system level permissions to new structure...'));
527    while (my $author = $author_iter->()) {
528        my @perms;
529        push @perms, 'administer' if $author->column('is_superuser');
530        push @perms, 'create_blog' if $author->column('can_create_blog') || $author->column('is_superuser');
531        push @perms, 'view_log' if $author->column('can_view_log') || $author->column('is_superuser');
532        push @perms, 'manage_plugins' if $author->column('is_superuser');
533        if (@perms) {
534            my $perm = MT::Permission->load({ author_id => $author->id,
535                blog_id => 0 });
536            if (!$perm) {
537                $perm = MT::Permission->new;
538                $perm->author_id($author->id);
539                $perm->blog_id(0);
540            }
541            $perm->set_these_permissions(@perms);
542            $perm->save;
543        }
544    }
545}
546
547sub update_3x_system_search_templates {
548    my $self = shift;
549
550    require MT::Template;
551    $self->progress($self->translate_escape('Updating system search template records...'));
552    my $tmpl_iter = MT::Template->load_iter({
553        type => 'search_template',
554    });
555    my %blogs;
556    while (my $tmpl = $tmpl_iter->()) {
557        $blogs{$tmpl->blog_id} = $tmpl->id;
558        $tmpl->type('search_results');
559        $tmpl->save;
560    }
561    # for any old 'search_template' system templates, remove the
562    # newly installed 'search_results' template.
563    foreach my $blog_id (keys %blogs) {
564        my $tmpl = MT::Template->load({ type => 'search_results',
565            blog_id => $blog_id, id => $blogs{$blog_id} }, {
566            not => { id => 1 } });
567        $tmpl->remove if $tmpl;
568    }
569    if (my @blog_ids = keys %blogs) {
570        MT::Template->remove(
571            { type => 'search_results', blog_id => \@blog_ids },
572            { not => { blog_id => 1 } }
573        );
574    }
575    0;
576}
577
578sub rename_php_plugin_filenames {
579    my $self = shift;
580
581    my $server_path = MT->instance->server_path() || '';
582    $server_path =~ s/\/*$//;
583    my $plugin_path = File::Spec->canonpath("$server_path/php/plugins");
584
585    # If PHP plugins directory doesn't exist, return without failing
586    return 0 if !-d $plugin_path;
587
588    opendir(DIR, $plugin_path)
589        or return 0;
590    my @files = grep { /^(?:function|block)\.(.*)\.php$/ } readdir(DIR);
591    closedir(DIR);
592
593    return 0 unless @files;
594
595    $self->progress($self->translate_escape('Renaming PHP plugin file names...'));
596    my @error_files = ();
597    for my $file (@files) {
598        my $newfile = lc $file;
599        next if $file eq $newfile;
600        if (!rename("$plugin_path/$file", "$plugin_path/$newfile")) {
601            push @error_files, $file;
602        }
603    }
604    if ($#error_files >= 0) {
605        $self->progress($self->translate_escape('Error renaming PHP files. Please check the Activity Log.'));
606        MT->log(
607            {
608                message => $self->translate_escape("Cannot rename in [_1]: [_2].", $plugin_path, join(', ', @error_files)),
609                level   => MT::Log::ERROR(),
610                category => 'upgrade',
611            }
612        );
613    }
614    1;
615}
616
617sub migrate_nofollow_settings {
618    my $self = shift;
619
620    $self->progress($self->translate_escape("Migrating Nofollow plugin settings..."));
621    require MT::PluginData;
622    my $cfg = MT->config;
623    my $plugins = $cfg->PluginSwitch || {};
624    my $nofollow_switch = $plugins->{'nofollow/nofollow.pl'};
625    my $enabled = defined $nofollow_switch ? ($nofollow_switch ? 1 : 0) : 1;
626    my $default_follow_auth_links = 1;
627
628    # For any configuration settings that exist
629    my @config = MT::PluginData->load({ plugin => 'Nofollow' });
630    my %blogs_saved;
631    foreach my $cfg (@config) {
632        if ($cfg->key =~ m/^configuration:blog:(\d+)/) {
633            my $blog = MT::Blog->load($1) or next;
634            my $setting = ($cfg->data || {})->{follow_auth_links};
635            $blog->follow_auth_links($setting) if defined $setting;
636            $blog->nofollow_urls($enabled);
637            $blog->save;
638            $blogs_saved{$blog->id} = 1;
639        } else {
640            my $setting = ($cfg->data || {})->{follow_auth_links};
641            $default_follow_auth_links = $setting if defined $setting;
642        }
643    }
644    $_->remove for @config;
645
646    my $blog_iter = MT::Blog->load_iter;
647    while (my $blog = $blog_iter->()) {
648        next if exists $blogs_saved{$blog->id};
649        $blog->nofollow_urls($enabled);
650        $blog->follow_auth_links($default_follow_auth_links);
651        $blog->save;
652    }
653
654    # Forcibly disable nofollow plugin now since this has become
655    # a core function.
656    $cfg->PluginSwitch('nofollow/nofollow.pl=0', 1);
657    $cfg->save_config();
658
659    return 0;
660}
661
662sub _merge_comment_response_templates_updater {
663    my ($blog) = @_;
664    require MT::Template;
665    my $tmpl = MT::Template->load({ blog_id => $blog->id, type => 'comment_response' });
666    unless ($tmpl) {
667        $tmpl = new MT::Template;
668        $tmpl->blog_id($blog->id);
669        $tmpl->type('comment_response');
670    }
671
672    my $confirm_template = <<'EOT';
673<MTSetVarBlock name="page_title"><__trans phrase="Comment Posted"></MTSetVarBlock>
674
675<MTSetVar name="heading" value="<__trans phrase="Confirmation...">">
676
677<MTSetVarBlock name="message">
678<p><__trans phrase="Your comment has been posted!"></p>
679</MTSetVarBlock>
680EOT
681
682    my $pending_template = <<'EOT';
683<MTSetVarBlock name="page_title"><__trans phrase="Comment Pending"></MTSetVarBlock>
684
685<MTSetVar name="heading" value="<__trans phrase="Thank you for commenting.">">
686
687<MTSetVarBlock name="message">
688<p><__trans phrase="Your comment has been received and held for approval by the blog owner."></p>
689</MTSetVarBlock>
690EOT
691
692    my $error_template = <<'EOT';
693<MTSetVarBlock name="page_title"><__trans phrase="Comment Submission Error"></MTSetVarBlock>
694
695<MTSetVar name="heading" value="$page_title">
696
697<MTSetVarBlock name="message">
698<p><__trans phrase="Your comment submission failed for the following reasons:"></p>
699<blockquote>
700    <$MTErrorMessage$>
701</blockquote>
702</MTSetVarBlock>
703EOT
704
705    my $header_template = <<'EOT';
706<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
707    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
708<html xmlns="http://www.w3.org/1999/xhtml" id="sixapart-standard">
709<head>
710    <meta http-equiv="Content-Type" content="text/html; charset=<$MTPublishCharset$>" />
711    <meta name="generator" content="<$MTProductName version="1"$>" />
712    <link rel="stylesheet" href="<$MTBlogURL$>styles-site.css" type="text/css" />
713    <title>
714    <__trans phrase="[_1]: [_2]" params="<$MTBlogName encode_html="1"$>%%<$MTGetVar name="page_title"$>">
715    </title>
716    <script type="text/javascript" src="<$MTBlogURL$>mt-site.js"></script>
717</head>
718<body class="layout-one-column comment-preview" onload="individualArchivesOnLoad(commenter_name)">
719    <div id="container">
720        <div id="container-inner" class="pkg">
721            <div id="banner">
722                <div id="banner-inner" class="pkg">
723                    <h1 id="banner-header"><a href="<$MTBlogURL$>" accesskey="1"><$MTBlogName encode_html="1"$></a></h1>
724                    <h2 id="banner-description"><$MTBlogDescription$></h2>
725                </div>
726            </div>
727            <div id="pagebody">
728                <div id="pagebody-inner" class="pkg">
729                    <div id="alpha">
730                        <div id="alpha-inner" class="pkg">
731EOT
732
733    my $footer_template = <<'EOT';
734                        </div>
735                    </div>
736                </div>
737            </div>
738        </div>
739    </div>
740</body>
741</html>
742EOT
743
744    my $message_template = <<'EOT';
745<h1><$MTGetVar name="heading"$></h1>
746
747<$MTGetVar name="message"$>
748
749<p><__trans phrase="Return to the <a href="[_1]">original entry</a>." params="<$MTEntryLink$>"></p>
750EOT
751
752    my $mt = MT->instance;
753    $tmpl->name($mt->translate("Comment Response"));
754    $tmpl->text($mt->translate_templatized(<<"EOT"));
755<MTSetVar name="system_template" value="1">
756<MTSetVar name="feedback_template" value="1">
757
758<MTIf name="body_class" eq="mt-comment-pending">
759$pending_template
760</MTIf>
761
762<MTIf name="body_class" eq="mt-comment-error">
763$error_template
764</MTIf>
765
766<MTIf name="body_class" eq="mt-comment-confirmation">
767$confirm_template
768</MTIf>
769
770$header_template
771
772$message_template
773
774$footer_template
775EOT
776    $tmpl->save;
777}
778
779sub core_populate_author_auth_type {
780    my ($u) = @_;
781    if ($u->type == 1) {
782        $u->auth_type(MT->config->AuthenticationModule || 'MT');
783    } else {
784        # for legacy OpenID plugin commenters
785        if ($u->name =~ m(^openid\n(.*)$)) {
786            my $url = $1;
787            if (eval { require Digest::MD5; 1; }) {
788                $url = Digest::MD5::md5_hex($url);
789            } else {
790                $url = substr $url, 0, 255;
791            }
792            $u->name($url);
793            $u->auth_type('OpenID');
794        }
795        elsif ($u->name =~ m!^[a-f0-9]{32}$!) {
796            # Vox OpenID URL; set auth_type to 'Vox'
797            if ($u->url =~ m!\.vox\.com/!) {
798                $u->auth_type('Vox');
799            }
800            # LJ OpenID URL; set auth_type to 'LiveJournal'
801            elsif ($u->url =~ m!\.livejournal\.com/!) {
802                $u->auth_type('LiveJournal');
803            }
804            else {
805                # Other custom auth, which for now means OpenID
806                $u->auth_type('OpenID');
807            }
808        }
809        else {
810            # Default to TypeKey for remaining plain name fields
811            $u->auth_type('TypeKey');
812        }
813    }
814}
815
816sub remove_indexes {
817    my $self = shift;
818
819    $self->progress($self->translate_escape('Removing unnecessary indexes...'));
820
821    my $driver = MT::Object->driver;
822
823    if ($driver->dbd =~ m/::Pg$|::Oracle$/) {
824        $driver->sql([
825            'drop index mt_asset_url',
826            'drop index mt_asset_file_path',
827            'drop index mt_blocklist_name',
828            'drop index mt_entry_blog_id',
829            'drop index mt_template_build_dynamic'
830        ]);
831    } elsif ($driver->dbd =~ m/::mysql$/) {
832        $driver->sql([
833            'drop index mt_asset_url on mt_asset',
834            'drop index mt_asset_file_path on mt_asset',
835            'drop index mt_blocklist_name on mt_blocklist',
836            'drop index mt_entry_blog_id on mt_entry',
837            'drop index mt_template_build_dynamic on mt_tempalte'
838        ]);
839    } elsif ($driver->dbd =~ m/::mssqlserver$/) {
840        $driver->sql([
841            'drop index mt_asset.mt_asset_url',
842            'drop index mt_asset.mt_asset_file_path',
843            'drop index mt_blocklist.mt_blocklist_name',
844            'drop index mt_entry.mt_entry_blog_id',
845            'drop index mt_tempalte.mt_template_build_dynamic'
846        ]);
847    }
848    1;
849}
850
851sub core_upgrade_meta {
852    my $self = shift;
853
854    my $types = MT->registry('object_types');
855    my %added_step;
856    TYPE: while (my ($registry_type, $reg_class) = each %$types) {
857        next TYPE if $registry_type eq 'plugin' && ref $reg_class;  # plugin reference
858
859        my $class = MT->model($registry_type);
860        next TYPE if !$class->has_meta();  # nothing to upgrade
861
862        # If this is a class-based package, find its super-most superclass with the same table.
863        my $class_type = $class->properties->{class_type};
864        if ($class_type && $class_type ne $class->datasource) {
865            if (my $super_class = MT->model($class->datasource)) {
866                $class = $super_class
867                    if $super_class->datasource eq $class->datasource;
868            }
869            # If there's no appropriate superclass, go to update with the class
870            # we have, not the class we want.
871        }
872
873        # Don't add another step for this table if we already made one.
874        next TYPE if $added_step{$class->datasource};
875
876        # Categories' and Folders' metadata are only custom fields, which are stored
877        # in plugindata anyway. They're converted in their own upgrade step. So don't
878        # handle them here.
879        next TYPE if $class->isa('MT::Category');
880
881        my %step_param = ( type => $registry_type );
882        $step_param{meta_column} = $class->properties->{meta_column}
883            if $class->properties->{meta_column};
884        $self->add_step('core_upgrade_meta_for_table', %step_param);
885
886        # Yay, we added a step for this table.
887        $added_step{$class->datasource} = 1;
888    }
889    return 0;
890}
891
892sub _save_meta {
893    my ($obj, $type, $value) = @_;
894
895    my $meta_obj = $obj->meta_pkg->new;
896
897    my @class_keys = @{ $obj->primary_key_tuple };
898    my @meta_keys  = @{ $meta_obj->primary_key_tuple };
899    for my $i (0..$#class_keys) {
900        my $class_field = $class_keys[$i];
901        my $meta_field  = $meta_keys[$i];
902        $meta_obj->$meta_field($obj->$class_field());
903    }
904
905    # Set the type without checking if it's defined, unlike real meta().
906    $meta_obj->type($type);
907
908    # Does this meta type have a data type defined?
909    my $datatype;
910    if (my $field = MT::Meta->metadata_by_name(ref $obj || $obj, $type)) {
911        if (my $type_id = $field->{type_id}) {
912            $datatype = $MT::Meta::Types{$type_id};
913        }
914    }
915    $datatype ||= 'vblob';
916
917    $meta_obj->$datatype($value);
918
919    my $meta_col_def = $meta_obj->column_def($datatype);
920    my $meta_is_blob = $meta_col_def ? $meta_col_def->{type} eq 'blob' : 0;
921    MT::Meta::Proxy::serialize_blob(undef, $meta_obj) if $meta_is_blob;
922    $meta_obj->save();
923    MT::Meta::Proxy::unserialize_blob($meta_obj) if $meta_is_blob;
924}
925
926sub core_upgrade_category_meta {
927    my $self = shift;
928    $self->add_step('core_upgrade_plugindata_meta_for_table', type => 'category');
929    $self->add_step('core_upgrade_plugindata_meta_for_table', type => 'folder');
930    return 0;
931}
932
933sub core_upgrade_plugindata_meta_for_table {
934    my $self = shift;
935    my $Installing = $MT::Upgrade::Installing;
936    return 0 if $Installing;
937    my (%param) = @_;
938    my $type = $param{type};
939    return 0 unless $type;
940    my $class = MT->model($type);
941    return 0 unless $class;
942
943    my $cfclass = MT->model('field');
944    return 0 if !$cfclass;
945
946    # this looks weird, but it winds up invoking
947    # the loading of custom field types and the
948    # installation of their meta properties
949    MT->registry('tags');
950
951    # special case for types that use CustomField plugindata
952    # for storing their custom field metadata instead of a 'meta'
953    # column.
954    require CustomFields::Upgrade;
955    # TODO: really this should vary on $type but it's already translated for "categories"
956    $self->progress($self->translate_escape('Moving metadata storage for categories...'));
957    CustomFields::Upgrade::customfields_move_meta($self, $type);
958
959    return 0;
960}
961
962sub core_upgrade_meta_for_table {
963    my $self = shift;
964    my $Installing = $MT::Upgrade::Installing;
965    return 0 if $Installing;
966    my (%param) = @_;
967    my $type = $param{type};
968    return 0 unless $type;
969    my $class = MT->model($type);
970    return 0 unless $class;
971
972    my $offset = int($param{offset} || 0);
973    my $count = int($param{count} || 0);
974
975    my $pid = join q{:}, $param{step} . "_type", $class;
976
977    my $db_class = $class;
978    my $class_type = $class->properties->{class_type};
979    if ($class_type && $class_type ne $class->datasource) {
980        if (my $super_class = MT->model($class->datasource)) {
981            $db_class = $super_class
982                if $super_class->datasource eq $class->datasource;
983        }
984        # If there's no appropriate superclass, go to update with the class
985        # we have, not the class we want.
986    }
987
988    my $driver = $db_class->dbi_driver;
989    my $dbh = $driver->rw_handle;
990    my $dbd = $driver->dbd;
991
992    my $meta_col = $param{meta_column} || 'meta';
993
994    my $ddl = $driver->dbd->ddl_class;
995    my $db_defs = $ddl->column_defs($db_class);
996    return 0 unless $db_defs && exists($db_defs->{$meta_col});
997
998    my $terms = {
999        $meta_col => { not_null => 1 }
1000    };
1001    my $args = {
1002        'limit'      => 101,
1003        'fetchonly' => [ 'id' ],  # meta is added to the select list separately
1004        'sort'      => 'id',
1005        'direction' => 'ascend',
1006        $offset ? ( 'offset' => $offset ) : ()
1007    };
1008    my $stmt = $driver->prepare_statement( $class, $terms, $args );
1009    my $db_meta_col = $dbd->db_column_name($class->datasource, $meta_col);
1010    ## Meta column has to be added in here because it's already
1011    ## gone from the column_names - something fetchonly relies on
1012    $stmt->add_select( $db_meta_col => $db_meta_col );
1013    my $sql = $stmt->as_sql;
1014    my $sth = $dbh->prepare($sql);
1015    return 0 if !$sth; # ignore this operation if _meta column doesn't exist
1016    $sth->execute
1017        or return $self->error($dbh->errstr || $DBI::errstr);
1018
1019    my $msg = $self->translate_escape("Upgrading metadata storage for [_1]", $class->class_label_plural);
1020
1021    if (!$offset) {
1022        $self->progress($msg, $pid);
1023    } else {
1024        my $count = $class->count();
1025        return 0 unless $count;
1026        $self->progress(sprintf($msg . " (%d%%)", ($offset/$count*100)), $pid);
1027    }
1028
1029    my $rows = 0;
1030
1031    require MT::Serialize;
1032    my $ser = MT::Serialize->new('MT');
1033    my %fields;
1034
1035    my @ids;
1036    my $cfclass = MT->model('field');
1037    while (my $row = $sth->fetchrow_arrayref) {
1038        $rows++;
1039        my ($id, $rawmeta) = @$row;  ## add_select pushes the column - it should be in this order
1040        if (defined $rawmeta) {
1041            push @ids, $id;
1042            if ($rawmeta =~ m/^SERG/) {
1043                # deserialize
1044                my $metadataref = $ser->unserialize($rawmeta);
1045                if ($metadataref) {
1046                    my $metadata = $$metadataref;
1047                    my $obj = $class->load( { id => $id }, { no_class => 1,
1048                        fetchonly => [ 'id',
1049                            ( $class_type ? ( $class->properties->{class_column} ) : () )
1050                        ]
1051                    });
1052                    if ($obj) {
1053                        foreach my $metaname (keys %$metadata) {
1054                            my $metavalue = $metadata->{$metaname};
1055                            if ($metaname eq 'customfields') {
1056                                next unless $cfclass;
1057
1058                                # extra work for custom fields;
1059                                # a hash into itself
1060                                my $cfdata = $metavalue;
1061                                next unless ref $cfdata eq 'HASH';
1062
1063                                foreach my $cfname (keys %$cfdata) {
1064                                    my $cfvalue = $cfdata->{$cfname};
1065                                    my $cftype = $type;
1066                                    if ($class_type) {
1067                                        $cftype = $obj->class_type;
1068                                    }
1069
1070                                    # make sure CustomFields::Field exists
1071                                    my $fld = $fields{$cfname}{$cftype} ||= $cfclass->load({ basename => $cfname, obj_type => $cftype });
1072                                    next unless $fld;
1073
1074                                    _save_meta($obj,
1075                                        'field.' . $cfname, $cfvalue);
1076                                }
1077                            } else {
1078                                _save_meta($obj, $metaname,
1079                                    $metavalue);
1080                            }
1081                        }
1082                    }
1083                }
1084            }
1085        }
1086        last if $rows == 100;
1087    }
1088    if ($rows == 100 && $sth->fetchrow_arrayref) {
1089        $rows++;
1090    }
1091    $sth->finish;
1092
1093    if ($rows == 101) {
1094        $offset += 100;
1095    } else {
1096        # done, so lets drop that meta column, what say you?
1097        if ($dbd->ddl_class->can_drop_column) {
1098            # if the driver cannot drop a column, it is likely
1099            # to get dropped as the table is updated for other
1100            # new columns anyway.
1101            $sql = $dbd->ddl_class->drop_column_sql($class, $meta_col);
1102            $self->add_step('core_drop_meta_for_table', class => $db_class, sql => $sql);
1103        }
1104        $self->progress($msg . ' (100%)', $pid);
1105        $offset = 0;  # done!
1106    }
1107    return $offset;
1108}
1109
1110sub core_drop_meta_for_table {
1111    my $self = shift;
1112    my (%param) = @_;
1113    my $class = $param{class};
1114    my $sql = $param{sql};
1115
1116    eval "require $class;";
1117    my $driver = $class->dbi_driver;
1118    my $dbh = $driver->rw_handle;
1119    my $err;
1120    eval {
1121        $dbh->do($sql) or $err = $dbh->errstr;
1122    };
1123    # ignore drop errors; the column has probably been
1124    # removed already
1125    #if ($err) {
1126    #    print STDERR "$err: $sql\n";
1127    #}
1128
1129    return 0;
1130}
1131
1132sub core_update_entry_counts {
1133    my $self = shift;
1134    my (%param) = @_;
1135
1136    my $class = MT->model('entry');
1137    return $self->error($self->translate_escape("Error loading class: [_1].", $param{type}))
1138        unless $class;
1139
1140    my $msg = $self->translate_escape("Assigning entry comment and TrackBack counts...");
1141    my $offset = $param{offset} || 0;
1142    my $count = $param{count};
1143    if (!$count) {
1144        $count = $class->count({ class => '*' });
1145    }
1146    return unless $count;
1147    if ($offset) {
1148        $self->progress(sprintf("$msg (%d%%)", ($offset/$count*100)), $param{step});
1149    } else {
1150        $self->progress($msg, $param{step});
1151    }
1152
1153    my $continue = 0;
1154    my $driver = $class->driver;
1155
1156    my $iter = $class->load_iter({ class => '*' }, { offset => $offset, limit => $MAX_ROWS+1 });
1157    my $start = time;
1158    my ( %touched, %c, %tb );
1159    my $rows = 0;
1160    while (my $e = $iter->()) {
1161        $rows++;
1162        $c{$e->id} = $e;
1163        if (my $tb = $e->trackback) {
1164            $tb{$tb->id} = $e;
1165        }
1166        $continue = 1, last if scalar $rows == $MAX_ROWS;
1167    }
1168    if ( $continue ) {
1169        $iter->end;
1170        $offset += $rows;
1171    }
1172
1173    # now gather counts -- comments
1174    if (my $grp_iter = MT::Comment->count_group_by({
1175        visible => 1,
1176        entry_id => [ keys %c ],
1177    }, {
1178        group => ['entry_id'],
1179    })) {
1180        while (my ($count, $id) = $grp_iter->()) {
1181            my $e = $c{$id} or next;
1182            if ((!defined $e->comment_count) || (($e->comment_count || 0) != $count)) {
1183                $e->comment_count($count);
1184                $touched{$e->id} = $e;
1185            }
1186        }
1187    }
1188
1189    # pings
1190    if ( %tb ) {
1191        if (my $grp_iter = MT::TBPing->count_group_by({
1192            visible => 1,
1193            tb_id => [ keys %tb ],
1194        }, {
1195            group => ['tb_id'],
1196        })) {
1197            while (my ($count, $id) = $grp_iter->()) {
1198                my $e = $tb{$id} or next;
1199                if ((!defined $e->ping_count) || (($e->ping_count || 0) != $count)) {
1200                    $e->ping_count($count);
1201                    $touched{$e->id} = $e;
1202                }
1203            }
1204        }
1205    }
1206
1207    foreach my $e (values %touched) {
1208        $e->save;
1209    }
1210
1211    if ($continue) {
1212        return { offset => $offset, count => $count };
1213    } else {
1214        $self->progress("$msg (100%)", $param{step});
1215    }
1216    1;
1217}
1218
12191;
Note: See TracBrowser for help on using the browser.