MTプラグインの重複配置は動作異常になる可能性があります
この記事は Movable Type Advent Calendar 2019 24日目の記事です。
MTプラグインの重複配置は動作異常になる可能性があります、というお話です。
1.はじめに
当方で開発しているMTプラグインを導入頂いたお客様から、「期待通りに動作しない」というご質問を、年に2~3回頂きます。
プラグインのバグもありますが、そうでない場合の原因として最も多いのが「古いバージョンの同じプラグインをpluginsディレクトリに置いている」です。
例えば、Workflowプラグインを購入されたお客様が、プラグインのバージョンアップで、前のバージョンのWorkflowプラグインのディレクトリ名を、"Workflo-backup"のようにリネームして、pluginsディレクトリに置いたままとします。
お客様は、リネームした古いプラグインは動作しないと思われているようですが、リネームしたプラグインは、バージョンアップしたプラグインとは別のプラグインとして登録されてしまいます。
同じ処理が2つ登録されてしまうと、perlとして正常に動作しなくなるようで、これが原因でお問い合わせがくるわけです。
結論から申し上げますと、プラグインのバージョンアップあるいは、プラグインの評価版から製品版に乗り換えられる際、前のプラグインはpluginsディレクトリから必ず削除または移動してください。
2.2つのプラグインが登録されてしまう仕組み
MTのプログラムをトレースして、2つのプラグインが登録されてしまう仕組みを説明します。
ブラウザからMTにアクセスするとinit()が起動します。
lib/MT.pm
sub init {
my $mt = shift;
my %param = @_;
$mt->bootstrap() unless $MT_DIR;
$mt->{mt_dir} = $MT_DIR;
$mt->{config_dir} = $CFG_DIR;
$mt->{app_dir} = $APP_DIR;
$mt->init_callbacks();
## Initialize the language to the default in case any errors occur in
## the rest of the initialization process.
$mt->init_config( \%param ) or return;
$mt->init_lang_defaults(@_) or return;
require MT::Plugin;
$mt->init_addons(@_) or return;
$mt->init_config_from_db( \%param ) or return;
$mt->init_debug_mode;
$mt->init_plugins(@_) or return;★
★でinit_plugins()が起動されます。
sub init_plugins {
my $mt = shift;
# Load compatibility module for prior version
# This should always be MT::Compat::v(MAJOR_RELEASE_VERSION - 1).
if ( MT->config('RequiredCompatibility') < 4.0 ) {
require MT::Compat::v3;
}
my $cfg = $mt->config;
my $use_plugins = $cfg->UsePlugins;
my @PluginPaths = $cfg->PluginPath;
my $PluginSwitch = $cfg->PluginSwitch || {};
my $plugin_sigs = join ',', sort keys %$PluginSwitch;
$mt->_init_plugins_core( $PluginSwitch, $use_plugins, \@PluginPaths );★
★で_init_plugins_core()が起動されます。
sub _init_plugins_core {
my $mt = shift;
my ( $PluginSwitch, $use_plugins, $PluginPaths ) = @_;
my $timer;
if ( $mt->config->PerformanceLogging ) {
$timer = $mt->get_timer();
}
foreach my $PluginPath (@$PluginPaths) {
my $plugin_lastdir = $PluginPath;
$plugin_lastdir =~ s![\\/]$!!;
$plugin_lastdir =~ s!^.*[\\/]!!;
if ( opendir my $DH, $PluginPath ) {
my @p = readdir $DH;★
ここが原因の根本(悪い意味ではなく実装上の仕様)ですが、★のreaddirで、指定されたプラグインパス(デフォルトはplugins)配下のディレクトリをすべて読み込みます。
先の例であれば、
$VAR1 = [ '.', '..', 'BlockEditor', 'Comments', 'FacebookCommenters', 'FormattedText', 'FormattedTextForTinyMCE', 'GoogleAnalytics', 'Markdown', 'OpenID', 'spamlookup', 'Textile', 'TinyMCE', 'Trackback', 'WidgetManager', 'Workflow', 'Workflow-backup', 'WXRImporter' ];
のように、'Workflow'と'Workflow-backup'の2つが含まれます。
このあとの処理で、yaml形式のプラグインであれば、
closedir $DH;
for my $plugin (@p) {
next if ( $plugin =~ /^\.\.?$/ || $plugin =~ /~$/ );
$plugin_full_path
= File::Spec->catfile( $PluginPath, $plugin );
if ( -f $plugin_full_path ) {
$plugin_envelope = $plugin_lastdir;
__load_plugin( $mt, $timer, $PluginSwitch,
$use_plugins, $plugin_full_path, $plugin )
if $plugin_full_path =~ /\.pl$/;
next;
}
my $plugin_dir = $plugin;
$plugin_envelope = "$plugin_lastdir/" . $plugin;
foreach my $lib (qw(lib extlib)) {
my $plib
= File::Spec->catdir( $plugin_full_path, $lib );
unshift @INC, $plib if -d $plib;
}
# handle config.yaml
my $yaml = File::Spec->catdir( $plugin_full_path,
'config.yaml' );
if ( -f $yaml ) {
__load_plugin_with_yaml( $use_plugins, $PluginSwitch,
$plugin_dir );★
next;
}
★で__load_plugin_with_yaml()が実行され、
sub __load_plugin_with_yaml {
my ( $use_plugins, $PluginSwitch, $plugin_dir ) = @_;
my $pclass
= $plugin_dir =~ m/\.pack$/
? 'MT::Component'
: 'MT::Plugin';
# Don't process disabled plugin config.yaml files.
if ($pclass eq 'MT::Plugin'
&& (!$use_plugins
|| ( exists $PluginSwitch->{$plugin_dir}
&& !$PluginSwitch->{$plugin_dir} )
)
)
{
$Plugins{$plugin_dir}{full_path} = $plugin_full_path;
$Plugins{$plugin_dir}{enabled} = 0;
return;
}
return if exists $Plugins{$plugin_dir};
my $id = lc $plugin_dir;
$id =~ s/\.\w+$//;
my $p = $pclass->new(
{ id => $id,
path => $plugin_full_path,
envelope => $plugin_envelope
}
);
# rebless? based on config?
local $plugin_sig = $plugin_dir;
$PluginSwitch->{$plugin_sig} = 1;
MT->add_plugin($p);
$p->init_callbacks();
}
プラグインとして追加されてしまうようです(これ以降トレースしていないので認識誤りでしたらお許しを)。
なお、システム管理画面のプラグイン一覧には、リネームしたプラグインは新しいプラグインと(コンフィグの設定が)同じなので、2つ表示されることはありません。
ということで、旧プラグインはリネームではなく、別のディレクトリに移動しましょう。
この記事には続きがありますが、時間切れなのでこの辺で。
- 2014年にリリースしたMovable Type(MT)プラグイン一覧
- PageButeプラグインでcanonical属性を変更する「PageButeCanonicalChangerプラグイン」
- Movable Typeで作成したブログ記事をEvernoteにクリップする「ClipToEvernoteプラグイン」
- Movable Typeにインストールしたプラグインがプラグイン一覧に表示されない件について
- Movable Typeでアイテム画像を編集できる「Pixenateプラグイン」
- AssetExporter プラグイン v0.02
- Movable Type プラグインハンドラでのクエリーパラメータ取得方法
- Movable Type 5の記事編集画面に任意のボタンを追加する3つのプラグイン
- Action Streams プラグイン(Movable Type 5 対応)の利用方法:その3
- Movable Type用OAuth対応Twitter投稿プラグイン「PostTweet」
- Movable Type用OAuth対応Twitter投稿プラグイン「PostTwiOauth」
- Action Streams プラグイン(Movable Type 5 対応)の利用方法:その2
- Action Streams プラグイン(Movable Type 5 対応)の利用方法:その1
- Movable Type プラグイン一覧(MT5対応)
- Konjak プラグイン