Merge branch 'develop' of https://github.com/shish/shimmie2 into develop

This commit is contained in:
Shish 2016-10-09 22:33:25 +01:00
commit 4b9278d09b
33 changed files with 245 additions and 95 deletions

View File

@ -53,6 +53,7 @@ AddType audio/ogg oga ogg opus
AddType image/jpeg jpg jpeg AddType image/jpeg jpg jpeg
AddType image/bmp bmp AddType image/bmp bmp
AddType image/svg+xml svg svgz AddType image/svg+xml svg svgz
AddType image/x-icon ico ani cur
AddType image/webp webp AddType image/webp webp
AddType video/mp4 f4v f4p m4v mp4 AddType video/mp4 f4v f4p m4v mp4
AddType video/ogg ogv AddType video/ogg ogv

View File

@ -1102,6 +1102,11 @@ class Tag {
$tag = preg_replace("/^(\.+[\/\\\\])+/", "", $tag); # trailing slashes? $tag = preg_replace("/^(\.+[\/\\\\])+/", "", $tag); # trailing slashes?
$tag = trim($tag, ", \t\n\r\0\x0B"); $tag = trim($tag, ", \t\n\r\0\x0B");
if(mb_strlen($tag, 'UTF-8') > 255){
flash_message("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n");
continue;
}
if(!empty($tag)) { if(!empty($tag)) {
$tag_array[] = $tag; $tag_array[] = $tag;
} }

View File

@ -15,6 +15,16 @@ function html_escape($input) {
return htmlentities($input, ENT_QUOTES, "UTF-8"); return htmlentities($input, ENT_QUOTES, "UTF-8");
} }
/**
* Unescape data that was made safe for printing into HTML
*
* @param $input
* @return string
*/
function html_unescape($input) {
return html_entity_decode($input, ENT_QUOTES, "UTF-8");
}
/** /**
* Make sure some data is safe to be used in integer context * Make sure some data is safe to be used in integer context
* *

View File

@ -47,6 +47,7 @@ class ArrowkeyNavigation extends Extension {
(function($){ (function($){
$(document).keyup(function(e) { $(document).keyup(function(e) {
if($(e.target).is('input', 'textarea')){ return; } if($(e.target).is('input', 'textarea')){ return; }
if (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) { return; }
if (e.keyCode == 37) { window.location.href = '{$prev_url}'; } if (e.keyCode == 37) { window.location.href = '{$prev_url}'; }
else if (e.keyCode == 39) { window.location.href = '{$next_url}'; } else if (e.keyCode == 39) { window.location.href = '{$next_url}'; }
}); });

View File

@ -259,8 +259,6 @@ class CommentListTheme extends Themelet {
else { else {
$h_userlink = '<a class="username" href="'.make_link('user/'.$h_name).'">'.$h_name.'</a>'; $h_userlink = '<a class="username" href="'.make_link('user/'.$h_name).'">'.$h_name.'</a>';
} }
$stripped_nonl = str_replace("\n", "\\n", substr($tfe->stripped, 0, 50));
$stripped_nonl = str_replace("\r", "\\r", $stripped_nonl);
$hb = ($comment->owner_class == "hellbanned" ? "hb" : ""); $hb = ($comment->owner_class == "hellbanned" ? "hb" : "");
if($trim) { if($trim) {
@ -280,9 +278,14 @@ class CommentListTheme extends Themelet {
} }
$h_reply = " - <a href='javascript: replyTo($i_image_id, $i_comment_id, \"$h_name\")'>Reply</a>"; $h_reply = " - <a href='javascript: replyTo($i_image_id, $i_comment_id, \"$h_name\")'>Reply</a>";
$h_ip = $user->can("view_ip") ? "<br>".show_ip($comment->poster_ip, "Comment posted {$comment->posted}") : ""; $h_ip = $user->can("view_ip") ? "<br>".show_ip($comment->poster_ip, "Comment posted {$comment->posted}") : "";
$h_del = $user->can("delete_comment") ? $h_del = "";
' - <a onclick="return confirm(\'Delete comment by '.$h_name.':\\n'.$stripped_nonl.'\');" '. if ($user->can("delete_comment")) {
'href="'.make_link('comment/delete/'.$i_comment_id.'/'.$i_image_id).'">Del</a>' : ''; $comment_preview = substr(html_unescape($tfe->stripped), 0, 50);
$j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview");
$h_delete_script = html_escape("return confirm($j_delete_confirm_message);");
$h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id");
$h_del = " - <a onclick='$h_delete_script' href='$h_delete_link'>Del</a>";
}
$html = " $html = "
<div class=\"comment $hb\" id=\"c$i_comment_id\"> <div class=\"comment $hb\" id=\"c$i_comment_id\">
<div class=\"info\"> <div class=\"info\">

View File

@ -35,20 +35,6 @@ class IcoFileHandler extends Extension {
} }
} }
public function onPageRequest(PageRequestEvent $event) {
global $page;
if($event->page_matches("get_ico")) {
$id = int_escape($event->get_arg(0));
$image = Image::by_id($id);
$hash = $image->hash;
$ha = substr($hash, 0, 2);
$page->set_type("image/x-icon");
$page->set_mode("data");
$page->set_data(file_get_contents("images/$ha/$hash"));
}
}
/** /**
* @param string $ext * @param string $ext
* @return bool * @return bool
@ -67,13 +53,15 @@ class IcoFileHandler extends Extension {
$image = new Image(); $image = new Image();
$fp = fopen($filename, "r"); $fp = fopen($filename, "r");
$header = unpack("snull/stype/scount", fread($fp, 6)); $header = unpack("Snull/Stype/Scount", fread($fp, 6));
$subheader = unpack("cwidth/cheight/ccolours/cnull/splanes/sbpp/lsize/loffset", fread($fp, 16)); $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16));
fclose($fp); fclose($fp);
$image->width = $subheader['width']; $width = $subheader['width'];
$image->height = $subheader['height']; $height = $subheader['height'];
$image->width = $width == 0 ? 256 : $width;
$image->height = $height == 0 ? 256 : $height;
$image->filesize = $metadata['size']; $image->filesize = $metadata['size'];
$image->hash = $metadata['hash']; $image->hash = $metadata['hash'];
@ -92,7 +80,7 @@ class IcoFileHandler extends Extension {
private function check_contents($file) { private function check_contents($file) {
if(!file_exists($file)) return false; if(!file_exists($file)) return false;
$fp = fopen($file, "r"); $fp = fopen($file, "r");
$header = unpack("snull/stype/scount", fread($fp, 6)); $header = unpack("Snull/Stype/Scount", fread($fp, 6));
fclose($fp); fclose($fp);
return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1));
} }

View File

@ -4,7 +4,6 @@ class IcoHandlerTest extends ShimmiePHPUnitTestCase {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("lib/static/favicon.ico", "shimmie favicon"); $image_id = $this->post_image("lib/static/favicon.ico", "shimmie favicon");
$this->get_page("post/view/$image_id"); // test for no crash $this->get_page("post/view/$image_id"); // test for no crash
$this->get_page("get_ico/$image_id"); // test for no crash
# FIXME: test that the thumb works # FIXME: test that the thumb works
# FIXME: test that it gets displayed properly # FIXME: test that it gets displayed properly

View File

@ -2,9 +2,10 @@
class IcoFileHandlerTheme extends Themelet { class IcoFileHandlerTheme extends Themelet {
public function display_image(Page $page, Image $image) { public function display_image(Page $page, Image $image) {
$ilink = make_link("get_ico/{$image->id}/{$image->id}.ico"); $ilink = $image->get_image_link();
$html = " $html = "
<img id='main_image' src='$ilink'> <img id='main_image' class='shm-main-image' alt='main image' src='$ilink'
data-width='{$image->width}' data-height='{$image->height}'>
"; ";
$page->add_block(new Block("Image", $html, "main", 10)); $page->add_block(new Block("Image", $html, "main", 10));
} }

View File

@ -1,5 +1,7 @@
$(function() { $(function() {
function zoom(zoom_type) { function zoom(zoom_type, save_cookie) {
save_cookie = save_cookie === undefined ? true : save_cookie;
var img = $('.shm-main-image'); var img = $('.shm-main-image');
if(zoom_type == "full") { if(zoom_type == "full") {
@ -21,14 +23,21 @@ $(function() {
$(".shm-zoomer").val(zoom_type); $(".shm-zoomer").val(zoom_type);
Cookies.set("ui-image-zoom", zoom_type, {expires: 365}); if (save_cookie) {
Cookies.set("ui-image-zoom", zoom_type, {expires: 365});
}
} }
$(".shm-zoomer").change(function(e) { $(".shm-zoomer").change(function(e) {
zoom(this.options[this.selectedIndex].value); zoom(this.options[this.selectedIndex].value);
}); });
$(window).resize(function(e) {
$(".shm-zoomer").each(function (e) {
zoom(this.options[this.selectedIndex].value, false)
});
});
$(".shm-main-image").click(function(e) { $("img.shm-main-image").click(function(e) {
switch(Cookies.get("ui-image-zoom")) { switch(Cookies.get("ui-image-zoom")) {
case "full": zoom("width"); break; case "full": zoom("width"); break;
default: zoom("full"); break; default: zoom("full"); break;

View File

@ -101,6 +101,9 @@ class MiniSVGParser {
/** @var int */ /** @var int */
public $height=0; public $height=0;
/** @var int */
private $xml_depth=0;
/** @param string $file */ /** @param string $file */
function __construct($file) { function __construct($file) {
$xml_parser = xml_parser_create(); $xml_parser = xml_parser_create();
@ -110,13 +113,15 @@ class MiniSVGParser {
} }
function startElement($parser, $name, $attrs) { function startElement($parser, $name, $attrs) {
if($name == "SVG") { if($name == "SVG" && $this->xml_depth == 0) {
$this->width = int_escape($attrs["WIDTH"]); $this->width = int_escape($attrs["WIDTH"]);
$this->height = int_escape($attrs["HEIGHT"]); $this->height = int_escape($attrs["HEIGHT"]);
} }
$this->xml_depth++;
} }
function endElement($parser, $name) { function endElement($parser, $name) {
$this->xml_depth--;
} }
} }

View File

@ -5,8 +5,8 @@ class SVGFileHandlerTheme extends Themelet {
$ilink = make_link("get_svg/{$image->id}/{$image->id}.svg"); $ilink = make_link("get_svg/{$image->id}/{$image->id}.svg");
// $ilink = $image->get_image_link(); // $ilink = $image->get_image_link();
$html = " $html = "
<object data='$ilink' type='image/svg+xml' width='{$image->width}' height='{$image->height}'> <object data='$ilink' type='image/svg+xml' data-width='{$image->width}' data-height='{$image->height}' id='main_image' class='shm-main-image'>
<embed src='$ilink' type='image/svg+xml' width='{$image->width}' height='{$image->height}' /> <embed src='$ilink' type='image/svg+xml' />
</object> </object>
"; ";
$page->add_block(new Block("Image", $html, "main", 10)); $page->add_block(new Block("Image", $html, "main", 10));

View File

@ -42,7 +42,12 @@ class VideoFileHandlerTheme extends Themelet {
$html .= $html_fallback; $html .= $html_fallback;
} else { } else {
$html .= " $html .= "
<video controls " . ($autoplay ? 'autoplay' : '') . " width=\"100%\" " . ($loop ? 'loop' : '') . "> <video controls class='shm-main-image' id='main_image' alt='main image'"
. ($autoplay ? ' autoplay' : '')
. ($loop ? ' loop' : '')
. " data-width='{$image->width}' "
. " data-height='{$image->height}'>
<source src='{$ilink}' type='{$supportedExts[$ext]}'> <source src='{$ilink}' type='{$supportedExts[$ext]}'>
<!-- If browser doesn't support filetype, fallback to flash --> <!-- If browser doesn't support filetype, fallback to flash -->

View File

@ -6,6 +6,7 @@ class HomeTheme extends Themelet {
$page->add_auto_html_headers(); $page->add_auto_html_headers();
$hh = $page->get_all_html_headers(); $hh = $page->get_all_html_headers();
$page->set_data(<<<EOD $page->set_data(<<<EOD
<!doctype html>
<html> <html>
<head> <head>
<title>$sitename</title> <title>$sitename</title>

View File

@ -37,7 +37,7 @@ class ImageViewCounter extends Extension {
$event->add_part( $event->add_part(
"<tr><th>Views:</th><td>". "<tr><th>Views:</th><td>".
$this->get_view_count($event->image->id) . $this->get_view_count($event->image->id) .
"</th></tr>", 38); "</tr>", 38);
} }
# Installs DB table # Installs DB table

View File

@ -24,13 +24,18 @@ function toggle_tag( button, id ) {
var string = list.val(); var string = list.val();
if( (string.indexOf(id) == 0) || (string.indexOf(":"+id) > -1) ) { if( (string.indexOf(id) == 0) || (string.indexOf(":"+id) > -1) ) {
$(button).css('border', 'none'); $(button).removeClass('mass-tagger-selected');
string = string.replace(id, ''); string = string.replace(id, '');
list.val(string); list.val(string);
} }
else { else {
$(button).css('border', '3px solid blue'); $(button).addClass('mass-tagger-selected');
string += id; string += id;
list.val(string); list.val(string);
} }
} }
$(function () {
// Clear the selection, in case it was autocompleted by the browser.
$('#mass_tagger_ids').val("");
});

View File

@ -0,0 +1,3 @@
.mass-tagger-selected {
border: 3px solid blue;
}

View File

@ -136,14 +136,13 @@ class PoolsTheme extends Themelet {
$page->set_title($heading); $page->set_title($heading);
$page->set_heading($heading); $page->set_heading($heading);
$nav_html = '<a href="'.make_link().'">Index</a>';
$poolnav_html = ' $poolnav_html = '
<a href="'.make_link("pool/list").'">Pool Index</a> <a href="'.make_link("pool/list").'">Pool Index</a>
<br><a href="'.make_link("pool/new").'">Create Pool</a> <br><a href="'.make_link("pool/new").'">Create Pool</a>
<br><a href="'.make_link("pool/updated").'">Pool Changes</a> <br><a href="'.make_link("pool/updated").'">Pool Changes</a>
'; ';
$page->add_block(new Block($nav_html, null, "left", 5, "indexnavleft")); $page->add_block(new NavBlock());
$page->add_block(new Block("Pool Navigation", $poolnav_html, "left", 10)); $page->add_block(new Block("Pool Navigation", $poolnav_html, "left", 10));
if(count($pools) == 1) { if(count($pools) == 1) {
@ -154,8 +153,9 @@ class PoolsTheme extends Themelet {
} }
} }
$bb = new BBCode(); $tfe = new TextFormattingEvent($pool['description']);
$page->add_block(new Block(html_escape($pool['title']), $bb->format($pool['description']), "main", 10)); send_event($tfe);
$page->add_block(new Block(html_escape($pool['title']), $tfe->formatted, "main", 10));
} }
} }
@ -194,7 +194,7 @@ class PoolsTheme extends Themelet {
global $user; global $user;
$editor = "\n".make_form( make_link('pool/import') ).' $editor = "\n".make_form( make_link('pool/import') ).'
<input type="text" name="pool_tag" id="edit_pool_tag" value="Please enter a tag" onclick="this.value=\'\';"/> <input type="text" name="pool_tag" id="edit_pool_tag" placeholder="Please enter a tag"/>
<input type="submit" name="edit" id="edit_pool_import_btn" value="Import"/> <input type="submit" name="edit" id="edit_pool_import_btn" value="Import"/>
<input type="hidden" name="pool_id" value="'.$pool['id'].'"> <input type="hidden" name="pool_id" value="'.$pool['id'].'">
</form> </form>
@ -214,7 +214,7 @@ class PoolsTheme extends Themelet {
if($user->id == $pool['user_id'] || $user->is_admin()){ if($user->id == $pool['user_id'] || $user->is_admin()){
$editor .= " $editor .= "
<script language='javascript' type='text/javascript'> <script type='text/javascript'>
<!-- <!--
function confirm_action() { function confirm_action() {
return confirm('Are you sure that you want to delete this pool?'); return confirm('Are you sure that you want to delete this pool?');
@ -231,7 +231,7 @@ class PoolsTheme extends Themelet {
if($check_all) { if($check_all) {
$editor .= " $editor .= "
<script language='javascript' type='text/javascript'> <script type='text/javascript'>
<!-- <!--
function setAll(value) { function setAll(value) {
$('[name=\"check[]\"]').attr('checked', value); $('[name=\"check[]\"]').attr('checked', value);
@ -257,7 +257,7 @@ class PoolsTheme extends Themelet {
$this->display_top($pool, "Importing Posts", true); $this->display_top($pool, "Importing Posts", true);
$pool_images = " $pool_images = "
<script language='javascript' type='text/javascript'> <script type='text/javascript'>
<!-- <!--
function confirm_action() { function confirm_action() {
return confirm('Are you sure you want to add selected posts to this pool?'); return confirm('Are you sure you want to add selected posts to this pool?');

View File

@ -15,24 +15,43 @@ class RandomList extends Extension {
global $config, $page; global $config, $page;
if($event->page_matches("random")) { if($event->page_matches("random")) {
if(isset($_GET['search'])) {
// implode(explode()) to resolve aliases and sanitise
$search = url_escape(Tag::implode(Tag::explode($_GET['search'], false)));
if(empty($search)) {
$page->set_mode("redirect");
$page->set_redirect(make_link("random"));
}
else {
$page->set_mode("redirect");
$page->set_redirect(make_link('random/'.$search));
}
return;
}
if($event->count_args() == 0) {
$search_terms = array();
}
else if($event->count_args() == 1) {
$search_terms = explode(' ', $event->get_arg(0));
}
else {
throw new SCoreException("Error: too many arguments.");
}
// set vars // set vars
$page->title = "Random Images";
$images_per_page = $config->get_int("random_images_list_count", 12); $images_per_page = $config->get_int("random_images_list_count", 12);
$random_images = array(); $random_images = array();
$random_html = "<b>Refresh the page to view more images</b>
<div class='shm-image-list'>";
// generate random images // generate random images
for ($i = 0; $i < $images_per_page; $i++) for ($i = 0; $i < $images_per_page; $i++) {
array_push($random_images, Image::by_random()); $random_image = Image::by_random($search_terms);
if (!$random_image) continue;
array_push($random_images, $random_image);
}
// create html to display images $this->theme->set_page($search_terms);
foreach ($random_images as $image) $this->theme->display_page($page, $random_images);
$random_html .= $this->theme->build_thumb_html($image);
// display it
$random_html .= "</div>";
$page->add_block(new Block("Random Images", $random_html));
} }
} }

View File

@ -1,4 +1,56 @@
<?php <?php
/* needed for access to build_thumb_html */
class RandomListTheme extends Themelet {} class RandomListTheme extends Themelet {
protected $search_terms;
/**
* @param string[] $search_terms
*/
public function set_page($search_terms) {
$this->search_terms = $search_terms;
}
/**
* @param Page $page
* @param Image[] $images
*/
public function display_page(Page $page, $images) {
$page->title = "Random Images";
$html = "<b>Refresh the page to view more images</b>";
if (count($images)) {
$html .= "<div class='shm-image-list'>";
foreach ($images as $image)
$html .= $this->build_thumb_html($image);
$html .= "</div>";
} else {
$html .= "<br/><br/>No images were found to match the search criteria";
}
$page->add_block(new Block("Random Images", $html));
$nav = $this->build_navigation($this->search_terms);
$page->add_block(new Block("Navigation", $nav, "left", 0));
}
/**
* @param string[] $search_terms
* @return string
*/
protected function build_navigation($search_terms) {
$h_search_string = html_escape(implode(" ", $search_terms));
$h_search_link = make_link("random");
$h_search = "
<p><form action='$h_search_link' method='GET'>
<input type='search' name='search' value='$h_search_string' placeholder='Search random list' class='autocomplete_tags' autocomplete='off' />
<input type='hidden' name='q' value='/random'>
<input type='submit' value='Find' style='display: none;' />
</form>
";
return $h_search;
}
}

View File

@ -103,14 +103,15 @@ class Ratings extends Extension {
} }
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) {
if($this->can_rate()) { $event->add_part($this->theme->get_rater_html($event->image->id, $event->image->rating, $this->can_rate()), 80);
$event->add_part($this->theme->get_rater_html($event->image->id, $event->image->rating), 80);
}
} }
public function onImageInfoSet(ImageInfoSetEvent $event) { public function onImageInfoSet(ImageInfoSetEvent $event) {
if($this->can_rate() && isset($_POST["rating"])) { if($this->can_rate() && isset($_POST["rating"])) {
send_event(new RatingSetEvent($event->image, $_POST['rating'])); $rating = $_POST["rating"];
if (Ratings::rating_is_valid($rating)) {
send_event(new RatingSetEvent($event->image, $rating));
}
} }
} }
@ -211,6 +212,22 @@ class Ratings extends Extension {
} }
} }
/**
* @param string $rating
* @return bool
*/
public static function rating_is_valid(/*string*/ $rating) {
switch($rating) {
case "s":
case "q":
case "e":
case "u":
return true;
default:
return false;
}
}
/** /**
* FIXME: this is a bit ugly and guessey, should have proper options * FIXME: this is a bit ugly and guessey, should have proper options
* *

View File

@ -6,17 +6,25 @@ class RatingsTheme extends Themelet {
* @param string $rating * @param string $rating
* @return string * @return string
*/ */
public function get_rater_html(/*int*/ $image_id, /*string*/ $rating) { public function get_rater_html(/*int*/ $image_id, /*string*/ $rating, /*bool*/ $can_rate) {
$s_checked = $rating == 's' ? " checked" : ""; $s_checked = $rating == 's' ? " checked" : "";
$q_checked = $rating == 'q' ? " checked" : ""; $q_checked = $rating == 'q' ? " checked" : "";
$e_checked = $rating == 'e' ? " checked" : ""; $e_checked = $rating == 'e' ? " checked" : "";
$human_rating = Ratings::rating_to_human($rating);
$html = " $html = "
<tr> <tr>
<th>Rating</th> <th>Rating</th>
<td> <td>
<input type='radio' name='rating' value='s' id='s'$s_checked><label for='s'>Safe</label> ".($can_rate ? "
<input type='radio' name='rating' value='q' id='q'$q_checked><label for='q'>Questionable</label> <span class='view'>$human_rating</span>
<input type='radio' name='rating' value='e' id='e'$e_checked><label for='e'>Explicit</label> <span class='edit'>
<input type='radio' name='rating' value='s' id='s'$s_checked><label for='s'>Safe</label>
<input type='radio' name='rating' value='q' id='q'$q_checked><label for='q'>Questionable</label>
<input type='radio' name='rating' value='e' id='e'$e_checked><label for='e'>Explicit</label>
</span>
" : "
$human_rating
")."
</td> </td>
</tr> </tr>
"; ";

View File

@ -30,7 +30,7 @@ class RelationshipsTheme extends Themelet {
global $user; global $user;
$h_parent_id = $image->parent_id; $h_parent_id = $image->parent_id;
$s_parent_id = $h_parent_id ?: "None."; $s_parent_id = $h_parent_id ?: "None";
$html = "<tr>\n". $html = "<tr>\n".
" <th>Parent</th>\n". " <th>Parent</th>\n".

View File

@ -262,7 +262,7 @@ class Setup extends Extension {
$full = (@$_SERVER["HTTPS"] ? "https://" : "http://") . $host . $_SERVER["PHP_SELF"]; $full = (@$_SERVER["HTTPS"] ? "https://" : "http://") . $host . $_SERVER["PHP_SELF"];
$test_url = str_replace("/index.php", "/nicetest", $full); $test_url = str_replace("/index.php", "/nicetest", $full);
$nicescript = "<script language='javascript'> $nicescript = "<script type='text/javascript'>
function getHTTPObject() { function getHTTPObject() {
if (window.XMLHttpRequest){ if (window.XMLHttpRequest){
return new XMLHttpRequest(); return new XMLHttpRequest();

View File

@ -214,7 +214,7 @@ class TagEdit extends Extension {
public function onImageInfoSet(ImageInfoSetEvent $event) { public function onImageInfoSet(ImageInfoSetEvent $event) {
global $user; global $user;
if($user->can("edit_image_owner")) { if($user->can("edit_image_owner") && isset($_POST['tag_edit__owner'])) {
$owner = User::by_name($_POST['tag_edit__owner']); $owner = User::by_name($_POST['tag_edit__owner']);
if ($owner instanceof User) { if ($owner instanceof User) {
send_event(new OwnerSetEvent($event->image, $owner)); send_event(new OwnerSetEvent($event->image, $owner));

View File

@ -21,6 +21,13 @@ class TagEditTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
public function testTagEdit_tooLong() {
$this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", str_repeat("a", 500));
$this->get_page("post/view/$image_id");
$this->assert_title("Image $image_id: tagme");
}
public function testSourceEdit() { public function testSourceEdit() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");

View File

@ -24,7 +24,7 @@ class TagEditTheme extends Themelet {
$html = make_form(make_link("tag_edit/mass_source_set"), "POST") . " $html = make_form(make_link("tag_edit/mass_source_set"), "POST") . "
<input type='hidden' name='tags' value='$h_terms'> <input type='hidden' name='tags' value='$h_terms'>
<input type='text' name='source' value=''> <input type='text' name='source' value=''>
<input type='submit' value='Set Source For All' onclick='return confirm(\"This will mass-edit all sources on the page.\nAre you sure you want to do this?\")'> <input type='submit' value='Set Source For All' onclick='return confirm(\"This will mass-edit all sources on the page.\\nAre you sure you want to do this?\")'>
</form> </form>
"; ";
return $html; return $html;

View File

@ -134,7 +134,7 @@ class TagEditCloud extends Extension {
} }
$size = sprintf("%.2f", max($row['scaled'],0.5)); $size = sprintf("%.2f", max($row['scaled'],0.5));
$js = htmlspecialchars('tageditcloud_toggle_tag(this,'.json_encode($full_tag).')',ENT_QUOTES); //Ugly, but it works $js = html_escape('tageditcloud_toggle_tag(this,'.json_encode($full_tag).')'); //Ugly, but it works
if(array_search($row['tag'],$image->get_tag_array()) !== FALSE) { if(array_search($row['tag'],$image->get_tag_array()) !== FALSE) {
if($used_first) { if($used_first) {

View File

@ -297,13 +297,15 @@ class TagList extends Extension {
$lastLetter = ""; $lastLetter = "";
foreach($tag_data as $row) { foreach($tag_data as $row) {
$h_tag = html_escape($row['tag']); $tag = $row['tag'];
$count = $row['count']; if($lastLetter != mb_strtolower(substr($tag, 0, count($starts_with)+1))) {
if($lastLetter != mb_strtolower(substr($h_tag, 0, count($starts_with)+1))) { $lastLetter = mb_strtolower(substr($tag, 0, count($starts_with)+1));
$lastLetter = mb_strtolower(substr($h_tag, 0, count($starts_with)+1)); $h_lastLetter = html_escape($lastLetter);
$html .= "<p>$lastLetter<br>"; $html .= "<p>$h_lastLetter<br>";
} }
$link = $this->tag_link($row['tag']); $link = $this->tag_link($tag);
$h_tag = html_escape($tag);
$count = $row['count'];
$html .= "<a href='$link'>$h_tag&nbsp;($count)</a>\n"; $html .= "<a href='$link'>$h_tag&nbsp;($count)</a>\n";
} }

View File

@ -216,7 +216,7 @@ class TagListTheme extends Themelet {
$count = $row['calc_count']; $count = $row['calc_count'];
// if($n++) $display_html .= "\n<br/>"; // if($n++) $display_html .= "\n<br/>";
if(!is_null($config->get_string('info_link'))) { if(!is_null($config->get_string('info_link'))) {
$link = html_escape(str_replace('$tag', $tag, $config->get_string('info_link'))); $link = html_escape(str_replace('$tag', url_escape($tag), $config->get_string('info_link')));
$display_html .= ' <a class="tag_info_link'.$tag_category_css.'" '.$tag_category_style.'href="'.$link.'">?</a>'; $display_html .= ' <a class="tag_info_link'.$tag_category_css.'" '.$tag_category_style.'href="'.$link.'">?</a>';
} }
$link = $this->tag_link($row['tag']); $link = $this->tag_link($row['tag']);

View File

@ -59,7 +59,7 @@ class UploadTheme extends Themelet {
$upload_list .= " $upload_list .= "
<tr> <tr>
<td colspan='2'><input type='file' name='data$i'></td> <td colspan='2'><input type='file' name='data$i'></td>
<td colspan='2'><input type='text' name='url$i'</td> <td colspan='2'><input type='text' name='url$i'></td>
<td colspan='2'><input type='text' name='tags$i' class='autocomplete_tags' autocomplete='off'></td> <td colspan='2'><input type='text' name='tags$i' class='autocomplete_tags' autocomplete='off'></td>
</tr> </tr>
"; ";

View File

@ -111,12 +111,15 @@ class CustomCommentListTheme extends CommentListTheme {
$i_image_id = int_escape($comment->image_id); $i_image_id = int_escape($comment->image_id);
$h_posted = autodate($comment->posted); $h_posted = autodate($comment->posted);
$stripped_nonl = str_replace("\n", "\\n", substr($tfe->stripped, 0, 50));
$stripped_nonl = str_replace("\r", "\\r", $stripped_nonl);
$h_userlink = "<a class='username' href='".make_link("user/$h_name")."'>$h_name</a>"; $h_userlink = "<a class='username' href='".make_link("user/$h_name")."'>$h_name</a>";
$h_del = $user->can("delete_comment") ? $h_del = "";
' - <a onclick="return confirm(\'Delete comment by '.$h_name.':\\n'.$stripped_nonl.'\');" '. if ($user->can("delete_comment")) {
'href="'.make_link('comment/delete/'.$i_comment_id.'/'.$i_image_id).'">Del</a>' : ''; $comment_preview = substr(html_unescape($tfe->stripped), 0, 50);
$j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview");
$h_delete_script = html_escape("return confirm($j_delete_confirm_message);");
$h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id");
$h_del = " - <a onclick='$h_delete_script' href='$h_delete_link'>Del</a>";
}
//$h_imagelink = $trim ? "<a href='".make_link("post/view/$i_image_id")."'>&gt;&gt;&gt;</a>\n" : ""; //$h_imagelink = $trim ? "<a href='".make_link("post/view/$i_image_id")."'>&gt;&gt;&gt;</a>\n" : "";
if($trim) { if($trim) {
return "<p class='comment'>$h_userlink $h_del<br/>$h_posted<br/>$h_comment</p>"; return "<p class='comment'>$h_userlink $h_del<br/>$h_posted<br/>$h_comment</p>";

View File

@ -101,12 +101,15 @@ class CustomCommentListTheme extends CommentListTheme {
$i_image_id = int_escape($comment->image_id); $i_image_id = int_escape($comment->image_id);
$h_posted = autodate($comment->posted); $h_posted = autodate($comment->posted);
$stripped_nonl = str_replace("\n", "\\n", substr($tfe->stripped, 0, 50));
$stripped_nonl = str_replace("\r", "\\r", $stripped_nonl);
$h_userlink = "<a class='username' href='".make_link("user/$h_name")."'>$h_name</a>"; $h_userlink = "<a class='username' href='".make_link("user/$h_name")."'>$h_name</a>";
$h_del = $user->can("delete_comment") ? $h_del = "";
' - <a onclick="return confirm(\'Delete comment by '.$h_name.':\\n'.$stripped_nonl.'\');" '. if ($user->can("delete_comment")) {
'href="'.make_link('comment/delete/'.$i_comment_id.'/'.$i_image_id).'">Del</a>' : ''; $comment_preview = substr(html_unescape($tfe->stripped), 0, 50);
$j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview");
$h_delete_script = html_escape("return confirm($j_delete_confirm_message);");
$h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id");
$h_del = " - <a onclick='$h_delete_script' href='$h_delete_link'>Del</a>";
}
//$h_imagelink = $trim ? "<a href='".make_link("post/view/$i_image_id")."'>&gt;&gt;&gt;</a>\n" : ""; //$h_imagelink = $trim ? "<a href='".make_link("post/view/$i_image_id")."'>&gt;&gt;&gt;</a>\n" : "";
if($trim) { if($trim) {
return "<p class='comment'>$h_userlink $h_del<br/>$h_posted<br/>$h_comment</p>"; return "<p class='comment'>$h_userlink $h_del<br/>$h_posted<br/>$h_comment</p>";

View File

@ -70,13 +70,16 @@ class CustomCommentListTheme extends CommentListTheme {
$i_comment_id = int_escape($comment->comment_id); $i_comment_id = int_escape($comment->comment_id);
$i_image_id = int_escape($comment->image_id); $i_image_id = int_escape($comment->image_id);
$stripped_nonl = str_replace("\n", "\\n", substr($tfe->stripped, 0, 50));
$stripped_nonl = str_replace("\r", "\\r", $stripped_nonl);
$h_userlink = "<a href='".make_link("user/$h_name")."'>$h_name</a>"; $h_userlink = "<a href='".make_link("user/$h_name")."'>$h_name</a>";
$h_date = $comment->posted; $h_date = $comment->posted;
$h_del = $user->can("delete_comment") ? $h_del = "";
' - <a onclick="return confirm(\'Delete comment by '.$h_name.':\\n'.$stripped_nonl.'\');" '. if ($user->can("delete_comment")) {
'href="'.make_link('comment/delete/'.$i_comment_id.'/'.$i_image_id).'">Del</a>' : ''; $comment_preview = substr(html_unescape($tfe->stripped), 0, 50);
$j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview");
$h_delete_script = html_escape("return confirm($j_delete_confirm_message);");
$h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id");
$h_del = " - <a onclick='$h_delete_script' href='$h_delete_link'>Del</a>";
}
$h_reply = "[<a href='".make_link("post/view/$i_image_id")."'>Reply</a>]"; $h_reply = "[<a href='".make_link("post/view/$i_image_id")."'>Reply</a>]";
if($inner_id == 0) { if($inner_id == 0) {