2025-08-20 16:34:20 +03:00
< ? php
use dokuwiki\Extension\EventHandler ;
use dokuwiki\Extension\Event ;
2025-09-10 14:44:08 +02:00
use dokuwiki\Logger ;
2025-08-20 16:34:20 +03:00
/**
2025-09-01 16:25:25 +02:00
* Action Component for the Bot Monitoring Plugin
2025-08-20 16:34:20 +03:00
*
* @ license GPL 3 ( http :// www . gnu . org / licenses / gpl . html )
* @ author Sascha Leib < sascha . leib ( at ) kolmio . com >
*/
2025-09-01 16:25:25 +02:00
class action_plugin_botmon extends DokuWiki_Action_Plugin {
2025-08-20 16:34:20 +03:00
/**
2025-08-20 22:58:42 +03:00
* Registers a callback functions
*
* @ param EventHandler $controller DokuWiki ' s event controller object
* @ return void
*/
public function register ( EventHandler $controller ) {
2025-09-13 23:20:43 +02:00
2025-10-14 20:58:33 +02:00
global $ACT ;
2025-09-13 23:20:43 +02:00
2025-10-14 20:58:33 +02:00
// insert header data into the page:
if ( $ACT == 'show' ) {
$controller -> register_hook ( 'TPL_METAHEADER_OUTPUT' , 'BEFORE' , $this , 'insertHeader' );
} else if ( $ACT == 'admin' && isset ( $_REQUEST [ 'page' ]) && $_REQUEST [ 'page' ] == 'botmon' ) {
$controller -> register_hook ( 'TPL_METAHEADER_OUTPUT' , 'BEFORE' , $this , 'insertAdminHeader' );
}
2025-09-13 23:20:43 +02:00
// write to the log after the page content was displayed:
$controller -> register_hook ( 'TPL_CONTENT_DISPLAY' , 'AFTER' , $this , 'writeServerLog' );
2025-08-20 22:58:42 +03:00
}
2025-09-12 15:38:28 +02:00
/* session information */
2025-09-12 17:50:05 +02:00
private $sessionId = null ;
private $sessionType = '' ;
2025-09-13 23:20:43 +02:00
private $ipAddress = null ;
2025-09-12 15:38:28 +02:00
2025-08-20 22:58:42 +03:00
/**
* Inserts tracking code to the page header
2025-10-14 20:58:33 +02:00
* ( only called on 'show' actions )
2025-08-20 22:58:42 +03:00
*
* @ param Event $event event object by reference
* @ return void
*/
2025-08-20 16:34:20 +03:00
public function insertHeader ( Event $event , $param ) {
global $INFO ;
2025-09-12 15:38:28 +02:00
// populate the session id and type:
$this -> getSessionInfo ();
2025-08-20 16:34:20 +03:00
// is there a user logged in?
2025-09-13 23:20:43 +02:00
$username = ( ! empty ( $INFO [ 'userinfo' ]) && ! empty ( $INFO [ 'userinfo' ][ 'name' ]) ? $INFO [ 'userinfo' ][ 'name' ] : '' );
2025-08-20 16:34:20 +03:00
// build the tracker code:
2025-10-14 20:58:33 +02:00
$code = " document._botmon = { 't0': Date.now(), 'session': ' " . json_encode ( $this -> sessionId ) . " '}; " . NL ;
2025-08-20 22:58:42 +03:00
if ( $username ) {
2025-10-14 20:58:33 +02:00
$code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";' . NL ;
2025-08-20 16:34:20 +03:00
}
2025-09-13 23:20:43 +02:00
// add the deferred script loader::
2025-10-14 20:58:33 +02:00
$code .= DOKU_TAB . DOKU_TAB . " addEventListener('DOMContentLoaded', function() { " . NL ;
$code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . " const e=document.createElement('script'); " . NL ;
$code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . " e.async=true;e.defer=true; " . NL ;
$code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . " e.src=' " . DOKU_BASE . " lib/plugins/botmon/client.js'; " . NL ;
$code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . " document.getElementsByTagName('head')[0].appendChild(e); " . NL ;
$code .= DOKU_TAB . DOKU_TAB . " }); " ;
2025-08-20 16:34:20 +03:00
2025-09-13 23:20:43 +02:00
$event -> data [ 'script' ][] = [ '_data' => $code ];
2025-09-06 20:01:03 +02:00
}
2025-10-14 20:58:33 +02:00
/**
* Inserts tracking code to the page header
* ( only called on 'show' actions )
*
* @ param Event $event event object by reference
* @ return void
*/
public function insertAdminHeader ( Event $event , $param ) {
$event -> data [ 'link' ][] = [ 'rel' => 'stylesheet' , 'href' => DOKU_BASE . 'lib/plugins/botmon/admin.css' , 'defer' => 'defer' ];
2025-10-15 20:12:42 +02:00
$event -> data [ 'script' ][] = [ 'src' => DOKU_BASE . 'lib/plugins/botmon/admin.js' , 'defer' => 'defer' , '_data' => '' ];
2025-10-14 20:58:33 +02:00
}
2025-09-06 20:01:03 +02:00
/**
* Writes data to the server log .
*
* @ return void
*/
2025-09-13 23:20:43 +02:00
public function writeServerLog ( Event $event , $param ) {
2025-09-06 20:01:03 +02:00
global $conf ;
global $INFO ;
2025-08-29 23:18:51 +03:00
2025-09-13 23:20:43 +02:00
// is there a user logged in?
$username = ( ! empty ( $INFO [ 'userinfo' ]) && ! empty ( $INFO [ 'userinfo' ][ 'name' ])
? $INFO [ 'userinfo' ][ 'name' ] : '' );
2025-09-05 09:15:08 +02:00
// clean the page ID
$pageId = preg_replace ( '/[\x00-\x1F]/' , " \ u { FFFD} " , $INFO [ 'id' ] ? ? '' );
2025-09-06 20:01:03 +02:00
// create the log array:
2025-08-20 22:58:42 +03:00
$logArr = Array (
2025-09-13 23:20:43 +02:00
$this -> ipAddress , /* remote IP */
2025-09-05 09:15:08 +02:00
$pageId , /* page ID */
2025-09-12 15:38:28 +02:00
$this -> sessionId , /* Session ID */
$this -> sessionType , /* session ID type */
2025-09-13 23:20:43 +02:00
$username , /* user name */
2025-08-30 13:01:50 +03:00
$_SERVER [ 'HTTP_USER_AGENT' ] ? ? '' , /* User agent */
2025-09-06 20:01:03 +02:00
$_SERVER [ 'HTTP_REFERER' ] ? ? '' , /* HTTP Referrer */
substr ( $conf [ 'lang' ], 0 , 2 ), /* page language */
2025-10-13 14:56:25 +02:00
implode ( ',' , array_unique ( array_map ( function ( $it ) { return substr ( trim ( $it ), 0 , 2 ); }, explode ( ',' , trim ( $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ], " \t ;,* " ))))), /* accepted client languages */
2025-09-13 23:20:43 +02:00
$this -> getCountryCode () /* GeoIP country code */
2025-08-20 22:58:42 +03:00
);
//* create the log line */
2025-09-01 10:15:36 +02:00
$filename = __DIR__ . '/logs/' . gmdate ( 'Y-m-d' ) . '.srv.txt' ; /* use GMT date for filename */
2025-08-20 22:58:42 +03:00
$logline = gmdate ( 'Y-m-d H:i:s' ); /* use GMT time for log entries */
foreach ( $logArr as $tab ) {
$logline .= " \t " . $tab ;
};
/* write the log line to the file */
$logfile = fopen ( $filename , 'a' );
if ( ! $logfile ) die ();
if ( fwrite ( $logfile , $logline . " \n " ) === false ) {
fclose ( $logfile );
die ();
}
/* Done */
fclose ( $logfile );
}
2025-09-12 15:38:28 +02:00
2025-09-13 23:20:43 +02:00
private function getCountryCode () {
2025-09-15 22:58:08 +02:00
$country = ( $this -> ipAddress == 'localhost' ? 'local' : 'ZZ' ); // default if no geoip is available!
2025-09-13 23:20:43 +02:00
$lib = $this -> getConf ( 'geoiplib' ); /* which library to use? (can only be phpgeoip or disabled) */
try {
// use GeoIP module?
if ( $lib == 'phpgeoip' && extension_loaded ( 'geoip' ) && geoip_db_avail ( GEOIP_COUNTRY_EDITION )) { // Use PHP GeoIP module
$result = geoip_country_code_by_name ( $_SERVER [ 'REMOTE_ADDR' ]);
$country = ( $result ? $result : $country );
}
} catch ( Exception $e ) {
Logger :: error ( 'BotMon Plugin: GeoIP Error' , $e -> getMessage ());
}
return $country ;
}
2025-09-12 15:38:28 +02:00
private function getSessionInfo () {
2025-09-13 23:20:43 +02:00
$this -> ipAddress = $_SERVER [ 'REMOTE_ADDR' ] ? ? null ;
if ( $this -> ipAddress == '127.0.0.1' || $this -> ipAddress == '::1' ) $this -> ipAddress = 'localhost' ;
2025-09-12 15:38:28 +02:00
// what is the session identifier?
if ( isset ( $_SESSION )) {
$sesKeys = array_keys ( $_SESSION ); /* DokuWiki Session ID preferred */
foreach ( $sesKeys as $key ) {
if ( substr ( $key , 0 , 2 ) == 'DW' ) {
$this -> sessionId = $key ;
$this -> sessionType = 'dw' ;
return ;
}
}
}
2025-09-12 17:50:05 +02:00
if ( ! $this -> sessionId ) { /* no DokuWiki Session ID, try PHP session ID */
2025-09-12 15:38:28 +02:00
$this -> sessionId = session_id ();
$this -> sessionType = 'php' ;
}
2025-09-13 23:20:43 +02:00
if ( ! $this -> sessionId && $this -> ipAddress ) { /* no PHP session ID, try IP address */
$this -> sessionId = $this -> ipAddress ;
2025-09-12 15:38:28 +02:00
$this -> sessionType = 'ip' ;
}
2025-09-12 17:50:05 +02:00
if ( ! $this -> sessionId ) { /* if everything else fails, just us a random ID */
2025-09-12 15:38:28 +02:00
$this -> sessionId = rand ( 1000000 , 9999999 );
$this -> sessionType = 'rand' ;
}
}
2025-08-20 16:34:20 +03:00
}