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

# Original Copyright (c) 2004-2006 David Raynes

package MT::Plugin::MultiBlog;

use strict;
use warnings;

use base qw( MT::Plugin );

our $VERSION = '2.2';
my $plugin = MT::Plugin::MultiBlog->new(
    {   id   => 'multiblog',
        name => 'MultiBlog',
        description =>
            '<MT_TRANS phrase="MultiBlog allows you to publish content from other blogs and define publishing rules and access controls between them.">',
        version                => $VERSION,
        author_name            => 'Six Apart, Ltd.',
        author_link            => 'http://www.sixapart.com/',
        system_config_template => 'system_config.tmpl',
        blog_config_template   => 'blog_config.tmpl',
        settings               => new MT::PluginSettings(
            [   [   'default_access_allowed',
                    { Default => 1, Scope => 'system' }
                ],
                [ 'rebuild_triggers', { Default => '', Scope => 'blog' } ],
                [   'blog_content_accessible',
                    { Default => '', Scope => 'blog' }
                ],
                [ 'other_triggers',            { Scope => 'blog' } ],
                [ 'blogs_in_website_triggers', { Scope => 'blog' } ],
                [ 'all_triggers',              { Scope => 'system' } ],
                [   'default_mtmultiblog_action',
                    { Default => 1, Scope => 'blog' }
                ],
                [   'default_mtmulitblog_blogs',
                    { Default => '', Scope => 'blog' }
                ],
            ]
        ),
        l10n_class => 'MultiBlog::L10N',
        registry   => {
            applications => {
                'cms' => {
                    methods => {
                        multiblog_add_trigger =>
                            '$multiblog::MT::Plugin::MultiBlog::add_trigger',
                    },
                },
            },
            tags => {
                help_url => sub {
                    MT->translate(
                        'http://www.movabletype.org/documentation/appendices/tags/%t.html'
                    );
                },
                block => {
                    Entries    => 'MultiBlog::preprocess_native_tags',
                    Categories => 'MultiBlog::preprocess_native_tags',
                    Comments   => 'MultiBlog::preprocess_native_tags',
                    Pages      => 'MultiBlog::preprocess_native_tags',
                    Folders    => 'MultiBlog::preprocess_native_tags',
                    Blogs      => 'MultiBlog::preprocess_native_tags',
                    Websites   => 'MultiBlog::preprocess_native_tags',
                    Assets     => 'MultiBlog::preprocess_native_tags',
                    Comments   => 'MultiBlog::preprocess_native_tags',
                    Pings      => 'MultiBlog::preprocess_native_tags',
                    Authors    => 'MultiBlog::preprocess_native_tags',
                    Tags       => 'MultiBlog::preprocess_native_tags',
                    MultiBlog  => 'MultiBlog::Tags::MultiBlog',
                    OtherBlog  => 'MultiBlog::Tags::MultiBlog',
                    MultiBlogLocalBlog =>
                        'MultiBlog::Tags::MultiBlogLocalBlog',
                    'MultiBlogIfLocalBlog?' =>
                        'MultiBlog::Tags::MultiBlogIfLocalBlog',
                },
                function => {
                    'Include' => 'MultiBlog::preprocess_native_tags',
                    'BlogCategoryCount' =>
                        'MultiBlog::preprocess_native_tags',
                    'BlogEntryCount' => 'MultiBlog::preprocess_native_tags',
                    'BlogPingCount'  => 'MultiBlog::preprocess_native_tags',
                    'TagSearchLink'  => 'MultiBlog::preprocess_native_tags',
                },
            },
        },
    }
);
MT->add_plugin($plugin);

# Register entry post-save callback for rebuild triggers
MT->add_callback( 'cms_post_save.entry', 10, $plugin,
    sub { $plugin->runner( 'post_entry_save', @_ ) } );
MT->add_callback( 'api_post_save.entry', 10, $plugin,
    sub { $plugin->runner( 'post_entry_save', @_ ) } );
MT->add_callback( 'cms_post_bulk_save.entries', 10, $plugin,
    sub { $plugin->runner( 'post_entries_bulk_save', @_ ) } );
MT->add_callback( 'scheduled_post_published', 10, $plugin,
    sub { $plugin->runner( 'post_entry_pub', @_ ) } );

# Register page post-save callback for rebuild triggers
MT->add_callback( 'cms_post_save.page', 10, $plugin,
    sub { $plugin->runner( 'post_entry_save', @_ ) } );
MT->add_callback( 'api_post_save.page', 10, $plugin,
    sub { $plugin->runner( 'post_entry_save', @_ ) } );
MT->add_callback( 'cms_post_bulk_save.pages', 10, $plugin,
    sub { $plugin->runner( 'post_entries_bulk_save', @_ ) } );

# Register Comment/TB post-save callbacks for rebuild triggers
MT->add_callback( 'MT::Comment::post_save', 10, $plugin,
    sub { $plugin->runner( 'post_feedback_save', 'comment_pub', @_ ) } );
MT->add_callback( 'MT::TBPing::post_save', 10, $plugin,
    sub { $plugin->runner( 'post_feedback_save', 'tb_pub', @_ ) } );

# Register restore callback to restore blog assciation of triggers
MT->add_callback( 'restore', 10, $plugin,
    sub { $plugin->runner( 'post_restore', @_ ) } );

sub instance {$plugin}

sub add_trigger {
    my $app = shift;

    return $plugin->translate("Permission denied.")
        unless $app->user->is_superuser()
            || (   $app->blog
                && $app->user->permissions( $app->blog->id )
                ->can_administer_blog() );

    my $blog_id = $app->blog->id;

    my $dialog_tmpl = $plugin->load_tmpl('dialog_create_trigger.tmpl');
    my $tmpl        = $app->listing(
        {   template => $dialog_tmpl,
            type     => 'blog',
            code     => sub {
                my ( $obj, $row ) = @_;
                if ($obj) {
                    $row->{label} = $obj->name;
                    $row->{link}  = $obj->site_url;
                }
            },
            terms => { id  => [$blog_id], },
            args  => { not => { id => 1 }, },
            params => {
                panel_type    => 'blog',
                dialog_title  => $plugin->translate('MultiBlog'),
                panel_title   => $plugin->translate('Create Trigger'),
                panel_label   => $plugin->translate("Website/Blog"),
                search_prompt => $plugin->translate("Search Weblogs") . ':',
                panel_description      => $plugin->translate("Description"),
                panel_multi            => 0,
                panel_first            => 1,
                panel_last             => 1,
                panel_searchable       => 1,
                multiblog_trigger_loop => trigger_loop(),
                multiblog_action_loop  => action_loop(),
                list_noncron           => 1,
                trigger_caption        => $plugin->translate('When this'),
            },
            pre_build => sub {
                my ($param) = @_;
                my $offset = $app->param('offset') || 0;
                my $limit  = $param->{limit};
                my $count  = 0;
                if ( !$app->param('search') ) {
                    if ( ( my $loop = $param->{object_loop} ) && !$offset ) {
                        if ( $app->blog && !$app->blog->is_blog ) {
                            $count++;
                            unshift @$loop,
                                {
                                id    => '_blogs_in_website',
                                label => $plugin->translate(
                                    '(All blogs in this website)'),
                                description => $plugin->translate(
                                    'Select to apply this trigger to all blogs in this website.'
                                ),
                                };
                        }
                        $count++;
                        unshift @$loop,
                            {
                            id    => '_all',
                            label => $plugin->translate(
                                '(All websites and blogs in this system)'),
                            description => $plugin->translate(
                                'Select to apply this trigger to all websites and blogs in this system.'
                            ),
                            };
                        splice( @$loop, $limit ) if scalar(@$loop) > $limit;
                    }
                }
                return $count;
            },
        }
    );
    return $app->build_page($tmpl);
}

sub trigger_loop {
    [   {   trigger_key  => 'entry_save',
            trigger_name => $plugin->translate('saves an entry/page'),
        },
        {   trigger_key  => 'entry_pub',
            trigger_name => $plugin->translate('publishes an entry/page'),
        },
        {   trigger_key  => 'comment_pub',
            trigger_name => $plugin->translate('publishes a comment'),
        },
        {   trigger_key  => 'tb_pub',
            trigger_name => $plugin->translate('publishes a TrackBack'),
        },
    ];
}

sub action_loop {
    [   {   action_id   => 'ri',
            action_name => $plugin->translate('rebuild indexes.'),
        },
        {   action_id => 'rip',
            action_name =>
                $plugin->translate('rebuild indexes and send pings.'),
        },
    ];
}

sub load_config {
    my $plugin = shift;
    my ( $args, $scope ) = @_;

    $plugin->SUPER::load_config(@_);

    if ( $scope =~ /blog:(\d+)/ ) {
        my $blog_id = $1;

        require MT::Blog;

        $args->{multiblog_trigger_loop} = trigger_loop();
        my %triggers
            = map { $_->{trigger_key} => $_->{trigger_name} }
            @{ $args->{multiblog_trigger_loop} };

        $args->{multiblog_action_loop} = action_loop();
        my %actions
            = map { $_->{action_id} => $_->{action_name} }
            @{ $args->{multiblog_action_loop} };

        my $rebuild_triggers = $args->{rebuild_triggers};
        my @rebuilds         = map {
            my ( $action, $id, $trigger ) = split( /:/, $_ );
            if ( $id eq '_all' ) {
                {   action_name  => $actions{$action},
                    action_value => $action,
                    blog_name    => $plugin->translate(
                        '(All websites and blogs in this system)'),
                    blog_id       => $id,
                    trigger_name  => $triggers{$trigger},
                    trigger_value => $trigger,
                };
            }
            elsif ( $id eq '_blogs_in_website' ) {
                {   action_name  => $actions{$action},
                    action_value => $action,
                    blog_name =>
                        $plugin->translate('(All blogs in this website)'),
                    blog_id       => $id,
                    trigger_name  => $triggers{$trigger},
                    trigger_value => $trigger,
                };
            }
            elsif ( my $blog = MT::Blog->load( $id, { cached_ok => 1 } ) ) {
                {   action_name   => $actions{$action},
                    action_value  => $action,
                    blog_name     => $blog->name,
                    blog_id       => $id,
                    trigger_name  => $triggers{$trigger},
                    trigger_value => $trigger,
                };
            }
            else {
                ();
            }
        } split( /\|/, $rebuild_triggers );
        $args->{rebuilds_loop} = \@rebuilds;
    }
    my $app = MT->instance;
    if ( $app->isa('MT::App') ) {
        $args->{blog_id} = $app->blog->id if $app->blog;
    }
}

sub save_config {
    my $plugin = shift;
    my ( $args, $scope ) = @_;

    $plugin->SUPER::save_config(@_);

    my ($blog_id);
    if ( $scope =~ /blog:(\d+)/ ) {
        $blog_id = $1;

        # Save blog-level content aggregation policy to single
        # system config hash for easy lookup
        my ( $cfg_old, $cfg_new ) = 0;
        my $override
            = $plugin->get_config_value( 'access_overrides', "system" ) || {};
        $cfg_new = $args->{blog_content_accessible};
        if ( exists $override->{$blog_id} ) {
            $cfg_old = $override->{$blog_id};
        }
        if ( $cfg_old != $cfg_new ) {
            $override->{$blog_id} = $cfg_new
                or delete $override->{$blog_id};
            $plugin->set_config_value( 'access_overrides', $override,
                'system' );
        }

        # Fiddle with rebuild triggers...
        my $rebuild_triggers = $args->{rebuild_triggers};
        my $old_triggers     = $args->{old_rebuild_triggers};

        # Check to see if the triggers changed
        if ( $old_triggers ne $rebuild_triggers ) {

# If so, remove all references to the current blog from the triggers cached in other blogs
            foreach ( split( /\|/, $old_triggers ) ) {
                my ( $action, $id, $trigger ) = split( /:/, $_ );
                my $name
                    = $id eq '_all'              ? "all_triggers"
                    : $id eq '_blogs_in_website' ? 'blogs_in_website_triggers'
                    :                              "other_triggers";
                my $scope;
                if ( $id eq '_all' ) {
                    $scope = "system";
                }
                elsif ( $id eq '_blogs_in_website' ) {
                    my $app        = MT::instance;
                    my $website_id = $app->param('blog_id');
                    next unless $website_id;
                    if ( my $website
                        = $app->model('website')->load($website_id) )
                    {
                        $scope = "blog:" . $website->id;
                    }
                }
                else {
                    $scope = "blog:$id";
                }
                my $d = $plugin->get_config_value( $name, $scope );
                next unless exists $d->{$trigger}{$blog_id};
                delete $d->{$trigger}{$blog_id};
                $plugin->set_config_value( $name, $d, $scope );
            }
        }
        foreach ( split( /\|/, $rebuild_triggers ) ) {
            my ( $action, $id, $trigger ) = split( /:/, $_ );
            my $name
                = $id eq '_all'              ? "all_triggers"
                : $id eq '_blogs_in_website' ? 'blogs_in_website_triggers'
                :                              "other_triggers";
            my $scope;
            if ( $id eq '_all' ) {
                $scope = "system";
            }
            elsif ( $id eq '_blogs_in_website' ) {
                my $app        = MT::instance;
                my $website_id = $app->param('blog_id');
                next unless $website_id;
                if ( my $website = $app->model('website')->load($website_id) )
                {
                    $scope = "blog:" . $website->id;
                }
            }
            else {
                $scope = "blog:$id";
            }

            my $d = $plugin->get_config_value( $name, $scope ) || {};
            $d->{$trigger}{$blog_id}{$action} = 1;
            $plugin->set_config_value( $name, $d, $scope );
        }
    }
}

sub reset_config {
    my $plugin = shift;
    my ( $args, $scope ) = @_;

    if ( $scope =~ /blog:(\d+)/ ) {
        my $blog_id = $1;

        # Get the blogs this one triggers from and update them
        # And then save the triggers this blog runs
        my $other_triggers
            = $plugin->get_config_value( 'other_triggers', $scope );
        my $rebuild_triggers
            = $plugin->get_config_value( 'rebuild_triggers', $scope );
        my $all_triggers
            = $plugin->get_config_value( 'all_triggers', 'system' );

        foreach ( split( /\|/, $rebuild_triggers ) ) {
            my ( $action, $id, $trigger ) = split( /:/, $_ );
            next if $id eq '_all';
            my $d = $plugin->get_config_value( 'other_triggers', "blog:$id" );
            delete $d->{$trigger}{$blog_id}
                if exists $d->{$trigger}{$blog_id};
            $plugin->set_config_value( 'other_triggers', $d, "blog:$id" );
        }

        # remove this blog from the 'all_triggers'
        if ($all_triggers) {
            my $changed = 0;
            foreach my $trigger ( keys %$all_triggers ) {
                if ( exists $all_triggers->{$trigger}{$blog_id} ) {
                    delete $all_triggers->{$trigger}{$blog_id};
                    $changed = 1;
                }
            }
            if ($changed) {
                $plugin->set_config_value( 'all_triggers', $all_triggers,
                    'system' );
            }
        }
        $plugin->SUPER::reset_config(@_);
        $plugin->set_config_value( 'other_triggers', $other_triggers,
            "blog:$blog_id" );
    }
    else {

        # reset should not alter the 'all_triggers' element which is
        # configured through the blog-level settings
        my $all_triggers = $plugin->get_config_value('all_triggers');
        $plugin->SUPER::reset_config(@_);
        $plugin->set_config_value( 'all_triggers', $all_triggers, 'system' );
    }
}

# Run-time loading for MultiBlog core methods
sub runner {
    my $plugin = shift;
    my $method = shift;
    require MultiBlog;
    MultiBlog::init_rebuilt_cache( MT->instance );
    no strict 'refs';
    return $_->( $plugin, @_ ) if $_ = \&{"MultiBlog::$method"};
    die "Failed to find MultiBlog::$method";
}

1;
