Merge branch 'master' of github.com:shish/shimmie2

This commit is contained in:
Shish 2012-03-10 19:13:42 +00:00
commit 89efc3b524
23 changed files with 306 additions and 275 deletions

View file

@ -35,6 +35,14 @@ class AdminBuildingEvent extends Event {
} }
} }
class AdminActionEvent extends Event {
var $action;
var $redirect = true;
public function __construct(/*string*/ $action) {
$this->action = $action;
}
}
class AdminPage extends Extension { class AdminPage extends Extension {
public function onPageRequest(PageRequestEvent $event) { public function onPageRequest(PageRequestEvent $event) {
global $page, $user; global $page, $user;
@ -44,61 +52,31 @@ class AdminPage extends Extension {
$this->theme->display_permission_denied(); $this->theme->display_permission_denied();
} }
else { else {
send_event(new AdminBuildingEvent($page)); if($event->count_args() == 0) {
} send_event(new AdminBuildingEvent($page));
}
if($event->page_matches("admin_utils")) {
if($user->is_admin() && $user->check_auth_token()) {
log_info("admin", "Util: {$_POST['action']}");
set_time_limit(0);
$redirect = false;
switch($_POST['action']) {
case 'delete by query':
$this->delete_by_query($_POST['query']);
$redirect = true;
break;
case 'lowercase all tags':
$this->lowercase_all_tags();
$redirect = true;
break;
case 'recount tag use':
$this->recount_tag_use();
$redirect = true;
break;
case 'purge unused tags':
$this->purge_unused_tags();
$redirect = true;
break;
case 'convert to innodb':
$this->convert_to_innodb();
$redirect = true;
break;
case 'database dump':
$this->dbdump($page);
break;
case 'reset image ids':
$this->reset_imageids();
$redirect = true;
break;
case 'image dump':
$this->imgdump($page);
break;
} }
else {
$action = $event->get_arg(0);
$aae = new AdminActionEvent($action);
if($redirect) { if($user->check_auth_token()) {
$page->set_mode("redirect"); log_info("admin", "Util: $action");
$page->set_redirect(make_link("admin")); set_time_limit(0);
send_event($aae);
}
if($aae->redirect) {
$page->set_mode("redirect");
$page->set_redirect(make_link("admin"));
}
} }
} }
} }
} }
public function onAdminBuilding(AdminBuildingEvent $event) { public function onAdminBuilding(AdminBuildingEvent $event) {
global $page; $this->theme->display_page();
$this->theme->display_page($page); $this->theme->display_form();
$this->theme->display_form($page);
} }
public function onUserBlockBuilding(UserBlockBuildingEvent $event) { public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
@ -108,17 +86,38 @@ class AdminPage extends Extension {
} }
} }
private function delete_by_query(/*array(string)*/ $query) { public function onAdminAction(AdminActionEvent $event) {
$action = $event->action;
if(method_exists($this, $action)) {
$event->redirect = $this->$action();
}
}
public function onPostListBuilding(PostListBuildingEvent $event) {
global $user;
if($user->is_admin() && !empty($event->search_terms)) {
$this->theme->display_dbq(implode(" ", $event->search_terms));
}
}
private function delete_by_query() {
global $page, $user; global $page, $user;
$query = $_POST['query'];
assert(strlen($query) > 1); assert(strlen($query) > 1);
foreach(Image::find_images(0, 1000000, Tag::explode($query)) as $image) { foreach(Image::find_images(0, 1000000, Tag::explode($query)) as $image) {
send_event(new ImageDeletionEvent($image)); send_event(new ImageDeletionEvent($image));
} }
$page->set_mode("redirect");
$page->set_redirect(make_link("post/list"));
return false;
} }
private function lowercase_all_tags() { private function lowercase_all_tags() {
global $database; global $database;
$database->execute("UPDATE tags SET tag=lower(tag)"); $database->execute("UPDATE tags SET tag=lower(tag)");
return true;
} }
private function recount_tag_use() { private function recount_tag_use() {
@ -128,16 +127,21 @@ class AdminPage extends Extension {
SET count = COALESCE( SET count = COALESCE(
(SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id), (SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id),
0 0
)"); )
");
return true;
} }
private function purge_unused_tags() { private function purge_unused_tags() {
global $database; global $database;
$this->recount_tag_use(); $this->recount_tag_use();
$database->Execute("DELETE FROM tags WHERE count=0"); $database->Execute("DELETE FROM tags WHERE count=0");
return true;
} }
private function dbdump(Page $page) { private function dump_database() {
global $page;
$matches = array(); $matches = array();
preg_match("#^(?P<proto>\w+)\:(?:user=(?P<user>\w+)(?:;|$)|password=(?P<password>\w+)(?:;|$)|host=(?P<host>[\w\.\-]+)(?:;|$)|dbname=(?P<dbname>[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); preg_match("#^(?P<proto>\w+)\:(?:user=(?P<user>\w+)(?:;|$)|password=(?P<password>\w+)(?:;|$)|host=(?P<host>[\w\.\-]+)(?:;|$)|dbname=(?P<dbname>[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches);
$software = $matches['proto']; $software = $matches['proto'];
@ -157,6 +161,8 @@ class AdminPage extends Extension {
$page->set_type("application/x-unknown"); $page->set_type("application/x-unknown");
$page->set_filename('shimmie-'.date('Ymd').'.sql'); $page->set_filename('shimmie-'.date('Ymd').'.sql');
$page->set_data(shell_exec($cmd)); $page->set_data(shell_exec($cmd));
return false;
} }
private function check_for_orphanned_images() { private function check_for_orphanned_images() {
@ -169,23 +175,29 @@ class AdminPage extends Extension {
} }
} }
} }
return true;
} }
/* /*
private function convert_to_innodb() { private function convert_to_innodb() {
global $database; global $database;
if($database->engine->name == "mysql") {
$tables = $database->db->MetaTables(); if($database->engine->name != "mysql") return;
foreach($tables as $table) {
log_info("upgrade", "converting $table to innodb"); $tables = $database->db->MetaTables();
$database->execute("ALTER TABLE $table TYPE=INNODB"); foreach($tables as $table) {
} log_info("upgrade", "converting $table to innodb");
$database->execute("ALTER TABLE $table TYPE=INNODB");
} }
return true;
} }
*/ */
private function reset_imageids() { private function reset_image_ids() {
global $database; global $database;
if($database->engine->name != "mysql") return;
//This might be a bit laggy on boards with lots of images (?) //This might be a bit laggy on boards with lots of images (?)
//Seems to work fine with 1.2k~ images though. //Seems to work fine with 1.2k~ images though.
$i = 0; $i = 0;
@ -221,15 +233,17 @@ class AdminPage extends Extension {
} }
$count = (count($image)) + 1; $count = (count($image)) + 1;
$database->execute("ALTER TABLE images AUTO_INCREMENT=".$count); $database->execute("ALTER TABLE images AUTO_INCREMENT=".$count);
return true;
} }
private function imgdump(Page $page) { private function download_all_images() {
global $database; global $database, $page;
$zip = new ZipArchive; $zip = new ZipArchive;
$images = $database->get_all("SELECT * FROM images"); $images = $database->get_all("SELECT * FROM images");
$filename = 'imgdump-'.date('Ymd').'.zip'; $filename = 'imgdump-'.date('Ymd').'.zip';
if($zip->open($filename, 1 ? ZIPARCHIVE::OVERWRITE:ZIPARCHIVE::CREATE)===TRUE){ if($zip->open($filename, 1 ? ZIPARCHIVE::OVERWRITE:ZIPARCHIVE::CREATE) === TRUE){
foreach($images as $img){ foreach($images as $img){
$hash = $img["hash"]; $hash = $img["hash"];
preg_match("^[A-Za-z0-9]{2}^", $hash, $matches); preg_match("^[A-Za-z0-9]{2}^", $hash, $matches);
@ -241,9 +255,11 @@ class AdminPage extends Extension {
} }
$zip->close(); $zip->close();
} }
$page->set_mode("redirect"); $page->set_mode("redirect");
$page->set_redirect(make_link($filename)); //Fairly sure there is better way to do this.. $page->set_redirect(make_link($filename)); //Fairly sure there is better way to do this..
//TODO: Delete file after downloaded? //TODO: Delete file after downloaded?
return false; // we do want a redirect, but a manual one
} }
} }
?> ?>

18
contrib/admin/style.css Normal file
View file

@ -0,0 +1,18 @@
.admin {
padding: 4px;
border-radius: 4px;
background: green;
margin: 6px;
width: 200px;
display: inline-block;
}
.admin.protected {
background: red;
}
.admin INPUT[type="submit"] {
width: 100%;
}
.admin.protected INPUT[type="submit"] {
width: 90%;
}

View file

@ -4,81 +4,61 @@ class AdminPageTheme extends Themelet {
/* /*
* Show the basics of a page, for other extensions to add to * Show the basics of a page, for other extensions to add to
*/ */
public function display_page(Page $page) { public function display_page() {
global $page;
$page->set_title("Admin Tools"); $page->set_title("Admin Tools");
$page->set_heading("Admin Tools"); $page->set_heading("Admin Tools");
$page->add_block(new NavBlock()); $page->add_block(new NavBlock());
} }
protected function button(/*string*/ $name, /*string*/ $action, /*boolean*/ $protected=false) {
$c_protected = $protected ? " protected" : "";
$html = make_form(make_link("admin/$action"), "POST", false, false, false, "admin$c_protected");
if($protected) {
$html .= "<input type='checkbox' onclick='$(\"#$action\").attr(\"disabled\", !$(this).is(\":checked\"))'>";
$html .= "<input type='submit' id='$action' value='$name' disabled='true'>";
}
else {
$html .= "<input type='submit' id='$action' value='$name'>";
}
$html .= "</form>\n";
return $html;
}
/* /*
* Show a form which links to admin_utils with POST[action] set to one of: * Show a form which links to admin_utils with POST[action] set to one of:
* 'lowercase all tags' * 'lowercase all tags'
* 'recount tag use' * 'recount tag use'
* 'purge unused tags' * 'purge unused tags'
*/ */
public function display_form(Page $page) { public function display_form() {
global $user; global $page, $database;
$html = ' $html = "";
<script type="text/javascript"> $html .= $this->button("All tags to lowercase", "lowercase_all_tags", true);
function imgidconfirm(){ $html .= $this->button("Recount tag use", "recount_tag_user", false);
if(document.getElementById("misc").selectedIndex == 4){ $html .= $this->button("Purge unused tags", "purge_unused_tags", true);
if(confirm("This function WILL break any bookmarks & links.\n The event log will also not be updated with new ids. \n Are you sure you wish to continue?")){ $html .= $this->button("Download all images", "image_dump", false);
return true; if($database->engine->name == "mysql") {
}else{ $html .= $this->button("Download database contents", "database_dump", false);
return false; $html .= $this->button("Reset image IDs", "reset_image_ids", true);
} }
}
}
</script>
'.make_form(make_link("admin_utils"),"post", false, false, "return imgidconfirm()")."
<select name='action' id='misc'>
<option value='lowercase all tags'>All tags to lowercase</option>
<option value='recount tag use'>Recount tag use</option>
<option value='purge unused tags'>Purge unused tags</option>
<option value='database dump'>Download database contents</option>
<option value='reset image ids'>Reset image ids</option>
<option value='image dump'>Download all images</option>
<!--<option value='convert to innodb'>Convert database to InnoDB (MySQL only)</option>-->
</select>
<input type='submit' value='Go'>
</form>
";
$page->add_block(new Block("Misc Admin Tools", $html)); $page->add_block(new Block("Misc Admin Tools", $html));
}
/* First check
Requires you to click the checkbox to enable the delete by query form */ public function display_dbq($terms) {
$dbqcheck = 'javascript:$(function() { global $page;
if($("#dbqcheck:checked").length != 0){
$("#dbqtags").attr("disabled", false); $h_terms = html_escape($terms);
$("#dbqsubmit").attr("disabled", false);
}else{ $html = make_form(make_link("admin/delete_by_query"), "POST") . "
$("#dbqtags").attr("disabled", true); <input type='button' class='shm-unlocker' data-unlock-id='dbqsubmit' value='Unlock'>
$("#dbqsubmit").attr("disabled", true); <input type='hidden' name='query' value='$h_terms'>
} <input type='submit' id='dbqsubmit' disabled='true' value='Delete All These Images'>
});';
/* Second check
Requires you to confirm the deletion by clicking ok. */
$html = "
<script type='text/javascript'>
function checkform(){
if(confirm('Are you sure you wish to delete all images using these tags?')){
return true;
}else{
return false;
}
}
</script>"
.make_form(make_link("admin_utils"),"post",false,false,"return checkform()")."
<input type='checkbox' id='dbqcheck' name='action' onclick='$dbqcheck'>
<input type='hidden' name='action' value='delete by query'>
<input type='text' id='dbqtags' disabled='true' name='query'>
<input type='submit' id='dbqsubmit' disabled='true' value='Go'>
</form> </form>
"; ";
$page->add_block(new Block("Delete by Query", $html)); $page->add_block(new Block("List Controls", $html, "left"));
} }
} }
?> ?>

View file

@ -4,19 +4,27 @@ class BlocksTest extends SCoreWebTestCase {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("setup"); $this->get_page("setup");
$this->set_field("_config_blocks_text", "badgers"); $this->set_field("_config_blocks_text", "
Title: some text
Area: main
Priority: 100
Pages: *
waffles
");
$this->click("Save Settings"); $this->click("Save Settings");
$this->get_page("post/list"); $this->get_page("post/list");
$this->assert_text("badgers"); $this->assert_text("some text");
$this->assert_text("waffles");
$this->get_page("setup"); $this->get_page("setup");
$this->set_field("_config_blocks_text", ""); $this->set_field("_config_blocks_text", "");
$this->click("Save Settings"); $this->click("Save Settings");
$this->get_page("post/list"); $this->get_page("post/list");
$this->assert_no_text("Note"); $this->assert_no_text("some text");
$this->assert_no_text("badgers"); $this->assert_no_text("waffles");
$this->log_out(); $this->log_out();
} }

View file

@ -0,0 +1,9 @@
<?php
class BookmarksTest extends ShimmieWebTestCase {
function testBookmarks() {
$this->get_page("bookmark/add");
$this->get_page("bookmark/remove");
}
}
?>

View file

@ -13,14 +13,21 @@ class FeaturedTest extends ShimmieWebTestCase {
$this->click("Feature This"); $this->click("Feature This");
$this->get_page("post/list"); $this->get_page("post/list");
$this->assert_text("Featured Image"); $this->assert_text("Featured Image");
# FIXME: test changing from one feature to another
$this->get_page("featured_image/download");
$this->assert_response(200);
$this->get_page("featured_image/view");
$this->assert_response(200);
$this->delete_image($image_id); $this->delete_image($image_id);
$this->log_out(); $this->log_out();
# after deletion, there should be no feature # after deletion, there should be no feature
$this->get_page("post/list"); $this->get_page("post/list");
$this->assert_no_text("Featured Image"); $this->assert_no_text("Featured Image");
# FIXME: test changing from one feature to another
} }
} }
?> ?>

View file

@ -2,10 +2,11 @@
class IcoHandlerTest extends ShimmieWebTestCase { class IcoHandlerTest extends ShimmieWebTestCase {
function testPixelHander() { function testPixelHander() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("favicon.ico", "shimmie favicon"); $image_id = $this->post_image("lib/static/favicon.ico", "shimmie favicon");
$this->assert_response(302); $this->assert_response(302);
$this->log_out(); $this->log_out();
$raw = $this->get_page("post/view/$image_id"); // test for no crash
$raw = $this->get_page("get_ico/$image_id"); // test for no crash $raw = $this->get_page("get_ico/$image_id"); // test for no crash
$this->log_in_as_admin(); $this->log_in_as_admin();

View file

@ -0,0 +1,37 @@
<?php
class HashBanTest extends ShimmieWebTestCase {
function testBan() {
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
$this->log_out();
$this->log_in_as_admin();
$this->get_page("post/view/$image_id");
$this->click("Ban and Delete");
$this->log_out();
$this->log_in_as_user();
$this->get_page("post/view/$image_id");
$this->assert_response(404);
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id");
$this->assert_response(404);
$this->log_out();
$this->log_in_as_admin();
$this->get_page("image_hash_ban/list/1");
$this->click("Remove");
$this->log_out();
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id");
$this->assert_response(200);
$this->log_out();
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
}
}
?>

View file

@ -2,6 +2,7 @@
class LogDatabaseTest extends SCoreWebTestCase { class LogDatabaseTest extends SCoreWebTestCase {
function testLog() { function testLog() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("log/view");
$this->log_out(); $this->log_out();
} }
} }

9
contrib/oekaki/test.php Normal file
View file

@ -0,0 +1,9 @@
<?php
class OekakiTest extends SCoreWebTestCase {
function testLog() {
$this->log_in_as_user();
$this->get_page("oekaki/create");
$this->log_out();
}
}
?>

View file

@ -19,7 +19,7 @@ class RandomTest extends ShimmieWebTestCase {
$this->log_out(); $this->log_out();
} }
function tesPostListBlock() { function testPostListBlock() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("setup"); $this->get_page("setup");
$this->set_field("_config_show_random_block", true); $this->set_field("_config_show_random_block", true);

View file

@ -37,7 +37,7 @@ class _SafeImage {
class ShimmieApi extends Extension { class ShimmieApi extends Extension {
public function onPageRequest(PageRequestEvent $event) { public function onPageRequest(PageRequestEvent $event) {
global $database, $page; global $database, $page, $user;
if($event->page_matches("api/shimmie")) { if($event->page_matches("api/shimmie")) {
$page->set_mode("data"); $page->set_mode("data");
@ -55,7 +55,7 @@ class ShimmieApi extends Extension {
"SELECT tag FROM tags WHERE tag LIKE ?", "SELECT tag FROM tags WHERE tag LIKE ?",
array($arg."%")); array($arg."%"));
} }
elseif(isset($_GET['id'])){ elseif(isset($_GET['tag'])){
$all = $database->get_all( $all = $database->get_all(
"SELECT tag FROM tags WHERE tag LIKE ?", "SELECT tag FROM tags WHERE tag LIKE ?",
array($_GET['tag']."%")); array($_GET['tag']."%"));
@ -76,9 +76,7 @@ class ShimmieApi extends Extension {
elseif(isset($_GET['id'])){ elseif(isset($_GET['id'])){
$image = Image::by_id(int_escape($_GET['id'])); $image = Image::by_id(int_escape($_GET['id']));
} }
else{ // FIXME: handle null image
$image = Image::by_id(int_escape("1")); //Default to id=1
}
$image->get_tag_array(); // tag data isn't loaded into the object until necessary $image->get_tag_array(); // tag data isn't loaded into the object until necessary
$safe_image = new _SafeImage($image); $safe_image = new _SafeImage($image);
$page->set_data(json_encode($safe_image)); $page->set_data(json_encode($safe_image));
@ -98,29 +96,24 @@ class ShimmieApi extends Extension {
} }
if($event->page_matches("api/shimmie/get_user")) { if($event->page_matches("api/shimmie/get_user")) {
if(isset($_GET['name'])){ $query = $user->id;
$all = $database->get_all( if($event->count_args() == 1) {
"SELECT id,name,joindate,class FROM users WHERE name=?", $query = $event->get_arg(0);
array($_GET['name'])); }
if(isset($_GET['name'])) {
$query = $_GET['name'];
}
if(isset($_GET['id'])) {
$query = $_GET['id'];
} }
if(isset($_GET['id'])){ $all = $database->get_row(
$all = $database->get_all( "SELECT id,name,joindate,class FROM users WHERE name=? OR id=?",
"SELECT id,name,joindate,class FROM users WHERE id=?", array($_GET['name'], int_escape($_GET['id'])));
array($_GET['id']));
}
if(!isset($_GET['id']) && !isset($_GET['name'])){
$all = $database->get_all(
"SELECT id,name,joindate,class FROM users WHERE id=?",
array("2")); //In 99% of cases, this will be the admin.
}
$all = $all[0];
//FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice.. //FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice..
// - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...);
for($i=0; $i<4; $i++) unset($all[$i]); for($i=0; $i<4; $i++) unset($all[$i]);
/*TODO: Might be worth making it possible just to get a certain stat (Using &stat=uploadcount or something)
This would lessen strain on DB? */
$all['uploadcount'] = Image::count_images(array("user_id=".$all['id'])); $all['uploadcount'] = Image::count_images(array("user_id=".$all['id']));
$all['uploadperday'] = sprintf("%.1f", ($all['uploadcount'] / (((time() - strtotime($all['joindate'])) / 86400) + 1))); $all['uploadperday'] = sprintf("%.1f", ($all['uploadcount'] / (((time() - strtotime($all['joindate'])) / 86400) + 1)));
$all['commentcount'] = $database->get_one( $all['commentcount'] = $database->get_one(

View file

@ -0,0 +1,29 @@
<?php
class ShimmieApiTest extends ShimmieWebTestCase {
function testAPI() {
$this->log_in_as_user();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
$this->get_page("api/shimmie/get_tags");
$this->get_page("api/shimmie/get_tags/pb");
$this->get_page("api/shimmie/get_tags?tag=pb");
$this->get_page("api/shimmie/get_image/$image_id");
$this->get_page("api/shimmie/get_image?id=$image_id");
$this->get_page("api/shimmie/find_images");
$this->get_page("api/shimmie/find_images/pbx");
$this->get_page("api/shimmie/find_images/pbx/1");
$this->get_page("api/shimmie/get_user/demo");
$this->get_page("api/shimmie/get_user?name=demo");
$this->get_page("api/shimmie/get_user?id=2");
// FIXME: test unspecified / bad values
// FIXME: test that json is encoded properly
$this->log_out();
$this->log_in_as_admin();
$this->delete_image($image_id);
$this->log_out();
}
}
?>

View file

@ -13,8 +13,8 @@ class TagHistoryTest extends ShimmieWebTestCase {
$this->click("Revert To"); $this->click("Revert To");
$this->assert_title("Image $image_id: pbx"); $this->assert_title("Image $image_id: pbx");
$this->get_page("tag_history"); $this->get_page("tag_history/all/1");
$this->click("Global Tag History"); $this->assert_title("Global Tag History");
$this->delete_image($image_id); $this->delete_image($image_id);
$this->log_out(); $this->log_out();

View file

@ -7,19 +7,19 @@ class TwitterSocTest extends SCoreWebTestCase {
$this->set_field("_config_twitter_soc_username", "shish2k"); $this->set_field("_config_twitter_soc_username", "shish2k");
$this->click("Save Settings"); $this->click("Save Settings");
/*
$this->get_page("post/list"); $this->get_page("post/list");
$this->assert_text("Note"); $this->assert_text("Tweets");
$this->assert_text("kittens"); /*
$this->assert_text("kittens"); // this is loaded with javascript
*/ */
$this->get_page("setup"); $this->get_page("setup");
$this->set_field("_config_twitter_soc_username", ""); $this->set_field("_config_twitter_soc_username", "");
$this->click("Save Settings"); $this->click("Save Settings");
/*
$this->get_page("post/list"); $this->get_page("post/list");
$this->assert_no_text("Note"); $this->assert_no_text("Tweets");
/*
$this->assert_no_text("kittens"); $this->assert_no_text("kittens");
*/ */

View file

@ -56,6 +56,7 @@ class Image {
$this->$name = $value; // hax $this->$name = $value; // hax
} }
$this->posted_timestamp = strtotime($this->posted); // pray $this->posted_timestamp = strtotime($this->posted); // pray
$this->locked = undb_bool($this->locked);
assert(is_numeric($this->id)); assert(is_numeric($this->id));
assert(is_numeric($this->height)); assert(is_numeric($this->height));
@ -411,8 +412,9 @@ class Image {
* @retval bool * @retval bool
*/ */
public function is_locked() { public function is_locked() {
return ($this->locked === true || $this->locked == "Y" || $this->locked == "t"); return $this->locked;
} }
public function set_locked($tf) { public function set_locked($tf) {
global $database; global $database;
$ln = $tf ? "Y" : "N"; $ln = $tf ? "Y" : "N";

View file

@ -172,15 +172,6 @@ function isValidDate($date) {
return false; return false;
} }
/**
* Return a pluraliser if necessary
*
* @retval string
*/
function plural($num, $single_form="", $plural_form="s") {
return ($num == 1) ? $single_form : $plural_form;
}
/** /**
* Give a HTML string which shows an IP (if the user is allowed to see IPs), * Give a HTML string which shows an IP (if the user is allowed to see IPs),
* and a link to ban that IP (if the user is allowed to ban IPs) * and a link to ban that IP (if the user is allowed to ban IPs)
@ -198,19 +189,6 @@ function show_ip($ip, $ban_reason) {
return $ip; return $ip;
} }
/**
* Turn an IP address into a colour, for easily spotting samefags
*
* NOTE: this should only be shown to admins, as it can be reversed
* to get an original IP address
*/
function ip2color($ip) {
$b = explode(".", $ip);
#return sprintf("#%02x%02x%02x", $b[0]/2, $b[1]/2, $b[2]/2);
return sprintf("hsl(%d, %d%%, %d%%)", $b[3]*360/256, $b[2]*100/256, $b[1]*100/256/2);
}
/** /**
* Different databases have different ways to represent booleans; this * Different databases have different ways to represent booleans; this
* will try and standardise them * will try and standardise them
@ -361,27 +339,6 @@ function make_form($target, $method="POST", $multipart=False, $form_id="", $onsu
return '<form action="'.$target.'" method="'.$method.'" '.$extra.'>'.$auth; return '<form action="'.$target.'" method="'.$method.'" '.$extra.'>'.$auth;
} }
/**
* Make a link to a static file in the current theme's
* directory
*/
function theme_file($filepath) {
global $config;
$theme = $config->get_string("theme","default");
return make_link('themes/'.$theme.'/'.$filepath);
}
function hsl_rainbow() {
$ct = Array();
$s = 100; $l = 25; for($h= 0; $h<360; $h+=60) {$ct[] = "hsl($h, $s%, $l%);";}
$s = 100; $l = 50; for($h= 0; $h<360; $h+=60) {$ct[] = "hsl($h, $s%, $l%);";}
$s = 50; $l = 25; for($h= 0; $h<360; $h+=60) {$ct[] = "hsl($h, $s%, $l%);";}
$s = 100; $l = 25; for($h=30; $h<360; $h+=60) {$ct[] = "hsl($h, $s%, $l%);";}
$s = 100; $l = 50; for($h=30; $h<360; $h+=60) {$ct[] = "hsl($h, $s%, $l%);";}
$s = 50; $l = 25; for($h=30; $h<360; $h+=60) {$ct[] = "hsl($h, $s%, $l%);";}
return $ct;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* CAPTCHA abstraction * * CAPTCHA abstraction *
@ -886,23 +843,6 @@ function full_copy($source, $target) {
} }
} }
/**
* @private
*/
function weighted_random($weights) {
$total = 0;
foreach($weights as $k => $w) {
$total += $w;
}
$r = mt_rand(0, $total);
foreach($weights as $k => $w) {
$r -= $w;
if($r <= 0) {
return $k;
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Event API * * Event API *

View file

@ -70,7 +70,7 @@ class CommentList extends Extension {
$config->set_default_int('comment_count', 5); $config->set_default_int('comment_count', 5);
$config->set_default_bool('comment_captcha', false); $config->set_default_bool('comment_captcha', false);
if($config->get_int("ext_comments_version") < 2) { if($config->get_int("ext_comments_version") < 3) {
// shortcut to latest // shortcut to latest
if($config->get_int("ext_comments_version") < 1) { if($config->get_int("ext_comments_version") < 1) {
$database->create_table("comments", " $database->create_table("comments", "
@ -84,14 +84,14 @@ class CommentList extends Extension {
INDEX (owner_ip), INDEX (owner_ip),
INDEX (posted), INDEX (posted),
FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE,
FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT
"); ");
$config->set_int("ext_comments_version", 2); $config->set_int("ext_comments_version", 3);
} }
// === // the whole history
if($config->get_int("ext_comments_version") < 1) { if($config->get_int("ext_comments_version") < 1) {
$database->Execute("CREATE TABLE comments ( $database->create_table("comments", "
id {$database->engine->auto_increment}, id {$database->engine->auto_increment},
image_id INTEGER NOT NULL, image_id INTEGER NOT NULL,
owner_id INTEGER NOT NULL, owner_id INTEGER NOT NULL,
@ -99,7 +99,7 @@ class CommentList extends Extension {
posted DATETIME DEFAULT NULL, posted DATETIME DEFAULT NULL,
comment TEXT NOT NULL, comment TEXT NOT NULL,
INDEX (image_id) INDEX (image_id)
) {$database->engine->create_table_extras}"); ");
$config->set_int("ext_comments_version", 1); $config->set_int("ext_comments_version", 1);
} }
@ -108,6 +108,14 @@ class CommentList extends Extension {
$database->Execute("CREATE INDEX comments_posted ON comments(posted)"); $database->Execute("CREATE INDEX comments_posted ON comments(posted)");
$config->set_int("ext_comments_version", 2); $config->set_int("ext_comments_version", 2);
} }
if($config->get_int("ext_comments_version") == 2) {
$config->set_int("ext_comments_version", 3);
$database->Execute("ALTER TABLE comments ADD CONSTRAINT foreign_comments_image_id FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE");
$database->Execute("ALTER TABLE comments ADD CONSTRAINT foreign_comments_owner_id FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT");
}
// FIXME: add foreign keys, bump to v3
} }
} }
@ -186,13 +194,6 @@ class CommentList extends Extension {
); );
} }
public function onImageDeletion(ImageDeletionEvent $event) {
global $database;
$image_id = $event->image->id;
$database->Execute("DELETE FROM comments WHERE image_id=:image_id", array("image_id"=>$image_id));
log_info("comment", "Deleting all comments for Image #$image_id");
}
// TODO: split akismet into a separate class, which can veto the event // TODO: split akismet into a separate class, which can veto the event
public function onCommentPosting(CommentPostingEvent $event) { public function onCommentPosting(CommentPostingEvent $event) {
$this->add_comment_wrapper($event->image_id, $event->user, $event->comment); $this->add_comment_wrapper($event->image_id, $event->user, $event->comment);

View file

@ -5,6 +5,9 @@ class Handle404Test extends SCoreWebTestCase {
$this->assert_response(404); $this->assert_response(404);
$this->assert_title('404'); $this->assert_title('404');
$this->assert_text("No handler could be found for the page 'not/a/page'"); $this->assert_text("No handler could be found for the page 'not/a/page'");
$this->get_page('favicon.ico');
$this->assert_response(200);
} }
} }
?> ?>

View file

@ -29,10 +29,6 @@ class SetupBuildingEvent extends Event {
public function SetupBuildingEvent(SetupPanel $panel) { public function SetupBuildingEvent(SetupPanel $panel) {
$this->panel = $panel; $this->panel = $panel;
} }
public function get_panel() {
return $this->panel;
}
} }
// }}} // }}}
/* SetupPanel {{{ /* SetupPanel {{{

View file

@ -87,7 +87,17 @@ class SetupTheme extends Themelet {
} }
protected function sb_to_html(SetupBlock $block) { protected function sb_to_html(SetupBlock $block) {
return "<div class='setupblock'><b>{$block->header}</b><br>{$block->body}</div>\n"; $h = $block->header;
$b = $block->body;
$i = preg_replace('/[^a-zA-Z0-9]/', '_', $h) . "-setup";
$html = "
<div class='setupblock brr'>
<b class='shm-toggler' data-toggle-id='$i'>$h</b>
<br><div id='$i'>$b</div>
</div>
<!-- cancel border -->
";
return $html;
} }
} }
?> ?>

View file

@ -76,6 +76,15 @@ $(document).ready(function() {
}) })
}); });
$(".shm-unlocker").each(function(idx, elm) {
var tid = $(elm).data("unlock-id");
var tob = $("#"+tid);
$(elm).click(function(e) {
$(elm).attr("disabled", true);
tob.attr("disabled", false);
});
});
if(document.location.hash.length > 3) { if(document.location.hash.length > 3) {
query = document.location.hash.substring(1); query = document.location.hash.substring(1);
a = document.getElementById("prevlink"); a = document.getElementById("prevlink");

View file

@ -1,38 +0,0 @@
<?php
/**
* A customised version of the Setup theme
*/
class CustomSetupTheme extends SetupTheme {
protected function sb_to_html(SetupBlock $block) {
$h = $block->header;
$b = $block->body;
$i = preg_replace('/[^a-zA-Z0-9]/', '_', $h) . "-setup";
$html = "
<script type='text/javascript'><!--
$(document).ready(function() {
$(\"#$i-toggle\").click(function() {
$(\"#$i\").slideToggle(\"slow\", function() {
if($(\"#$i\").is(\":hidden\")) {
$.cookie(\"$i-hidden\", 'true', {path: '/'});
}
else {
$.cookie(\"$i-hidden\", 'false', {path: '/'});
}
});
});
if($.cookie(\"$i-hidden\") == 'true') {
$(\"#$i\").hide();
}
});
//--></script>
<div class='setupblock brr'>
<b id='$i-toggle'>$h</b>
<br><div id='$i'>$b</div>
</div>
<!-- cancel border -->
";
return $html;
}
}
?>