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

Revision 3014, 42.0 kB (checked in by auno, 15 months ago)

Set manually publishing option properly for upgrading from MT3. BugzID:81689

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            updater       => {
353                type      => 'entry',
354                label     => 'Assigning entry comment and TrackBack counts...',
355                condition => sub {
356                    require MT::Comment;
357                    my $comment_count = MT::Comment->count(
358                        {
359                            entry_id => $_[0]->id,
360                            visible  => 1,
361                        }
362                    );
363                    $_[0]->comment_count($comment_count);
364                    require MT::Trackback;
365                    require MT::TBPing;
366                    my $tb = MT::Trackback->load( { entry_id => $_[0]->id } );
367                    my $ping_count;
368                    if ($tb) {
369                        my $ping_count = MT::TBPing->count(
370                            {
371                                tb_id   => $tb->id,
372                                visible => 1,
373                            }
374                        );
375                        $_[0]->ping_count($ping_count);
376                    }
377                    ( $comment_count || $ping_count );
378                },
379                # only count once and set it, so code do nothing.
380                # it doesn't have the unnecessary save.
381                code => sub { 1; },
382            },
383        },
384        'core_assign_object_embedded' => {
385            version_limit => 4.0052,
386            priority => 3.2,
387            updater => {
388                type => 'objectasset',
389                label => 'Assigning embedded flag to asset placements...',
390                code => sub {
391                    $_[0]->embedded(1);
392                },
393                sql => 'update mt_objectasset set objectasset_embedded=1',
394            },
395        },
396        'core_set_template_build_type' => {
397            version_limit => 4.0053,
398            priority => 3.2,
399            updater => {
400                type  => 'blog',
401                label => 'Updating template build types...',
402                code  => sub {
403                    my ($blog) = @_;
404                    require MT::CMS::Blog;
405                    my $App = $MT::Upgrade::App;
406                    MT::CMS::Blog::update_publishing_profile( $App, $blog );
407                    require MT::Template;
408                    require MT::PublishOption;
409                    my @tmpls = MT::Template->load( { blog_id => $blog->id } );
410                    foreach my $tmpl (@tmpls) {
411
412                        if ( $tmpl->build_dynamic ) {
413                            require MT::TemplateMap;
414                            $tmpl->build_type( MT::PublishOption::DYNAMIC() );
415                            $tmpl->save;
416                            my @maps = MT::TemplateMap->load(
417                                { template_id => $tmpl->id } );
418                            foreach my $map (@maps) {
419                                $map->build_type(
420                                    MT::PublishOption::DYNAMIC() );
421                                $map->save;
422                            }
423                        }
424                        if ( !$tmpl->rebuild_me && $tmpl->type eq 'index' ) {
425                            $tmpl->build_type(
426                                MT::PublishOption::MANUALLY() );
427                            $tmpl->save;
428                        }
429                    }
430                    return 0;
431                },
432            },
433        },
434        'core_enable_address_book' => {
435            version_limit => 4.0054,
436            priority => 3.2,
437            code => sub {
438                require MT::Notification;
439                if (MT::Notification->exist()) {
440                    my $cfg = MT->config;
441                    if (! $cfg->EnableAddressBook) {
442                        $cfg->EnableAddressBook(1, 1);
443                        $cfg->save;
444                    }
445                }
446                return 0;
447            },
448        },
449        'core_upgrade_meta' => {
450            version_limit => 4.0057,
451            priority => 3.2,
452            code => \&core_upgrade_meta,
453        },
454        'core_upgrade_category_meta' => {
455            version_limit => 4.0057,
456            priority => 3.2,
457            code => \&core_upgrade_category_meta,
458        },
459
460        # Helper upgrade routines for core_upgrade_meta
461        # and possibly other object types that require
462        # this migration; version_limit is unspecified, so
463        # these can only be invoked if another upgrade
464        # operation utilizes them.
465        'core_upgrade_meta_for_table' => {
466            priority => 1.5,
467            code => \&core_upgrade_meta_for_table,
468        },
469        'core_upgrade_plugindata_meta_for_table' => {
470            priority => 1.5,
471            code => \&core_upgrade_plugindata_meta_for_table,
472        },
473        'core_drop_meta_for_table' => {
474            priority => 3.4,
475            code => \&core_drop_meta_for_table,
476        },
477        'core_replace_file_template_format' => {
478            version_limit => 4.0058,
479            priority => 3.2,
480            updater => {
481                type => 'templatemap',
482                label => 'Replacing file formats to use CategoryLabel tag...',
483                condition => sub {
484                    ( $_[0]->file_template || '' ) =~ m/%-?C/;
485                },
486                code => sub {
487                    my ($map) = shift;
488                    my $file_template = $map->file_template();
489                    $file_template =~ s/%C/<MTCategoryLabel dirify="1">/g;
490                    $file_template =~ s/%-C/<MTCategoryLabel dirify="-">/g;
491                    $map->file_template($file_template);
492                },
493            },
494        },
495        'core_assign_all_permisssions_blog_admin' => {
496            version_limit => 4.0063,
497            priority => 3.4,
498            updater => {
499                type => 'permission',
500                label => 'Assigning all permissions to blog administrator...',
501                condition => sub {
502                    $_[0]->can_administer_blog && $_[0]->blog_id;
503                },
504                code => sub {
505                    my ($perm) = shift;
506                    $perm->set_full_permissions;
507                },
508            },
509        },
510        'core_recover_sysadmin_permissions' => {
511            version_limit => 4.0066,
512            priority => 3.5,
513            updater => {
514                type => 'permission',
515                label => 'Recover permissions of system administrators...',
516                condition => sub {
517                    !$_[0]->blog_id && !$_[0]->has('administer') && $_[0]->can_administer_blog;
518                },
519                code => sub {
520                    my ($perm) = shift;
521                    $perm->set_permissions('system');
522                },
523            },
524        },
525    };
526}
527
528### Subroutines
529
530sub _process_masks {
531    my ($perm) = @_;
532
533    my $mask = $perm->role_mask;
534    return unless $mask;
535    my @perms;
536    for my $key (keys %MT::Upgrade::LegacyPerms) {
537        if (int($mask) & int($key)) {
538            if (2 eq $key) { # post
539                push @perms, 'create_post', 'publish_post';
540            } elsif (64 eq $key) { #edit_config
541                push @perms, 'edit_config', 'set_publish_paths', 'manage_feedback';
542            } elsif (4096 eq $key) { #adminsiter_blog
543                push @perms, 'administer_blog', 'manage_pages';
544            } elsif (2048 eq $key) { #not_comment
545                $perm->restrictions("'comment'");
546            } else {
547                push @perms, $MT::Upgrade::LegacyPerms{$key};
548            }
549        }
550    }
551    my $perm_str = scalar(@perms) ? "'" . join("','", @perms) . "'" : q();
552    $perm->permissions($perm_str);
553    $perm->role_mask(0); ## remove legacy permissions
554    $perm;
555}
556
557sub deprecate_bitmask_permissions {
558    my $self = shift;
559   
560    require MT::Permission;
561    my $perm_iter = MT::Permission->load_iter;
562    $self->progress($self->translate_escape('Migrating permission records to new structure...'));
563    while (my $perm = $perm_iter->()) {
564        if (_process_masks($perm)) {
565            $perm->save;
566        }
567    }
568
569    require MT::Role;
570    my $role_iter = MT::Role->load_iter;
571    $self->progress($self->translate_escape('Migrating role records to new structure...'));
572    while (my $role = $role_iter->()) {
573        if (_process_masks($role)) {
574            # do not have to rebuild permissions here.
575            # "save" here causes segfault in sqlite.
576            $role->update;
577        }
578    }
579}
580
581sub migrate_system_privileges {
582    my $self = shift;
583
584    require MT::Permission;
585    my $author_iter = MT::Author->load_iter({ type => MT::Author::AUTHOR() });
586    $self->progress($self->translate_escape('Migrating system level permissions to new structure...'));
587    while (my $author = $author_iter->()) {
588        my @perms;
589        push @perms, 'administer' if $author->column('is_superuser');
590        push @perms, 'create_blog' if $author->column('can_create_blog') || $author->column('is_superuser');
591        push @perms, 'view_log' if $author->column('can_view_log') || $author->column('is_superuser');
592        push @perms, 'manage_plugins' if $author->column('is_superuser');
593        if (@perms) {
594            my $perm = MT::Permission->load({ author_id => $author->id,
595                blog_id => 0 });
596            if (!$perm) {
597                $perm = MT::Permission->new;
598                $perm->author_id($author->id);
599                $perm->blog_id(0);
600            }
601            $perm->set_these_permissions(@perms);
602            $perm->save;
603        }
604    }
605}
606
607sub update_3x_system_search_templates {
608    my $self = shift;
609
610    require MT::Template;
611    $self->progress($self->translate_escape('Updating system search template records...'));
612    my $tmpl_iter = MT::Template->load_iter({
613        type => 'search_template',
614    });
615    my %blogs;
616    while (my $tmpl = $tmpl_iter->()) {
617        $blogs{$tmpl->blog_id} = $tmpl->id;
618        $tmpl->type('search_results');
619        $tmpl->save;
620    }
621    # for any old 'search_template' system templates, remove the
622    # newly installed 'search_results' template.
623    foreach my $blog_id (keys %blogs) {
624        my $tmpl = MT::Template->load({ type => 'search_results',
625            blog_id => $blog_id, id => $blogs{$blog_id} }, {
626            not => { id => 1 } });
627        $tmpl->remove if $tmpl;
628    }
629    if (my @blog_ids = keys %blogs) {
630        MT::Template->remove(
631            { type => 'search_results', blog_id => \@blog_ids },
632            { not => { blog_id => 1 } }
633        );
634    }
635    0;
636}
637
638sub rename_php_plugin_filenames {
639    my $self = shift;
640
641    my $server_path = MT->instance->server_path() || '';
642    $server_path =~ s/\/*$//;
643    my $plugin_path = File::Spec->canonpath("$server_path/php/plugins");
644
645    # If PHP plugins directory doesn't exist, return without failing
646    return 0 if !-d $plugin_path;
647
648    opendir(DIR, $plugin_path)
649        or return 0;
650    my @files = grep { /^(?:function|block)\.(.*)\.php$/ } readdir(DIR);
651    closedir(DIR);
652
653    return 0 unless @files;
654
655    $self->progress($self->translate_escape('Renaming PHP plugin file names...'));
656    my @error_files = ();
657    for my $file (@files) {
658        my $newfile = lc $file;
659        next if $file eq $newfile;
660        if (!rename("$plugin_path/$file", "$plugin_path/$newfile")) {
661            push @error_files, $file;
662        }
663    }
664    if ($#error_files >= 0) {
665        $self->progress($self->translate_escape('Error renaming PHP files. Please check the Activity Log.'));
666        MT->log(
667            {
668                message => $self->translate_escape("Cannot rename in [_1]: [_2].", $plugin_path, join(', ', @error_files)),
669                level   => MT::Log::ERROR(),
670                category => 'upgrade',
671            }
672        );
673    }
674    1;
675}
676
677sub migrate_nofollow_settings {
678    my $self = shift;
679
680    $self->progress($self->translate_escape("Migrating Nofollow plugin settings..."));
681    require MT::PluginData;
682    my $cfg = MT->config;
683    my $plugins = $cfg->PluginSwitch || {};
684    my $nofollow_switch = $plugins->{'nofollow/nofollow.pl'};
685    my $enabled = defined $nofollow_switch ? ($nofollow_switch ? 1 : 0) : 1;
686    my $default_follow_auth_links = 1;
687
688    # For any configuration settings that exist
689    my @config = MT::PluginData->load({ plugin => 'Nofollow' });
690    my %blogs_saved;
691    foreach my $cfg (@config) {
692        if ($cfg->key =~ m/^configuration:blog:(\d+)/) {
693            my $blog = MT::Blog->load($1) or next;
694            my $setting = ($cfg->data || {})->{follow_auth_links};
695            $blog->follow_auth_links($setting) if defined $setting;
696            $blog->nofollow_urls($enabled);
697            $blog->save;
698            $blogs_saved{$blog->id} = 1;
699        } else {
700            my $setting = ($cfg->data || {})->{follow_auth_links};
701            $default_follow_auth_links = $setting if defined $setting;
702        }
703    }
704    $_->remove for @config;
705
706    my $blog_iter = MT::Blog->load_iter;
707    while (my $blog = $blog_iter->()) {
708        next if exists $blogs_saved{$blog->id};
709        $blog->nofollow_urls($enabled);
710        $blog->follow_auth_links($default_follow_auth_links);
711        $blog->save;
712    }
713
714    # Forcibly disable nofollow plugin now since this has become
715    # a core function.
716    $cfg->PluginSwitch('nofollow/nofollow.pl=0', 1);
717    $cfg->save_config();
718
719    return 0;
720}
721
722sub _merge_comment_response_templates_updater {
723    my ($blog) = @_;
724    require MT::Template;
725    my $tmpl = MT::Template->load({ blog_id => $blog->id, type => 'comment_response' });
726    unless ($tmpl) {
727        $tmpl = new MT::Template;
728        $tmpl->blog_id($blog->id);
729        $tmpl->type('comment_response');
730    }
731
732    my $confirm_template = <<'EOT';
733<MTSetVarBlock name="page_title"><__trans phrase="Comment Posted"></MTSetVarBlock>
734
735<MTSetVar name="heading" value="<__trans phrase="Confirmation...">">
736
737<MTSetVarBlock name="message">
738<p><__trans phrase="Your comment has been posted!"></p>
739</MTSetVarBlock>
740EOT
741
742    my $pending_template = <<'EOT';
743<MTSetVarBlock name="page_title"><__trans phrase="Comment Pending"></MTSetVarBlock>
744
745<MTSetVar name="heading" value="<__trans phrase="Thank you for commenting.">">
746
747<MTSetVarBlock name="message">
748<p><__trans phrase="Your comment has been received and held for approval by the blog owner."></p>
749</MTSetVarBlock>
750EOT
751
752    my $error_template = <<'EOT';
753<MTSetVarBlock name="page_title"><__trans phrase="Comment Submission Error"></MTSetVarBlock>
754
755<MTSetVar name="heading" value="$page_title">
756
757<MTSetVarBlock name="message">
758<p><__trans phrase="Your comment submission failed for the following reasons:"></p>
759<blockquote>
760    <$MTErrorMessage$>
761</blockquote>
762</MTSetVarBlock>
763EOT
764
765    my $header_template = <<'EOT';
766<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
767    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
768<html xmlns="http://www.w3.org/1999/xhtml" id="sixapart-standard">
769<head>
770    <meta http-equiv="Content-Type" content="text/html; charset=<$MTPublishCharset$>" />
771    <meta name="generator" content="<$MTProductName version="1"$>" />
772    <link rel="stylesheet" href="<$MTBlogURL$>styles-site.css" type="text/css" />
773    <title>
774    <__trans phrase="[_1]: [_2]" params="<$MTBlogName encode_html="1"$>%%<$MTGetVar name="page_title"$>">
775    </title>
776    <script type="text/javascript" src="<$MTBlogURL$>mt-site.js"></script>
777</head>
778<body class="layout-one-column comment-preview" onload="individualArchivesOnLoad(commenter_name)">
779    <div id="container">
780        <div id="container-inner" class="pkg">
781            <div id="banner">
782                <div id="banner-inner" class="pkg">
783                    <h1 id="banner-header"><a href="<$MTBlogURL$>" accesskey="1"><$MTBlogName encode_html="1"$></a></h1>
784                    <h2 id="banner-description"><$MTBlogDescription$></h2>
785                </div>
786            </div>
787            <div id="pagebody">
788                <div id="pagebody-inner" class="pkg">
789                    <div id="alpha">
790                        <div id="alpha-inner" class="pkg">
791EOT
792
793    my $footer_template = <<'EOT';
794                        </div>
795                    </div>
796                </div>
797            </div>
798        </div>
799    </div>
800</body>
801</html>
802EOT
803
804    my $message_template = <<'EOT';
805<h1><$MTGetVar name="heading"$></h1>
806
807<$MTGetVar name="message"$>
808
809<p><__trans phrase="Return to the <a href="[_1]">original entry</a>." params="<$MTEntryLink$>"></p>
810EOT
811
812    my $mt = MT->instance;
813    $tmpl->name($mt->translate("Comment Response"));
814    $tmpl->text($mt->translate_templatized(<<"EOT"));
815<MTSetVar name="system_template" value="1">
816<MTSetVar name="feedback_template" value="1">
817
818<MTIf name="body_class" eq="mt-comment-pending">
819$pending_template
820</MTIf>
821
822<MTIf name="body_class" eq="mt-comment-error">
823$error_template
824</MTIf>
825
826<MTIf name="body_class" eq="mt-comment-confirmation">
827$confirm_template
828</MTIf>
829
830$header_template
831
832$message_template
833
834$footer_template
835EOT
836    $tmpl->save;
837}
838
839sub core_populate_author_auth_type {
840    my ($u) = @_;
841    if ($u->type == 1) {
842        $u->auth_type(MT->config->AuthenticationModule || 'MT');
843    } else {
844        # for legacy OpenID plugin commenters
845        if ($u->name =~ m(^openid\n(.*)$)) {
846            my $url = $1;
847            if (eval { require Digest::MD5; 1; }) {
848                $url = Digest::MD5::md5_hex($url);
849            } else {
850                $url = substr $url, 0, 255;
851            }
852            $u->name($url);
853            $u->auth_type('OpenID');
854        }
855        elsif ($u->name =~ m!^[a-f0-9]{32}$!) {
856            # Vox OpenID URL; set auth_type to 'Vox'
857            if ($u->url =~ m!\.vox\.com/!) {
858                $u->auth_type('Vox');
859            }
860            # LJ OpenID URL; set auth_type to 'LiveJournal'
861            elsif ($u->url =~ m!\.livejournal\.com/!) {
862                $u->auth_type('LiveJournal');
863            }
864            else {
865                # Other custom auth, which for now means OpenID
866                $u->auth_type('OpenID');
867            }
868        }
869        else {
870            # Default to TypeKey for remaining plain name fields
871            $u->auth_type('TypeKey');
872        }
873    }
874}
875
876sub remove_indexes {
877    my $self = shift;
878
879    $self->progress($self->translate_escape('Removing unnecessary indexes...'));
880
881    my $driver = MT::Object->driver;
882
883    if ($driver->dbd =~ m/::Pg$|::Oracle$/) {
884        $driver->sql([
885            'drop index mt_asset_url',
886            'drop index mt_asset_file_path',
887            'drop index mt_blocklist_name',
888            'drop index mt_entry_blog_id',
889            'drop index mt_template_build_dynamic'
890        ]);
891    } elsif ($driver->dbd =~ m/::mysql$/) {
892        $driver->sql([
893            'drop index mt_asset_url on mt_asset',
894            'drop index mt_asset_file_path on mt_asset',
895            'drop index mt_blocklist_name on mt_blocklist',
896            'drop index mt_entry_blog_id on mt_entry',
897            'drop index mt_template_build_dynamic on mt_tempalte'
898        ]);
899    } elsif ($driver->dbd =~ m/::mssqlserver$/) {
900        $driver->sql([
901            'drop index mt_asset.mt_asset_url',
902            'drop index mt_asset.mt_asset_file_path',
903            'drop index mt_blocklist.mt_blocklist_name',
904            'drop index mt_entry.mt_entry_blog_id',
905            'drop index mt_tempalte.mt_template_build_dynamic'
906        ]);
907    }
908    1;
909}
910
911sub core_upgrade_meta {
912    my $self = shift;
913
914    my $types = MT->registry('object_types');
915    my %added_step;
916    TYPE: while (my ($registry_type, $reg_class) = each %$types) {
917        next TYPE if $registry_type eq 'plugin' && ref $reg_class;  # plugin reference
918
919        my $class = MT->model($registry_type);
920        next TYPE if !$class->has_meta();  # nothing to upgrade
921
922        # If this is a class-based package, find its super-most superclass with the same table.
923        my $class_type = $class->properties->{class_type};
924        if ($class_type && $class_type ne $class->datasource) {
925            if (my $super_class = MT->model($class->datasource)) {
926                $class = $super_class
927                    if $super_class->datasource eq $class->datasource;
928            }
929            # If there's no appropriate superclass, go to update with the class
930            # we have, not the class we want.
931        }
932
933        # Don't add another step for this table if we already made one.
934        next TYPE if $added_step{$class->datasource};
935
936        # Categories' and Folders' metadata are only custom fields, which are stored
937        # in plugindata anyway. They're converted in their own upgrade step. So don't
938        # handle them here.
939        next TYPE if $class->isa('MT::Category');
940
941        my %step_param = ( type => $registry_type );
942        $step_param{meta_column} = $class->properties->{meta_column}
943            if $class->properties->{meta_column};
944        $self->add_step('core_upgrade_meta_for_table', %step_param);
945
946        # Yay, we added a step for this table.
947        $added_step{$class->datasource} = 1;
948    }
949    return 0;
950}
951
952sub _save_meta {
953    my ($obj, $type, $value) = @_;
954
955    my $meta_obj = $obj->meta_pkg->new;
956
957    my @class_keys = @{ $obj->primary_key_tuple };
958    my @meta_keys  = @{ $meta_obj->primary_key_tuple };
959    for my $i (0..$#class_keys) {
960        my $class_field = $class_keys[$i];
961        my $meta_field  = $meta_keys[$i];
962        $meta_obj->$meta_field($obj->$class_field());
963    }
964
965    # Set the type without checking if it's defined, unlike real meta().
966    $meta_obj->type($type);
967
968    # Does this meta type have a data type defined?
969    my $datatype;
970    if (my $field = MT::Meta->metadata_by_name(ref $obj || $obj, $type)) {
971        if (my $type_id = $field->{type_id}) {
972            $datatype = $MT::Meta::Types{$type_id};
973        }
974    }
975    $datatype ||= 'vblob';
976
977    $meta_obj->$datatype($value);
978
979    my $meta_col_def = $meta_obj->column_def($datatype);
980    my $meta_is_blob = $meta_col_def ? $meta_col_def->{type} eq 'blob' : 0;
981    MT::Meta::Proxy::serialize_blob(undef, $meta_obj) if $meta_is_blob;
982    $meta_obj->save();
983    MT::Meta::Proxy::unserialize_blob($meta_obj) if $meta_is_blob;
984}
985
986sub core_upgrade_category_meta {
987    my $self = shift;
988    $self->add_step('core_upgrade_plugindata_meta_for_table', type => 'category');
989    $self->add_step('core_upgrade_plugindata_meta_for_table', type => 'folder');
990    return 0;
991}
992
993sub core_upgrade_plugindata_meta_for_table {
994    my $self = shift;
995    my $Installing = $MT::Upgrade::Installing;
996    return 0 if $Installing;
997    my (%param) = @_;
998    my $type = $param{type};
999    return 0 unless $type;
1000    my $class = MT->model($type);
1001    return 0 unless $class;
1002
1003    my $cfclass = MT->model('field');
1004    return 0 if !$cfclass;
1005
1006    # this looks weird, but it winds up invoking
1007    # the loading of custom field types and the
1008    # installation of their meta properties
1009    MT->registry('tags');
1010
1011    # special case for types that use CustomField plugindata
1012    # for storing their custom field metadata instead of a 'meta'
1013    # column.
1014    require CustomFields::Upgrade;
1015    # TODO: really this should vary on $type but it's already translated for "categories"
1016    $self->progress($self->translate_escape('Moving metadata storage for categories...'));
1017    CustomFields::Upgrade::customfields_move_meta($self, $type);
1018
1019    return 0;
1020}
1021
1022sub core_upgrade_meta_for_table {
1023    my $self = shift;
1024    my $Installing = $MT::Upgrade::Installing;
1025    return 0 if $Installing;
1026    my (%param) = @_;
1027    my $type = $param{type};
1028    return 0 unless $type;
1029    my $class = MT->model($type);
1030    return 0 unless $class;
1031
1032    my $offset = int($param{offset} || 0);
1033    my $count = int($param{count} || 0);
1034
1035    my $pid = join q{:}, $param{step} . "_type", $class;
1036
1037    my $db_class = $class;
1038    my $class_type = $class->properties->{class_type};
1039    if ($class_type && $class_type ne $class->datasource) {
1040        if (my $super_class = MT->model($class->datasource)) {
1041            $db_class = $super_class
1042                if $super_class->datasource eq $class->datasource;
1043        }
1044        # If there's no appropriate superclass, go to update with the class
1045        # we have, not the class we want.
1046    }
1047
1048    my $driver = $db_class->dbi_driver;
1049    my $dbh = $driver->rw_handle;
1050    my $dbd = $driver->dbd;
1051
1052    my $meta_col = $param{meta_column} || 'meta';
1053
1054    my $ddl = $driver->dbd->ddl_class;
1055    my $db_defs = $ddl->column_defs($db_class);
1056    return 0 unless $db_defs && exists($db_defs->{$meta_col});
1057
1058    my $terms = {
1059        $meta_col => { not_null => 1 }
1060    };
1061    my $args = {
1062        'limit'      => 101,
1063        'fetchonly' => [ 'id' ],  # meta is added to the select list separately
1064        $offset ? ( 'offset' => $offset ) : ()
1065    };
1066    my $stmt = $driver->prepare_statement( $class, $terms, $args );
1067    my $db_meta_col = $dbd->db_column_name($class->datasource, $meta_col);
1068    ## Meta column has to be added in here because it's already
1069    ## gone from the column_names - something fetchonly relies on
1070    $stmt->add_select( $db_meta_col => $db_meta_col );
1071    my $sql = $stmt->as_sql;
1072    my $sth = $dbh->prepare($sql);
1073    return 0 if !$sth; # ignore this operation if _meta column doesn't exist
1074    $sth->execute
1075        or return $self->error($dbh->errstr || $DBI::errstr);
1076
1077    my $msg = $self->translate_escape("Upgrading metadata storage for [_1]", $class->class_label_plural);
1078
1079    if (!$offset) {
1080        $self->progress($msg, $pid);
1081    } else {
1082        my $count = $class->count();
1083        return 0 unless $count;
1084        $self->progress(sprintf($msg . " (%d%%)", ($offset/$count*100)), $pid);
1085    }
1086
1087    my $rows = 0;
1088
1089    require MT::Serialize;
1090    my $ser = MT::Serialize->new('MT');
1091    my %fields;
1092
1093    my @ids;
1094    my $cfclass = MT->model('field');
1095    while (my $row = $sth->fetchrow_arrayref) {
1096        $rows++;
1097        my ($id, $rawmeta) = @$row;  ## add_select pushes the column - it should be in this order
1098        if (defined $rawmeta) {
1099            push @ids, $id;
1100            if ($rawmeta =~ m/^SERG/) {
1101                # deserialize
1102                my $metadataref = $ser->unserialize($rawmeta);
1103                if ($metadataref) {
1104                    my $metadata = $$metadataref;
1105                    my $obj = $class->load( { id => $id }, { no_class => 1,
1106                        fetchonly => [ 'id',
1107                            ( $class_type ? ( $class->properties->{class_column} ) : () )
1108                        ]
1109                    });
1110                    if ($obj) {
1111                        foreach my $metaname (keys %$metadata) {
1112                            my $metavalue = $metadata->{$metaname};
1113                            if ($metaname eq 'customfields') {
1114                                next unless $cfclass;
1115
1116                                # extra work for custom fields;
1117                                # a hash into itself
1118                                my $cfdata = $metavalue;
1119                                next unless ref $cfdata eq 'HASH';
1120
1121                                foreach my $cfname (keys %$cfdata) {
1122                                    my $cfvalue = $cfdata->{$cfname};
1123                                    my $cftype = $type;
1124                                    if ($class_type) {
1125                                        $cftype = $obj->class_type;
1126                                    }
1127
1128                                    # make sure CustomFields::Field exists
1129                                    my $fld = $fields{$cfname}{$cftype} ||= $cfclass->load({ basename => $cfname, obj_type => $cftype });
1130                                    next unless $fld;
1131
1132                                    _save_meta($obj,
1133                                        'field.' . $cfname, $cfvalue);
1134                                }
1135                            } else {
1136                                _save_meta($obj, $metaname,
1137                                    $metavalue);
1138                            }
1139                        }
1140                    }
1141                }
1142            }
1143        }
1144        last if $rows == 100;
1145    }
1146    if ($rows == 100 && $sth->fetchrow_arrayref) {
1147        $rows++;
1148    }
1149    $sth->finish;
1150
1151    if ($rows == 101) {
1152        $offset += 100;
1153    } else {
1154        # done, so lets drop that meta column, what say you?
1155        if ($dbd->ddl_class->can_drop_column) {
1156            # if the driver cannot drop a column, it is likely
1157            # to get dropped as the table is updated for other
1158            # new columns anyway.
1159            $sql = $dbd->ddl_class->drop_column_sql($class, $meta_col);
1160            $self->add_step('core_drop_meta_for_table', class => $db_class, sql => $sql);
1161        }
1162        $self->progress($msg . ' (100%)', $pid);
1163        $offset = 0;  # done!
1164    }
1165    return $offset;
1166}
1167
1168sub core_drop_meta_for_table {
1169    my $self = shift;
1170    my (%param) = @_;
1171    my $class = $param{class};
1172    my $sql = $param{sql};
1173
1174    eval "require $class;";
1175    my $driver = $class->dbi_driver;
1176    my $dbh = $driver->rw_handle;
1177    my $err;
1178    eval {
1179        $dbh->do($sql) or $err = $dbh->errstr;
1180    };
1181    # ignore drop errors; the column has probably been
1182    # removed already
1183    #if ($err) {
1184    #    print STDERR "$err: $sql\n";
1185    #}
1186
1187    return 0;
1188}
1189
11901;
Note: See TracBrowser for help on using the browser.