# Movable Type (r) Open Source (C) 2001-2012 Six Apart, Ltd.
# This program is distributed under the terms of the
# GNU General Public License, version 2.
#
# $Id$

package MT::Upgrade::Core;

use strict;

MT->add_callback( 'MT::Upgrade::seed_database', 5, undef, \&seed_database );
MT->add_callback( 'MT::Upgrade::upgrade_end', 5, undef,
    sub { $_[1]->add_step('core_upgrade_templates') } );

sub upgrade_functions {
    my $self = shift;
    my (%param) = @_;
    return {
        'core_upgrade_templates' => {
            code     => \&upgrade_templates,
            priority => 5,
        },
        'core_init_blog_custom_dynamic_templates' => {
            on_field => 'MT::Blog->custom_dynamic_templates',
            priority => 3.1,
            updater  => {
                type      => 'blog',
                condition => sub { !defined $_[0]->custom_dynamic_templates },
                code      => sub { $_[0]->custom_dynamic_templates('none') },
                label     => 'Assigning custom dynamic template settings...',
                sql       => q{update mt_blog
                            set blog_custom_dynamic_templates = 'none'
                          where blog_custom_dynamic_templates is null},
            }
        },
        'core_init_author_type' => {
            on_field => 'MT::Author->type',
            priority => 3.1,
            updater  => {
                type      => 'author',
                condition => sub { !$_[0]->type },
                code      => sub { $_[0]->type(1) },
                label     => 'Assigning user types...',
                sql       => 'update mt_author set author_type = 1
                        where author_type is null or author_type = 0',
            }
        },
        'core_init_category_parent' => {
            on_field => 'MT::Category->parent',
            priority => 3.1,
            updater  => {
                type      => 'category',
                condition => sub { !defined $_[0]->parent },
                code      => sub { $_[0]->parent(0) },
                label     => 'Assigning category parent fields...',
                sql       => 'update mt_category set category_parent = 0
                        where category_parent is null',
            }
        },
        'core_init_template_build_dynamic' => {
            on_field => 'MT::Template->build_dynamic',
            priority => 3.1,
            updater  => {
                type      => 'template',
                condition => sub { !defined $_[0]->build_dynamic },
                code      => sub { $_[0]->build_dynamic(0) },
                label     => 'Assigning template build dynamic settings...',
                sql => 'update mt_template set template_build_dynamic = 0
                        where template_build_dynamic is null',
            }
        },
        'core_init_comment_visible' => {
            on_field => 'MT::Comment->visible',
            priority => 3.1,
            updater  => {
                type      => 'comment',
                condition => sub { !defined $_[0]->visible },
                code      => sub { $_[0]->visible(1) },
                label     => 'Assigning visible status for comments...',
                sql       => 'update mt_comment set comment_visible = 1
                        where comment_visible is null',
            }
        },
        'core_init_tbping_visible' => {
            on_field => 'MT::TBPing->visible',
            priority => 3.1,
            updater  => {
                type      => 'tbping',
                condition => sub { !defined $_[0]->visible },
                code      => sub { $_[0]->visible(1) },
                label     => 'Assigning visible status for TrackBacks...',
                sql       => 'update mt_tbping set tbping_visible = 1
                        where tbping_visible is null',
            }
        },
        'core_install_default_roles' => {
            code => sub {
                require MT::Role;
                return MT::Role->create_default_roles;
            },
            on_class => 'MT::Role',
            priority => 3.1,
        },
        ## FIXME: currently MT::Upgrade::core_update_records can't run
        ## with multi-class like asset.* ...
        ## should fix it and rewrite this as simple updater.
        'core_update_asset_pathinfo' => {
            version_limit => 5.0,
            priority      => 3.1,
            code          => sub {
                my $upgrade = shift;
                require MT::Asset;
                my $iter = MT::Asset->load_iter( { class => '*' } );
                $upgrade->progress(
                    MT::Upgrade->translate_escape(
                        "Upgrading Asset path informations...")
                );
                while ( my $asset = $iter->() ) {
                    my $values = $asset->get_values;
                    my ( $path, $url )
                        = ( $values->{file_path}, $values->{url} );
                    $path =~ s{%s/support/}{%s/};
                    $url  =~ s{%s/support/}{%s/};
                    $asset->file_path($path);
                    $asset->url($url);
                    $asset->save;
                }
                0;
            },
        },
    };
}

### Subroutines

sub seed_database {
    my $cb      = shift;
    my $self    = shift;
    my (%param) = @_;

    require MT::Author;
    return undef if MT::Author->exist;

    $self->progress(
        $self->translate_escape(
            "Creating initial website and user records...")
    );

    local $MT::CallbacksEnabled = 1;

    require MT::L10N;
    my $lang
        = exists $param{user_lang}
        ? $param{user_lang}
        : MT->config->DefaultLanguage;
    my $LH = MT::L10N->get_handle($lang);

    # TBD: parameter for username/password provided by user from $app
    my $author = MT::Author->new;
    $author->name(
        exists $param{user_name}
        ? _uri_unescape_utf8( $param{user_name} )
        : 'Melody' );
    $author->type( MT::Author::AUTHOR() );
    $author->set_password(
        exists $param{user_password}
        ? _uri_unescape_utf8( $param{user_password} )
        : 'Nelson' );
    $author->email(
        exists $param{user_email}
        ? _uri_unescape_utf8( $param{user_email} )
        : '' );
    $author->nickname(
        exists $param{user_nickname}
        ? _uri_unescape_utf8( $param{user_nickname} )
        : '' );
    $author->is_superuser(1);
    $author->can_create_blog(1);
    $author->can_create_website(1);
    $author->can_view_log(1);
    $author->can_manage_plugins(1);
    $author->preferred_language($lang);
    $author->external_id(
        MT::Author->pack_external_id( $param{user_external_id} ) )
        if exists $param{user_external_id};
    $author->auth_type( MT->config->AuthenticationModule );
    $author->save
        or return $self->error(
        $self->translate_escape(
            "Error saving record: [_1].",
            $author->errstr
        )
        );

    my $App = $MT::Upgrade::App;
    $App->{author} = $author if ref $App;

    require MT::Role;
    MT::Role->create_default_roles(%param)
        or return $self->error(
        $self->translate_escape(
            "Error creating role record: [_1].",
            MT::Role->errstr
        )
        );

    require MT::Website;
    my $website = MT::Website->create_default_website(
        exists $param{website_name}
        ? _uri_unescape_utf8( $param{website_name} )
        : MT->translate('First Website'), $param{website_theme}
        )
        or return $self->error(
        $self->translate_escape(
            "Error saving record: [_1].",
            MT::Website->errstr
        )
        );
    $website->site_path(
        exists $param{website_path}
        ? _uri_unescape_utf8( $param{website_path} )
        : '' );
    $website->site_url(
        exists $param{website_url}
        ? _uri_unescape_utf8( $param{website_url} )
        : '' );
    $website->server_offset(
        exists $param{website_timezone}
        ? ( $param{website_timezone} || 0 )
        : 0 );
    $website->save
        or return $self->error(
        $self->translate_escape(
            "Error saving record: [_1].",
            $website->errstr
        )
        );
    MT->run_callbacks( 'blog_template_set_change', { blog => $website } );
    $author->save;

    require MT::Association;
    require MT::Role;
    my ($website_admin_role)
        = MT::Role->load_by_permission("administer_website");
    MT::Association->link( $website => $website_admin_role => $author );

    if ( $param{use_system_email} ) {
        my $cfg = MT->config;
        $cfg->EmailAddressMain( _uri_unescape_utf8( $param{user_email} ), 1 );
        $cfg->save;
    }

    1;
}

sub upgrade_templates {
    my $self = shift;
    my (%opt) = @_;

    my $install = $opt{Install} || 0;

    my $updated = 0;

    my $tmpl_list;
    require MT::DefaultTemplates;
    $tmpl_list = MT::DefaultTemplates->templates || [];

    my $mt = MT->instance;
    my @arch_tmpl;

    require MT::Template;
    require MT::Blog;

    my $installer = sub {
        my ( $val, $blog_id ) = @_;

        my $terms = {};
        $terms->{type} = $val->{type};
        $terms->{name} = $val->{name}
            if $val->{set} ne 'system';
        $terms->{blog_id} = $blog_id;

        return 1 if MT::Template->exist($terms);

        $self->progress(
            $self->translate_escape(
                "Creating new template: '[_1]'.",
                $val->{name}
            )
        );

        my $obj = MT::Template->new;
        $obj->build_dynamic(0);
        if (   ( 'widgetset' eq $val->{type} )
            && ( exists $val->{widgets} ) )
        {
            my $modulesets = delete $val->{widgets};
            $obj->modulesets(
                MT::Template->widgets_to_modulesets( $modulesets, $blog_id )
            );
        }
        foreach my $v ( keys %$val ) {
            $obj->column( $v, $val->{$v} ) if $obj->has_column($v);
        }
        $obj->blog_id($blog_id);
        $obj->save
            or return $self->error(
            $self->translate_escape(
                "Error saving record: [_1].",
                $obj->errstr
            )
            );
        $updated = 1;
        if ( $val->{mappings} ) {
            push @arch_tmpl,
                {
                template => $obj,
                mappings => $val->{mappings},
                };
        }
        return 1;
    };

    my $Installing = $MT::Upgrade::Installing;

    for my $val (@$tmpl_list) {
        if ( !$Installing ) {
            next if $val->{type} eq 'search_results';
        }
        if ( !$install ) {
            if ( !$val->{global} ) {
                next if $val->{set} ne 'system';
            }
        }

        my $p = $val->{plugin} || $mt;
        if ( $val->{global} ) {
            $val->{name} = $p->translate( $val->{name} );
            $val->{text} = $p->translate_templatized( $val->{text} );
            $installer->( $val, 0 ) or return;
        }
        else {
            my $iter = MT::Blog->load_iter();
            while ( my $blog = $iter->() ) {
                my $current_language = MT->current_language;
                MT->set_language( $blog->language );
                $val->{name} = $p->translate( $val->{name} );
                $val->{text} = $p->translate_templatized( $val->{text} );
                MT->set_language($current_language);
                $installer->( $val, $blog->id );
            }
        }
    }

    if (@arch_tmpl) {
        $self->progress(
            $self->translate_escape(
                "Mapping templates to blog archive types...")
        );
        require MT::TemplateMap;

        for my $map_set (@arch_tmpl) {
            my $tmpl     = $map_set->{template};
            my $mappings = $map_set->{mappings};
            foreach my $map_key ( keys %$mappings ) {
                my $m  = $mappings->{$map_key};
                my $at = $m->{archive_type};

                # my $preferred = $mappings->{$map_key}{preferred};
                my $map = MT::TemplateMap->new;
                $map->archive_type($at);
                $map->is_preferred(1);
                $map->template_id( $tmpl->id );
                $map->file_template( $m->{file_template} )
                    if $m->{file_template};
                $map->blog_id( $tmpl->blog_id );
                $map->save;
            }
        }
    }

    $updated;
}

sub _uri_unescape_utf8 {
    my ($text) = @_;
    use URI::Escape;
    $text = uri_unescape($text);
    return Encode::decode_utf8($text);
}

1;
