Index: /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/OpenIDwithBanList.pl
===================================================================
--- /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/OpenIDwithBanList.pl (revision 385)
+++ /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/OpenIDwithBanList.pl (revision 385)
@@ -0,0 +1,82 @@
+# OpenIDwithBanList plugin for Movable Type
+# Author: Fumiaki Yoshimatsu (http://www.luckypines.com/mt/)
+# Released under the Artistic License.
+
+package MT::Plugin::OpenIDwithBanList;
+
+use strict;
+use warnings;
+
+use MT;
+use base qw(MT::Plugin);
+
+my $plugin = MT::Plugin::OpenIDwithBanList->new({
+    id => 'OpenIDwithBanList',
+    name => "OpenID commenter authenticator with the list of banned IdP.",
+    version => '0.1',
+    author_name => "Fumiaki Yoshimatsu",
+    author_link => "http://www.luckypines.com/mt/",
+});
+MT->add_plugin($plugin);
+
+sub init_registry {
+    my $plugin = shift;
+    $plugin->registry({
+        'applications' => {
+            'cms' => {
+                methods => {
+                    'list_idp'  => '$OpenIDwithBanList::OpenID::WithBanList::list_idp',
+                    'delete_idp' => '$OpenIDwithBanList::OpenID::WithBanList::delete_idp',
+                    'add_idp'    => '$OpenIDwithBanList::OpenID::WithBanList::add_idp',
+                },
+                menus => {
+                    'manage:idp' => {
+                        label => "Banned IdPs",
+                        mode  => "list_idp",
+                        order => 1000,
+                        view  => "blog",
+                    },
+                }
+            }
+        },
+        'commenter_authenticators' => {
+            'openid_banlist' => {
+                label => 'OpenID w/ ban list',
+                class => 'OpenID::WithBanList',
+                login_form => <<OpenID,
+<form method="post" action="<mt:var name="script_url">">
+<input type="hidden" name="__mode" value="login_external" />
+<input type="hidden" name="blog_id" value="<mt:var name="blog_id">" />
+<input type="hidden" name="entry_id" value="<mt:var name="entry_id">" />
+<input type="hidden" name="static" value="<mt:var name="static" escape="html">" />
+<fieldset>
+<mtapp:setting
+    id="openid_display"
+    label="<__trans phrase="OpenID URL">"
+    hint="<__trans phrase="Sign in using your OpenID identity.">">
+<input type="hidden" name="key" value="openid_banlist" />
+<input name="openid_url" style="background: #fff url('<mt:var name="static_uri">images/comment/openid_logo.png') no-repeat left; padding-left: 18px; padding-bottom: 1px; border: 1px solid #5694b6; width: 304px; font-size: 110%;" />
+    <p class="hint"><__trans phrase="OpenID is an open and decentralized single sign-on identity system."></p>
+</mtapp:setting>
+
+<div class="pkg">
+<div class="left"><input type="submit" name="submit" value="<__trans phrase="Sign In">" /></div>
+<div class="right"><img src="<mt:var name="static_uri">images/comment/openid_enabled.png" /></div>
+</div>
+<p><img src="<mt:var name="static_uri">images/comment/blue_moreinfo.png"> <a href="http://www.openid.net/"><__trans phrase="Learn more about OpenID."></a></p>
+</fieldset>
+</form>
+OpenID
+                login_form_params => \&MT::_commenter_auth_params,
+                condition         => \&MT::_openid_commenter_condition,
+                logo              => 'images/comment/signin_openid.png',
+                logo_small        => 'images/comment/openid_logo.png',
+            },
+        },
+    });
+}
+
+sub instance { $plugin }
+
+1;
+
Index: /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/lib/OpenID/WithBanList.pm
===================================================================
--- /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/lib/OpenID/WithBanList.pm (revision 385)
+++ /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/lib/OpenID/WithBanList.pm (revision 385)
@@ -0,0 +1,148 @@
+# OpenIDwithBanList plugin for Movable Type
+# Author: Fumiaki Yoshimatsu (http://www.luckypines.com/mt/)
+# Released under the Artistic License.
+
+package OpenID::WithBanList;
+use strict;
+use base qw( MT::Auth::OpenID );
+
+sub login {
+    my $class = shift;
+    my ($app) = @_;
+    my $q = $app->{query};
+    return $app->errtrans("Invalid request.")
+        unless $q->param('blog_id');
+    my $blog = $app->model('blog')->load($q->param('blog_id'))
+        or return $app->errtrans("Invalid request.");
+    my %param = $app->param_hash;
+    my $csr = MT::Auth::OpenID::_get_csr(\%param, $blog) or return;
+    my $identity = $q->param('openid_url');
+    if (!$identity &&
+        (my $u = $q->param('openid_userid')) && $class->can('url_for_userid')) {
+        $identity = $class->url_for_userid($u);
+    }
+    my $claimed_identity = $csr->claimed_identity($identity);
+    if ( $claimed_identity && ( my $idp_server = $claimed_identity->{server} ) ) {
+        # check to see if the server is in the ban list
+        my $banned = 0;
+        require MT::PluginData;
+        my $plugindata = MT::PluginData->get_by_key({ plugin => 'OpenID::WithBanList', key => $blog->id . "_banlist" });
+        if ( $plugindata ) {
+            my $list = $plugindata->data || [];
+            for my $hash ( @$list ) {
+                my $url = $hash->{url};
+                if ( $idp_server =~ m|$url|i ) {
+                    $banned = 1;
+                    MT->log( 
+                        $app->translate('Banned login attempt by OpenID [_1].  [_2] is banned.', $identity, $url)
+                    );
+                    return $app->errtrans('Your OpenID may be valid but you cannot use it here.  Try different OpenID.');
+                }
+            }
+        }
+    }
+    $class->SUPER::login(@_);
+}
+
+sub list_idp {
+    my $app = shift;
+    my $q = $app->param;
+
+    return $app->errtrans("Invalid request.")
+        unless $q->param('blog_id');
+    my $blog = $app->model('blog')->load($q->param('blog_id'))
+        or return $app->errtrans("Invalid request.");
+
+    require MT::PluginData;
+    my $plugindata = MT::PluginData->get_by_key({ plugin => 'OpenID::WithBanList', key => $blog->id . "_banlist" });
+
+    $app->load_tmpl( 'list_idps.tmpl', {
+        blog_id => $blog->id,
+        object_loop => $plugindata->data,
+    });
+}
+
+sub add_idp {
+    my $app = shift;
+    my $q = $app->param;
+
+    return $app->errtrans("Invalid request.")
+        unless $q->param('blog_id');
+    my $blog = $app->model('blog')->load($q->param('blog_id'))
+        or return $app->errtrans("Invalid request.");
+    return $app->errtrans("Invalid request.")
+        unless $q->param('new_url');
+    my $url = MT::Util::encode_html($q->param('new_url'));
+
+    $app->validate_magic() or return;
+
+    # First ban the existing commenters...
+    my $cmntr_iter = $app->model('commenter')->load_iter({
+        name => { like => "%$url%" },
+        type => MT::Author::COMMENTER(),
+        auth_type => [ 'OpenID', 'openid_banlist' ],
+    });
+    while ( my $cmntr = $cmntr_iter->() ) {
+        next if MT::Author::BANNED() == $cmntr->commenter_status($blog->id);
+        $cmntr->ban($blog->id);
+    }
+
+    my $param = {
+        blog_id => $blog->id,
+    };
+
+    require MT::PluginData;
+    my $plugindata = MT::PluginData->get_by_key({ plugin => 'OpenID::WithBanList', key => $blog->id . "_banlist" });
+    if ( $plugindata ) {
+        my $list = $plugindata->data || [];
+        my @keys = map { $_->{idp_id} } @$list;
+        @keys = sort { $b <=> $a } @keys;
+        my $next = $keys[0] + 1;
+        push @$list, { idp_id => $next, url => $url };
+        $plugindata->data($list);
+        if ( $plugindata->save() ) {
+            $param->{saved} = 1;
+        }
+        else {
+            $param->{error} = $plugindata->errstr;
+        }
+        $param->{object_loop} = $list;
+    }
+    $app->load_tmpl( 'list_idps.tmpl', $param);
+}
+
+sub delete_idp {
+    my $app = shift;
+    my $q = $app->param;
+
+    return $app->errtrans("Invalid request.")
+        unless $q->param('blog_id');
+    my $blog = $app->model('blog')->load($q->param('blog_id'))
+        or return $app->errtrans("Invalid request.");
+
+    $app->validate_magic() or return;
+
+    my $param = {
+        blog_id => $blog->id,
+    };
+
+    require MT::PluginData;
+    my $plugindata = MT::PluginData->get_by_key({ plugin => 'OpenID::WithBanList', key => $blog->id . "_banlist" });
+    if ( $plugindata ) {
+        my $list = $plugindata->data || [];
+        my %id_hash = map { $_ => 1 } $q->param('id');
+        my @new_list = map { $_ } grep { !exists( $id_hash{ $_->{idp_id} } ) } @$list;
+        $plugindata->data(\@new_list);
+        if ( $plugindata->save() ) {
+            $param->{saved_deleted} = 1;
+        }
+        else {
+            $param->{error} = $plugindata->errstr;
+        }
+        $param->{object_loop} = \@new_list;
+    }
+    $app->load_tmpl( 'list_idps.tmpl', $param);
+}
+
+1;
+
Index: /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/tmpl/list_idps.tmpl
===================================================================
--- /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/tmpl/list_idps.tmpl (revision 385)
+++ /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/tmpl/list_idps.tmpl (revision 385)
@@ -0,0 +1,188 @@
+<mt:setvarblock name="page_title"><__trans phrase="Manage Banned IdPs"></mt:setvarblock>
+<mt:setvarblock name="html_head" append="1">
+<script type="text/javascript">
+<!--
+function setCreateMode () {
+    DOM.addClassName(getByID('create-new-link'), 'hidden');
+    var f = getByID('idp-listing-form');
+    f['__mode'].value = 'add_idp';
+    if (getByID('zero-state')) {
+        getByID('zero-state').style.display = 'none'; // hide any messaging 
+    }
+    if (getByID('msg-block')) {
+        getByID('msg-block').style.display = 'none'; // hide any messaging 
+    }
+    if (document.all) {
+        getByID('create-0').style.display = 'block'; // show subcategory creation row
+    } else {
+        getByID('create-0').style.display = 'table-row';
+    }
+
+    getByID('new-url').focus();
+
+    if (getByID('action-col-head'))
+        getByID('action-col-head').style.display = 'none'; // hide header of actions column
+    if (getByID('delete-col-head'))
+        getByID('delete-col-head').style.display = 'none'; // hide header of delete column
+    if (getByID('footer-list-actions'))
+        getByID('footer-list-actions').style.display = 'none';
+    return false;
+}
+
+function cancelCreateMode () {
+    DOM.removeClassName(getByID('create-new-link'), 'hidden');
+    getByID('create-0').style.display = 'none'; // hide subcategory creation row
+    if (document.all) {
+        if (getByID('action-col-head'))
+            getByID('action-col-head').style.display = 'block'; // show header of actions column
+        if (getByID('delete-col-head'))
+            getByID('delete-col-head').style.display = 'block'; // show header of delete column
+    } else {
+        if (getByID('action-col-head'))
+            getByID('action-col-head').style.display = 'table-cell';
+        if (getByID('delete-col-head'))
+            getByID('delete-col-head').style.display = 'table-cell';
+    }
+    if (getByID('footer-list-actions')) {
+        getByID('footer-list-actions').style.display = 'block';
+    }
+}
+
+var tableSelect;
+function init()
+{
+    // setup
+    tableSelect = new TC.TableSelect( "idp-listing-table" );
+    tableSelect.rowSelect = true;
+}
+
+TC.attachLoadEvent( init );
+//-->
+</script>
+</mt:setvarblock>
+<mt:setvarblock name="content_header">
+    <div id="msg-block">
+        <mt:if name="saved">
+            <mtapp:statusmsg
+                id="saved"
+                class="success"
+                rebuild="all">
+                <__trans phrase="Your IdP changes and additions have been made.">
+            </mtapp:statusmsg>
+        </mt:if>
+
+        <mt:if name="saved_deleted">
+            <mtapp:statusmsg
+                id="saved-deleted"
+                class="success"
+                rebuild="all">
+                <__trans phrase="You have successfully deleted the selected IdP.">
+            </mtapp:statusmsg>
+        </mt:if>
+    </div>
+</mt:setvarblock>
+<mt:setvarblock name="action_buttons">
+    <a href="javascript:void(0)"
+        onclick="doRemoveItems(getByID('idp-listing-form'), '<__trans phrase="idp" escape="js">', '<__trans phrase="idps" escape="js">'); return false;"
+        accesskey="x"
+        title="<__trans phrase="Delete selected IdP (x)">"
+        ><__trans phrase="Delete"></a>
+</mt:setvarblock>
+<mt:var name="position_actions_top" value="1">
+<mt:setvarblock name="content_header">
+    <p id="create-new-link"><a href="javascript:void(0)" onclick="return setCreateMode();" class="icon-left icon-create"><__trans phrase="Add new URL"></a></p>
+</mt:setvarblock>
+<mt:include name="include/header.tmpl">
+<mtapp:listing
+    type="idp"
+    hide_pager="1">
+    <mt:if __first__>
+        <thead>
+            <tr>
+                <th id="delete-col-head" class="cb"><input type="checkbox" name="id-head" value="all" class="select" /></th>
+                <th id="cat-categories" class="category"><__trans phrase="IdP URL"></th>
+            </tr>
+        </thead>
+        <tbody>
+    </mt:if>
+            <tr class="create-row create-inline" id="create-0" style="display:none">
+                <td colspan="2">
+                    <mtapp:setting
+                        id="new-parent-category"
+                        label="URL of the IdP"
+                        label_class="inline field-no-header"
+                        hint=""
+                        show_hint="0">
+                        <input type="text" name="new_url" id="new-url" value="" maxlength="100" />
+                    </mtapp:setting>
+                    <mtapp:setting
+                        id="actions"
+                        label="<__trans phrase="Actions">"
+                        label_class="inline field-no-header"
+                        hint=""
+                        show_hint="0">
+                        <span class="buttons"> 
+                            <a href="javascript:void(0)" mt:command="submit"
+                                title="<__trans phrase="Ban URL">" 
+                                ><__trans phrase="Ban URL"></a> 
+                            <a href="javascript:void(0)" 
+                                onclick="cancelCreateMode(); return false"
+                                ><__trans phrase="Cancel"></a> 
+                        </span> 
+                    </mtapp:setting>
+                </td>
+            </tr>
+            <tr class="<mt:if name="__odd__">odd<mt:else>even</mt:if>" id="category-<mt:var name="idp_id">">
+                <td class="cb" id="delete-<mt:var name="idp_id">"><input type="checkbox" name="id" value="<mt:var name="idp_id">" class="select" /></td>
+                <td class="category"><mt:var name="url"></td>
+            </tr>
+    <mt:if __last__>
+        </tbody>
+    </mt:if>
+<mt:else>
+    <mtapp:statusmsg id="zero-state" class="info zero-state">
+        <__trans phrase="No banned IdPs could be found.">
+    </mtapp:statusmsg>
+    <form id="idp-listing-form" class="listing-form" action="<$mt:var name="script_url"$>">
+        <input type="hidden" name="__mode" value="add_idp" />
+        <input type="hidden" name="_type" value="idp" />
+
+        <input type="hidden" name="return_args" value="<$mt:var name="return_args" escape="html"$>" />
+        <input type="hidden" name="blog_id" value="<$mt:var name="blog_id"$>" />
+        <input type="hidden" name="magic_token" value="<$mt:var name="magic_token"$>" />
+        <div id="actions-bar-top" class="actions-bar actions-bar-top">
+        <table id="idp-listing-table" class="idp-listing-table compact" cellspacing="0">
+        <tbody>
+            <tr class="create-row create-inline" id="create-0" style="display:none">
+                <td colspan="2">
+                    <mtapp:setting
+                        id="new-parent-category"
+                        label="URL of the IdP"
+                        label_class="inline field-no-header"
+                        hint=""
+                        show_hint="0">
+                        <input type="text" name="new_url" id="new-url" value="" maxlength="100" />
+                    </mtapp:setting>
+                    <mtapp:setting
+                        id="actions"
+                        label="<__trans phrase="Actions">"
+                        label_class="inline field-no-header"
+                        hint=""
+                        show_hint="0">
+                        <span class="buttons"> 
+                            <a href="javascript:void(0)" mt:command="submit"
+                                title="<__trans phrase="Ban URL">" 
+                                ><__trans phrase="Ban URL"></a> 
+                            <a href="javascript:void(0)" 
+                                onclick="cancelCreateMode(); return false"
+                                ><__trans phrase="Cancel"></a> 
+                        </span> 
+                    </mtapp:setting>
+                </td>
+            </tr>
+        </tbody>
+        </table>
+    </form>
+</mtapp:listing>
+<mt:include name="include/footer.tmpl">
+
Index: /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/README
===================================================================
--- /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/README (revision 385)
+++ /trunk/OpenIDwithBanList/plugins/OpenIDwithBanList/README (revision 385)
@@ -0,0 +1,18 @@
+= OpenID commenter authenticator with ban list =
+This is a plugin for Movable Type 4.0 and up.  This plugin is to ban certain OpenID by setting up the list of ID providers.  Example of usage may be that you don't want anonymous OpenID user to comment to your community blog.
+
+To be honest, I, the developer of the plugin, am not sure if this is a good thing to ban an ID based on its provider.  The small the provider the greater the chance they might have more bad users?  I don't think so.
+
+== Installation ==
+Place all of the items under plugins directory into the plugins directory of your Movable Type installation.  This includes copying a file directly under plugins/OpenIdwithBanList directory, and copying two directories (lib and tmpl) under the directory as well.
+
+== Set up ==
+Go to the blog's setting screen.  Go to Registration screen.  Check "OpenID w/ ban list" option and save.
+
+Now go to Manage - Banned IdPs to show list of banned IdPs.  You can add and delete IdP in this screen.
+
+You can ban OpenID by its portion.  For example, if you ban http://www.luckypines.com/, you are banning any OpenID including http://www.luckypines.com/ in it from commenting.  Any existing commenter who is already registered and commented is also banned when you add the URL, if the OpenID for him/her includes the portion.
+
+Unfortunately, deleting IdP from the list does not unban existing commenters currently.
+
+Enjoy.  Let's not ban too many OpenID ID providers out there for no reason...
