Nucleusの乗っ取り、その1:スキン変数を自由にマネージする。
2008年7月11日
久しぶりに、Nucleusネタ。乗っ取るといっても、クラッキングのことではなくて、プラグインを用いてコアの機能を大幅に変更してしまう話。
ある方とPMでやりとりしていたときに思いついたプラグインがあって、眠らせておくにはもったいないアイデアなのでここで紹介。
Nucleusのスキン変数は、コアで用意されているものと、プラグインで用意されているものと2種類ある。プラグインで用意できるものは、プラグイン名から『NP_』を除いた残りの名前だけ。
また、コアで用意されているものをプラグインでオーバライドすることはできない。例えば、NP_blog という名のプラグインを作成しても、スキン変数<%blog%>ではコアのコードが呼び出されてしまう。
コアの変更はせずに、これらを何とかしようという話。
次のプラグインは、スキン及びアイテム中で、『<% %>』以外に、『[% %]』もNucleus変数として使えるようにするもの。
アイテムに関しては、event_PreItem()で処理する、簡単な動作。
スキンに関しては、event_PreSkinParse()で処理する(3.22以降が必要)。ただし、<%parsedinclude%>の際にはこのイベントが呼ばれないので、表題のNucleusの乗っ取り『スキン変数を自由にマネージする』が必要になる。
1)まず<%parsedinclude%>をオーバライドするために、このプラグインのコード中、convert()メソッドの2行めで、<%parsedinclude(xxx)%>を<%ConverTags(parsedinclude,xxx)%>に書き換えている。
2)結果としてdoSkinVar()メソッドが呼び出されるから、1番目の引数に『parsedinclude』を取るものはparse_parsedinclude()メソッドにリダイレクトする。
3)parse_parsedinclude()メソッド中に、機能拡張したコードを記述する。
同じ原理で、すべてのスキン変数を管理することが可能のはず。
ある方とPMでやりとりしていたときに思いついたプラグインがあって、眠らせておくにはもったいないアイデアなのでここで紹介。
Nucleusのスキン変数は、コアで用意されているものと、プラグインで用意されているものと2種類ある。プラグインで用意できるものは、プラグイン名から『NP_』を除いた残りの名前だけ。
また、コアで用意されているものをプラグインでオーバライドすることはできない。例えば、NP_blog という名のプラグインを作成しても、スキン変数<%blog%>ではコアのコードが呼び出されてしまう。
コアの変更はせずに、これらを何とかしようという話。
次のプラグインは、スキン及びアイテム中で、『<% %>』以外に、『[% %]』もNucleus変数として使えるようにするもの。
<?php
class NP_ConvertTags extends NucleusPlugin {
function getName() { return preg_replace('/\.php$/','',basename(__FILE__)); }
function getMinNucleusVersion() { return 330; }
function getAuthor() { return 'Katsumi'; }
function getVersion() { return '0.1'; }
function getURL() {return 'http://japan.nucleuscms.org/wiki/plugins:authors:katsumi';}
function getDescription() { return $this->getName().' plugin'; }
function supportsFeature($what) { return ($what=='SqlTablePrefix')?1:0; }
function getEventList() { return array('PreSkinParse','PreItem'); }
function doSkinVar($skinType,$type,$p2='') {
switch($type){
case 'parsedinclude':
default:
return $this->parse_parsedinclude($p2);
}
}
function event_PreItem(&$data) {
// Convert
$item=&$data['item'];
$item->body=$this->convert($item->body);
$item->more=$this->convert($item->more);
}
var $handler,$parser;
function event_PreSkinParse(&$data) {
// Convert
$contents=&$data['contents'];
$contents=$this->convert($contents);
// Set handler and parser
$skin=&$data['skin'];
$type=&$data['type'];
$actions = $skin->getAllowedActionsForType($type);
$this->handler =& new ACTIONS($type, $skin);
$this->parser =& new PARSER($actions, $this->handler);
$this->handler->setParser($this->parser);
$this->handler->setSkin($skin);
}
function parse_parsedinclude($filename) {
// check current level
if ($this->handler->level > 3) return; // max. depth reached (avoid endless loop)
$filename = $this->handler->getIncludeFileName($filename);
if (!file_exists($filename)) return '';
$fsize = filesize($filename);
// nothing to include
if ($fsize <= 0)
return;
$this->handler->level++;
// read file
$fd = fopen ($filename, 'r');
$contents = fread ($fd, $fsize);
fclose ($fd);
// convert
$contents=$this->convert($contents);
// parse file contents
$this->parser->parse($contents);
$this->handler->level--;
}
var $search=array('/\[%([\s\S]*?)%\]/');
function convert(&$text) {
$temp=preg_replace($this->search,'<%$1%>',$text);
return preg_replace('/<%parsedinclude\(([^\(]*)\)%>/','<%ConvertTags(parsedinclude,$1)%>',$temp);
}
}
?>アイテムに関しては、event_PreItem()で処理する、簡単な動作。
スキンに関しては、event_PreSkinParse()で処理する(3.22以降が必要)。ただし、<%parsedinclude%>の際にはこのイベントが呼ばれないので、表題のNucleusの乗っ取り『スキン変数を自由にマネージする』が必要になる。
1)まず<%parsedinclude%>をオーバライドするために、このプラグインのコード中、convert()メソッドの2行めで、<%parsedinclude(xxx)%>を<%ConverTags(parsedinclude,xxx)%>に書き換えている。
2)結果としてdoSkinVar()メソッドが呼び出されるから、1番目の引数に『parsedinclude』を取るものはparse_parsedinclude()メソッドにリダイレクトする。
3)parse_parsedinclude()メソッド中に、機能拡張したコードを記述する。
同じ原理で、すべてのスキン変数を管理することが可能のはず。