Add embedding support (YT, Vimeo, SoundCloud) and make links in messages clickable

This commit is contained in:
Trevor Slocum 2015-08-07 00:01:18 -07:00
parent 5c0f55681c
commit b721aac519
8 changed files with 183 additions and 39 deletions

View file

@ -14,6 +14,7 @@ For demos see the [TinyIB Installations](https://github.com/tslocum/TinyIB/wiki)
Features
------------
- GIF, JPG, PNG, SWF and WebA/WebM upload.
- YouTube, Vimeo and SoundCloud embedding.
- CAPTCHA.
- Reference links >>###
- Delete post via password.

View file

@ -89,12 +89,53 @@ if (isset($_POST['message']) || isset($_POST['file'])) {
$post['message'] = $_POST['message']; // Treat message as raw HTML
} else {
$rawposttext = '';
$post['message'] = str_replace("\n", '<br>', colorQuote(postLink(cleanString(rtrim($_POST['message'])))));
$post['message'] = str_replace("\n", '<br>', makeLinksClickable(colorQuote(postLink(cleanString(rtrim($_POST['message']))))));
}
$post['password'] = ($_POST['password'] != '') ? md5(md5($_POST['password'])) : '';
$post['nameblock'] = nameBlock($post['name'], $post['tripcode'], $post['email'], time(), $rawposttext);
if (isset($_FILES['file'])) {
if (isset($_POST['embed']) && trim($_POST['embed']) != '') {
list($service, $embed) = getEmbed(trim($_POST['embed']));
if (empty($embed) || !isset($embed['html']) || !isset($embed['title']) || !isset($embed['thumbnail_url'])) {
fancyDie("Invalid embed URL. Only YouTube, Vimeo, and SoundCloud URLs are supported.");
}
$post['file_hex'] = $service;
$temp_file = time() . substr(microtime(), 2, 3) . '.tmp';
$file_location = "thumb/" . $temp_file;
file_put_contents($file_location, file_get_contents($embed['thumbnail_url']));
$file_info = getimagesize($file_location);
$file_mime = $file_info['mime'];
$post['image_width'] = $file_info[0];
$post['image_height'] = $file_info[1];
if ($file_mime == "image/jpeg") {
$post['thumb'] = $temp_file . '.jpg';
} else if ($file_mime == "image/gif") {
$post['thumb'] = $temp_file . '.gif';
} else if ($file_mime == "image/png") {
$post['thumb'] = $temp_file . '.png';
} else {
fancyDie("Error while processing audio/video.");
}
$thumb_location = "thumb/" . $post['thumb'];
list($thumb_maxwidth, $thumb_maxheight) = thumbnailDimensions($post);
if (!createThumbnail($file_location, $thumb_location, $thumb_maxwidth, $thumb_maxheight)) {
fancyDie("Could not create thumbnail.");
}
addVideoOverlay($thumb_location);
$thumb_info = getimagesize($thumb_location);
$post['thumb_width'] = $thumb_info[0];
$post['thumb_height'] = $thumb_info[1];
$post['file_original'] = cleanString($embed['title']);
$post['file'] = str_ireplace(array('src="https://', 'src="http://'), 'src="//', $embed['html']);
} else if (isset($_FILES['file'])) {
if ($_FILES['file']['name'] != "") {
validateFileUpload();
@ -233,11 +274,21 @@ if (isset($_POST['message']) || isset($_POST['file'])) {
}
if ($post['file'] == '') { // No file uploaded
if ($post['parent'] == TINYIB_NEWTHREAD && (TINYIB_PIC || TINYIB_SWF || TINYIB_WEBM) && !TINYIB_NOFILEOK) {
fancyDie("A file is required to start a thread.");
$allowed = "";
if (TINYIB_PIC || TINYIB_SWF || TINYIB_WEBM) {
$allowed = "file";
}
if (TINYIB_EMBED) {
if ($allowed != "") {
$allowed .= " or ";
}
$allowed .= "embed URL";
}
if ($post['parent'] == TINYIB_NEWTHREAD && $allowed != "" && !TINYIB_NOFILEOK) {
fancyDie("A $allowed is required to start a thread.");
}
if (str_replace('<br>', '', $post['message']) == "") {
fancyDie("Please enter a message" . ((TINYIB_PIC || TINYIB_SWF || TINYIB_WEBM) ? " and/or upload a file" : "") . ".");
fancyDie("Please enter a message" . ($allowed != "" ? " and/or upload a $allowed" : "") . ".");
}
} else {
echo $post['file_original'] . ' uploaded.<br>';

View file

@ -27,6 +27,9 @@ if (!defined('TINYIB_SWF')) {
if (!defined('TINYIB_WEBM')) {
define('TINYIB_WEBM', false);
}
if (!defined('TINYIB_EMBED')) {
define('TINYIB_EMBED', false);
}
if (!defined('TINYIB_THUMBNAIL')) {
define('TINYIB_THUMBNAIL', 'gd');
}

View file

@ -213,7 +213,7 @@ function colorQuote($message) {
}
function deletePostImages($post) {
if ($post['file'] != '') {
if ($post['file_hex'] != 'YouTube' && $post['file_hex'] != 'Vimeo' && $post['file_hex'] != 'SoundCloud' && $post['file'] != '') {
@unlink('src/' . $post['file']);
}
if ($post['thumb'] != '') {
@ -499,3 +499,19 @@ function strallpos($haystack, $needle, $offset = 0) {
}
return $result;
}
function getEmbed($url) {
$services = array('YouTube' => "http://www.youtube.com/oembed?url=" . urlencode($url) . "&format=json", 'Vimeo' => "http://vimeo.com/api/oembed.json?url=" . urlencode($url), 'SoundCloud' => "http://soundcloud.com/oembed?format=json&url=" . $url);
foreach ($services as $service => $service_url) {
$curl = curl_init($service_url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
$return = curl_exec($curl);
curl_close($curl);
$result = json_decode($return, true);
if (!empty($result)) {
break;
}
}
return array($service, $result);
}

View file

@ -22,6 +22,7 @@ EOF;
<link rel="stylesheet" type="text/css" href="css/global.css">
<link rel="stylesheet" type="text/css" href="css/futaba.css" title="Futaba">
<link rel="alternate stylesheet" type="text/css" href="css/burichan.css" title="Burichan">
<script src="js/jquery.js"></script>
<script src="js/tinyib.js"></script>
</head>
EOF;
@ -71,6 +72,15 @@ function supportedFileTypes() {
return $types_formatted;
}
function makeLinksClickable($text) {
$text = preg_replace('!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9@:%\!_+.,~#?&;//=]+)!i', '<a href="$1" target="_blank">$1</a>', $text);
$text = preg_replace('/\(\<a href\=\"(.*)\)"\ target\=\"\_blank\">(.*)\)\<\/a>/i', '(<a href="$1" target="_blank">$2</a>)', $text);
$text = preg_replace('/\<a href\=\"(.*)\."\ target\=\"\_blank\">(.*)\.\<\/a>/i', '<a href="$1" target="_blank">$2</a>.', $text);
$text = preg_replace('/\<a href\=\"(.*)\,"\ target\=\"\_blank\">(.*)\,\<\/a>/i', '<a href="$1" target="_blank">$2</a>,', $text);
return $text;
}
function buildPost($post, $res) {
$return = "";
$threadid = ($post['parent'] == TINYIB_NEWTHREAD) ? $post['id'] : $post['parent'];
@ -85,6 +95,11 @@ function buildPost($post, $res) {
$post["omitted"] = 0;
}
$embed = '';
if ($post["file_hex"] == "YouTube" || $post["file_hex"] == "Vimeo" || $post["file_hex"] == "SoundCloud") {
$embed = str_replace("'", "\'", $post['file']);
}
if ($post["parent"] != TINYIB_NEWTHREAD) {
$return .= <<<EOF
<table>
@ -95,6 +110,24 @@ function buildPost($post, $res) {
</td>
<td class="reply" id="reply${post["id"]}">
EOF;
} elseif ($embed != "") {
$return .= <<<EOF
<span class="filesize">Embed: <a href="#" id="tiembed${post['id']}">${post["file_original"]}</a>&ndash;(${post["file_hex"]})</span>
<br>
<span id="thumbembed${post['id']}">
<a href="#" id="exembed${post['id']}">
<img src="thumb/${post["thumb"]}" alt="${post["id"]}" class="thumb opthumb" id="thumbnail${post['id']}" width="${post["thumb_width"]}" height="${post["thumb_height"]}">
</a>
</span>
<div id="embed${post['id']}" class="thumb" style="display: none;"></div>
<script type="text/javascript">
$("#tiembed${post['id']}, #exembed${post['id']}").click(function(){
showEmbed('${post['id']}', '$embed');
return false;
});
</script>
EOF;
} elseif ($post["file"] != "") {
$return .= <<<EOF
<span class="filesize">File: <a href="src/${post["file"]}">${post["file"]}</a>&ndash;(${post["file_size_formatted"]}, ${post["image_width"]}x${post["image_height"]}, ${post["file_original"]})</span>
@ -123,8 +156,27 @@ ${post["nameblock"]}
</span>
EOF;
if ($post['parent'] != TINYIB_NEWTHREAD && $post["file"] != "") {
$return .= <<<EOF
if ($post['parent'] != TINYIB_NEWTHREAD) {
if ($embed != "") {
$return .= <<<EOF
<br>
<span class="filesize"><a href="#" id="tiembed${post['id']}">${post['file_original']}</a>&ndash;(${post['file_hex']})</span>
<br>
<span id="thumbembed${post['id']}">
<a href="#" id="exembed${post['id']}">
<img src="thumb/${post["thumb"]}" alt="${post["id"]}" class="thumb" id="thumbnail${post['id']}" width="${post["thumb_width"]}" height="${post["thumb_height"]}">
</a>
</span>
<div id="embed${post['id']}" class="thumb" style="display: none;"></div>
<script type="text/javascript">
$("#tiembed${post['id']}, #exembed${post['id']}").click(function(){
showEmbed('${post['id']}', '$embed');
return false;
});
</script>
EOF;
} else if ($post["file"] != "") {
$return .= <<<EOF
<br>
<span class="filesize"><a href="src/${post["file"]}">${post["file"]}</a>&ndash;(${post["file_size_formatted"]}, ${post["image_width"]}x${post["image_height"]}, ${post["file_original"]})</span>
<br>
@ -132,6 +184,7 @@ EOF;
<span id="thumb${post["id"]}"><img src="thumb/${post["thumb"]}" alt="${post["id"]}" class="thumb" width="${post["thumb_width"]}" height="${post["thumb_height"]}"></span>
</a>
EOF;
}
}
if ($post['parent'] == TINYIB_NEWTHREAD && $res == TINYIB_INDEXPAGE) {
@ -212,6 +265,7 @@ EOF;
$reqmod_html = '';
$filetypes_html = '';
$file_input_html = '';
$embed_input_html = '';
$unique_posts_html = '';
$captcha_html = '';
@ -249,6 +303,19 @@ EOF;
EOF;
}
if (TINYIB_EMBED) {
$embed_input_html = <<<EOF
<tr>
<td class="postblock">
Embed
</td>
<td>
<input type="text" name="embed" size="28" accesskey="e">&nbsp;&nbsp;(paste a YouTube URL)
</td>
</tr>
EOF;
}
if (TINYIB_REQMOD != 'disable') {
$reqmod_html = '<li>All posts' . (TINYIB_REQMOD == 'files' ? ' with a file attached' : '') . ' will be moderated before being shown.</li>';
}
@ -315,6 +382,7 @@ EOF;
</tr>
$captcha_html
$file_input_html
$embed_input_html
<tr>
<td class="postblock">
Password

4
js/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -4,55 +4,55 @@ function getCookie(name) {
if (parts.length == 2) return parts.pop().split(";").shift();
}
function storePassword() {
var newpostpassword = document.getElementById("newpostpassword");
if (newpostpassword) {
var expiration_date = new Date();
expiration_date.setFullYear(expiration_date.getFullYear() + 7);
document.cookie = "tinyib_password=" + encodeURIComponent(newpostpassword.value) + "; path=/; expires=" + expiration_date.toGMTString();
}
}
function quotePost(postID) {
var message_element = document.getElementById("message");
if (message_element) {
message_element.focus();
message_element.value += '>>' + postID + "\n";
}
$("#message").val('>>' + postID + "\n").focus();
return false;
}
function reloadCAPTCHA() {
var captcha_element = document.getElementById("captcha");
if (captcha_element) {
captcha_element.focus();
captcha_element.value = "";
}
var captchaimg_element = document.getElementById("captchaimage");
if (captchaimg_element) {
captchaimg_element.src += "#new";
}
$("#captcha").val("").focus();
$("#captchaimage").attr("src", $("#captchaimage").attr("src") + "#new")
return false;
}
document.addEventListener('DOMContentLoaded', function () {
var newpostpassword = document.getElementById("newpostpassword");
function showEmbed(id, embedhtml){
if($("#thumbembed"+ id).attr('expanded') != 'true') {
$("#thumbembed"+ id).hide();
$("#embed"+ id).show();
$("#embed"+ id).html(embedhtml);
$("#thumbembed"+ id).attr('expanded', 'true');
}else{
$("#embed"+ id).hide();
$("#embed"+ id).html('');
$("#thumbembed"+ id).show();
$("#thumbembed"+ id).attr('expanded', 'false');
}
}
$(function() {
var newpostpassword = $("#newpostpassword");
if (newpostpassword) {
newpostpassword.addEventListener("change", storePassword);
newpostpassword.change(function () {
var newpostpassword = $("#newpostpassword");
if (newpostpassword) {
var expiration_date = new Date();
expiration_date.setFullYear(expiration_date.getFullYear() + 7);
document.cookie = "tinyib_password=" + encodeURIComponent(newpostpassword.val()) + "; path=/; expires=" + expiration_date.toGMTString();
}
});
}
var password = getCookie("tinyib_password");
if (password && password != "") {
if (newpostpassword) {
newpostpassword.value = password;
newpostpassword.val(password);
}
var deletepostpassword = document.getElementById("deletepostpassword");
var deletepostpassword = $("#deletepostpassword");
if (deletepostpassword) {
deletepostpassword.value = password;
deletepostpassword.val(password);
}
}

View file

@ -29,10 +29,11 @@ define('TINYIB_DELAY', 30); // Delay (in seconds) between posts from t
define('TINYIB_MAXTHREADS', 100); // Oldest threads are discarded when the thread count passes this limit [0 to disable]
define('TINYIB_MAXREPLIES', 0); // Maximum replies before a thread stops bumping [0 to disable]
// File types
// Upload types
define('TINYIB_PIC', true); // Enable .jpg, .png and .gif image file upload
define('TINYIB_SWF', false); // Enable .swf Flash file upload
define('TINYIB_WEBM', false); // Enable .weba and .webm audio/video file upload (see README for instructions)
define('TINYIB_EMBED', false); // Enable embedding (e.g. YouTube, Vimeo, SoundCloud)
// File control
define('TINYIB_MAXKB', 2048); // Maximum file size in kilobytes [0 to disable]