diff options
| author | Czterooki <czterooki1337@gmail.com> | 2013-07-20 18:28:57 -0400 |
|---|---|---|
| committer | Czterooki <czterooki1337@gmail.com> | 2013-07-20 18:28:57 -0400 |
| commit | bc1bc9f6ef08d778b7218e86ea00f43c1d9bce81 (patch) | |
| tree | 104fcb30ac68a0f76c0aec37fb01ff65c409f927 | |
| parent | ca5283abaddb6014bc6075ea9fbf3a8daa19f2cc (diff) | |
| parent | d31be1958de76e5cabc14ddd410287055b6dac0c (diff) | |
Merge branch 'master' of github.com:vichan-devel/Tinyboard
| -rw-r--r-- | inc/config.php | 6 | ||||
| -rw-r--r-- | inc/display.php | 29 | ||||
| -rw-r--r-- | inc/mod/pages.php | 134 | ||||
| -rw-r--r-- | js/quote-selection.js | 24 | ||||
| -rw-r--r-- | js/youtube.js | 43 | ||||
| -rw-r--r-- | mod.php | 4 | ||||
| -rw-r--r-- | post.php | 85 | ||||
| -rw-r--r-- | templates/mod/dashboard.html | 11 | ||||
| -rw-r--r-- | templates/mod/search_form.html | 18 | ||||
| -rw-r--r-- | templates/mod/search_results.html | 177 | ||||
| -rw-r--r-- | templates/themes/sitemap/info.php | 2 |
11 files changed, 434 insertions, 99 deletions
diff --git a/inc/config.php b/inc/config.php index a3b3d528..6860ba42 100644 --- a/inc/config.php +++ b/inc/config.php @@ -899,8 +899,8 @@ // Number of news entries to display per page $config['mod']['news_page'] = 40; - // Maximum number of results to display for a search, per board - $config['mod']['search_results'] = 75; + // Number of results to dispaly per page + $config['mod']['search_page'] = 200; // How many entries to show per page in the moderator noticeboard $config['mod']['noticeboard_page'] = 50; @@ -1057,7 +1057,7 @@ $config['mod']['master_pm'] = ADMIN; // Rebuild everything $config['mod']['rebuild'] = ADMIN; - // Search through posts + // Search through posts, IP address notes and bans $config['mod']['search'] = JANITOR; // Read the moderator noticeboard $config['mod']['noticeboard'] = JANITOR; diff --git a/inc/display.php b/inc/display.php index ba55903d..fe8bb048 100644 --- a/inc/display.php +++ b/inc/display.php @@ -241,6 +241,29 @@ function secure_link($href) { return $href . '/' . make_secure_link_token($href); } +function embed_html($link) { + global $config; + + foreach ($config['embedding'] as $embed) { + if ($html = preg_replace($embed[0], $embed[1], $link)) { + if ($html == $link) + continue; // Nope + + $html = str_replace('%%tb_width%%', $config['embed_width'], $html); + $html = str_replace('%%tb_height%%', $config['embed_height'], $html); + + return $html; + } + } + + if ($link[0] == '<') { + // Prior to v0.9.6-dev-8, HTML code for embedding was stored in the database instead of the link. + return $link; + } + + return 'Embedding error.'; +} + class Post { public function __construct($id, $thread, $subject, $email, $name, $trip, $capcode, $body, $time, $thumb, $thumbx, $thumby, $file, $filex, $filey, $filesize, $filename, $ip, $embed, $root=null, $mod=false) { global $config; @@ -269,6 +292,9 @@ class Post { $this->root = $root; $this->mod = $mod; + if ($this->embed) + $this->embed = embed_html($this->embed); + if ($this->mod) // Fix internal links // Very complicated regex @@ -365,6 +391,9 @@ class Thread { $this->mod = $mod; $this->hr = $hr; + if ($this->embed) + $this->embed = embed_html($this->embed); + if ($this->mod) // Fix internal links // Very complicated regex diff --git a/inc/mod/pages.php b/inc/mod/pages.php index 12ad1e92..d5ec6018 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -162,6 +162,140 @@ function mod_dashboard() { mod_page(_('Dashboard'), 'mod/dashboard.html', $args); } +function mod_search_redirect() { + global $config; + + if (!hasPermission($config['mod']['search'])) + error($config['error']['noaccess']); + + if (isset($_POST['query'], $_POST['type']) && in_array($_POST['type'], array('posts', 'IP_notes', 'bans', 'log'))) { + $query = $_POST['query']; + $query = urlencode($query); + $query = str_replace('_', '%5F', $query); + $query = str_replace('+', '_', $query); + + header('Location: ?/search/' . $_POST['type'] . '/' . $query, true, $config['redirect_http']); + } else { + header('Location: ?/', true, $config['redirect_http']); + } +} + +function mod_search($type, $search_query_escaped, $page_no = 1) { + global $pdo, $config; + + if (!hasPermission($config['mod']['search'])) + error($config['error']['noaccess']); + + // Unescape query + $query = str_replace('_', ' ', $search_query_escaped); + $query = urldecode($query); + $search_query = $query; + + // Form a series of LIKE clauses for the query. + // This gets a little complicated. + + // Escape "escape" character + $query = str_replace('!', '!!', $query); + + // Escape SQL wildcard + $query = str_replace('%', '!%', $query); + + // Use asterisk as wildcard instead + $query = str_replace('*', '%', $query); + + // Array of phrases to match + $match = array(); + + // Exact phrases ("like this") + if (preg_match_all('/"(.+?)"/', $query, $exact_phrases)) { + $exact_phrases = $exact_phrases[1]; + foreach ($exact_phrases as $phrase) { + $query = str_replace("\"{$phrase}\"", '', $query); + $match[] = $pdo->quote($phrase); + } + } + + // Non-exact phrases (ie. plain keywords) + $keywords = explode(' ', $query); + foreach ($keywords as $word) { + if (empty($word)) + continue; + $match[] = $pdo->quote($word); + } + + // Which `field` to search? + if ($type == 'posts') + $sql_field = 'body'; + if ($type == 'IP_notes') + $sql_field = 'body'; + if ($type == 'bans') + $sql_field = 'reason'; + if ($type == 'log') + $sql_field = 'text'; + + // Build the "LIKE 'this' AND LIKE 'that'" etc. part of the SQL query + $sql_like = ''; + foreach ($match as $phrase) { + if (!empty($sql_like)) + $sql_like .= ' AND '; + $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase); + $sql_like .= '`' . $sql_field . '` LIKE ' . $phrase . ' ESCAPE \'!\''; + } + + + // Compile SQL query + + if ($type == 'posts') { + error('Searching posts is under development. Sorry.'); + } + + if ($type == 'IP_notes') { + $query = 'SELECT * FROM `ip_notes` LEFT JOIN `mods` ON `mod` = `mods`.`id` WHERE ' . $sql_like . ' ORDER BY `time` DESC'; + $sql_table = 'ip_notes'; + if (!hasPermission($config['mod']['view_notes']) || !hasPermission($config['mod']['show_ip'])) + error($config['error']['noaccess']); + } + + if ($type == 'bans') { + $query = 'SELECT `bans`.*, `username` FROM `bans` LEFT JOIN `mods` ON `mod` = `mods`.`id` WHERE ' . $sql_like . ' ORDER BY (`expires` IS NOT NULL AND `expires` < UNIX_TIMESTAMP()), `set` DESC'; + $sql_table = 'bans'; + if (!hasPermission($config['mod']['view_banlist'])) + error($config['error']['noaccess']); + } + + if ($type == 'log') { + $query = 'SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM `modlogs` LEFT JOIN `mods` ON `mod` = `mods`.`id` WHERE ' . $sql_like . ' ORDER BY `time` DESC'; + $sql_table = 'modlogs'; + if (!hasPermission($config['mod']['modlog'])) + error($config['error']['noaccess']); + } + + // Execute SQL query (with pages) + $q = query($query . ' LIMIT ' . (($page_no - 1) * $config['mod']['search_page']) . ', ' . $config['mod']['search_page']) or error(db_error()); + $results = $q->fetchAll(PDO::FETCH_ASSOC); + + // Get total result count + $q = query('SELECT COUNT(*) FROM `' . $sql_table . '` WHERE ' . $sql_like) or error(db_error()); + $result_count = $q->fetchColumn(); + + if ($type == 'bans') { + foreach ($results as &$ban) { + if (filter_var($ban['ip'], FILTER_VALIDATE_IP) !== false) + $ban['real_ip'] = true; + } + } + + // $results now contains the search results + + mod_page(_('Search results'), 'mod/search_results.html', array( + 'search_type' => $type, + 'search_query' => $search_query, + 'search_query_escaped' => $search_query_escaped, + 'result_count' => $result_count, + 'results' => $results + )); +} + function mod_edit_board($boardName) { global $board, $config; diff --git a/js/quote-selection.js b/js/quote-selection.js index 0c5b7e7d..0722c0b1 100644 --- a/js/quote-selection.js +++ b/js/quote-selection.js @@ -1,6 +1,9 @@ /* * quote-selection.js * + * This is a little buggy. + * Allows you to quote a post by just selecting some text, then beginning to type. + * * Released under the MIT license * Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org> * @@ -31,12 +34,15 @@ $(document).ready(function(){ var altKey = false; var ctrlKey = false; + var metaKey = false; $(document).keyup(function(e) { - if (e.altKey) + if (e.keyCode == 18) altKey = false; - else if (e.ctrlKey) + else if (e.keyCode == 17) ctrlKey = false; + else if (e.keyCode == 91) + metaKey = false; }); $(document).keydown(function(e) { @@ -44,9 +50,11 @@ $(document).ready(function(){ altKey = true; else if (e.ctrlKey) ctrlKey = true; + else if (e.metaKey) + metaKey = true; - if (altKey || ctrlKey) { - console.log('CTRL/ALT used. Ignoring'); + if (altKey || ctrlKey || metaKey) { + // console.log('CTRL/ALT/Something used. Ignoring'); return; } @@ -56,20 +64,20 @@ $(document).ready(function(){ var selection = window.getSelection(); var $post = $(selection.anchorNode).parents('.post'); if ($post.length == 0) { - console.log('Start of selection was not post div', $(selection.anchorNode).parent()); + // console.log('Start of selection was not post div', $(selection.anchorNode).parent()); return; } var postID = $post.find('.post_no:eq(1)').text(); if (postID != $(selection.focusNode).parents('.post').find('.post_no:eq(1)').text()) { - console.log('Selection left post div', $(selection.focusNode).parent()); + // console.log('Selection left post div', $(selection.focusNode).parent()); return; } ; var selectedText = selection.toString(); - console.log('Selected text: ' + selectedText.replace(/\n/g, '\\n').replace(/\r/g, '\\r')); + // console.log('Selected text: ' + selectedText.replace(/\n/g, '\\n').replace(/\r/g, '\\r')); if ($('body').hasClass('debug')) alert(selectedText); @@ -86,7 +94,7 @@ $(document).ready(function(){ /* to solve some bugs on weird browsers, we need to replace \r\n with \n and then undo that after */ var quote = (last_quote != postID ? '>>' + postID + '\r\n' : '') + $.trim(selectedText).replace(/\r\n/g, '\n').replace(/^/mg, '>').replace(/\n/g, '\r\n') + '\r\n'; - console.log('Deselecting text'); + // console.log('Deselecting text'); selection.removeAllRanges(); if (document.selection) { diff --git a/js/youtube.js b/js/youtube.js new file mode 100644 index 00000000..f64d4874 --- /dev/null +++ b/js/youtube.js @@ -0,0 +1,43 @@ +/* +* youtube +* https://github.com/savetheinternet/Tinyboard/blob/master/js/youtube.js +* +* Don't load the YouTube player unless the video image is clicked. +* This increases performance issues when many videos are embedded on the same page. +* Currently only compatiable with YouTube. +* +* Proof of concept. +* +* Released under the MIT license +* Copyright (c) 2013 Michael Save <savetheinternet@tinyboard.org> +* +* Usage: +* $config['embedding'] = array(); +* $config['embedding'][0] = array( +* '/^https?:\/\/(\w+\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9\-_]{10,11})(&.+)?$/i', +* '<div class="video-container" data-video="$2"><a href="$0" target="_blank" class="file"><img style="width:360px;height:270px;" src="//img.youtube.com/vi/$2/0.jpg"/></a></div>' +); +* $config['additional_javascript'][] = 'js/jquery.min.js'; +* $config['additional_javascript'][] = 'js/youtube.js'; +* +*/ + + +onready(function(){ + var do_embed_yt = function(tag) { + $('div.video-container a', tag).click(function() { + var videoID = $(this.parentNode).data('video'); + + $(this.parentNode).html('<iframe style="float:left;margin: 10px 20px" type="text/html" width="360" height="270" src="//www.youtube.com/embed/' + videoID + '?autoplay=1" frameborder="0"/>'); + + return false; + }); + }; + do_embed_yt(document); + + // allow to work with auto-reload.js, etc. + $(document).bind('new_post', function(e, post) { + do_embed_yt(post); + }); +}); + @@ -56,6 +56,10 @@ $pages = array( '/IP/([\w.:]+)/remove_note/(\d+)' => 'ip_remove_note', // remove note from ip address '/bans' => 'bans', // ban list '/bans/(\d+)' => 'bans', // ban list + + '/search' => 'search_redirect', // search + '/search/(posts|IP_notes|bans|log)/(.+)/(\d+)' => 'search', // search + '/search/(posts|IP_notes|bans|log)/(.+)' => 'search', // search // CSRF-protected moderator actions '/ban' => 'secure_POST ban', // new ban @@ -251,19 +251,10 @@ if (isset($_POST['delete'])) { // yep; validate it $value = $_POST['embed']; foreach ($config['embedding'] as &$embed) { - if ($html = preg_replace($embed[0], $embed[1], $value)) { - if ($html == $value) { - // Nope. - continue; - } - - // Width and height - $html = str_replace('%%tb_width%%', $config['embed_width'], $html); - $html = str_replace('%%tb_height%%', $config['embed_height'], $html); - - // Validated. It works. - $post['embed'] = $html; - // This looks messy right now, I know. I'll work on a better alternative later. + if (preg_match($embed[0], $value)) { + // Valid link + $post['embed'] = $value; + // This is bad, lol. $post['no_longer_require_an_image_for_op'] = true; break; } @@ -442,72 +433,8 @@ if (isset($_POST['delete'])) { if ($config['thumb_method'] == 'convert') { if ($post['extension'] == 'jpg' || $post['extension'] == 'jpeg') { $exif = exif_read_data($upload); - if (isset($exif['Orientation'])) { - switch($exif['Orientation']) { - case 1: - // Normal - $args = false; - break; - case 2: - // 888888 - // 88 - // 8888 - // 88 - // 88 - - $args = '-flop'; - break; - case 3: - - // 88 - // 88 - // 8888 - // 88 - // 888888 - - $args = '-flip -flop'; - break; - case 4: - // 88 - // 88 - // 8888 - // 88 - // 888888 - - $args = '-flip'; - break; - case 5: - // 8888888888 - // 88 88 - // 88 - - $args = '-rotate 90 -flop'; - break; - case 6: - // 88 - // 88 88 - // 8888888888 - - $args = '-rotate 90'; - break; - case 7: - // 88 - // 88 88 - // 8888888888 - - $args = '-rotate "-90" -flop'; - break; - case 8: - // 8888888888 - // 88 88 - // 88 - - $args = '-rotate "-90"'; - break; - } - - if ($args) - shell_exec('convert ' . escapeshellarg($upload) . ' ' . $args . ' ' . escapeshellarg($upload)); + if (isset($exif['Orientation']) && $exif['Orientation'] != 1) { + shell_exec('convert ' . escapeshellarg($upload) . ' -auto-orient ' . escapeshellarg($upload)); } } } diff --git a/templates/mod/dashboard.html b/templates/mod/dashboard.html index e54b3cd3..619663f5 100644 --- a/templates/mod/dashboard.html +++ b/templates/mod/dashboard.html @@ -101,22 +101,17 @@ </ul> </fieldset> -{# +{% if mod|hasPermission(config.mod.search) %} <fieldset> <legend>{% trans 'Search' %}</legend> <ul> <li> - <form style="display:inline" action="?/search" method="post"> - <label style="display:inline" for="search">{% trans 'Phrase:' %}</label> - <input id="search" name="search" type="text" size="35"> - <input type="submit" value="{% trans 'Search' %}"> - </form> - <p class="unimportant">{% trans '(Search is case-insensitive, and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.)' %}</p> + {% include 'mod/search_form.html' %} </li> </ul> </fieldset> -#} +{% endif %} {% if config.debug %} <fieldset> diff --git a/templates/mod/search_form.html b/templates/mod/search_form.html new file mode 100644 index 00000000..d8a2fed4 --- /dev/null +++ b/templates/mod/search_form.html @@ -0,0 +1,18 @@ +<form style="display:inline" action="?/search" method="post"> + <label style="display:inline" for="search">{% trans 'Phrase:' %}</label> + <input id="search" name="query" type="text" size="60" value="{{ search_query|e }}"> + <select name="type"> + <option value="posts"{% if search_type == 'posts'%} selected{% endif %}>{% trans 'Posts' %}</option> + {% if mod|hasPermission(config.mod.view_notes) and mod|hasPermission(config.mod.show_ip) %} + <option value="IP_notes"{% if search_type == 'IP_notes'%} selected{% endif %}>{% trans 'IP address notes' %}</option> + {% endif %} + {% if mod|hasPermission(config.mod.view_banlist) %} + <option value="bans"{% if search_type == 'bans'%} selected{% endif %}>{% trans 'Bans' %}</option> + {% endif %} + {% if mod|hasPermission(config.mod.modlog) %} + <option value="log"{% if search_type == 'log'%} selected{% endif %}>{% trans 'Moderation log' %}</option> + {% endif %} + </select> + <input type="submit" value="{% trans 'Search' %}"> +</form> +<p class="unimportant">{% trans '(Search is case-insensitive and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.)' %}</p>
\ No newline at end of file diff --git a/templates/mod/search_results.html b/templates/mod/search_results.html new file mode 100644 index 00000000..b441a28f --- /dev/null +++ b/templates/mod/search_results.html @@ -0,0 +1,177 @@ +<fieldset style="margin-bottom:20px"> + <legend>{% trans 'Search' %}</legend> + + <ul> + <li> + {% include 'mod/search_form.html' %} + </li> + </ul> +</fieldset> + +<p style="text-align:center">Showing {{ result_count }} result{% if result_count != 1 %}s{% endif %}.</p> + +{% if search_type == 'IP_notes' %} + <table class="modlog"> + <tr> + <th>{% trans 'IP address' %}</th> + <th>{% trans 'Staff' %}</th> + <th>{% trans 'Note' %}</th> + <th>{% trans 'Date' %}</th> + </tr> + {% for note in results %} + <tr> + <td class="minimal"> + <a href="?/IP/{{ note.ip }}#notes">{{ note.ip }}</a> + </td> + <td class="minimal"> + {% if note.username %} + <a href="?/new_PM/{{ note.username|e }}">{{ note.username|e }}</a> + {% else %} + <em>{% trans 'deleted?' %}</em> + {% endif %} + </td> + <td> + {{ note.body }} + </td> + <td class="minimal"> + {{ note.time|date(config.post_date) }} + </td> + </tr> + {% endfor %} + </table> +{% endif %} + +{% if search_type == 'bans' %} + <table class="modlog" style="width:100%"> + <tr> + <th>{% trans 'IP address/mask' %}</th> + <th>{% trans 'Reason' %}</th> + <th>{% trans 'Board' %}</th> + <th>{% trans 'Set' %}</th> + <th>{% trans 'Duration' %}</th> + <th>{% trans 'Expires' %}</th> + <th>{% trans 'Seen' %}</th> + <th>{% trans 'Staff' %}</th> + </tr> + {% for ban in results %} + <tr{% if ban.expires != 0 and ban.expires < time() %} style="text-decoration:line-through"{% endif %}> + <td style="white-space: nowrap"> + {% if ban.real_ip %} + <a href="?/IP/{{ ban.ip }}#bans">{{ ban.ip }}</a> + {% else %} + {{ ban.ip|e }} + {% endif %} + </td> + <td> + {% if ban.reason %} + {{ ban.reason }} + {% else %} + - + {% endif %} + </td> + <td style="white-space: nowrap"> + {% if ban.board %} + {{ config.board_abbreviation|sprintf(ban.board) }} + {% else %} + <em>{% trans 'all boards' %}</em> + {% endif %} + </td> + <td style="white-space: nowrap"> + <span title="{{ ban.set|date(config.post_date) }}"> + {{ ban.set|ago }} ago + </span> + </td> + <td style="white-space: nowrap"> + {% if ban.expires == 0 %} + - + {% else %} + {{ (ban.expires - ban.set + time()) | until }} + {% endif %} + </td> + <td style="white-space: nowrap"> + {% if ban.expires == 0 %} + <em>{% trans 'never' %}</em> + {% else %} + {{ ban.expires|date(config.post_date) }} + {% if ban.expires > time() %} + <small>(in {{ ban.expires|until }})</small> + {% endif %} + {% endif %} + </td> + <td> + {% if ban.seen %} + {% trans 'Yes' %} + {% else %} + {% trans 'No' %} + {% endif %} + </td> + <td> + {% if ban.username %} + {% if mod|hasPermission(config.mod.view_banstaff) %} + <a href="?/new_PM/{{ ban.username|e }}">{{ ban.username|e }}</a> + {% else %} + {% if mod|hasPermission(config.mod.view_banquestionmark) %} + <em>?</em> + {% else %} + + {% endif %} + {% endif %} + {% elseif ban.mod == -1 %} + <em>system</em> + {% else %} + <em>{% trans 'deleted?' %}</em> + {% endif %} + </td> + </tr> + {% endfor %} + </table> +{% endif %} + +{% if search_type == 'log' %} + <table class="modlog"> + <tr> + <th>{% trans 'Staff' %}</th> + <th>{% trans 'IP address' %}</th> + <th>{% trans 'Time' %}</th> + <th>{% trans 'Board' %}</th> + <th>{% trans 'Action' %}</th> + </tr> + {% for log in results %} + <tr> + <td class="minimal"> + {% if log.username %} + <a href="?/log:{{ log.username|e }}">{{ log.username|e }}</a> + {% elseif log.mod == -1 %} + <em>system</em> + {% else %} + <em>{% trans 'deleted?' %}</em> + {% endif %} + </td> + <td class="minimal"> + <a href="?/IP/{{ log.ip }}">{{ log.ip }}</a> + </td> + <td class="minimal"> + <span title="{{ log.time|date(config.post_date) }}">{{ log.time|ago }}</span> + </td> + <td class="minimal"> + {% if log.board %} + <a href="?/{{ config.board_path|sprintf(log.board) }}{{ config.file_index }}">{{ config.board_abbreviation|sprintf(log.board) }}</a> + {% else %} + - + {% endif %} + </td> + <td> + {{ log.text }} + </td> + </tr> + {% endfor %} + </table> +{% endif %} + +{% if result_count > results|count %} + <p class="unimportant" style="text-align:center;word-wrap:break-word"> + {% for i in range(0, (result_count - 1) / config.mod.search_page) %} + <a href="?/search/{{ search_type }}/{{ search_query_escaped }}/{{ i + 1 }}">[{{ i + 1 }}]</a> + {% endfor %} + </p> +{% endif %}
\ No newline at end of file diff --git a/templates/themes/sitemap/info.php b/templates/themes/sitemap/info.php index 1198c2e3..43b529fa 100644 --- a/templates/themes/sitemap/info.php +++ b/templates/themes/sitemap/info.php @@ -23,7 +23,7 @@ 'name' => 'url', 'type' => 'text', 'comment' => '(with trailing slash)', - 'default' => 'http://' . (isset ($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : "localhost") . $config['root'], + 'default' => (isset($_SERVER['HTTP_HOST']) ? 'http://' . $_SERVER['HTTP_HOST'] . $config['root'] : ''), 'size' => '20' ); |
