From 96495504631d6a4d466b93a835d7d66853e2e12b Mon Sep 17 00:00:00 2001 From: Michael Save Date: Fri, 13 Apr 2012 02:11:41 +1000 Subject: start on mod interface rewrite --- mod-old.php | 3128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3128 insertions(+) create mode 100644 mod-old.php (limited to 'mod-old.php') diff --git a/mod-old.php b/mod-old.php new file mode 100644 index 00000000..31273e39 --- /dev/null +++ b/mod-old.php @@ -0,0 +1,3128 @@ + str_replace('%s', '(\w{1,8})', preg_quote($config['board_path'], '/')), + 'page' => str_replace('%d', '(\d+)', preg_quote($config['file_page'], '/')), + 'img' => preg_quote($config['dir']['img'], '/'), + 'thumb' => preg_quote($config['dir']['thumb'], '/'), + 'res' => preg_quote($config['dir']['res'], '/'), + 'index' => preg_quote($config['file_index'], '/') + ); + + if(preg_match('/^\/?$/', $query)) { + // Dashboard + $fieldset = array( + 'Boards' => '', + 'Noticeboard' => '', + 'Administration' => '', + 'Themes' => '', + 'Search' => '', + 'Update' => '', + 'Logout' => '' + ); + + // Boards + $fieldset['Boards'] .= ulBoards(); + + if(hasPermission($config['mod']['noticeboard'])) { + if(!$config['cache']['enabled'] || !($fieldset['Noticeboard'] = cache::get('noticeboard_preview'))) { + $query = prepare("SELECT `noticeboard`.*, `username` FROM `noticeboard` LEFT JOIN `mods` ON `mods`.`id` = `mod` ORDER BY `id` DESC LIMIT :limit"); + $query->bindValue(':limit', $config['mod']['noticeboard_dashboard'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + $fieldset['Noticeboard'] .= '
  • '; + + $_body = ''; + while($notice = $query->fetch()) { + $_body .= '
  • ' . + ($notice['subject'] ? + $notice['subject'] + : + '' . _('no subject') . '' + ) . + ' — by ' . + (isset($notice['username']) ? + utf8tohtml($notice['username']) + : '???') . + ' at ' . + strftime($config['post_date'], $notice['time']) . + '
  • '; + } + if(!empty($_body)) { + $fieldset['Noticeboard'] .= '
  • '; + } + if($config['cache']['enabled']) + cache::set('noticeboard_preview', $fieldset['Noticeboard']); + } + + $fieldset['Noticeboard'] .= '' . _('View all entries') . '
  • '; + + $query = prepare("SELECT COUNT(*) AS `count` FROM `pms` WHERE `to` = :id AND `unread` = 1"); + $query->bindValue(':id', $mod['id']); + $query->execute() or error(db_error($query)); + $count = $query->fetch(); + $count = $count['count']; + + $fieldset['Noticeboard'] .= '
  • ' . _('PM Inbox') . + ($count > 0 + ? + ' (' . $count . ' unread)' + : '') . + '
  • '; + + $fieldset['Noticeboard'] .= '
  • ' . _('News') . '
  • '; + } + + + if(hasPermission($config['mod']['reports'])) { + $fieldset['Administration'] .= '
  • ' . _('Report queue') . '
  • '; + } + if(hasPermission($config['mod']['view_banlist'])) { + $fieldset['Administration'] .= '
  • ' . _('Ban list') . '
  • '; + } + if(hasPermission($config['mod']['manageusers'])) { + $fieldset['Administration'] .= '
  • ' . _('Manage users') . '
  • '; + } elseif(hasPermission($config['mod']['change_password'])) { + $fieldset['Administration'] .= '
  • ' . _('Change own password') . '
  • '; + } + if(hasPermission($config['mod']['modlog'])) { + $fieldset['Administration'] .= '
  • ' . _('Moderation log') . '
  • '; + } + if(hasPermission($config['mod']['rebuild'])) { + $fieldset['Administration'] .= '
  • ' . _('Rebuild static files') . '
  • '; + } + if(hasPermission($config['mod']['rebuild']) && $config['cache']['enabled']) { + $fieldset['Administration'] .= '
  • ' . _('Clear cache') . '
  • '; + } + if(hasPermission($config['mod']['show_config'])) { + $fieldset['Administration'] .= '
  • ' . _('Show configuration') . '
  • '; + } + + if(hasPermission($config['mod']['themes'])) { + $fieldset['Themes'] .= '
  • ' . _('Manage themes') . '
  • '; + } + + if(hasPermission($config['mod']['search'])) { + $fieldset['Search'] .= '
  • ' . + ' ' . + '' . + '' . + '
    ' . + '

    ' . _('(Search is case-insensitive, and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.)') . '

    ' . + '
  • '; + } + + if($mod['type'] >= ADMIN && $config['check_updates']) { + if(!$config['version']) + error(_('Could not find current version! (Check .installed)')); + if(isset($_COOKIE['update'])) { + $latest = unserialize($_COOKIE['update']); + } else { + $ctx = stream_context_create(array( + 'http' => array( + 'timeout' => 3 + ) + ) + ); + + if($code = @file_get_contents('http://tinyboard.org/version.txt', 0, $ctx)) { + eval($code); + if(preg_match('/v(\d+)\.(\d)\.(\d+)(-dev.+)?$/', $config['version'], $m)) { + $current = array( + 'massive' => (int)$m[1], + 'major' => (int)$m[2], + 'minor' => (int)$m[3] + ); + if(isset($m[4])) { + // Development versions are always ahead in the versioning numbers + $current['minor'] --; + } + } + // Check if it's newer + if( $latest['massive'] > $current['massive'] || + $latest['major'] > $current['major'] || + ($latest['massive'] == $current['massive'] && + $latest['major'] == $current['major'] && + $latest['minor'] > $current['minor'] + )) { + $latest = $latest; + } else $latest = false; + } else { + // Couldn't get latest version + // TODO: Display some sort of warning message + $latest = false; + } + + + setcookie('update', serialize($latest), time() + $config['check_updates_time'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, false, true); + } + + if($latest) { + $fieldset['Update'] .= + '
  • A newer version of Tinyboard (v' . + $latest['massive'] . '.' . + $latest['major'] . '.' . + $latest['minor'] . + ') is available! See http://tinyboard.org/ for upgrade instructions.
  • '; + } + } + + $fieldset['Logout'] .= '
  • ' . _('Logout') . '
  • '; + + // TODO: Statistics, etc, in the dashboard. + + $body = ''; + foreach($fieldset as $title => $data) { + if($data) + $body .= '
    ' . _($title) . '
    '; + } + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('Dashboard'), + 'body'=>$body, + '__mod'=>true + )); + } elseif(preg_match('/^\/logout$/', $query)) { + destroyCookies(); + + header('Location: ?/', true, $config['redirect_http']); + } elseif(preg_match('/^\/confirm\/(.+)$/', $query, $matches)) { + $uri = &$matches[1]; + + $body = '

    ' . + 'Are you sure you want to do that?' . + 'We were unable to serve a confirmation dialog for ' . + '?/' . utf8tohtml($uri) . '' . + ', probably due to Javascript being disabled.' . + '

    ' . + '

    Confirm

    '; + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'Confirm', + 'body'=>$body, + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/upgrade$/', $query)) { + if($mod['type'] != ADMIN) + error($config['error']['noaccess']); + + if(is_dir('.git')) { + // use git instead + + $body = '

    git pull

    '; + $body .= '

    ' . str_replace("\n", '
    ', shell_exec('git pull')) . '

    '; + $body .= '
    '; + echo Element('page.html', array( + 'config' => $config, + 'title' => 'Upgraded', + 'body' => $body + )); + exit; + } + + if(!extension_loaded('curl')) + error('You need the cURL PHP extension to do that.'); + + if(!class_exists('ZipArchive')) + error('You need the ZipArchive class to do that.'); + + if(!in_array('zip', stream_get_wrappers())) + error('You need the zip:// stream wrapper to do that.'); + + $temp = tempnam($config['tmp'], 'tinyboard'); + + $fp = fopen($temp, 'w+'); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, 'https://github.com/savetheinternet/Tinyboard/zipball/master'); + curl_setopt($curl, CURLOPT_FAILONERROR, true); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_TIMEOUT, 45); + curl_setopt($curl, CURLOPT_FILE, $fp); + curl_setopt($curl, CURLOPT_WRITEHEADER, $header = tmpfile()); + curl_setopt($curl, CURLOPT_HEADER, true); + + curl_exec($curl); + + if(curl_errno($curl)) + error('Failed downloading newest revision: ' . curl_error($curl)); + + curl_close($curl); + + fflush($fp); + fclose($fp); + + fseek($header, 0); + $version = false; + while($line = fgets($header)) { + if(preg_match('/^Content-Disposition: attachment; filename=savetheinternet-Tinyboard-(.+)\.zip\s?$/', $line, $m)) { + $version = $m[1]; + } + } + fclose($header); + + $zip = new ZipArchive(); + if(!$zip->open($temp)) + error('Could not make sense of the ZIP archive.'); + + $version = preg_replace('/^savetheinternet-Tinyboard-(\w+)\//', '$1', $dir = $zip->getNameIndex(0)); + + $errors = array(); + for($i = 1; $i < $zip->numFiles; $i++) { + $filename = str_replace($dir, '', $zip->getNameIndex($i)); + + if($filename == 'inc/instance-config.php') + continue; // don't override config + + // are we able to write here? + if(!((file_exists($filename) && is_writable($filename)) || (!file_exists($filename) && is_writable(dirname($filename))))) { + // nope + $errors[] = 'Cannot write to ' . $filename . '!'; + } + } + + $zip->close(); + + if($errors) { + $body = '

    Error(s) upgrading

    Tinyboard can not self-upgrade until the following is fixed:

    Please fix the above errors and refresh to try again.

    '; + + unlink($temp); + + echo Element('page.html', array( + 'config' => $config, + 'title' => 'Error(s) upgrading', + 'body' => $body + )); + exit; + } + + // For some reason, reading the ZIP entries in PHP doesn't seem to work very well. + // Use shell instead. + shell_exec('TEMP_DIR=$(mktemp -d); unzip -q ' . escapeshellarg($temp) . ' -d $TEMP_DIR -x "' . escapeshellarg($dir) . 'inc/instance-config.php"; mv -v $TEMP_DIR/' . escapeshellarg($dir) . '* "' . getcwd() . '"; rm -rf $TEMP_DIR'); + + unlink($temp); + + echo Element('page.html', array( + 'config' => $config, + 'title' => 'Upgraded', + 'body' => '

    Upgrading seems to have gone okay. You are now at revision ' . $version . '.

    ' + )); + } elseif(preg_match('/^\/log(\/(\d+))?$/', $query, $match)) { + if(!hasPermission($config['mod']['modlog'])) error($config['error']['noaccess']); + + $page = isset($match[2]) ? $match[2] : 1; + + $boards = array(); + $_boards = listBoards(); + foreach($_boards as &$_b) { + $boards[$_b['id']] = $_b['uri']; + } + + $query = prepare("SELECT `mod` as `id`, `username`, `ip`, `board`, `time`, `text` FROM `modlogs` LEFT JOIN `mods` ON `mod` = `mods`.`id` ORDER BY `time` DESC LIMIT :offset, :limit"); + $query->bindValue(':limit', $config['mod']['modlog_page'], PDO::PARAM_INT); + $query->bindValue(':offset', ($page - 1) * $config['mod']['modlog_page'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if(!$query->rowCount()) { + $body = '

    (Nothing to display.)

    '; + } else { + $body = '' . + '' . + '' . + '' . + '' . + '' . + '' . + ''; + while($log = $query->fetch()) { + $log_id = 'log_' . md5($log['text']); + + if($config['cache']['enabled'] && $_log = cache::get($log_id)) + $log['text'] = $_log; + else { + + $log['text'] = utf8tohtml($log['text']); + $log['text'] = preg_replace('/(\d+\.\d+\.\d+\.\d+)/', '$1', $log['text']); + + if(isset($boards[$log['board']])) { + if(preg_match('/post #(\d+)/', $log['text'], $match)) { + $post_query = prepare(sprintf("SELECT `thread` FROM `posts_%s` WHERE `id` = :id", $boards[$log['board']])); + $post_query->bindValue(':id', $match[1], PDO::PARAM_INT); + $post_query->execute() or error(db_error($query)); + + if($post = $post_query->fetch()) { + $log['text'] = preg_replace('/post (#(\d+))/', + 'post $1', $log['text']); + } else { + $log['text'] = preg_replace('/post (#(\d+))/', 'post $1', $log['text']); + } + + if($config['cache']['enabled']) + cache::set($log_id, $log['text']); + } + } + } + + $body .= '' . + '' . + '' . + '' . + '' + : '-') . + '' . + ''; + } + + $body .= '
    ' . _('User') . '' . _('IP address') . '' . _('Ago') . '' . _('Board') . '' . _('Action') . '
    ' . + ($log['username'] ? + '' . $log['username'] . '' + : '' . ($log['id'] < 0 ? 'system' : 'deleted?') . '') . + '' . ($log['id'] < 0 ? '–' : '' . $log['ip'] . '') . '' . ago($log['time']) . '' . + ($log['board'] ? + '' . sprintf($config['board_abbreviation'], $log['board']) . '' . $log['text'] . '
    '; + + $query = prepare("SELECT COUNT(*) AS `count` FROM `modlogs`"); + $query->execute() or error(db_error($query)); + $count = $query->fetch(); + + $body .= '

    '; + for($x = 0; $x < $count['count'] / $config['mod']['modlog_page']; $x ++) { + $body .= '[' . ($x + 1) . '] '; + } + $body .= '

    '; + } + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('Moderation log'), + 'body'=>$body, + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/themes\/none$/', $query, $match)) { + if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); + + // Clearsettings + query("TRUNCATE TABLE `theme_settings`") or error(db_error()); + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'No theme', + 'body'=>'

    Successfully uninstalled all themes.

    ' . + '

    Go back to themes.

    ', + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/themes\/([\w\-]+)\/rebuild$/', $query, $match)) { + if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); + + rebuildTheme($match[1], 'all'); + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'Rebuilt', + 'body'=>'

    Successfully rebuilt the ' . $match[1] . ' theme.

    ' . + '

    Go back to themes.

    ', + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/themes\/(\w+)\/uninstall$/', $query, $match)) { + if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); + + $query = prepare("DELETE FROM `theme_settings` WHERE `theme` = :theme"); + $query->bindValue(':theme', $match[1]); + $query->execute() or error(db_error($query)); + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'Uninstalled', + 'body'=>'

    Successfully uninstalled the ' . $match[1] . ' theme.

    ' . + '

    Go back to themes.

    ', + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/themes(\/([\w\-]+))?$/', $query, $match)) { + if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); + + if(!is_dir($config['dir']['themes'])) + error(_('Themes directory doesn\'t exist!')); + if(!$dir = opendir($config['dir']['themes'])) + error(_('Cannot open themes directory; check permissions.')); + + if(isset($match[2])) { + $_theme = &$match[2]; + + if(!$theme = loadThemeConfig($_theme)) { + error($config['error']['invalidtheme']); + } + + if(isset($_POST['install'])) { + // Check if everything is submitted + foreach($theme['config'] as &$c) { + if(!isset($_POST[$c['name']]) && $c['type'] != 'checkbox') + error(sprintf($config['error']['required'], $c['title'])); + } + + // Clear previous settings + $query = prepare("DELETE FROM `theme_settings` WHERE `theme` = :theme"); + $query->bindValue(':theme', $_theme); + $query->execute() or error(db_error($query)); + + foreach($theme['config'] as &$c) { + $query = prepare("INSERT INTO `theme_settings` VALUES(:theme, :name, :value)"); + $query->bindValue(':theme', $_theme); + $query->bindValue(':name', $c['name']); + $query->bindValue(':value', $_POST[$c['name']]); + $query->execute() or error(db_error($query)); + } + + $query = prepare("INSERT INTO `theme_settings` VALUES(:theme, NULL, NULL)"); + $query->bindValue(':theme', $_theme); + $query->execute() or error(db_error($query)); + + $result = true; + $body = ''; + if(isset($theme['install_callback'])) { + $ret = $theme['install_callback'](themeSettings($_theme)); + if($ret && !empty($ret)) { + if(is_array($ret) && count($ret) == 2) { + $result = $ret[0]; + $ret = $ret[1]; + } + $body .= '
    ' . $ret . '
    '; + } + } + + if($result) { + $body .= '

    Successfully installed and built theme.

    '; + } else { + // install failed + $query = prepare("DELETE FROM `theme_settings` WHERE `theme` = :theme"); + $query->bindValue(':theme', $_theme); + $query->execute() or error(db_error($query)); + } + + $body .= '

    Go back to themes.

    '; + + // Build themes + rebuildThemes('all'); + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>($result ? 'Installed "' . utf8tohtml($theme['name']) . '"' : 'Installation failed!'), + 'body'=>$body, + 'mod'=>true + ) + ); + } else { + $body = '
    '; + + if(!isset($theme['config']) || empty($theme['config'])) { + $body .= '

    (No configuration required.)

    '; + } else { + $settings = themeSettings($_theme); + + $body .= ''; + foreach($theme['config'] as &$c) { + $body .= ''; + } + $body .= '
    ' . $c['title'] . ''; + switch($c['type']) { + case 'text': + default: + $body .= ''; + } + if(isset($c['comment'])) + $body .= ' ' . $c['comment'] . ''; + $body .= '
    '; + } + + $body .= '

    '; + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'Installing "' . utf8tohtml($theme['name']) . '"', + 'body'=>$body, + 'mod'=>true + ) + ); + } + } else { + + $themes_in_use = array(); + $query = query("SELECT `theme` FROM `theme_settings` WHERE `name` IS NULL AND `value` IS NULL") or error(db_error()); + while($theme = $query->fetch()) { + $themes_in_use[$theme['theme']] = true; + } + + // Scan directory for themes + $themes = array(); + while($file = readdir($dir)) { + if($file[0] != '.' && is_dir($config['dir']['themes'] . '/' . $file)) { + $themes[] = $file; + } + } + closedir($dir); + + $body = ''; + if(empty($themes)) { + $body = '

    (No themes installed.)

    '; + } else { + $body .= ''; + foreach($themes as &$_theme) { + $theme = loadThemeConfig($_theme); + + markup($theme['description']); + + $body .= '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + ''; + } + $body .= '
    ' . _('Name') . '' . utf8tohtml($theme['name']) . '
    ' . _('Version') . '' . utf8tohtml($theme['version']) . '
    ' . _('Description') . '' . $theme['description'] . '
    ' . _('Thumbnail') . '
    ' . _('Actions') . '

    '; + } + + if(!empty($themes_in_use)) + $body .= '

    ' . _('Uninstall all themes.') . '

    '; + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('Manage themes'), + 'body'=>$body, + 'mod'=>true + ) + ); + } + } elseif(preg_match('/^\/noticeboard\/delete\/(\d+)$/', $query, $match)) { + if(!hasPermission($config['mod']['noticeboard_delete'])) error($config['error']['noaccess']); + + $query = prepare("DELETE FROM `noticeboard` WHERE `id` = :id"); + $query->bindValue(':id', $match[1], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if($config['cache']['enabled']) + cache::delete('noticeboard_preview'); + + header('Location: ?/noticeboard', true, $config['redirect_http']); + } elseif(preg_match('/^\/noticeboard$/', $query)) { + if(!hasPermission($config['mod']['noticeboard'])) error($config['error']['noaccess']); + + $body = ''; + + if(hasPermission($config['mod']['noticeboard_post']) && isset($_POST['subject']) && isset($_POST['body']) && !empty($_POST['body'])) { + $query = prepare("INSERT INTO `noticeboard` VALUES (NULL, :mod, :time, :subject, :body)"); + $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); + $query->bindvalue(':time', time(), PDO::PARAM_INT); + $query->bindValue(':subject', utf8tohtml($_POST['subject'])); + + markup($_POST['body']); + $query->bindValue(':body', $_POST['body']); + $query->execute() or error(db_error($query)); + + if($config['cache']['enabled']) + cache::delete('noticeboard_preview'); + + header('Location: ?/noticeboard#' . $pdo->lastInsertId(), true, $config['redirect_http']); + } else { + + if(hasPermission($config['mod']['noticeboard_post'])) { + $body .= '
    New post
    ' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '
    ' . $mod['username'] . '
    ' . _('Subject') . '
    ' . _('Body') . '
    ' . + '
    '; + } + + $query = prepare("SELECT `noticeboard`.*, `username` FROM `noticeboard` LEFT JOIN `mods` ON `mods`.`id` = `mod` ORDER BY `id` DESC LIMIT :limit"); + $query->bindValue(':limit', $config['mod']['noticeboard_display'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + while($notice = $query->fetch()) { + $body .= '
    ' . + (hasPermission($config['mod']['noticeboard_delete']) ? + '[delete]' + : '') . + '

    ' . + ($notice['subject'] ? + $notice['subject'] + : + '' . _('no subject') . '' + ) . + ' — by ' . + (isset($notice['username']) ? + utf8tohtml($notice['username']) + : + '???' + ) . + ' at ' . + strftime($config['post_date'], $notice['time']) . + '

    ' . $notice['body'] . '

    '; + } + + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('Noticeboard'), + 'body'=>$body, + 'mod'=>true + ) + ); + } + } elseif(preg_match('/^\/news\/delete\/(\d+)$/', $query, $match)) { + if(!hasPermission($config['mod']['noticeboard_delete'])) error($config['error']['noaccess']); + + $query = prepare("DELETE FROM `news` WHERE `id` = :id"); + $query->bindValue(':id', $match[1], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + rebuildThemes('news'); + + header('Location: ?/news', true, $config['redirect_http']); + } elseif(preg_match('/^\/news$/', $query)) { + $body = ''; + + if(hasPermission($config['mod']['news'])) { + if(isset($_POST['subject']) && isset($_POST['body']) && !empty($_POST['body'])) { + $query = prepare("INSERT INTO `news` VALUES (NULL, :name, :time, :subject, :body)"); + + if(isset($_POST['name']) && hasPermission($config['mod']['news_custom'])) + $name = &$_POST['name']; + else + $name = &$mod['username']; + + $query->bindValue(':name', utf8tohtml($name), PDO::PARAM_INT); + $query->bindvalue(':time', time(), PDO::PARAM_INT); + $query->bindValue(':subject', utf8tohtml($_POST['subject'])); + + markup($_POST['body']); + $query->bindValue(':body', $_POST['body']); + $query->execute() or error(db_error($query)); + + rebuildThemes('news'); + } + + $body .= '
    New post
    ' . + '' . + '' . + (hasPermission($config['mod']['news_custom']) ? + '' + : + '') . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '' . + '
    ' . _('Name') . '' . $mod['username'] . '
    ' . _('Subject') . '
    ' . _('Body') . '
    ' . + '
    '; + } + + $query = prepare("SELECT * FROM `news` ORDER BY `id` DESC LIMIT :limit"); + $query->bindValue(':limit', $config['mod']['noticeboard_display'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + while($news = $query->fetch()) { + $body .= '
    ' . + (hasPermission($config['mod']['news_delete']) ? + '[delete]' + : '') . + '

    ' . + ($news['subject'] ? + $news['subject'] + : + '' . _('no subject') . '' + ) . + ' — by ' . + $news['name'] . + ' at ' . + strftime($config['post_date'], $news['time']) . + '

    ' . $news['body'] . '

    '; + } + + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('News'), + 'body'=>$body, + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/inbox\/readall$/', $query, $match)) { + $query = prepare("UPDATE `pms` SET `unread` = 0 WHERE `to` = :id"); + $query->bindValue(':id', $mod['id'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + modLog('Marked all PMs as read'); + + header('Location: ?/inbox', true, $config['redirect_http']); + } elseif(preg_match('/^\/inbox$/', $query, $match)) { + $query = prepare("SELECT `unread`,`pms`.`id`, `time`, `sender`, `to`, `message`, `username` FROM `pms` LEFT JOIN `mods` ON `mods`.`id` = `sender` WHERE `to` = :mod ORDER BY `unread` DESC, `time` DESC"); + $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if($query->rowCount() == 0) { + $body = '

    (' . _('No private messages for you.') . ')

    '; + } else { + $unread_pms = 0; + + $body = ''; + while($pm = $query->fetch()) { + $body .= '' . + '' . + '' . + '' . + '' . + ''; + + if($pm['unread']) + $unread_pms++; + } + $body .= '
    IDFromDateMessage snippet
    ' . $pm['id'] . '' . + ($pm['username'] ? + '' . $pm['username'] . '' + : 'deleted?') . + '' . strftime($config['post_date'], $pm['time']) . '' . pm_snippet($pm['message']) . '
    '; + + if($unread_pms) { + $body = '

    (Mark all as read)

    ' . $body; + } + } + + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('PM Inbox') . ' (' . ($query->rowCount() == 0 ? _('empty') : $unread_pms . ' ' . _('unread')) . ')', + 'body'=>$body, + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/PM\/(\d+)$/', $query, $match)) { + $id = &$match[1]; + + if(hasPermission($config['mod']['master_pm'])) { + $query = prepare("SELECT `pms`.`id`, `time`, `sender`, `unread`, `to`, `message`, `username` FROM `pms` LEFT JOIN `mods` ON `mods`.`id` = `sender` WHERE `pms`.`id` = :id"); + } else { + $query = prepare("SELECT `pms`.`id`, `time`, `sender`, `unread`, `to`, `message`, `username` FROM `pms` LEFT JOIN `mods` ON `mods`.`id` = `sender` WHERE `pms`.`id` = :id AND `to` = :mod"); + $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); + } + + $query->bindValue(':id', $id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if(!$pm = $query->fetch()) { + // Mod doesn't exist + error($config['error']['404']); + } + + if(isset($_POST['delete'])) { + $query = prepare("DELETE FROM `pms` WHERE `id` = :id"); + $query->bindValue(':id', $id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + modLog('Deleted a PM'); + + header('Location: ?/inbox', true, $config['redirect_http']); + } else { + if($pm['unread']) { + $query = prepare("UPDATE `pms` SET `unread` = 0 WHERE `id` = :id"); + $query->bindValue(':id', $id, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + modLog('Read a PM'); + } + + if($pm['to'] != $mod['id']) { + $query = prepare("SELECT `username` FROM `mods` WHERE `id` = :id"); + $query->bindValue(':id', $pm['to'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if($_mod = $query->fetch()) { + $__to = &$_mod['username']; + } else { + $__to = false; + } + } + + $body = '
    ' . + + '' . + + (isset($__to) ? + '' + : '') . + + '' . + + '' . + + '
    From' . + (!$pm['username'] ? + '???' + : + '' . utf8tohtml($pm['username']) . '' + ) . + '
    To' . + ($__to === false ? + '???' + : + '' . utf8tohtml($__to) . '' + ) . + '
    Date ' . strftime($config['post_date'], $pm['time']) . '
    Message ' . $pm['message'] . '
    ' . + + '

    ' . + + '
    ' . + + '

    Reply with quote

    '; + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'Private message', + 'body'=>$body, + 'mod'=>true + ) + ); + } + } elseif(preg_match('/^\/new_PM\/(\d+)(\/(\d+))?$/', $query, $match)) { + if(!hasPermission($config['mod']['create_pm'])) error($config['error']['noaccess']); + + $to = &$match[1]; + + $query = prepare("SELECT `username`,`id` FROM `mods` WHERE `id` = :id"); + $query->bindValue(':id', $to, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if(!$to = $query->fetch()) { + // Mod doesn't exist + error($config['error']['404']); + } + + if(isset($_POST['message'])) { + // Post message + $message = &$_POST['message']; + + if(empty($message)) + error($config['error']['tooshort_body']); + + markup($message); + + $query = prepare("INSERT INTO `pms` VALUES (NULL, :sender, :to, :message, :time, 1)"); + $query->bindValue(':sender', $mod['id'], PDO::PARAM_INT); + $query->bindValue(':to', $to['id'], PDO::PARAM_INT); + $query->bindValue(':message', $message); + $query->bindValue(':time', time(), PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + modLog('Sent a PM to ' . $to['username']); + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'PM sent', + 'body'=>'

    Message sent successfully to ' . utf8tohtml($to['username']) . '.

    ', + 'mod'=>true + ) + ); + } else { + $value = ''; + if(isset($match[3])) { + $reply = &$match[3]; + + $query = prepare("SELECT `message` FROM `pms` WHERE `sender` = :sender AND `to` = :mod AND `id` = :id"); + $query->bindValue(':sender', $to['id'], PDO::PARAM_INT); + $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); + $query->bindValue(':id', $reply, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + if($pm = $query->fetch()) { + $value = quote($pm['message']); + } + } + + + $body = '
    ' . + + '' . + + '' . + + '' . + + '
    To' . + (hasPermission($config['mod']['editusers']) ? + '' . utf8tohtml($to['username']) . '' : + utf8tohtml($to['username']) + ) . + '
    Message
    ' . + + '

    ' . + + '
    '; + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'New PM for ' . utf8tohtml($to['username']), + 'body'=>$body, + 'mod'=>true + ) + ); + } + } elseif(preg_match('/^\/search$/', $query)) { + if(!hasPermission($config['mod']['search'])) error($config['error']['noaccess']); + + $body = '

    Search

    ' . + '

    ' . + '' . + '' . + '

    ' . + '

    (Search is case-insensitive, and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.)

    ' . + '
    '; + + if(isset($_POST['search']) && !empty($_POST['search'])) { + $phrase = &$_POST['search']; + $_body = ''; + + $filters = array(); + + function search_filters($m) { + global $filters; + $name = $m[2]; + $value = isset($m[4]) ? $m[4] : $m[3]; + + if(!in_array($name, array('id', 'thread', 'subject', 'email', 'name', 'trip', 'capcode', 'filename', 'filehash', 'ip'))) { + // unknown filter + return $m[0]; + } + + $filters[$name] = $value; + + return $m[1]; + } + + $phrase = trim(preg_replace_callback('/(^|\s)(\w+):("(.*)?"|[^\s]*)/', 'search_filters', $phrase)); + + // Escape escape character + $phrase = str_replace('!', '!!', $phrase); + + // Remove SQL wildcard + $phrase = str_replace('%', '!%', $phrase); + + // Use asterisk as wildcard to suit convention + $phrase = str_replace('*', '%', $phrase); + + $like = ''; + $match = array(); + + // Find exact phrases + if(preg_match_all('/"(.+?)"/', $phrase, $m)) { + foreach($m[1] as &$quote) { + $phrase = str_replace("\"{$quote}\"", '', $phrase); + $match[] = $pdo->quote($quote); + } + } + + $words = explode(' ', $phrase); + foreach($words as &$word) { + if(empty($word)) + continue; + $match[] = $pdo->quote($word); + } + + $like = ''; + foreach($match as &$phrase) { + if(!empty($like)) + $like .= ' AND '; + $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase); + $like .= '`body` LIKE ' . $phrase . ' ESCAPE \'!\''; + } + + foreach($filters as $name => $value) { + if(!empty($like)) + $like .= ' AND '; + $like .= '`' . $name . '` = '. $pdo->quote($value); + } + + $like = str_replace('%', '%%', $like); + + $boards = listBoards(); + foreach($boards as &$_b) { + openBoard($_b['uri']); + + $query = prepare(sprintf("SELECT * FROM `posts_%s` WHERE " . $like . " ORDER BY `time` DESC LIMIT :limit", $board['uri'])); + $query->bindValue(':limit', $config['mod']['search_results'], PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + $temp = ''; + while($post = $query->fetch()) { + if(!$post['thread']) { + $po = new Thread($post['id'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'], $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'], $post['filename'], $post['ip'], $post['sticky'], $post['locked'], $post['sage'], $post['embed'], '?/', $mod, false); + } else { + $po = new Post($post['id'], $post['thread'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'], $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'], $post['filename'], $post['ip'], $post['embed'], '?/', $mod); + } + $temp .= $po->build(true) . '
    '; + } + + if(!empty($temp)) + $_body .= '
    ' . $query->rowCount() . ' result' . ($query->rowCount() != 1 ? 's' : '') . ' on ' . + sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] . + '' . $temp . '
    '; + } + + $body .= '
    '; + if(!empty($_body)) + $body .= $_body; + else + $body .= '

    (No results.)

    '; + } + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>'Search', + 'body'=>$body, + 'mod'=>true + ) + ); + } elseif(preg_match('/^\/users$/', $query)) { + if(!hasPermission($config['mod']['manageusers'])) error($config['error']['noaccess']); + + $body = '
    '; + + $query = query("SELECT *, (SELECT `time` FROM `modlogs` WHERE `mod` = `id` ORDER BY `time` DESC LIMIT 1) AS `last`, (SELECT `text` FROM `modlogs` WHERE `mod` = `id` ORDER BY `time` DESC LIMIT 1) AS `action` FROM `mods` ORDER BY `type` DESC,`id`") or error(db_error()); + while($_mod = $query->fetch()) { + $type = $_mod['type'] == JANITOR ? 'Janitor' : ($_mod['type'] == MOD ? 'Mod' : 'Admin'); + + $_mod['boards'] = explode(',', $_mod['boards']); + foreach($_mod['boards'] as &$_board) { + if($_board != '*') + $_board = '/' . $_board . '/'; + } + + $body .= '' . + '' . + + '' . + + '' . + + '' . + + '' . + + ''; + } + + $body .= '
    ' . _('ID') . '' . _('Username') . '' . _('Type') . '' . _('Boards') . '' . _('Last action') . '…
    ' . + $_mod['id'] . + '' . + utf8tohtml($_mod['username']) . + '' . + $type . + '' . + implode(', ', $_mod['boards']) . + '' . + ($_mod['last'] ? + (hasPermission($config['mod']['modlog']) ? + '' . ago($_mod['last']) . '' + : ago($_mod['last'])) + : 'never') . + '' . + (hasPermission($config['mod']['promoteusers']) ? + ($_mod['type'] != ADMIN ? + '▲' + :'') . + ($_mod['type'] != JANITOR ? + '▼' + :'') + : '' + ) . + (hasPermission($config['mod']['editusers']) || + (hasPermission($config['mod']['change_password']) && $_mod['id'] == $mod['id'])? + '[edit]' + : '' ) . + (hasPermission($config['mod']['create_pm']) ? + '[PM]' + : '' ) . + '
    '; + + if(hasPermission($config['mod']['createusers'])) { + $body .= '

    ' . _('Create new user') . '

    '; + } + + $body .= '
    '; + + echo Element('page.html', array( + 'config'=>$config, + 'title'=>_('Manage users'), + 'body'=>$body + ,'mod'=>true + ) + ); + } elseif(preg_match('/^\/users\/new$/', $query)) { + if(!hasPermission($config['mod']['createusers'])) error($config['error']['noaccess']); + + if(isset($_POST['username']) && isset($_POST['password'])) { + if(!isset($_POST['type'])) { + error(sprintf($config['error']['required'], 'type')); + } + + if($_POST['type'] != ADMIN && $_POST['type'] != MOD && $_POST['type'] != JANITOR) { + error(sprintf($config['error']['invalidfield'], 'type')); + } + + // Check if already exists + $query = prepare("SELECT `id` FROM `mods` WHERE `username` = :username"); + $query->bindValue(':username', $_POST['username']); + $query->execute() or error(db_error($query)); + + if($_mod = $query->fetch()) { + error(sprintf($config['error']['modexists'], $_mod['id'])); + } + + $boards = array(); + foreach($_POST as $name => $null) { + if(preg_match('/^board_(.+)$/', $name, $m)) + $boards[] = $m[1]; + } + $boards = implode(',', $boards); + + $query = prepare("INSERT INTO `mods` VALUES (NULL, :username, :password, :type, :boards)"); + $query->bindValue(':username', $_POST['username']); + $query->bindValue(':password', sha1($_POST['password'])); + $query->bindValue(':type', $_POST['type'], PDO::PARAM_INT); + $query->bindValue(':boards', $boards); + $query->execute() or error(db_error($query)); + + modLog('Create a new user: "' . $_POST['username'] . '"'); + header('Location: ?/users', true, $config['redirect_http']); + } else { + + $__boards = '