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 26447b8b4fSSascha Leib // populate the session id and type: 27447b8b4fSSascha Leib $this->setSessionInfo(); 28d49ab213SSascha Leib 29*393de67cSSascha Leib // temporary fix: save the method of the request: 30*393de67cSSascha Leib $this->tempMethod = $_SERVER['REQUEST_METHOD']; 31*393de67cSSascha Leib 325f2c1759SSascha Leib // insert header data into the page: 33d49ab213SSascha Leib if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') { 346980370bSSascha Leib $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); 35d49ab213SSascha Leib 36d49ab213SSascha Leib // Override the page rendering, if a captcha needs to be displayed: 37*393de67cSSascha Leib $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'insertCaptchaCode'); 38d49ab213SSascha Leib 39e56d7b71SSascha Leib } else if ($ACT == 'admin' && isset($_REQUEST['page']) && $_REQUEST['page'] == 'botmon') { 40e56d7b71SSascha Leib $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader'); 41e56d7b71SSascha Leib } 425f2c1759SSascha Leib 43d49ab213SSascha Leib // also show a captcha before the image preview 44d49ab213SSascha Leib $controller->register_hook('TPL_IMG_DISPLAY', 'BEFORE', $this, 'showImageCaptcha'); 45f5f4ca13SSascha Leib 465f2c1759SSascha Leib // write to the log after the page content was displayed: 475f2c1759SSascha Leib $controller->register_hook('TPL_CONTENT_DISPLAY', 'AFTER', $this, 'writeServerLog'); 485f2c1759SSascha Leib 496980370bSSascha Leib } 506980370bSSascha Leib 51b148c85eSSascha Leib /* session information */ 52f6a7ebc1SSascha Leib private $sessionId = null; 53f6a7ebc1SSascha Leib private $sessionType = ''; 54*393de67cSSascha Leib private $showCaptcha = 'X'; 55*393de67cSSascha Leib private $tempMethod = ''; 56b148c85eSSascha Leib 576980370bSSascha Leib /** 586980370bSSascha Leib * Inserts tracking code to the page header 59e56d7b71SSascha Leib * (only called on 'show' actions) 606980370bSSascha Leib * 616980370bSSascha Leib * @param Event $event event object by reference 626980370bSSascha Leib * @return void 636980370bSSascha Leib */ 646980370bSSascha Leib public function insertHeader(Event $event, $param) { 656980370bSSascha Leib 666980370bSSascha Leib global $INFO; 676980370bSSascha Leib 68b148c85eSSascha Leib 696980370bSSascha Leib // build the tracker code: 70d49ab213SSascha Leib $code = $this->getBMHeader(); 716980370bSSascha Leib 725f2c1759SSascha Leib // add the deferred script loader:: 73e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL; 74e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "const e=document.createElement('script');" . NL; 75e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.async=true;e.defer=true;" . NL; 76e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL; 77e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL; 78e56d7b71SSascha Leib $code .= DOKU_TAB . DOKU_TAB . "});"; 795f2c1759SSascha Leib $event->data['script'][] = ['_data' => $code]; 80451abfadSSascha Leib } 81451abfadSSascha Leib 82d49ab213SSascha Leib /* create the BM object code for insertion into a script element: */ 83d49ab213SSascha Leib private function getBMHeader() { 84d49ab213SSascha Leib 85d49ab213SSascha Leib // build the tracker code: 86d49ab213SSascha 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; 87d49ab213SSascha Leib 88d49ab213SSascha Leib // is there a user logged in? 89d49ab213SSascha Leib $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : ''); 90d49ab213SSascha Leib if ($username) { 91d49ab213SSascha Leib $code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL; 92d49ab213SSascha Leib } 93d49ab213SSascha Leib 94d49ab213SSascha Leib return $code; 95d49ab213SSascha Leib 96d49ab213SSascha Leib } 97d49ab213SSascha Leib 98451abfadSSascha Leib /** 99e56d7b71SSascha Leib * Inserts tracking code to the page header 100e56d7b71SSascha Leib * (only called on 'show' actions) 101e56d7b71SSascha Leib * 102e56d7b71SSascha Leib * @param Event $event event object by reference 103e56d7b71SSascha Leib * @return void 104e56d7b71SSascha Leib */ 105e56d7b71SSascha Leib public function insertAdminHeader(Event $event, $param) { 106e56d7b71SSascha Leib 107e56d7b71SSascha Leib $event->data['link'][] = ['rel' => 'stylesheet', 'href' => DOKU_BASE.'lib/plugins/botmon/admin.css', 'defer' => 'defer']; 1080edf1a56SSascha Leib $event->data['script'][] = ['src' => DOKU_BASE.'lib/plugins/botmon/admin.js', 'defer' => 'defer', '_data' => '']; 109e56d7b71SSascha Leib } 110e56d7b71SSascha Leib 111e56d7b71SSascha Leib /** 112451abfadSSascha Leib * Writes data to the server log. 113451abfadSSascha Leib * 114451abfadSSascha Leib * @return void 115451abfadSSascha Leib */ 1165f2c1759SSascha Leib public function writeServerLog(Event $event, $param) { 117451abfadSSascha Leib 118451abfadSSascha Leib global $conf; 119451abfadSSascha Leib global $INFO; 120091b5998SSascha Leib 1215f2c1759SSascha Leib // is there a user logged in? 1225f2c1759SSascha Leib $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) 1235f2c1759SSascha Leib ? $INFO['userinfo']['name'] : ''); 1245f2c1759SSascha Leib 125b2e3bd8bSSascha Leib // clean the page ID 126b2e3bd8bSSascha Leib $pageId = preg_replace('/[\x00-\x1F]/', "\u{FFFD}", $INFO['id'] ?? ''); 127b2e3bd8bSSascha Leib 128451abfadSSascha Leib // create the log array: 129cf9f7fe8SSascha Leib $logArr = Array( 130f5f4ca13SSascha Leib $_SERVER['REMOTE_ADDR'], /* remote IP */ 131b2e3bd8bSSascha Leib $pageId, /* page ID */ 132b148c85eSSascha Leib $this->sessionId, /* Session ID */ 133b148c85eSSascha Leib $this->sessionType, /* session ID type */ 1345f2c1759SSascha Leib $username, /* user name */ 1352f2bc93aSSascha Leib $_SERVER['HTTP_USER_AGENT'] ?? '', /* User agent */ 136451abfadSSascha Leib $_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */ 137451abfadSSascha Leib substr($conf['lang'],0,2), /* page language */ 138a93de874SSascha Leib implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ 1392c641262SSascha Leib $this->getCountryCode(), /* GeoIP country code */ 140*393de67cSSascha Leib $this->showCaptcha, /* show captcha? */ 141*393de67cSSascha Leib $this->tempMethod /* show captcha? */ 142d49ab213SSascha Leib ); 143cf9f7fe8SSascha Leib 144cf9f7fe8SSascha Leib //* create the log line */ 1454cddc661SSascha Leib $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */ 146cf9f7fe8SSascha Leib $logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */ 147cf9f7fe8SSascha Leib foreach ($logArr as $tab) { 148cf9f7fe8SSascha Leib $logline .= "\t" . $tab; 149cf9f7fe8SSascha Leib }; 150cf9f7fe8SSascha Leib 151cf9f7fe8SSascha Leib /* write the log line to the file */ 152cf9f7fe8SSascha Leib $logfile = fopen($filename, 'a'); 153cf9f7fe8SSascha Leib if (!$logfile) die(); 154cf9f7fe8SSascha Leib if (fwrite($logfile, $logline . "\n") === false) { 155cf9f7fe8SSascha Leib fclose($logfile); 156cf9f7fe8SSascha Leib die(); 1576980370bSSascha Leib } 158cf9f7fe8SSascha Leib 159cf9f7fe8SSascha Leib /* Done */ 160cf9f7fe8SSascha Leib fclose($logfile); 161cf9f7fe8SSascha Leib } 162b148c85eSSascha Leib 1635f2c1759SSascha Leib private function getCountryCode() { 1645f2c1759SSascha Leib 165f5f4ca13SSascha Leib $country = ( $_SERVER['REMOTE_ADDR'] == '127.0.0.1' ? 'local' : 'ZZ' ); // default if no geoip is available! 1665f2c1759SSascha Leib 1675f2c1759SSascha Leib $lib = $this->getConf('geoiplib'); /* which library to use? (can only be phpgeoip or disabled) */ 1685f2c1759SSascha Leib 1695f2c1759SSascha Leib try { 1705f2c1759SSascha Leib 1715f2c1759SSascha Leib // use GeoIP module? 1725f2c1759SSascha Leib if ($lib == 'phpgeoip' && extension_loaded('geoip') && geoip_db_avail(GEOIP_COUNTRY_EDITION)) { // Use PHP GeoIP module 1735f2c1759SSascha Leib $result = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']); 1745f2c1759SSascha Leib $country = ($result ? $result : $country); 1755f2c1759SSascha Leib } 1765f2c1759SSascha Leib } catch (Exception $e) { 1775f2c1759SSascha Leib Logger::error('BotMon Plugin: GeoIP Error', $e->getMessage()); 1785f2c1759SSascha Leib } 1795f2c1759SSascha Leib 1805f2c1759SSascha Leib return $country; 1815f2c1759SSascha Leib } 1825f2c1759SSascha Leib 183447b8b4fSSascha Leib private function setSessionInfo() { 184b148c85eSSascha Leib 185b148c85eSSascha Leib // what is the session identifier? 186b148c85eSSascha Leib if (isset($_SESSION)) { 187b148c85eSSascha Leib $sesKeys = array_keys($_SESSION); /* DokuWiki Session ID preferred */ 188b148c85eSSascha Leib foreach ($sesKeys as $key) { 189b148c85eSSascha Leib if (substr($key, 0, 2) == 'DW') { 190b148c85eSSascha Leib $this->sessionId = $key; 191b148c85eSSascha Leib $this->sessionType = 'dw'; 192b148c85eSSascha Leib return; 193b148c85eSSascha Leib } 194b148c85eSSascha Leib } 195b148c85eSSascha Leib } 196f6a7ebc1SSascha Leib if (!$this->sessionId) { /* no DokuWiki Session ID, try PHP session ID */ 197b148c85eSSascha Leib $this->sessionId = session_id(); 198b148c85eSSascha Leib $this->sessionType = 'php'; 199b148c85eSSascha Leib } 200f5f4ca13SSascha Leib if (!$this->sessionId) { /* no PHP session ID, try IP address */ 201f5f4ca13SSascha Leib $this->sessionId = $_SERVER['REMOTE_ADDR']; 202b148c85eSSascha Leib $this->sessionType = 'ip'; 203b148c85eSSascha Leib } 204447b8b4fSSascha Leib 205447b8b4fSSascha Leib if (!$this->sessionId) { /* if all fails, use random data */ 206447b8b4fSSascha Leib $this->sessionId = rand(100000000, 999999999); 207447b8b4fSSascha Leib $this->sessionType = 'rnd'; 208447b8b4fSSascha Leib } 209447b8b4fSSascha Leib 210b148c85eSSascha Leib } 211f5f4ca13SSascha Leib 212*393de67cSSascha Leib public function insertCaptchaCode(Event $event) { 213f5f4ca13SSascha Leib 21412993035SSascha Leib $useCaptcha = $this->getConf('useCaptcha'); 215f5f4ca13SSascha Leib 216d49ab213SSascha Leib $cCode = '-'; 217d49ab213SSascha Leib if ($useCaptcha !== 'disabled') { 218d49ab213SSascha Leib if ($this->captchaWhitelisted()) { 219d49ab213SSascha Leib $cCode = 'W'; // whitelisted 220d49ab213SSascha Leib } elseif ($this->hasCaptchaCookie()) { 221d49ab213SSascha Leib $cCode = 'N'; // user already has a cookie 222d49ab213SSascha Leib } else { 223d49ab213SSascha Leib $cCode = 'Y'; // show the captcha 2242c641262SSascha Leib 2252c641262SSascha Leib 22612993035SSascha Leib echo '<h1 class="sectionedit1">'; tpl_pagetitle(); echo "</h1>\n"; // always show the original page title 227f5f4ca13SSascha Leib $event->preventDefault(); // don't show normal content 22812993035SSascha Leib switch ($useCaptcha) { 229*393de67cSSascha Leib case 'loremipsum': 230*393de67cSSascha Leib $this->insertLoremIpsum(); // show dada filler instead of text 23112993035SSascha Leib break; 23212993035SSascha Leib case 'dada': 23312993035SSascha Leib $this->insertDadaFiller(); // show dada filler instead of text 23412993035SSascha Leib break; 23512993035SSascha Leib } 236f5f4ca13SSascha Leib $this->insertCaptchaLoader(); // and load the captcha 237f5f4ca13SSascha Leib } 238f5f4ca13SSascha Leib } 239d49ab213SSascha Leib $this->showCaptcha = $cCode; // store the captcha code for the logfile 240d49ab213SSascha Leib 241d49ab213SSascha Leib } 242f5f4ca13SSascha Leib 243d49ab213SSascha Leib public function showImageCaptcha(Event $event, $param) { 244d49ab213SSascha Leib 245d49ab213SSascha Leib $useCaptcha = $this->getConf('useCaptcha'); 246d49ab213SSascha Leib 247d49ab213SSascha Leib echo '<script>' . $this->getBMHeader($event, $param) . '</script>'; 248d49ab213SSascha Leib 249d49ab213SSascha Leib $cCode = '-'; 250d49ab213SSascha Leib if ($useCaptcha !== 'disabled') { 251d49ab213SSascha Leib if ($this->captchaWhitelisted()) { 252d49ab213SSascha Leib $cCode = 'W'; // whitelisted 253d49ab213SSascha Leib } 254d49ab213SSascha Leib elseif ($this->hasCaptchaCookie()) { 255d49ab213SSascha Leib $cCode = 'N'; // user already has a cookie 256d49ab213SSascha Leib } 257d49ab213SSascha Leib else { 258d49ab213SSascha Leib $cCode = 'Y'; // show the captcha 259d49ab213SSascha Leib 260d49ab213SSascha 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 261d49ab213SSascha Leib $event->preventDefault(); // don't show normal content 262d49ab213SSascha Leib 263d49ab213SSascha Leib // TODO Insert dummy image 264d49ab213SSascha Leib $this->insertCaptchaLoader(); // and load the captcha 265d49ab213SSascha Leib } 266d49ab213SSascha Leib }; 267d49ab213SSascha Leib 268d49ab213SSascha Leib $this->showCaptcha = $cCode; // store the captcha code for the logfile 269d49ab213SSascha Leib } 270d49ab213SSascha Leib 271d49ab213SSascha Leib private function hasCaptchaCookie() { 272f5f4ca13SSascha Leib 273cdc02cd4SSascha Leib $cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null; 274f5f4ca13SSascha Leib 275cdc02cd4SSascha Leib $today = substr((new DateTime())->format('c'), 0, 10); 27612993035SSascha Leib 277cdc02cd4SSascha Leib $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; 278cdc02cd4SSascha Leib $expected = hash('sha256', $raw); 27912993035SSascha Leib 280cdc02cd4SSascha Leib //echo '<ul><li>cookie: ' . $cookieVal . '</li><li>expected: ' . $expected . '</li><li>matches: ' .($cookieVal == $expected ? 'true' : 'false') . '</li></ul>'; 281cdc02cd4SSascha Leib 282d49ab213SSascha Leib return $cookieVal == $expected; 283f5f4ca13SSascha Leib } 284f5f4ca13SSascha Leib 2852c641262SSascha Leib // check if the visitor's IP is on a whitelist: 2862c641262SSascha Leib private function captchaWhitelisted() { 2872c641262SSascha Leib 2882c641262SSascha Leib // normalise IP address: 2892c641262SSascha Leib $ip = inet_pton($_SERVER['REMOTE_ADDR']); 2902c641262SSascha Leib 2912c641262SSascha Leib // find which file to open: 2922c641262SSascha Leib $prefixes = ['user', 'default']; 2932c641262SSascha Leib foreach ($prefixes as $pre) { 2942c641262SSascha Leib $filename = __DIR__ .'/config/' . $pre . '-whitelist.txt'; 2952c641262SSascha Leib if (file_exists($filename)) { 2962c641262SSascha Leib break; 2972c641262SSascha Leib } 2982c641262SSascha Leib } 2992c641262SSascha Leib 3002c641262SSascha Leib if (file_exists($filename)) { 3012c641262SSascha Leib $lines = file($filename, FILE_SKIP_EMPTY_LINES); 3022c641262SSascha Leib foreach ($lines as $line) { 3032c641262SSascha Leib if (trim($line) !== '' && !str_starts_with($line, '#')) { 3042c641262SSascha Leib $col = explode("\t", $line); 3052c641262SSascha Leib if (count($col) >= 2) { 3062c641262SSascha Leib $from = inet_pton($col[0]); 3072c641262SSascha Leib $to = inet_pton($col[1]); 3082c641262SSascha Leib 3092c641262SSascha Leib if ($ip >= $from && $ip <= $to) { 310d49ab213SSascha Leib return true; /* IP whitelisted */ 3112c641262SSascha Leib } 3122c641262SSascha Leib } 3132c641262SSascha Leib } 3142c641262SSascha Leib } 3152c641262SSascha Leib } 316d49ab213SSascha Leib return false; /* IP not found in whitelist */ 3172c641262SSascha Leib } 3182c641262SSascha Leib 319f5f4ca13SSascha Leib private function insertCaptchaLoader() { 320620d9253SSascha Leib 32112993035SSascha Leib echo '<script>' . NL; 32212993035SSascha Leib 32312993035SSascha Leib // add the deferred script loader:: 32412993035SSascha Leib echo DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL; 32512993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "const cj=document.createElement('script');" . NL; 32612993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "cj.async=true;cj.defer=true;cj.type='text/javascript';" . NL; 32712993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "cj.src='".DOKU_BASE."lib/plugins/botmon/captcha.js';" . NL; 32812993035SSascha Leib echo DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(cj);" . NL; 32912993035SSascha Leib echo DOKU_TAB . "});"; 330620d9253SSascha Leib 331620d9253SSascha Leib // add the locales for the captcha: 332620d9253SSascha Leib echo DOKU_TAB . '$BMLocales = {' . NL; 333620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgTitle": ' . json_encode($this->getLang('bm_dlgTitle')) . ',' . NL; 334620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgSubtitle": ' . json_encode($this->getLang('bm_dlgSubtitle')) . ',' . NL; 335620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgConfirm": ' . json_encode($this->getLang('bm_dlgConfirm')) . ',' . NL; 336620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgChecking": ' . json_encode($this->getLang('bm_dlgChecking')) . ',' . NL; 337620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgLoading": ' . json_encode($this->getLang('bm_dlgLoading')) . ',' . NL; 338620d9253SSascha Leib echo DOKU_TAB . DOKU_TAB . '"dlgError": ' . json_encode($this->getLang('bm_dlgError')) . ',' . NL; 339620d9253SSascha Leib echo DOKU_TAB . '};' . NL; 340620d9253SSascha Leib 34112993035SSascha Leib echo '</script>' . NL; 342f5f4ca13SSascha Leib 343f5f4ca13SSascha Leib } 344f5f4ca13SSascha Leib 34512993035SSascha Leib // inserts a blank box to ensure there is enough space for the captcha: 346*393de67cSSascha Leib private function insertLoremIpsum() { 34712993035SSascha Leib 348*393de67cSSascha Leib echo '<div class="level1">' . NL; 349*393de67cSSascha Leib echo '<p>' . NL . 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'. NL . '</p>' . NL; 350*393de67cSSascha Leib echo '<p>' . NL . 'At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga.'. NL . '</p>' . NL; 351*393de67cSSascha Leib echo '</div>' . NL; 352*393de67cSSascha Leib 35312993035SSascha Leib } 35412993035SSascha Leib 35512993035SSascha Leib /* Generates a few paragraphs of Dada text to show instead of the article content */ 356f5f4ca13SSascha Leib private function insertDadaFiller() { 357f5f4ca13SSascha Leib 35812993035SSascha Leib global $conf; 35912993035SSascha Leib global $TOC; 36012993035SSascha Leib global $ID; 361f5f4ca13SSascha Leib 36212993035SSascha Leib // list of languages to search for the wordlist 36312993035SSascha Leib $langs = array_unique([$conf['lang'], 'la']); 364f5f4ca13SSascha Leib 36512993035SSascha Leib // find path to the first available wordlist: 36612993035SSascha Leib foreach ($langs as $lang) { 36712993035SSascha Leib $filename = __DIR__ .'/lang/' . $lang . '/wordlist.txt'; /* language-specific wordlist */ 36812993035SSascha Leib if (file_exists($filename)) { 36912993035SSascha Leib break; 37012993035SSascha Leib } 37112993035SSascha Leib } 372f5f4ca13SSascha Leib 37312993035SSascha Leib // load the wordlist file: 37412993035SSascha Leib if (file_exists($filename)) { 37512993035SSascha Leib $words = array(); 37612993035SSascha Leib $totalWeight = 0; 37712993035SSascha Leib $lines = file($filename, FILE_SKIP_EMPTY_LINES); 37812993035SSascha Leib foreach ($lines as $line) { 37912993035SSascha Leib $arr = explode("\t", $line); 38012993035SSascha Leib $arr[1] = ( count($arr) > 1 ? (int) trim($arr[1]) : 1 ); 38112993035SSascha Leib $totalWeight += (int) $arr[1]; 38212993035SSascha Leib array_push($words, $arr); 38312993035SSascha Leib } 38412993035SSascha Leib } else { 38512993035SSascha Leib echo '<script> console.log("Can’t generate filler text: wordlist file not found!"); </script>'; 38612993035SSascha Leib return; 38712993035SSascha Leib } 388f5f4ca13SSascha Leib 38912993035SSascha Leib // If a TOC exists, use it for the headlines: 39012993035SSascha Leib if(is_array($TOC)) { 39112993035SSascha Leib $toc = $TOC; 39212993035SSascha Leib } else { 39312993035SSascha Leib $meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE); 39412993035SSascha Leib //$tocok = (isset($meta['internal']['toc']) ? $meta['internal']['toc'] : $tocok = true); 39512993035SSascha Leib $toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null; 39612993035SSascha Leib } 39712993035SSascha Leib if (!$toc) { // no TOC, generate my own: 39812993035SSascha Leib $hlCount = mt_rand(0, (int) $conf['tocminheads']); 39912993035SSascha Leib $toc = array(); 40012993035SSascha Leib for ($i=0; $i<$hlCount; $i++) { 40112993035SSascha Leib array_push($toc, $this->dadaMakeHeadline($words, $totalWeight)); // $toc 40212993035SSascha Leib } 40312993035SSascha Leib } 40412993035SSascha Leib 40512993035SSascha Leib // if H1 heading is not in the TOC, add a chappeau section: 40612993035SSascha Leib $chapeauCount = mt_rand(1, 3); 40712993035SSascha Leib if ((int) $conf['toptoclevel'] > 1) { 40812993035SSascha Leib echo "<div class=\"level1\">\n"; 40912993035SSascha Leib for ($i=0; $i<$chapeauCount; $i++) { 41012993035SSascha Leib echo $this->dadaMakeParagraph($words, $totalWeight); 41112993035SSascha Leib } 41212993035SSascha Leib echo "</div>\n"; 41312993035SSascha Leib } 41412993035SSascha Leib 41512993035SSascha Leib // text sections for each sub-headline: 41612993035SSascha Leib foreach ($toc as $hl) { 41712993035SSascha Leib echo $this->dadaMakeSection($words, $totalWeight, $hl); 41812993035SSascha Leib } 41912993035SSascha Leib } 42012993035SSascha Leib 42112993035SSascha Leib private function dadaMakeSection($words, $totalWeight, $hl) { 42212993035SSascha Leib 42312993035SSascha Leib global $conf; 42412993035SSascha Leib 42512993035SSascha Leib // how many paragraphs? 42612993035SSascha Leib $paragraphCount = mt_rand(1, 4); 42712993035SSascha Leib 42812993035SSascha Leib // section level 42912993035SSascha Leib $topTocLevel = (int) $conf['toptoclevel']; 43012993035SSascha Leib $secLevel = $hl['level'] + 1;; 43112993035SSascha Leib 43212993035SSascha Leib // return value: 43312993035SSascha Leib $sec = ""; 43412993035SSascha Leib 43512993035SSascha Leib // make a headline: 43612993035SSascha Leib if ($topTocLevel > 1 || $secLevel > 1) { 43712993035SSascha Leib $sec .= "<h{$secLevel} id=\"{$hl['hid']}\">{$hl['title']}</h{$secLevel}>\n"; 43812993035SSascha Leib } 43912993035SSascha Leib 44012993035SSascha Leib // add the paragraphs: 44112993035SSascha Leib $sec .= "<div class=\"level{$secLevel}\">\n"; 44212993035SSascha Leib for ($i=0; $i<$paragraphCount; $i++) { 44312993035SSascha Leib $sec .= $this->dadaMakeParagraph($words, $totalWeight); 44412993035SSascha Leib } 44512993035SSascha Leib $sec .= "</div>\n"; 44612993035SSascha Leib 44712993035SSascha Leib return $sec; 44812993035SSascha Leib } 44912993035SSascha Leib 45012993035SSascha Leib private function dadaMakeHeadline($words, $totalWeight) { 45112993035SSascha Leib 45212993035SSascha Leib // how many words to generate? 45312993035SSascha Leib $wordCount = mt_rand(2, 5); 45412993035SSascha Leib 45512993035SSascha Leib // function returns an array: 45612993035SSascha Leib $r = Array(); 45712993035SSascha Leib 45812993035SSascha Leib // generate the headline: 45912993035SSascha Leib $hlArr = array(); 46012993035SSascha Leib for ($i=0; $i<$wordCount; $i++) { 46112993035SSascha Leib array_push($hlArr, $this->dadaSelectRandomWord($words, $totalWeight)); 46212993035SSascha Leib } 46312993035SSascha Leib 46412993035SSascha Leib $r['title'] = ucfirst(implode(' ', $hlArr)); 46512993035SSascha Leib 46612993035SSascha Leib $r['hid'] = preg_replace('/[^\w\d\-]+/i', '_', strtolower($r['title'])); 46712993035SSascha Leib $r['type'] = 'ul'; // always ul! 46812993035SSascha Leib $r['level'] = 1; // always level 1 for now 46912993035SSascha Leib 47012993035SSascha Leib return $r; 47112993035SSascha Leib } 47212993035SSascha Leib 47312993035SSascha Leib private function dadaMakeParagraph($words, $totalWeight) { 47412993035SSascha Leib 47512993035SSascha Leib // how many words to generate? 47612993035SSascha Leib $sentenceCount = mt_rand(2, 5); 47712993035SSascha Leib 47812993035SSascha Leib $paragraph = array(); 47912993035SSascha Leib for ($i=0; $i<$sentenceCount; $i++) { 48012993035SSascha Leib array_push($paragraph, $this->dadaMakeSentence($words, $totalWeight)); 48112993035SSascha Leib } 48212993035SSascha Leib 48312993035SSascha Leib return "<p>\n" . implode(' ', $paragraph) . "\n</p>\n"; 48412993035SSascha Leib 48512993035SSascha Leib } 48612993035SSascha Leib 48712993035SSascha Leib private function dadaMakeSentence($words, $totalWeight) { 48812993035SSascha Leib 48912993035SSascha Leib // how many words to generate? 49012993035SSascha Leib $wordCount = mt_rand(4, 20); 49112993035SSascha Leib 49212993035SSascha Leib // generate the sentence: 49312993035SSascha Leib $sentence = array(); 49412993035SSascha Leib for ($i=0; $i<$wordCount; $i++) { 49512993035SSascha Leib array_push($sentence, $this->dadaSelectRandomWord($words, $totalWeight)); 49612993035SSascha Leib } 49712993035SSascha Leib 49812993035SSascha Leib return ucfirst(implode(' ', $sentence)) . '.'; 49912993035SSascha Leib 50012993035SSascha Leib } 50112993035SSascha Leib 50212993035SSascha Leib private function dadaSelectRandomWord($list, $totalWeight) { 50312993035SSascha Leib 50412993035SSascha Leib // get a random selection: 50512993035SSascha Leib $rand = mt_rand(0, $totalWeight); 50612993035SSascha Leib 50712993035SSascha Leib // match the selection to the weighted list: 50812993035SSascha Leib $cumulativeWeight = 0; 50912993035SSascha Leib for ($i=0; $i<count($list); $i++) { 51012993035SSascha Leib $cumulativeWeight += $list[$i][1]; 51112993035SSascha Leib if ($cumulativeWeight >= $rand) { 51212993035SSascha Leib return $list[$i][0]; 51312993035SSascha Leib } 51412993035SSascha Leib } 51512993035SSascha Leib return '***'; 516f5f4ca13SSascha Leib } 517f5f4ca13SSascha Leib 5186980370bSSascha Leib}