16980370bSSascha Leib<?php 26980370bSSascha Leib 36980370bSSascha Leibuse dokuwiki\Extension\EventHandler; 46980370bSSascha Leibuse dokuwiki\Extension\Event; 55fbe88f7SSascha Leibuse dokuwiki\Logger; 66980370bSSascha Leib 76980370bSSascha Leib/** 87bd08c30SSascha Leib * Action Component for the Bot Monitoring Plugin 96980370bSSascha Leib * 106980370bSSascha Leib * @license GPL 3 (http://www.gnu.org/licenses/gpl.html) 116980370bSSascha Leib * @author Sascha Leib <sascha.leib(at)kolmio.com> 126980370bSSascha Leib */ 136980370bSSascha Leib 147bd08c30SSascha Leibclass action_plugin_botmon extends DokuWiki_Action_Plugin { 156980370bSSascha Leib 166980370bSSascha Leib /** 176980370bSSascha Leib * Registers a callback functions 186980370bSSascha Leib * 196980370bSSascha Leib * @param EventHandler $controller DokuWiki's event controller object 206980370bSSascha Leib * @return void 216980370bSSascha Leib */ 226980370bSSascha Leib public function register(EventHandler $controller) { 235f2c1759SSascha Leib 24e56d7b71SSascha Leib global $ACT; 25e56d7b71SSascha Leib 26*447b8b4fSSascha Leib // populate the session id and type: 27*447b8b4fSSascha Leib $this->setSessionInfo(); 28d49ab213SSascha Leib 295f2c1759SSascha Leib // insert header data into the page: 30d49ab213SSascha Leib if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') { 316980370bSSascha Leib $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); 32d49ab213SSascha Leib 33d49ab213SSascha Leib // Override the page rendering, if a captcha needs to be displayed: 34d49ab213SSascha Leib $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'showCaptcha'); 35d49ab213SSascha Leib 36e56d7b71SSascha Leib } else if ($ACT == 'admin' && isset($_REQUEST['page']) && $_REQUEST['page'] == 'botmon') { 37e56d7b71SSascha Leib $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader'); 38e56d7b71SSascha Leib } 395f2c1759SSascha Leib 40d49ab213SSascha Leib // also show a captcha before the image preview 41d49ab213SSascha Leib $controller->register_hook('TPL_IMG_DISPLAY', 'BEFORE', $this, 'showImageCaptcha'); 42f5f4ca13SSascha Leib 435f2c1759SSascha Leib // write to the log after the page content was displayed: 445f2c1759SSascha Leib $controller->register_hook('TPL_CONTENT_DISPLAY', 'AFTER', $this, 'writeServerLog'); 455f2c1759SSascha Leib 466980370bSSascha Leib } 476980370bSSascha Leib 48b148c85eSSascha Leib /* session information */ 49f6a7ebc1SSascha Leib private $sessionId = null; 50f6a7ebc1SSascha Leib private $sessionType = ''; 512c641262SSascha Leib private $showCaptcha = '-'; 52b148c85eSSascha Leib 536980370bSSascha Leib /** 546980370bSSascha Leib * Inserts tracking code to the page header 55e56d7b71SSascha Leib * (only called on 'show' actions) 566980370bSSascha Leib * 576980370bSSascha Leib * @param Event $event event object by reference 586980370bSSascha Leib * @return void 596980370bSSascha Leib */ 606980370bSSascha Leib public function insertHeader(Event $event, $param) { 616980370bSSascha Leib 626980370bSSascha Leib global $INFO; 636980370bSSascha Leib 64b148c85eSSascha Leib 656980370bSSascha Leib // build the tracker code: 66d49ab213SSascha Leib $code = $this->getBMHeader(); 676980370bSSascha Leib 685f2c1759SSascha Leib // add the deferred script loader:: 69e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL; 70e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "const e=document.createElement('script');" . NL; 71e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.async=true;e.defer=true;" . NL; 72e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL; 73e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL; 74e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . "});"; 755f2c1759SSascha Leib $event->data['script'][] = ['_data' => $code]; 76451abfadSSascha Leib } 77451abfadSSascha Leib 78d49ab213SSascha Leib /* create the BM object code for insertion into a script element: */ 79d49ab213SSascha Leib private function getBMHeader() { 80d49ab213SSascha Leib 81d49ab213SSascha Leib // build the tracker code: 82d49ab213SSascha Leib $code = DOKU_TAB . DOKU_TAB . "document._botmon = {t0: Date.now(), session: " . json_encode($this->sessionId) . ", seed: " . json_encode($this->getConf('captchaSeed')) . ", ip: " . json_encode($_SERVER['REMOTE_ADDR']) . "};" . NL; 83d49ab213SSascha Leib 84d49ab213SSascha Leib // is there a user logged in? 85d49ab213SSascha Leib $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : ''); 86d49ab213SSascha Leib if ($username) { 87d49ab213SSascha Leib $code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL; 88d49ab213SSascha Leib } 89d49ab213SSascha Leib 90d49ab213SSascha Leib return $code; 91d49ab213SSascha Leib 92d49ab213SSascha Leib } 93d49ab213SSascha Leib 94451abfadSSascha Leib /** 95e56d7b71SSascha Leib * Inserts tracking code to the page header 96e56d7b71SSascha Leib * (only called on 'show' actions) 97e56d7b71SSascha Leib * 98e56d7b71SSascha Leib * @param Event $event event object by reference 99e56d7b71SSascha Leib * @return void 100e56d7b71SSascha Leib */ 101e56d7b71SSascha Leib public function insertAdminHeader(Event $event, $param) { 102e56d7b71SSascha Leib 103e56d7b71SSascha Leib $event->data['link'][] = ['rel' => 'stylesheet', 'href' => DOKU_BASE.'lib/plugins/botmon/admin.css', 'defer' => 'defer']; 1040edf1a56SSascha Leib $event->data['script'][] = ['src' => DOKU_BASE.'lib/plugins/botmon/admin.js', 'defer' => 'defer', '_data' => '']; 105e56d7b71SSascha Leib } 106e56d7b71SSascha Leib 107e56d7b71SSascha Leib /** 108451abfadSSascha Leib * Writes data to the server log. 109451abfadSSascha Leib * 110451abfadSSascha Leib * @return void 111451abfadSSascha Leib */ 1125f2c1759SSascha Leib public function writeServerLog(Event $event, $param) { 113451abfadSSascha Leib 114451abfadSSascha Leib global $conf; 115451abfadSSascha Leib global $INFO; 116091b5998SSascha Leib 1175f2c1759SSascha Leib // is there a user logged in? 1185f2c1759SSascha Leib $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) 1195f2c1759SSascha Leib ? $INFO['userinfo']['name'] : ''); 1205f2c1759SSascha Leib 121b2e3bd8bSSascha Leib // clean the page ID 122b2e3bd8bSSascha Leib $pageId = preg_replace('/[\x00-\x1F]/', "\u{FFFD}", $INFO['id'] ?? ''); 123b2e3bd8bSSascha Leib 124451abfadSSascha Leib // create the log array: 125cf9f7fe8SSascha Leib $logArr = Array( 126f5f4ca13SSascha Leib $_SERVER['REMOTE_ADDR'], /* remote IP */ 127b2e3bd8bSSascha Leib $pageId, /* page ID */ 128b148c85eSSascha Leib $this->sessionId, /* Session ID */ 129b148c85eSSascha Leib $this->sessionType, /* session ID type */ 1305f2c1759SSascha Leib $username, /* user name */ 1312f2bc93aSSascha Leib $_SERVER['HTTP_USER_AGENT'] ?? '', /* User agent */ 132451abfadSSascha Leib $_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */ 133451abfadSSascha Leib substr($conf['lang'],0,2), /* page language */ 134a93de874SSascha Leib implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ 1352c641262SSascha Leib $this->getCountryCode(), /* GeoIP country code */ 136d49ab213SSascha Leib $this->showCaptcha /* show captcha? */ 137d49ab213SSascha Leib ); 138cf9f7fe8SSascha Leib 139cf9f7fe8SSascha Leib //* create the log line */ 1404cddc661SSascha Leib $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */ 141cf9f7fe8SSascha Leib $logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */ 142cf9f7fe8SSascha Leib foreach ($logArr as $tab) { 143cf9f7fe8SSascha Leib $logline .= "\t" . $tab; 144cf9f7fe8SSascha Leib }; 145cf9f7fe8SSascha Leib 146cf9f7fe8SSascha Leib /* write the log line to the file */ 147cf9f7fe8SSascha Leib $logfile = fopen($filename, 'a'); 148cf9f7fe8SSascha Leib if (!$logfile) die(); 149cf9f7fe8SSascha Leib if (fwrite($logfile, $logline . "\n") === false) { 150cf9f7fe8SSascha Leib fclose($logfile); 151cf9f7fe8SSascha Leib die(); 1526980370bSSascha Leib } 153cf9f7fe8SSascha Leib 154cf9f7fe8SSascha Leib /* Done */ 155cf9f7fe8SSascha Leib fclose($logfile); 156cf9f7fe8SSascha Leib } 157b148c85eSSascha Leib 1585f2c1759SSascha Leib private function getCountryCode() { 1595f2c1759SSascha Leib 160f5f4ca13SSascha Leib $country = ( $_SERVER['REMOTE_ADDR'] == '127.0.0.1' ? 'local' : 'ZZ' ); // default if no geoip is available! 1615f2c1759SSascha Leib 1625f2c1759SSascha Leib $lib = $this->getConf('geoiplib'); /* which library to use? (can only be phpgeoip or disabled) */ 1635f2c1759SSascha Leib 1645f2c1759SSascha Leib try { 1655f2c1759SSascha Leib 1665f2c1759SSascha Leib // use GeoIP module? 1675f2c1759SSascha Leib if ($lib == 'phpgeoip' && extension_loaded('geoip') && geoip_db_avail(GEOIP_COUNTRY_EDITION)) { // Use PHP GeoIP module 1685f2c1759SSascha Leib $result = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']); 1695f2c1759SSascha Leib $country = ($result ? $result : $country); 1705f2c1759SSascha Leib } 1715f2c1759SSascha Leib } catch (Exception $e) { 1725f2c1759SSascha Leib Logger::error('BotMon Plugin: GeoIP Error', $e->getMessage()); 1735f2c1759SSascha Leib } 1745f2c1759SSascha Leib 1755f2c1759SSascha Leib return $country; 1765f2c1759SSascha Leib } 1775f2c1759SSascha Leib 178*447b8b4fSSascha Leib private function setSessionInfo() { 179b148c85eSSascha Leib 180b148c85eSSascha Leib // what is the session identifier? 181b148c85eSSascha Leib if (isset($_SESSION)) { 182b148c85eSSascha Leib $sesKeys = array_keys($_SESSION); /* DokuWiki Session ID preferred */ 183b148c85eSSascha Leib foreach ($sesKeys as $key) { 184b148c85eSSascha Leib if (substr($key, 0, 2) == 'DW') { 185b148c85eSSascha Leib $this->sessionId = $key; 186b148c85eSSascha Leib $this->sessionType = 'dw'; 187b148c85eSSascha Leib return; 188b148c85eSSascha Leib } 189b148c85eSSascha Leib } 190b148c85eSSascha Leib } 191f6a7ebc1SSascha Leib if (!$this->sessionId) { /* no DokuWiki Session ID, try PHP session ID */ 192b148c85eSSascha Leib $this->sessionId = session_id(); 193b148c85eSSascha Leib $this->sessionType = 'php'; 194b148c85eSSascha Leib } 195f5f4ca13SSascha Leib if (!$this->sessionId) { /* no PHP session ID, try IP address */ 196f5f4ca13SSascha Leib $this->sessionId = $_SERVER['REMOTE_ADDR']; 197b148c85eSSascha Leib $this->sessionType = 'ip'; 198b148c85eSSascha Leib } 199*447b8b4fSSascha Leib 200*447b8b4fSSascha Leib if (!$this->sessionId) { /* if all fails, use random data */ 201*447b8b4fSSascha Leib $this->sessionId = rand(100000000, 999999999); 202*447b8b4fSSascha Leib $this->sessionType = 'rnd'; 203*447b8b4fSSascha Leib } 204*447b8b4fSSascha Leib 205b148c85eSSascha Leib } 206f5f4ca13SSascha Leib 207f5f4ca13SSascha Leib public function showCaptcha(Event $event) { 208f5f4ca13SSascha Leib 20912993035SSascha Leib $useCaptcha = $this->getConf('useCaptcha'); 210f5f4ca13SSascha Leib 211d49ab213SSascha Leib $cCode = '-'; 212d49ab213SSascha Leib if ($useCaptcha !== 'disabled') { 213d49ab213SSascha Leib if ($this->captchaWhitelisted()) { 214d49ab213SSascha Leib $cCode = 'W'; // whitelisted 215d49ab213SSascha Leib } elseif ($this->hasCaptchaCookie()) { 216d49ab213SSascha Leib $cCode = 'N'; // user already has a cookie 217d49ab213SSascha Leib } else { 218d49ab213SSascha Leib $cCode = 'Y'; // show the captcha 2192c641262SSascha Leib 2202c641262SSascha Leib 22112993035SSascha Leib echo '<h1 class="sectionedit1">'; tpl_pagetitle(); echo "</h1>\n"; // always show the original page title 222f5f4ca13SSascha Leib $event->preventDefault(); // don't show normal content 22312993035SSascha Leib switch ($useCaptcha) { 22412993035SSascha Leib case 'blank': 22512993035SSascha Leib $this->insertBlankBox(); // show dada filler instead of text 22612993035SSascha Leib break; 22712993035SSascha Leib case 'dada': 22812993035SSascha Leib $this->insertDadaFiller(); // show dada filler instead of text 22912993035SSascha Leib break; 23012993035SSascha Leib } 231f5f4ca13SSascha Leib $this->insertCaptchaLoader(); // and load the captcha 232f5f4ca13SSascha Leib } 233f5f4ca13SSascha Leib } 234d49ab213SSascha Leib $this->showCaptcha = $cCode; // store the captcha code for the logfile 235d49ab213SSascha Leib 236d49ab213SSascha Leib } 237f5f4ca13SSascha Leib 238d49ab213SSascha Leib public function showImageCaptcha(Event $event, $param) { 239d49ab213SSascha Leib 240d49ab213SSascha Leib $useCaptcha = $this->getConf('useCaptcha'); 241d49ab213SSascha Leib 242d49ab213SSascha Leib echo '<script>' . $this->getBMHeader($event, $param) . '</script>'; 243d49ab213SSascha Leib 244d49ab213SSascha Leib $cCode = '-'; 245d49ab213SSascha Leib if ($useCaptcha !== 'disabled') { 246d49ab213SSascha Leib if ($this->captchaWhitelisted()) { 247d49ab213SSascha Leib $cCode = 'W'; // whitelisted 248d49ab213SSascha Leib } 249d49ab213SSascha Leib elseif ($this->hasCaptchaCookie()) { 250d49ab213SSascha Leib $cCode = 'N'; // user already has a cookie 251d49ab213SSascha Leib } 252d49ab213SSascha Leib else { 253d49ab213SSascha Leib $cCode = 'Y'; // show the captcha 254d49ab213SSascha Leib 255d49ab213SSascha Leib echo '<svg width="100%" height="100%" viewBox="0 0 800 400" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M1,1l798,398" style="fill:none;stroke:#f00;stroke-width:1px;"/><path d="M1,399l798,-398" style="fill:none;stroke:#f00;stroke-width:1px;"/><rect x="1" y="1" width="798" height="398" style="fill:none;stroke:#000;stroke-width:1px;"/></svg>'; // placeholder image 256d49ab213SSascha Leib $event->preventDefault(); // don't show normal content 257d49ab213SSascha Leib 258d49ab213SSascha Leib // TODO Insert dummy image 259d49ab213SSascha Leib $this->insertCaptchaLoader(); // and load the captcha 260d49ab213SSascha Leib } 261d49ab213SSascha Leib }; 262d49ab213SSascha Leib 263d49ab213SSascha Leib $this->showCaptcha = $cCode; // store the captcha code for the logfile 264d49ab213SSascha Leib } 265d49ab213SSascha Leib 266d49ab213SSascha Leib private function hasCaptchaCookie() { 267f5f4ca13SSascha Leib 268cdc02cd4SSascha Leib $cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null; 269f5f4ca13SSascha Leib 270cdc02cd4SSascha Leib $today = substr((new DateTime())->format('c'), 0, 10); 27112993035SSascha Leib 272cdc02cd4SSascha Leib $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; 273cdc02cd4SSascha Leib $expected = hash('sha256', $raw); 27412993035SSascha Leib 275cdc02cd4SSascha Leib //echo '<ul><li>cookie: ' . $cookieVal . '</li><li>expected: ' . $expected . '</li><li>matches: ' .($cookieVal == $expected ? 'true' : 'false') . '</li></ul>'; 276cdc02cd4SSascha Leib 277d49ab213SSascha Leib return $cookieVal == $expected; 278f5f4ca13SSascha Leib } 279f5f4ca13SSascha Leib 2802c641262SSascha Leib // check if the visitor's IP is on a whitelist: 2812c641262SSascha Leib private function captchaWhitelisted() { 2822c641262SSascha Leib 2832c641262SSascha Leib // normalise IP address: 2842c641262SSascha Leib $ip = inet_pton($_SERVER['REMOTE_ADDR']); 2852c641262SSascha Leib 2862c641262SSascha Leib // find which file to open: 2872c641262SSascha Leib $prefixes = ['user', 'default']; 2882c641262SSascha Leib foreach ($prefixes as $pre) { 2892c641262SSascha Leib $filename = __DIR__ .'/config/' . $pre . '-whitelist.txt'; 2902c641262SSascha Leib if (file_exists($filename)) { 2912c641262SSascha Leib break; 2922c641262SSascha Leib } 2932c641262SSascha Leib } 2942c641262SSascha Leib 2952c641262SSascha Leib if (file_exists($filename)) { 2962c641262SSascha Leib $lines = file($filename, FILE_SKIP_EMPTY_LINES); 2972c641262SSascha Leib foreach ($lines as $line) { 2982c641262SSascha Leib if (trim($line) !== '' && !str_starts_with($line, '#')) { 2992c641262SSascha Leib $col = explode("\t", $line); 3002c641262SSascha Leib if (count($col) >= 2) { 3012c641262SSascha Leib $from = inet_pton($col[0]); 3022c641262SSascha Leib $to = inet_pton($col[1]); 3032c641262SSascha Leib 3042c641262SSascha Leib if ($ip >= $from && $ip <= $to) { 305d49ab213SSascha Leib return true; /* IP whitelisted */ 3062c641262SSascha Leib } 3072c641262SSascha Leib } 3082c641262SSascha Leib } 3092c641262SSascha Leib } 3102c641262SSascha Leib } 311d49ab213SSascha Leib return false; /* IP not found in whitelist */ 3122c641262SSascha Leib } 3132c641262SSascha Leib 314f5f4ca13SSascha Leib private function insertCaptchaLoader() { 315620d9253SSascha Leib 31612993035SSascha Leib echo '<script>' . NL; 31712993035SSascha Leib 31812993035SSascha Leib // add the deferred script loader:: 31912993035SSascha Leib echo DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL; 32012993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "const cj=document.createElement('script');" . NL; 32112993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "cj.async=true;cj.defer=true;cj.type='text/javascript';" . NL; 32212993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "cj.src='".DOKU_BASE."lib/plugins/botmon/captcha.js';" . NL; 32312993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(cj);" . NL; 32412993035SSascha Leib echo DOKU_TAB . "});"; 325620d9253SSascha Leib 326620d9253SSascha Leib // add the locales for the captcha: 327620d9253SSascha Leib echo DOKU_TAB . '$BMLocales = {' . NL; 328620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgTitle": ' . json_encode($this->getLang('bm_dlgTitle')) . ',' . NL; 329620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgSubtitle": ' . json_encode($this->getLang('bm_dlgSubtitle')) . ',' . NL; 330620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgConfirm": ' . json_encode($this->getLang('bm_dlgConfirm')) . ',' . NL; 331620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgChecking": ' . json_encode($this->getLang('bm_dlgChecking')) . ',' . NL; 332620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgLoading": ' . json_encode($this->getLang('bm_dlgLoading')) . ',' . NL; 333620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgError": ' . json_encode($this->getLang('bm_dlgError')) . ',' . NL; 334620d9253SSascha Leib echo DOKU_TAB . '};' . NL; 335620d9253SSascha Leib 33612993035SSascha Leib echo '</script>' . NL; 337f5f4ca13SSascha Leib 338f5f4ca13SSascha Leib } 339f5f4ca13SSascha Leib 34012993035SSascha Leib // inserts a blank box to ensure there is enough space for the captcha: 34112993035SSascha Leib private function insertBlankBox() { 34212993035SSascha Leib 34312993035SSascha Leib echo '<p style="min-height: 100px;"> </p>'; 34412993035SSascha Leib } 34512993035SSascha Leib 34612993035SSascha Leib /* Generates a few paragraphs of Dada text to show instead of the article content */ 347f5f4ca13SSascha Leib private function insertDadaFiller() { 348f5f4ca13SSascha Leib 34912993035SSascha Leib global $conf; 35012993035SSascha Leib global $TOC; 35112993035SSascha Leib global $ID; 352f5f4ca13SSascha Leib 35312993035SSascha Leib // list of languages to search for the wordlist 35412993035SSascha Leib $langs = array_unique([$conf['lang'], 'la']); 355f5f4ca13SSascha Leib 35612993035SSascha Leib // find path to the first available wordlist: 35712993035SSascha Leib foreach ($langs as $lang) { 35812993035SSascha Leib $filename = __DIR__ .'/lang/' . $lang . '/wordlist.txt'; /* language-specific wordlist */ 35912993035SSascha Leib if (file_exists($filename)) { 36012993035SSascha Leib break; 36112993035SSascha Leib } 36212993035SSascha Leib } 363f5f4ca13SSascha Leib 36412993035SSascha Leib // load the wordlist file: 36512993035SSascha Leib if (file_exists($filename)) { 36612993035SSascha Leib $words = array(); 36712993035SSascha Leib $totalWeight = 0; 36812993035SSascha Leib $lines = file($filename, FILE_SKIP_EMPTY_LINES); 36912993035SSascha Leib foreach ($lines as $line) { 37012993035SSascha Leib $arr = explode("\t", $line); 37112993035SSascha Leib $arr[1] = ( count($arr) > 1 ? (int) trim($arr[1]) : 1 ); 37212993035SSascha Leib $totalWeight += (int) $arr[1]; 37312993035SSascha Leib array_push($words, $arr); 37412993035SSascha Leib } 37512993035SSascha Leib } else { 37612993035SSascha Leib echo '<script> console.log("Can’t generate filler text: wordlist file not found!"); </script>'; 37712993035SSascha Leib return; 37812993035SSascha Leib } 379f5f4ca13SSascha Leib 38012993035SSascha Leib // If a TOC exists, use it for the headlines: 38112993035SSascha Leib if(is_array($TOC)) { 38212993035SSascha Leib $toc = $TOC; 38312993035SSascha Leib } else { 38412993035SSascha Leib $meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE); 38512993035SSascha Leib //$tocok = (isset($meta['internal']['toc']) ? $meta['internal']['toc'] : $tocok = true); 38612993035SSascha Leib $toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null; 38712993035SSascha Leib } 38812993035SSascha Leib if (!$toc) { // no TOC, generate my own: 38912993035SSascha Leib $hlCount = mt_rand(0, (int) $conf['tocminheads']); 39012993035SSascha Leib $toc = array(); 39112993035SSascha Leib for ($i=0; $i<$hlCount; $i++) { 39212993035SSascha Leib array_push($toc, $this->dadaMakeHeadline($words, $totalWeight)); // $toc 39312993035SSascha Leib } 39412993035SSascha Leib } 39512993035SSascha Leib 39612993035SSascha Leib // if H1 heading is not in the TOC, add a chappeau section: 39712993035SSascha Leib $chapeauCount = mt_rand(1, 3); 39812993035SSascha Leib if ((int) $conf['toptoclevel'] > 1) { 39912993035SSascha Leib echo "<div class=\"level1\">\n"; 40012993035SSascha Leib for ($i=0; $i<$chapeauCount; $i++) { 40112993035SSascha Leib echo $this->dadaMakeParagraph($words, $totalWeight); 40212993035SSascha Leib } 40312993035SSascha Leib echo "</div>\n"; 40412993035SSascha Leib } 40512993035SSascha Leib 40612993035SSascha Leib // text sections for each sub-headline: 40712993035SSascha Leib foreach ($toc as $hl) { 40812993035SSascha Leib echo $this->dadaMakeSection($words, $totalWeight, $hl); 40912993035SSascha Leib } 41012993035SSascha Leib } 41112993035SSascha Leib 41212993035SSascha Leib private function dadaMakeSection($words, $totalWeight, $hl) { 41312993035SSascha Leib 41412993035SSascha Leib global $conf; 41512993035SSascha Leib 41612993035SSascha Leib // how many paragraphs? 41712993035SSascha Leib $paragraphCount = mt_rand(1, 4); 41812993035SSascha Leib 41912993035SSascha Leib // section level 42012993035SSascha Leib $topTocLevel = (int) $conf['toptoclevel']; 42112993035SSascha Leib $secLevel = $hl['level'] + 1;; 42212993035SSascha Leib 42312993035SSascha Leib // return value: 42412993035SSascha Leib $sec = ""; 42512993035SSascha Leib 42612993035SSascha Leib // make a headline: 42712993035SSascha Leib if ($topTocLevel > 1 || $secLevel > 1) { 42812993035SSascha Leib $sec .= "<h{$secLevel} id=\"{$hl['hid']}\">{$hl['title']}</h{$secLevel}>\n"; 42912993035SSascha Leib } 43012993035SSascha Leib 43112993035SSascha Leib // add the paragraphs: 43212993035SSascha Leib $sec .= "<div class=\"level{$secLevel}\">\n"; 43312993035SSascha Leib for ($i=0; $i<$paragraphCount; $i++) { 43412993035SSascha Leib $sec .= $this->dadaMakeParagraph($words, $totalWeight); 43512993035SSascha Leib } 43612993035SSascha Leib $sec .= "</div>\n"; 43712993035SSascha Leib 43812993035SSascha Leib return $sec; 43912993035SSascha Leib } 44012993035SSascha Leib 44112993035SSascha Leib private function dadaMakeHeadline($words, $totalWeight) { 44212993035SSascha Leib 44312993035SSascha Leib // how many words to generate? 44412993035SSascha Leib $wordCount = mt_rand(2, 5); 44512993035SSascha Leib 44612993035SSascha Leib // function returns an array: 44712993035SSascha Leib $r = Array(); 44812993035SSascha Leib 44912993035SSascha Leib // generate the headline: 45012993035SSascha Leib $hlArr = array(); 45112993035SSascha Leib for ($i=0; $i<$wordCount; $i++) { 45212993035SSascha Leib array_push($hlArr, $this->dadaSelectRandomWord($words, $totalWeight)); 45312993035SSascha Leib } 45412993035SSascha Leib 45512993035SSascha Leib $r['title'] = ucfirst(implode(' ', $hlArr)); 45612993035SSascha Leib 45712993035SSascha Leib $r['hid'] = preg_replace('/[^\w\d\-]+/i', '_', strtolower($r['title'])); 45812993035SSascha Leib $r['type'] = 'ul'; // always ul! 45912993035SSascha Leib $r['level'] = 1; // always level 1 for now 46012993035SSascha Leib 46112993035SSascha Leib return $r; 46212993035SSascha Leib } 46312993035SSascha Leib 46412993035SSascha Leib private function dadaMakeParagraph($words, $totalWeight) { 46512993035SSascha Leib 46612993035SSascha Leib // how many words to generate? 46712993035SSascha Leib $sentenceCount = mt_rand(2, 5); 46812993035SSascha Leib 46912993035SSascha Leib $paragraph = array(); 47012993035SSascha Leib for ($i=0; $i<$sentenceCount; $i++) { 47112993035SSascha Leib array_push($paragraph, $this->dadaMakeSentence($words, $totalWeight)); 47212993035SSascha Leib } 47312993035SSascha Leib 47412993035SSascha Leib return "<p>\n" . implode(' ', $paragraph) . "\n</p>\n"; 47512993035SSascha Leib 47612993035SSascha Leib } 47712993035SSascha Leib 47812993035SSascha Leib private function dadaMakeSentence($words, $totalWeight) { 47912993035SSascha Leib 48012993035SSascha Leib // how many words to generate? 48112993035SSascha Leib $wordCount = mt_rand(4, 20); 48212993035SSascha Leib 48312993035SSascha Leib // generate the sentence: 48412993035SSascha Leib $sentence = array(); 48512993035SSascha Leib for ($i=0; $i<$wordCount; $i++) { 48612993035SSascha Leib array_push($sentence, $this->dadaSelectRandomWord($words, $totalWeight)); 48712993035SSascha Leib } 48812993035SSascha Leib 48912993035SSascha Leib return ucfirst(implode(' ', $sentence)) . '.'; 49012993035SSascha Leib 49112993035SSascha Leib } 49212993035SSascha Leib 49312993035SSascha Leib private function dadaSelectRandomWord($list, $totalWeight) { 49412993035SSascha Leib 49512993035SSascha Leib // get a random selection: 49612993035SSascha Leib $rand = mt_rand(0, $totalWeight); 49712993035SSascha Leib 49812993035SSascha Leib // match the selection to the weighted list: 49912993035SSascha Leib $cumulativeWeight = 0; 50012993035SSascha Leib for ($i=0; $i<count($list); $i++) { 50112993035SSascha Leib $cumulativeWeight += $list[$i][1]; 50212993035SSascha Leib if ($cumulativeWeight >= $rand) { 50312993035SSascha Leib return $list[$i][0]; 50412993035SSascha Leib } 50512993035SSascha Leib } 50612993035SSascha Leib return '***'; 507f5f4ca13SSascha Leib } 508f5f4ca13SSascha Leib 5096980370bSSascha Leib}