Compare commits
79 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3cf59cc545 | ||
|
327d0dd51f | ||
|
90fcccdab0 | ||
|
9d868750e2 | ||
|
770e313da1 | ||
|
2002d555c1 | ||
|
36064595f5 | ||
|
2c4582f147 | ||
|
9993370fc6 | ||
|
5d42ee21de | ||
|
6e0b3bca6d | ||
|
32d12602cf | ||
|
058f2ff1ed | ||
|
79767033b7 | ||
|
8f05de4b66 | ||
|
34d2165dfa | ||
|
593dc2f694 | ||
|
a1199c3bd4 | ||
|
4b666a8f1a | ||
|
9044abdfc2 | ||
|
e17c85d379 | ||
|
554ff9051c | ||
|
04f335b797 | ||
|
661b5eac78 | ||
|
763538c096 | ||
|
5f473aff6d | ||
|
c4fdf771ee | ||
|
94725f1266 | ||
|
f1ce267015 | ||
|
25bfb82653 | ||
|
0c334f05ed | ||
|
bea09d836d | ||
|
384e8bbdda | ||
|
96509995a8 | ||
|
642a3d02e1 | ||
|
f180624ebf | ||
|
482312b11e | ||
|
aaa8ccf874 | ||
|
c2ab5e5fcd | ||
|
3f0d6562ef | ||
|
d1ce3db7e2 | ||
|
24a35c37ea | ||
|
457d5d8ad9 | ||
|
56ccd7df40 | ||
|
f8b414a15f | ||
|
51f6c486a2 | ||
|
367546fe06 | ||
|
ffde804f24 | ||
|
b57ba4d484 | ||
|
ce8d6f71fd | ||
|
1fd2fe084b | ||
|
7e92bdbd8e | ||
|
4c5f415da7 | ||
|
b7fc28e17f | ||
|
b2f8165a59 | ||
|
57b50d8881 | ||
|
9f595e6273 | ||
|
0c9fff3d11 | ||
|
d3ceb8ef97 | ||
|
64db094721 | ||
|
d47fd5f25c | ||
|
f7d1df2727 | ||
|
67455583e5 | ||
|
2e40bc73c8 | ||
|
c012717bce | ||
|
7b4dde4918 | ||
|
5ad77079a6 | ||
|
43028ebd67 | ||
|
f821e752fc | ||
|
73d5fe129a | ||
|
a226a17787 | ||
|
03c9d6b298 | ||
|
37a0aeb8ae | ||
|
47361369ff | ||
|
6c53088929 | ||
|
a6d68c6f1b | ||
|
57a6c39f2a | ||
|
a5f7b400f0 | ||
|
2a990735a0 |
49
README.txt
49
README.txt
@ -6,11 +6,12 @@
|
||||
/_______ /|___| /__|__|_| /__|_| /__|\___ >_______ \
|
||||
\/ \/ \/ \/ \/ \/
|
||||
|
||||
Shimmie 2.1 Release Candidate
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
RSS, BBCode + Emoticons, Wiki, Theme engine 2 + Danbooru theme, UTF8 support,
|
||||
Word filter, LOTS of database optimisations, Removal of ConfigSaveEvent and
|
||||
many small fixes and improvements \o/
|
||||
Shimmie 2.2
|
||||
~~~~~~~~~~~
|
||||
Integrated extension management, tag history, RSS for search results, unified
|
||||
image info editor, extensible file format support (ZIP, SWF, SVG, MP3, ...),
|
||||
wget transloader, event log filtering and sorting, as well as the usual misc
|
||||
improvements and bug fixes~
|
||||
|
||||
|
||||
Requirements
|
||||
@ -19,12 +20,9 @@ MySQL 4.1+
|
||||
PHP 5.0+
|
||||
GD or ImageMagick
|
||||
|
||||
PHP 4 support has currently been dropped, because
|
||||
a) It's a pain in the ass to support
|
||||
b) Nobody has told me they want it
|
||||
|
||||
If you want PHP 4 support, mail me, and I'll see if I can get it working for
|
||||
version 2.2...
|
||||
PHP 4 support has currently been dropped, because it's a pain in the ass to
|
||||
support. If you are unfortunate enough to be stuck on a PHP4-only host, I'd
|
||||
be glad to host your image board for you :3
|
||||
|
||||
|
||||
Installation
|
||||
@ -38,40 +36,13 @@ Installation
|
||||
not, you should be given instructions on how to fix any errors~
|
||||
|
||||
|
||||
Upgrade from 2.0.X
|
||||
Upgrade from 2.1.X
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Should be automatic, just unzip and copy across config.php, images and thumbs
|
||||
folders from the old version. This includes automatically messing with the
|
||||
database -- back it up first!
|
||||
|
||||
|
||||
Upgrade from 0.8.4
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
BIG NOTE: 0.8.4 is the only version the upgrader supports; please upgrade to
|
||||
that before going any further! Feel free to try other versions, just don't
|
||||
complain when it doesn't work :P
|
||||
|
||||
Upgrade process:
|
||||
1) Make backups of everything. The most important things are your database
|
||||
data, and your images folder. config.php and the thumbs folder are also
|
||||
very helpful.
|
||||
2) Check that your backups actually contain the important data, they aren't
|
||||
just empty files with the right names...
|
||||
3) Create a new, blank database, separate from the old one
|
||||
4) Unzip shimmie2 into a different folder than shimmie1
|
||||
5) Visit the URL of shimmie2
|
||||
6) Fill in the old database location, the new database location, and the full
|
||||
path to the old installation folder (the folder where the old "images" and
|
||||
"thumbs" can be found)
|
||||
7) Click "upgrade"
|
||||
8) Wait a couple of minutes while data is copied from the old install into the
|
||||
new one. You may wish to spend these minutes in prayer :P
|
||||
9) Log in with an existing admin account and set things up to taste
|
||||
|
||||
The old installation can now be removed, but you may wish to keep it around
|
||||
until you're sure everything in v2 is working properly~
|
||||
|
||||
|
||||
Contact
|
||||
~~~~~~~
|
||||
http://forum.shishnet.org/viewforum.php?f=6 -- discussion forum
|
||||
|
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Misc Admin Utils
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Various non-essential utilities
|
||||
*/
|
||||
|
||||
class AdminUtils extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("admin_utils", "AdminUtilsTheme");
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "admin_utils")) {
|
||||
if($event->user->is_admin()) {
|
||||
set_time_limit(0);
|
||||
|
||||
switch($_POST['action']) {
|
||||
case 'lowercase all tags':
|
||||
$this->lowercase_all_tags();
|
||||
break;
|
||||
case 'recount tag use':
|
||||
$this->recount_tag_use();
|
||||
break;
|
||||
case 'purge unused tags':
|
||||
$this->purge_unused_tags();
|
||||
break;
|
||||
}
|
||||
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("admin"));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'AdminBuildingEvent')) {
|
||||
global $page;
|
||||
$this->theme->display_form($page);
|
||||
}
|
||||
}
|
||||
|
||||
private function lowercase_all_tags() {
|
||||
global $database;
|
||||
$database->execute("UPDATE tags SET tag=lower(tag)");
|
||||
}
|
||||
private function recount_tag_use() {
|
||||
global $database;
|
||||
$database->Execute("UPDATE tags SET count=(SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id)");
|
||||
}
|
||||
private function purge_unused_tags() {
|
||||
global $database;
|
||||
$this->recount_tag_use();
|
||||
$database->Execute("DELETE FROM tags WHERE count=0");
|
||||
}
|
||||
private function check_for_orphanned_images() {
|
||||
$orphans = array();
|
||||
foreach(glob("images/*") as $dir) {
|
||||
foreach(glob("$dir/*") as $file) {
|
||||
$hash = str_replace("$dir/", "", $file);
|
||||
if(!$this->db_has_hash($hash)) {
|
||||
$orphans[] = $hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new AdminUtils());
|
||||
?>
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
class AdminUtilsTheme extends Themelet {
|
||||
/*
|
||||
* Show a form which links to admin_utils with POST[action] set to one of:
|
||||
* 'lowercase all tags'
|
||||
* 'recount tag use'
|
||||
* 'purge unused tags'
|
||||
*/
|
||||
public function display_form($page) {
|
||||
$html = "
|
||||
<p><form action='".make_link("admin_utils")."' method='POST'>
|
||||
<select name='action'>
|
||||
<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>
|
||||
</select>
|
||||
<input type='submit' value='Go'>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Misc Admin Tools", $html));
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Autocomplete
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Auto-complete for search and upload tags
|
||||
*/
|
||||
|
||||
class AutoComplete extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "index" || $event->page_name == "view")) {
|
||||
$event->page->add_header("<script>autocomplete_url='".html_escape(make_link("autocomplete"))."';</script>");
|
||||
}
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "autocomplete")) {
|
||||
$event->page->set_mode("data");
|
||||
$event->page->set_type("text/plain");
|
||||
$event->page->set_data($this->get_completions($event->get_arg(0)));
|
||||
}
|
||||
}
|
||||
|
||||
private function get_completions($start) {
|
||||
global $database;
|
||||
$tags = $database->db->GetCol("SELECT tag,count FROM tags WHERE tag LIKE ? ORDER BY count DESC", array($start.'%'));
|
||||
return implode("\n", $tags);
|
||||
}
|
||||
}
|
||||
add_event_listener(new AutoComplete());
|
||||
?>
|
@ -1,98 +0,0 @@
|
||||
|
||||
// addEvent(window, "load", function() {
|
||||
// initAjax("searchBox", "search_completions");
|
||||
// initAjax("tagBox", "upload_completions");
|
||||
// });
|
||||
|
||||
function endWord(sentance) {
|
||||
words = sentance.split(" ");
|
||||
return words[words.length-1];
|
||||
}
|
||||
|
||||
var resultCache = new Array();
|
||||
resultCache[""] = new Array();
|
||||
|
||||
function complete(boxname, text) {
|
||||
box = byId(boxname);
|
||||
words = box.value.split(" ");
|
||||
box.value = "";
|
||||
for(n=0; n<words.length-1; n++) {
|
||||
box.value += words[n]+" ";
|
||||
}
|
||||
box.value += text+" ";
|
||||
box.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
function fillCompletionZone(boxname, areaname, results) {
|
||||
byId(areaname).innerHTML = "";
|
||||
for(i=0; i<results.length; i++) {
|
||||
byId(areaname).innerHTML += "<br><a href=\"#\" onclick=\"complete('"+boxname+"', '"+results[i]+"');\">"+results[i]+"</a>";
|
||||
}
|
||||
}
|
||||
|
||||
function initAjax(boxname, areaname) {
|
||||
var box = byId(boxname);
|
||||
if(!box) return;
|
||||
|
||||
addEvent(
|
||||
box,
|
||||
"keyup",
|
||||
function f() {
|
||||
starter = endWord(box.value);
|
||||
|
||||
if(resultCache[starter]) {
|
||||
fillCompletionZone(boxname, areaname, resultCache[starter]);
|
||||
}
|
||||
else {
|
||||
ajaxRequest(
|
||||
"ajax.php?start="+starter,
|
||||
function g(text) {
|
||||
resultCache[starter] = text.split("\n");
|
||||
fillCompletionZone(boxname, areaname, resultCache[starter]);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//completion_cache = new array();
|
||||
|
||||
input = byId("search_input");
|
||||
output = byId("search_completions");
|
||||
|
||||
function get_cached_completions(start) {
|
||||
// if(completion_cache[start]) {
|
||||
// return completion_cache[start];
|
||||
// }
|
||||
// else {
|
||||
return null;
|
||||
// }
|
||||
}
|
||||
function get_completions(start) {
|
||||
cached = get_cached_completions(start);
|
||||
if(cached) {
|
||||
output.innerHTML = cached;
|
||||
}
|
||||
else {
|
||||
ajaxRequest(autocomplete_url+"/"+start, function(data) {set_completions(start, data);});
|
||||
}
|
||||
}
|
||||
function set_completions(start, data) {
|
||||
// completion_cache[start] = data;
|
||||
output.innerHTML = data;
|
||||
}
|
||||
|
||||
if(input) {
|
||||
input.onkeyup = function() {
|
||||
if(input.value.length < 3) {
|
||||
output.innerHTML = "";
|
||||
}
|
||||
else {
|
||||
get_completions(input.value);
|
||||
}
|
||||
};
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: Comment Word Ban
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: For stopping spam and other comment abuse
|
||||
*/
|
||||
|
@ -1,4 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Bulk Add
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Bulk add server-side images
|
||||
*/
|
||||
|
||||
class BulkAdd extends Extension {
|
||||
var $theme;
|
@ -52,7 +52,7 @@ class DanbooruApi extends Extension
|
||||
if(preg_match("/md5:([0-9a-fA-F]*)/i", $event->term, $matches))
|
||||
{
|
||||
$hash = strtolower($matches[1]);
|
||||
$event->set_querylet(new Querylet("AND (images.hash = '$hash')"));
|
||||
$event->set_querylet(new Querylet("images.hash = '$hash'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Downtime
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Show a "down for maintenance" page
|
||||
*/
|
||||
|
||||
class Downtime extends Extension {
|
||||
var $theme;
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: Emoticon Filter
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Turn :smile: into a link to smile.gif
|
||||
*/
|
||||
|
@ -1,4 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: System Info
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Show various bits of system information, for debugging
|
||||
*/
|
||||
|
||||
class ET extends Extension {
|
||||
var $theme;
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: EventLog
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: A log of things that happen, for abuse tracking
|
||||
*/
|
||||
|
@ -1,95 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Extension Manager
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: A thing for point & click extension management
|
||||
*/
|
||||
|
||||
class ExtManager extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("ext_manager", "ExtManagerTheme");
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "ext_manager")) {
|
||||
if($event->user->is_admin()) {
|
||||
if($event->get_arg(0) == "set") {
|
||||
$this->set_things($_POST);
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect("ext_manager");
|
||||
}
|
||||
else {
|
||||
$this->theme->display_table($event->page, $this->get_extensions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'UserBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_link("Extension Manager", make_link("ext_manager"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_extensions() {
|
||||
$extensions = array();
|
||||
foreach(glob("contrib/*/main.php") as $main) {
|
||||
$extension = array();
|
||||
$matches = array();
|
||||
$data = file_get_contents($main);
|
||||
preg_match("#contrib/(.*)/main.php#", $main, $matches);
|
||||
$extension["ext_name"] = $matches[1];
|
||||
if(preg_match("/Name: (.*)/", $data, $matches)) {
|
||||
$extension["name"] = $matches[1];
|
||||
}
|
||||
if(preg_match("/Link: (.*)/", $data, $matches)) {
|
||||
$extension["link"] = $matches[1];
|
||||
}
|
||||
if(preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $data, $matches)) {
|
||||
$extension["author"] = $matches[1];
|
||||
$extension["email"] = $matches[2];
|
||||
}
|
||||
else if(preg_match("/Author: (.*)/", $data, $matches)) {
|
||||
$extension["author"] = $matches[1];
|
||||
}
|
||||
if(preg_match("/Description: (.*)/", $data, $matches)) {
|
||||
$extension["description"] = $matches[1];
|
||||
}
|
||||
$extension["enabled"] = $this->is_enabled($extension["ext_name"]);
|
||||
$extensions[] = $extension;
|
||||
}
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
private function is_enabled($fname) {
|
||||
return file_exists("ext/$fname");
|
||||
}
|
||||
|
||||
private function set_things($settings) {
|
||||
foreach(glob("contrib/*/main.php") as $main) {
|
||||
$matches = array();
|
||||
preg_match("#contrib/(.*)/main.php#", $main, $matches);
|
||||
$fname = $matches[1];
|
||||
|
||||
$this->set_enabled($fname, $settings["ext_$fname"]);
|
||||
}
|
||||
}
|
||||
private function set_enabled($fname, $enabled) {
|
||||
if($enabled) {
|
||||
// enable if currently disabled
|
||||
if(!file_exists("ext/$fname")) {
|
||||
symlink("../contrib/$fname", "ext/$fname");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// disable if currently enabled
|
||||
if(file_exists("ext/$fname")) {
|
||||
unlink("ext/$fname");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new ExtManager());
|
||||
?>
|
60
contrib/featured/main.php
Normal file
60
contrib/featured/main.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Featured Image
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Bring a specific image to the users' attentions
|
||||
*/
|
||||
|
||||
class Featured extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("featured", "FeaturedTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
$config->set_default_int('featured_id', 0);
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "set_feature")) {
|
||||
global $user;
|
||||
if($user->is_admin() && isset($_POST['image_id'])) {
|
||||
global $config;
|
||||
$id = int_escape($_POST['image_id']);
|
||||
if($id > 0) {
|
||||
$config->set_int("featured_id", $id);
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("post/view/$id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'PostListBuildingEvent')) {
|
||||
global $config, $database;
|
||||
$fid = $config->get_int("featured_id");
|
||||
if($fid > 0) {
|
||||
$image = $database->get_image($fid);
|
||||
if(!is_null($image)) {
|
||||
$this->theme->display_featured($event->page, $image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(is_a($event, 'SetupBuildingEvent')) {
|
||||
$sb = new SetupBlock("Featured Image");
|
||||
$sb->add_int_option("featured_id", "Image ID: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
*/
|
||||
|
||||
if(is_a($event, 'ImageAdminBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new Featured());
|
||||
?>
|
20
contrib/featured/theme.php
Normal file
20
contrib/featured/theme.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
class FeaturedTheme extends Themelet {
|
||||
/*
|
||||
* Show $text on the $page
|
||||
*/
|
||||
public function display_featured($page, $image) {
|
||||
$page->add_block(new Block("Featured Image", $this->build_thumb_html($image), "left", 3));
|
||||
}
|
||||
|
||||
public function get_buttons_html($image_id) {
|
||||
return "
|
||||
<form action='".make_link("set_feature")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$image_id'>
|
||||
<input type='submit' value='Feature This'>
|
||||
</form>
|
||||
";
|
||||
}
|
||||
}
|
||||
?>
|
@ -36,11 +36,7 @@ class ArchiveFileHandler extends Extension {
|
||||
|
||||
private function supported_ext($ext) {
|
||||
$exts = array("zip");
|
||||
$ext = strtolower($ext);
|
||||
foreach($exts as $supported) {
|
||||
if($ext == $supported) return true;
|
||||
}
|
||||
return false;
|
||||
return array_contains($exts, strtolower($ext));
|
||||
}
|
||||
|
||||
// copied from bulk add extension
|
||||
|
@ -11,43 +11,42 @@ class FlashFileHandler extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("handle_flash", "FlashFileHandlerTheme");
|
||||
|
||||
if(is_a($event, 'DataUploadEvent') && $event->type == "swf" && $this->check_contents($event->tmpname)) {
|
||||
if(is_a($event, 'DataUploadEvent') && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
if(!copy($event->tmpname, "images/$ha/$hash")) {
|
||||
$event->veto("Flash Handler failed to move file from uploads to archive");
|
||||
return;
|
||||
}
|
||||
if(!move_upload_to_archive($event)) return;
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
$event->veto("Flash Handler failed to create image object from data");
|
||||
$event->veto("Flash Handler failed to create image object from data. ".
|
||||
"Note: compressed flash files are currently unsupported");
|
||||
return;
|
||||
}
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
}
|
||||
|
||||
if(is_a($event, 'ThumbnailGenerationEvent') && $event->type == "swf") {
|
||||
if(is_a($event, 'ThumbnailGenerationEvent') && $this->supported_ext($event->type)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
// FIXME: scale image, as not all boards use 192x192
|
||||
copy("ext/handle_flash/thumb.jpg", "thumbs/$ha/$hash");
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent') && $event->image->ext == "swf") {
|
||||
if(is_a($event, 'DisplayingImageEvent') && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
}
|
||||
}
|
||||
|
||||
private function supported_ext($ext) {
|
||||
$exts = array("swf");
|
||||
return array_contains($exts, strtolower($ext));
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
global $config;
|
||||
|
||||
$image = new Image();
|
||||
|
||||
// FIXME: need more flash format specs :|
|
||||
$image->width = 0;
|
||||
$image->height = 0;
|
||||
|
||||
$image->filesize = $metadata['size'];
|
||||
$image->hash = $metadata['hash'];
|
||||
$image->filename = $metadata['filename'];
|
||||
@ -55,12 +54,77 @@ class FlashFileHandler extends Extension {
|
||||
$image->tag_array = tag_explode($metadata['tags']);
|
||||
$image->source = $metadata['source'];
|
||||
|
||||
// redundant, since getimagesize() works on SWF o_O
|
||||
// $rect = $this->swf_get_bounds($filename);
|
||||
// if(is_null($rect)) {
|
||||
// return $null;
|
||||
// }
|
||||
// $image->width = $rect[1];
|
||||
// $image->height = $rect[3];
|
||||
|
||||
if(!($info = getimagesize($filename))) return null;
|
||||
|
||||
$image->width = $info[0];
|
||||
$image->height = $info[1];
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
private function str_to_binarray($string) {
|
||||
$binary = array();
|
||||
for($j=0; $j<strlen($string); $j++) {
|
||||
$c = ord($string[$j]);
|
||||
for($i=7; $i>=0; $i--) {
|
||||
$binary[] = ($c >> $i) & 0x01;
|
||||
}
|
||||
}
|
||||
return $binary;
|
||||
}
|
||||
|
||||
private function binarray_to_int($binarray, $start=0, $length=32) {
|
||||
$int = 0;
|
||||
for($i=$start; $i<$start + $length; $i++) {
|
||||
$int = $int << 1;
|
||||
$int = $int + ($binarray[$i] == "1" ? 1 : 0);
|
||||
}
|
||||
return $int;
|
||||
}
|
||||
|
||||
private function swf_get_bounds($filename) {
|
||||
$fp = fopen($filename, "r");
|
||||
$head = fread($fp, 3);
|
||||
$version = fread($fp, 1);
|
||||
$length = fread($fp, 4);
|
||||
|
||||
if($head == "FWS") {
|
||||
$data = fread($fp, 16);
|
||||
}
|
||||
else if($head == "CWS") {
|
||||
$data = fread($fp, 128*1024);
|
||||
$data = gzuncompress($data);
|
||||
$data = substr($data, 0, 16);
|
||||
}
|
||||
|
||||
$bounds = array();
|
||||
$rect_bin = $this->str_to_binarray($data);
|
||||
$nbits = $this->binarray_to_int($rect_bin, 0, 5);
|
||||
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 0 * $nbits, $nbits) / 20;
|
||||
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 1 * $nbits, $nbits) / 20;
|
||||
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 2 * $nbits, $nbits) / 20;
|
||||
$bounds[] = $this->binarray_to_int($rect_bin, 5 + 3 * $nbits, $nbits) / 20;
|
||||
|
||||
return $bounds;
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
// FIXME: flash magic header?
|
||||
return (file_exists($file));
|
||||
if(!file_exists($file)) return false;
|
||||
|
||||
$fp = fopen($file, "r");
|
||||
$head = fread($fp, 3);
|
||||
fclose($fp);
|
||||
if(!array_contains(array("CWS", "FWS"), $head)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
add_event_listener(new FlashFileHandler());
|
||||
|
@ -6,11 +6,16 @@ class FlashFileHandlerTheme extends Themelet {
|
||||
// FIXME: object and embed have "height" and "width"
|
||||
$html = "
|
||||
<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
|
||||
codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0'>
|
||||
codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0'
|
||||
height='{$image->height}'
|
||||
width='{$image->width}'
|
||||
>
|
||||
<param name='movie' value='$ilink'/>
|
||||
<param name='quality' value='high' />
|
||||
<embed src='$ilink' quality='high'
|
||||
pluginspage='http://www.macromedia.com/go/getflashplayer'
|
||||
height='{$image->height}'
|
||||
width='{$image->width}'
|
||||
type='application/x-shockwave-flash'></embed>
|
||||
</object>";
|
||||
$page->add_block(new Block("Image", $html, "main", 0));
|
||||
|
116
contrib/handle_ico/main.php
Normal file
116
contrib/handle_ico/main.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: ICO File Handler
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Handle windows icons
|
||||
*/
|
||||
|
||||
class IcoFileHandler extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("handle_ico", "IcoFileHandlerTheme");
|
||||
|
||||
if(is_a($event, 'DataUploadEvent') && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
if(!move_upload_to_archive($event)) return;
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
$event->veto("Handler failed to create image object from data");
|
||||
return;
|
||||
}
|
||||
|
||||
$iae = new ImageAdditionEvent($event->user, $image);
|
||||
send_event($iae);
|
||||
if($iae->vetoed) {
|
||||
$event->veto($iae->veto_reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'ThumbnailGenerationEvent') && $this->supported_ext($event->type)) {
|
||||
$this->create_thumb($event->hash);
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent') && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "get_ico")) {
|
||||
global $database;
|
||||
$id = int_escape($event->get_arg(0));
|
||||
$image = $database->get_image($id);
|
||||
$hash = $image->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
|
||||
$event->page->set_type("image/x-icon");
|
||||
$event->page->set_mode("data");
|
||||
$event->page->set_data(file_get_contents("images/$ha/$hash"));
|
||||
}
|
||||
}
|
||||
|
||||
private function supported_ext($ext) {
|
||||
$exts = array("ico", "ani", "cur");
|
||||
return array_contains($exts, strtolower($ext));
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
global $config;
|
||||
|
||||
$image = new Image();
|
||||
|
||||
$info = "";
|
||||
$fp = fopen($filename, "r");
|
||||
$header = unpack("snull/stype/scount", fread($fp, 6));
|
||||
|
||||
$subheader = unpack("cwidth/cheight/ccolours/cnull/splanes/sbpp/lsize/loffset", fread($fp, 16));
|
||||
fclose($fp);
|
||||
|
||||
$image->width = $subheader['width'];
|
||||
$image->height = $subheader['height'];
|
||||
|
||||
$image->filesize = $metadata['size'];
|
||||
$image->hash = $metadata['hash'];
|
||||
$image->filename = $metadata['filename'];
|
||||
$image->ext = $metadata['extension'];
|
||||
$image->tag_array = tag_explode($metadata['tags']);
|
||||
$image->source = $metadata['source'];
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
if(!file_exists($file)) return false;
|
||||
$fp = fopen($file, "r");
|
||||
$header = unpack("snull/stype/scount", fread($fp, 6));
|
||||
fclose($fp);
|
||||
return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1));
|
||||
}
|
||||
|
||||
private function create_thumb($hash) {
|
||||
global $config;
|
||||
|
||||
$ha = substr($hash, 0, 2);
|
||||
$inname = "images/$ha/$hash";
|
||||
$outname = "thumbs/$ha/$hash";
|
||||
|
||||
$w = $config->get_int("thumb_width");
|
||||
$h = $config->get_int("thumb_height");
|
||||
$q = $config->get_int("thumb_quality");
|
||||
$mem = $config->get_int("thumb_max_memory") / 1024 / 1024; // IM takes memory in MB
|
||||
|
||||
if($config->get_bool("ico_convert")) {
|
||||
// "-limit memory $mem" broken?
|
||||
exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname");
|
||||
}
|
||||
else {
|
||||
copy($inname, $outname);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
add_event_listener(new IcoFileHandler());
|
||||
?>
|
12
contrib/handle_ico/theme.php
Normal file
12
contrib/handle_ico/theme.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
class IcoFileHandlerTheme extends Themelet {
|
||||
public function display_image($page, $image) {
|
||||
$ilink = make_link("get_ico/{$image->id}/{$image->id}.ico");
|
||||
$html = "
|
||||
<img id='main_image' src='$ilink'>
|
||||
";
|
||||
$page->add_block(new Block("Image", $html, "main", 0));
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,67 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: MP3 File Handler
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Description: Handle MP3 files
|
||||
*/
|
||||
|
||||
class MP3FileHandler extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("handle_mp3", "MP3FileHandlerTheme");
|
||||
|
||||
if(is_a($event, 'DataUploadEvent') && $event->type == "mp3" && $this->check_contents($event->tmpname)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
if(!copy($event->tmpname, "images/$ha/$hash")) {
|
||||
$event->veto("MP3 Handler failed to move file from uploads to archive");
|
||||
return;
|
||||
}
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
$event->veto("MP3 Handler failed to create image object from data");
|
||||
return;
|
||||
}
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
}
|
||||
|
||||
if(is_a($event, 'ThumbnailGenerationEvent') && $event->type == "mp3") {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
// FIXME: scale image, as not all boards use 192x192
|
||||
copy("ext/handle_mp3/thumb.jpg", "thumbs/$ha/$hash");
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent') && $event->image->ext == "mp3") {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
}
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
global $config;
|
||||
|
||||
$image = new Image();
|
||||
|
||||
// FIXME: need more flash format specs :|
|
||||
$image->width = 0;
|
||||
$image->height = 0;
|
||||
|
||||
$image->filesize = $metadata['size'];
|
||||
$image->hash = $metadata['hash'];
|
||||
$image->filename = $metadata['filename'];
|
||||
$image->ext = $metadata['extension'];
|
||||
$image->tag_array = tag_explode($metadata['tags']);
|
||||
$image->source = $metadata['source'];
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
// FIXME: mp3 magic header?
|
||||
return (file_exists($file));
|
||||
}
|
||||
}
|
||||
add_event_listener(new MP3FileHandler());
|
||||
?>
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
class MP3FileHandlerTheme extends Themelet {
|
||||
public function display_image($page, $image) {
|
||||
$data_href = get_base_href();
|
||||
$ilink = $image->get_image_link();
|
||||
$html = "
|
||||
<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
|
||||
codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0'
|
||||
width='400' height='170'>
|
||||
<param name='movie' value='$data_href/ext/handle_mp3/xspf_player.swf?song_url=$ilink'/>
|
||||
<param name='quality' value='high' />
|
||||
<embed src='$data_href/ext/handle_mp3/xspf_player.swf?song_url=$ilink' quality='high'
|
||||
pluginspage='http://www.macromedia.com/go/getflashplayer'
|
||||
width='400' height='170'
|
||||
type='application/x-shockwave-flash'></embed>
|
||||
</object>
|
||||
<p><a href='$ilink'>Download</a>";
|
||||
$page->add_block(new Block("Music", $html, "main", 0));
|
||||
}
|
||||
}
|
||||
?>
|
Binary file not shown.
Before Width: | Height: | Size: 5.6 KiB |
@ -1,448 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2005, Fabricio Zuardi
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
stop();
|
||||
//autoplay=true
|
||||
//repeat_playlist = true;
|
||||
//playlist_size = 3;
|
||||
//player_title = "customizeable title test"
|
||||
//song_url = "http://downloads.betterpropaganda.com/music/Imperial_Teen-Ivanka_128.mp3";
|
||||
//playlist_url = "http://cchits.ning.com/recent/xspf/?xn_auth=no";
|
||||
//playlist_url = "http://hideout.com.br/shows/radio-test.xspf";
|
||||
//radio_mode = true;
|
||||
//song_title = "Imperial Teen - Ivanka";
|
||||
autoload=true;
|
||||
//info_button_text = "Add to Cart"
|
||||
//playlist_url = "http%3A%2F%2Fwebjay%2Eorg%2Fby%2Flucas%5Fgonze%2Flaconic%2Exspf"
|
||||
//playlist_url= "http://hideout.com.br/tests/hideout2325.xspf"
|
||||
//constants
|
||||
DEFAULT_PLAYLIST_URL = "http://hideout.com.br/shows/allshows.xspf";
|
||||
DEFAULT_WELCOME_MSG = "Hideout XSPF Music Player - by Fabricio Zuardi";
|
||||
LOADING_PLAYLIST_MSG = "Loading Playlist...";
|
||||
DEFAULT_LOADED_PLAYLIST_MSG = "- click to start"
|
||||
DEFAULT_INFOBUTTON_TXT = "Info"
|
||||
//playlists has priority over songs, if a playlist_url parameter is found the song_url is ignored
|
||||
//default playlist if none is passed through query string
|
||||
if(!playlist_url){
|
||||
if(!song_url){
|
||||
playlist_url = DEFAULT_PLAYLIST_URL;
|
||||
}else{
|
||||
single_music_playlist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><playlist version=\"1\" xmlns = \"http://xspf.org/ns/0/\"><trackList>";
|
||||
single_music_playlist += "<track><location>"+song_url+"</location><annotation>"+song_title+"</annotation></track>"
|
||||
single_music_playlist += "</trackList></playlist>"
|
||||
}
|
||||
}
|
||||
info_mc._visible=false;
|
||||
if(!info_button_text){
|
||||
info_button_text = DEFAULT_INFOBUTTON_TXT;
|
||||
}
|
||||
info_mc.display_txt.text = info_button_text;
|
||||
|
||||
//variables initialization
|
||||
playlist_array = [];
|
||||
track_index = 0;
|
||||
playlist_mc.track_count = 0;
|
||||
pause_position = 0;
|
||||
volume_level = 100;
|
||||
playlist_xml = new XML();
|
||||
playlist_xml.ignoreWhite = true;
|
||||
playlist_xml.onLoad = playlistLoaded;
|
||||
mysound = new Sound(this);
|
||||
playlist_listener = new Object();
|
||||
playlist_list.addEventListener("change", playlist_listener)
|
||||
//play_btn.onPress = playTrack;
|
||||
//functions
|
||||
//xml parser
|
||||
function playlistLoaded (success){
|
||||
if(success){
|
||||
var root_node = this.firstChild;
|
||||
for(var node = root_node.firstChild; node != null; node = node.nextSibling){
|
||||
if(node.nodeName == "title"){
|
||||
playlist_title = node.firstChild.nodeValue;
|
||||
}
|
||||
if(node.nodeName == "trackList"){
|
||||
//tracks
|
||||
var tracks_array = [];
|
||||
for(var track_node = node.firstChild; track_node != null; track_node = track_node.nextSibling){
|
||||
var track_obj = new Object()
|
||||
//track attributes
|
||||
for(var track_child = track_node.firstChild; track_child != null; track_child = track_child.nextSibling){
|
||||
if(track_child.nodeName=="location"){
|
||||
track_obj.location = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="image"){
|
||||
track_obj.image = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="title"){
|
||||
track_obj.title = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="creator"){
|
||||
track_obj.creator = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="annotation"){
|
||||
track_obj.annotation = track_child.firstChild.nodeValue
|
||||
}
|
||||
if(track_child.nodeName=="info"){
|
||||
track_obj.info = track_child.firstChild.nodeValue
|
||||
}
|
||||
}
|
||||
track_obj.label = (tracks_array.length+1) +". ";
|
||||
if(track_obj.title) {
|
||||
if(track_obj.creator) {
|
||||
track_obj.label += track_obj.creator+' - ';
|
||||
}
|
||||
track_obj.label += track_obj.title;
|
||||
} else {
|
||||
track_obj.label += track_obj.annotation;
|
||||
}
|
||||
tracks_array.push(track_obj)
|
||||
addTrack(track_obj.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
playlist_array = tracks_array;
|
||||
if(!playlist_size) playlist_size = playlist_array.length;
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
if(autoplay){
|
||||
loadTrack()
|
||||
}else{
|
||||
start_btn_mc.start_btn.onPress = loadTrack;
|
||||
track_display_mc.display_txt.text = playlist_title+" "+DEFAULT_LOADED_PLAYLIST_MSG;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
annotation_txt.text = "Error opening "+playlist_url;
|
||||
}
|
||||
}
|
||||
|
||||
playlist_listener.change = function(eventObject){
|
||||
annotation_txt.text = playlist_list.selectedItem.annotation;
|
||||
location_txt.text = playlist_list.selectedItem.location;
|
||||
}
|
||||
|
||||
function loadTrack(){
|
||||
|
||||
//Radio Mode feature by nosferathoo, more info in: https://sourceforge.net/tracker/index.php?func=detail&aid=1341940&group_id=128363&atid=711474
|
||||
if (radio_mode && track_index==playlist_size-1) {
|
||||
playlist_url=playlist_array[track_index].location;
|
||||
for (i=0;i<playlist_mc.track_count;++i) {
|
||||
removeMovieClip(playlist_mc.tracks_mc["track_"+i+"_mc"]);
|
||||
}
|
||||
playlist_mc.track_count=0;
|
||||
playlist_size=0;
|
||||
track_index=0;
|
||||
autoload=true;
|
||||
autoplay=true;
|
||||
loadPlaylist();
|
||||
return(0);
|
||||
}
|
||||
|
||||
start_btn_mc.start_btn._visible = false;
|
||||
track_display_mc.display_txt.text = playlist_array[track_index].label;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
cover_mc.content_mc["photo"+last_track_index].removeMovieClip();
|
||||
mysound.loadSound(playlist_array[track_index].location,true);
|
||||
play_mc.gotoAndStop(2)
|
||||
|
||||
//image from playlist
|
||||
if(playlist_array[track_index].image!=undefined){
|
||||
cover_mc.content_mc.createEmptyMovieClip("photo"+track_index,track_index)
|
||||
cover_mc.content_mc["photo"+track_index].loadMovie(playlist_array[track_index].image)
|
||||
}else{
|
||||
}
|
||||
//info button
|
||||
if(playlist_array[track_index].info!=undefined){
|
||||
info_mc._visible = true;
|
||||
info_mc.info_btn.onPress = function(){
|
||||
getURL(playlist_array[track_index].info,"_blank")
|
||||
}
|
||||
}else{
|
||||
info_mc._visible = false;
|
||||
}
|
||||
_root.onEnterFrame=function(){
|
||||
//HACK doesnt need to set the volume at every enterframe
|
||||
mysound.setVolume(this.volume_level)
|
||||
var sound_load_percent = (mysound.getBytesLoaded()/mysound.getBytesTotal())*100
|
||||
track_display_mc.loader_mc.load_bar_mc._xscale = sound_load_percent;
|
||||
var image_load_percent = (cover_mc.content_mc["photo"+track_index].getBytesLoaded()/cover_mc.content_mc["photo"+track_index].getBytesTotal())*100
|
||||
cover_mc.load_bar_mc._xscale = image_load_percent;
|
||||
if((cover_mc.content_mc["photo"+track_index].getBytesLoaded()>4)&&(image_load_percent==100)){
|
||||
//image loaded
|
||||
//make image fit
|
||||
cover_mc.content_mc["photo"+track_index]._width = cover_mc.load_bar_mc._width
|
||||
cover_mc.content_mc["photo"+track_index]._height = cover_mc.load_bar_mc._height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stop_btn.onRelease = stopTrack;
|
||||
play_mc.play_btn.onRelease = playTrack
|
||||
next_btn.onRelease = nextTrack
|
||||
prev_btn.onRelease = prevTrack
|
||||
mysound.onSoundComplete = nextTrack;
|
||||
volume_mc.volume_btn.onPress = volumeChange;
|
||||
volume_mc.volume_btn.onRelease = volume_mc.volume_btn.onReleaseOutside = function(){
|
||||
this._parent.onMouseMove = this._parent.onMouseDown = null;
|
||||
}
|
||||
|
||||
function volumeChange(){
|
||||
this._parent.onMouseMove = this._parent.onMouseDown = function(){
|
||||
var percent = (this._xmouse/this._width)*100
|
||||
if(percent>100)percent=100;
|
||||
if(percent<0)percent=0;
|
||||
this.volume_bar_mc._xscale = percent
|
||||
this._parent.volume_level = percent;
|
||||
mysound.setVolume(percent)
|
||||
}
|
||||
}
|
||||
|
||||
function stopTrack() {
|
||||
mysound.stop();
|
||||
play_mc.gotoAndStop(1)
|
||||
mysound.stop();
|
||||
mysound.start();
|
||||
mysound.stop();
|
||||
_root.pause_position = 0;
|
||||
|
||||
};
|
||||
function playTrack() {
|
||||
if(play_mc._currentframe==1){ //play
|
||||
seekTrack(_root.pause_position)
|
||||
play_mc.gotoAndStop(2)
|
||||
}else if(play_mc._currentframe==2){
|
||||
_root.pause_position = mysound.position;
|
||||
mysound.stop();
|
||||
play_mc.gotoAndStop(1)
|
||||
}
|
||||
|
||||
};
|
||||
function seekTrack(p_offset:Number){
|
||||
mysound.stop()
|
||||
mysound.start(int((p_offset)/1000),1)
|
||||
}
|
||||
function nextTrack(){
|
||||
if(track_index<playlist_size-1){
|
||||
last_track_index = track_index;
|
||||
track_index ++;
|
||||
loadTrack();
|
||||
}else{
|
||||
if(repeat_playlist){
|
||||
last_track_index = track_index;
|
||||
track_index = 0;
|
||||
loadTrack()
|
||||
}
|
||||
}
|
||||
playlist_mc.tracks_mc["track_"+last_track_index+"_mc"].bg_mc.gotoAndStop(1)
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
}
|
||||
|
||||
function prevTrack(){
|
||||
if(track_index>0){
|
||||
last_track_index = track_index;
|
||||
track_index --;
|
||||
loadTrack();
|
||||
}
|
||||
playlist_mc.tracks_mc["track_"+last_track_index+"_mc"].bg_mc.gotoAndStop(1)
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
}
|
||||
|
||||
function scrollTitle(){
|
||||
track_display_mc.display_txt._x -= 5;
|
||||
if (track_display_mc.display_txt._x+track_display_mc.display_txt._width<0){
|
||||
track_display_mc.display_txt._x = track_display_mc.mask_mc._width;
|
||||
}
|
||||
}
|
||||
|
||||
function resizeUI(){
|
||||
bg_mc._width = Stage.width;
|
||||
track_display_mc.loader_mc._width = Stage.width - track_display_mc._x - 2;
|
||||
track_display_mc.mask_mc._width = track_display_mc.loader_mc._width-3;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
volume_mc._x = Stage.width - 22;
|
||||
start_btn_mc._xscale = Stage.width;
|
||||
//playlist area tinnier that the album cover
|
||||
if(Stage.width<2.5*cover_mc._width){
|
||||
//
|
||||
if (Stage.height>2.5*cover_mc._height){
|
||||
//send album cover to bottom
|
||||
cover_mc._y = Stage.height - cover_mc._height -2- info_mc._height -2;
|
||||
info_mc._y = Stage.height - info_mc._height -2;
|
||||
var covervisible =1;
|
||||
}else{
|
||||
var covervisible =0;
|
||||
//hide album cover
|
||||
cover_mc._y = Stage.height;
|
||||
}
|
||||
//send playlist to left
|
||||
playlist_mc._x = cover_mc._x;
|
||||
scrollbar_mc.bg_mc._height = Stage.height - (19+(cover_mc._height+info_mc._height+4)*covervisible);
|
||||
playlist_mc.bg_mc._height = Stage.height - (19+(cover_mc._height+info_mc._height+4)*covervisible);
|
||||
playlist_mc.mask_mc._height = Stage.height - (23+(cover_mc._height+info_mc._height+4)*covervisible);
|
||||
}else{
|
||||
cover_mc._y = 17;
|
||||
info_mc._y = 153;
|
||||
playlist_mc._x = 138;
|
||||
scrollbar_mc.bg_mc._height = Stage.height -19;
|
||||
playlist_mc.bg_mc._height = Stage.height - 19;
|
||||
playlist_mc.mask_mc._height = Stage.height - 23;
|
||||
}
|
||||
scrollbar_mc._x = Stage.width - 12;
|
||||
playlist_mc.mask_mc._width = Stage.width - (playlist_mc._x + 19);
|
||||
playlist_mc.bg_mc._width = Stage.width - (playlist_mc._x + 14);
|
||||
}
|
||||
function addTrack(track_label){
|
||||
if(playlist_mc.track_count>0) {
|
||||
playlist_mc.tracks_mc.track_0_mc.duplicateMovieClip("track_"+playlist_mc.track_count+"_mc",playlist_mc.track_count);
|
||||
}
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"]._y += playlist_mc.track_count*14;
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].display_txt.autoSize = "left";
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].display_txt.text = track_label;
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].bg_mc.index = playlist_mc.track_count;
|
||||
playlist_mc.tracks_mc["track_"+playlist_mc.track_count+"_mc"].bg_mc.select_btn.onPress = function(){
|
||||
last_track_index = track_index;
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(1)
|
||||
track_index = this._parent.index;
|
||||
playlist_mc.tracks_mc["track_"+track_index+"_mc"].bg_mc.gotoAndStop(2)
|
||||
loadTrack();
|
||||
}
|
||||
playlist_mc.track_count ++;
|
||||
}
|
||||
//scroll
|
||||
|
||||
scrollbar_mc.up_btn.onPress = function(){
|
||||
this._parent.v = -1;
|
||||
this._parent.onEnterFrame = scrollTracks;
|
||||
}
|
||||
scrollbar_mc.down_btn.onPress = function(){
|
||||
this._parent.v = 1;
|
||||
this._parent.onEnterFrame = scrollTracks;
|
||||
}
|
||||
scrollbar_mc.up_btn.onRelease = scrollbar_mc.down_btn.onRelease = function(){
|
||||
this._parent.onEnterFrame = null;
|
||||
}
|
||||
scrollbar_mc.handler_mc.drag_btn.onPress = function(){
|
||||
var scroll_top_limit = 19;
|
||||
var scroll_bottom_limit = scrollbar_mc.bg_mc._height - scrollbar_mc.handler_mc._height - 2;
|
||||
this._parent.startDrag(false,this._parent._x,scroll_top_limit,this._parent._x,scroll_bottom_limit)
|
||||
this._parent.isdragging = true;
|
||||
this._parent.onEnterFrame = scrollTracks;
|
||||
}
|
||||
scrollbar_mc.handler_mc.drag_btn.onRelease = scrollbar_mc.handler_mc.drag_btn.onReleaseOutside = function(){
|
||||
stopDrag()
|
||||
this._parent.isdragging = false;
|
||||
this._parent.onEnterFrame = null;
|
||||
}
|
||||
function scrollTracks(){
|
||||
var scroll_top_limit = 19;
|
||||
var scroll_bottom_limit = scrollbar_mc.bg_mc._height - scrollbar_mc.handler_mc._height - 2;
|
||||
var list_bottom_limit = 1;
|
||||
var list_top_limit = (1-Math.round(playlist_mc.tracks_mc._height))+Math.floor(playlist_mc.mask_mc._height/14)*14
|
||||
if(playlist_mc.tracks_mc._height>playlist_mc.mask_mc._height){
|
||||
if(scrollbar_mc.handler_mc.isdragging){
|
||||
var percent = (scrollbar_mc.handler_mc._y-scroll_top_limit)/(scroll_bottom_limit-scroll_top_limit)
|
||||
playlist_mc.tracks_mc._y = (list_top_limit-list_bottom_limit)*percent+list_bottom_limit;
|
||||
}else{
|
||||
if(scrollbar_mc.v==-1){
|
||||
if(playlist_mc.tracks_mc._y+14<list_bottom_limit){
|
||||
playlist_mc.tracks_mc._y += 14;
|
||||
}else{
|
||||
playlist_mc.tracks_mc._y = list_bottom_limit;
|
||||
}
|
||||
var percent = (playlist_mc.tracks_mc._y-1)/(list_top_limit-1)
|
||||
scrollbar_mc.handler_mc._y = percent*(scroll_bottom_limit - scroll_top_limit)+scroll_top_limit;
|
||||
}else if(scrollbar_mc.v==1){
|
||||
if(playlist_mc.tracks_mc._y-14>list_top_limit){
|
||||
playlist_mc.tracks_mc._y -= 14;
|
||||
}else{
|
||||
playlist_mc.tracks_mc._y = list_top_limit;
|
||||
}
|
||||
var percent = (playlist_mc.tracks_mc._y-1)/(list_top_limit-1)
|
||||
scrollbar_mc.handler_mc._y = percent*(scroll_bottom_limit - scroll_top_limit)+scroll_top_limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function loadPlaylist(){
|
||||
track_display_mc.display_txt.text = LOADING_PLAYLIST_MSG;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
|
||||
//playlist
|
||||
if(playlist_url){
|
||||
playlist_xml.load(unescape(playlist_url))
|
||||
}else{
|
||||
//single track
|
||||
playlist_xml.parseXML(single_music_playlist)
|
||||
playlist_xml.onLoad(true);
|
||||
}
|
||||
}
|
||||
|
||||
//first click - load playlist
|
||||
start_btn_mc.start_btn.onPress = function(){
|
||||
autoplay = true;
|
||||
loadPlaylist();
|
||||
}
|
||||
|
||||
|
||||
//main
|
||||
Stage.scaleMode = "noScale"
|
||||
Stage.align = "LT";
|
||||
this.onResize = resizeUI;
|
||||
Stage.addListener(this);
|
||||
if(!player_title) player_title = DEFAULT_WELCOME_MSG;
|
||||
track_display_mc.display_txt.autoSize = "left";
|
||||
track_display_mc.display_txt.text = player_title;
|
||||
if(track_display_mc.display_txt._width>track_display_mc.mask_mc._width){
|
||||
track_display_mc.onEnterFrame = scrollTitle;
|
||||
}else{
|
||||
track_display_mc.onEnterFrame = null;
|
||||
track_display_mc.display_txt._x = 0;
|
||||
}
|
||||
//start to play automatically if parameter autoplay is present
|
||||
if(autoplay){
|
||||
start_btn_mc.start_btn.onPress();
|
||||
} else if (autoload){
|
||||
loadPlaylist()
|
||||
}
|
||||
//customized menu
|
||||
var my_cm:ContextMenu = new ContextMenu();
|
||||
my_cm.customItems.push(new ContextMenuItem("Stop", stopTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Play!", playTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Next", nextTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Previous", prevTrack));
|
||||
my_cm.customItems.push(new ContextMenuItem("Download this song", function(){getURL(playlist_array[track_index].location,"_blank")},true));
|
||||
my_cm.customItems.push(new ContextMenuItem("Add song to Webjay playlist", function(){getURL("http://webjay.org/poster?media="+escape(playlist_array[track_index].location),"_blank")}));
|
||||
my_cm.customItems.push(new ContextMenuItem("About Hideout", function(){getURL("http://www.hideout.com.br","_blank")},true));
|
||||
//my_cm.customItems.push(new ContextMenuItem("Crossfade", function(){}));
|
||||
//my_cm.customItems.push(new ContextMenuItem("Mando Diao - Paralyzed", function(){}));
|
||||
my_cm.hideBuiltInItems();
|
||||
this.menu = my_cm;
|
||||
resizeUI();
|
Binary file not shown.
Binary file not shown.
@ -1,10 +0,0 @@
|
||||
Copyright (c) 2005, Fabricio Zuardi
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -11,13 +11,10 @@ class SVGFileHandler extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("handle_svg", "SVGFileHandlerTheme");
|
||||
|
||||
if(is_a($event, 'DataUploadEvent') && $event->type == "svg" && $this->check_contents($event->tmpname)) {
|
||||
if(is_a($event, 'DataUploadEvent') && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
if(!copy($event->tmpname, "images/$ha/$hash")) {
|
||||
$event->veto("SVG Handler failed to move file from uploads to archive");
|
||||
return;
|
||||
}
|
||||
if(!move_upload_to_archive($event)) return;
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
@ -27,7 +24,7 @@ class SVGFileHandler extends Extension {
|
||||
send_event(new ImageAdditionEvent($event->user, $image));
|
||||
}
|
||||
|
||||
if(is_a($event, 'ThumbnailGenerationEvent') && $event->type == "svg") {
|
||||
if(is_a($event, 'ThumbnailGenerationEvent') && $this->supported_ext($event->type)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
|
||||
@ -47,7 +44,7 @@ class SVGFileHandler extends Extension {
|
||||
// }
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent') && $event->image->ext == "svg") {
|
||||
if(is_a($event, 'DisplayingImageEvent') && $this->supported_ext($event->image->ext)) {
|
||||
$this->theme->display_image($event->page, $event->image);
|
||||
}
|
||||
|
||||
@ -64,14 +61,19 @@ class SVGFileHandler extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
private function supported_ext($ext) {
|
||||
$exts = array("svg");
|
||||
return array_contains($exts, strtolower($ext));
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
global $config;
|
||||
|
||||
$image = new Image();
|
||||
|
||||
// FIXME: ugh, xml parsing :|
|
||||
$image->width = 0;
|
||||
$image->height = 0;
|
||||
$msp = new MiniSVGParser($filename);
|
||||
$image->width = $msp->width;
|
||||
$image->height = $msp->height;
|
||||
|
||||
$image->filesize = $metadata['size'];
|
||||
$image->hash = $metadata['hash'];
|
||||
@ -84,9 +86,33 @@ class SVGFileHandler extends Extension {
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
// FIXME: magic header?
|
||||
return (file_exists($file));
|
||||
if(!file_exists($file)) return false;
|
||||
|
||||
$msp = new MiniSVGParser($file);
|
||||
return $msp->valid;
|
||||
}
|
||||
}
|
||||
|
||||
class MiniSVGParser {
|
||||
var $valid=false, $width=0, $height=0;
|
||||
|
||||
function MiniSVGParser($file) {
|
||||
$xml_parser = xml_parser_create();
|
||||
xml_set_element_handler($xml_parser, array($this, "startElement"), array($this, "endElement"));
|
||||
$this->valid = xml_parse($xml_parser, file_get_contents($file), true);
|
||||
xml_parser_free($xml_parser);
|
||||
}
|
||||
|
||||
function startElement($parser, $name, $attrs) {
|
||||
if($name == "SVG") {
|
||||
$this->width = int_escape($attrs["WIDTH"]);
|
||||
$this->height = int_escape($attrs["HEIGHT"]);
|
||||
}
|
||||
}
|
||||
|
||||
function endElement($parser, $name) {
|
||||
}
|
||||
}
|
||||
|
||||
add_event_listener(new SVGFileHandler());
|
||||
?>
|
||||
|
@ -2,12 +2,11 @@
|
||||
|
||||
class SVGFileHandlerTheme extends Themelet {
|
||||
public function display_image($page, $image) {
|
||||
$link = make_link("get_svg/{$image->id}/{$image->id}.svg");
|
||||
$ilink = $image->get_image_link();
|
||||
// FIXME: object and embed have "height" and "width"
|
||||
$ilink = make_link("get_svg/{$image->id}/{$image->id}.svg");
|
||||
// $ilink = $image->get_image_link();
|
||||
$html = "
|
||||
<object data='$ilink' type='image/svg+xml' width='300' height='300'>
|
||||
<embed src='$ilink' type='image/svg+xml' width='300' height='300' />
|
||||
<object data='$ilink' type='image/svg+xml' width='{$image->width}' height='{$image->height}'>
|
||||
<embed src='$ilink' type='image/svg+xml' width='{$image->width}' height='{$image->height}' />
|
||||
</object>
|
||||
";
|
||||
$page->add_block(new Block("Image", $html, "main", 0));
|
||||
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: Home Extension
|
||||
* Author: Bzchan <bzchan@animemahou.com>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Extension adds a page "home" containing user specified
|
||||
* links and a counter showing total number of posts. The
|
||||
|
@ -36,6 +36,7 @@ EOD
|
||||
<div class='space'>
|
||||
<form action='".make_link("post/list")."' method='GET'>
|
||||
<input id='search_input' name='search' size='55' type='text' value='' autocomplete='off' /><br/>
|
||||
<input type='hidden' name='q' value='/post/list'>
|
||||
<input type='submit' value='Search'/>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -29,40 +29,30 @@ class AddImageHashBanEvent extends Event {
|
||||
$this->reason = $reason;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
class Image_Hash_Ban extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("Image_Hash_Ban", "ImageBanTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("Image_Hash_Ban", "ImageBanTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
if($config->get_int("ext_imageban_version") < 1) {
|
||||
$this->install();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(is_a($event, 'UploadingImageEvent')) {
|
||||
|
||||
global $database;
|
||||
|
||||
|
||||
$image = $event->image;
|
||||
$tmp_hash = $image->hash;
|
||||
|
||||
if ($database->db->GetOne("SELECT COUNT(*) FROM image_bans WHERE hash = ?", $tmp_hash) == 1) {
|
||||
$event->veto("This image has been banned!");
|
||||
}
|
||||
|
||||
|
||||
if(is_a($event, 'DataUploadEvent')) {
|
||||
global $database;
|
||||
|
||||
if ($database->db->GetOne("SELECT COUNT(*) FROM image_bans WHERE hash = ?", $event->hash) == 1) {
|
||||
$event->veto("This image has been banned!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "image_hash_ban")) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
if($event->user->is_admin()) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if(isset($_POST['hash']) && isset($_POST['reason'])) {
|
||||
send_event(new AddImageHashBanEvent($_POST['hash'], $_POST['reason']));
|
||||
@ -70,7 +60,15 @@ class Image_Hash_Ban extends Extension {
|
||||
global $page;
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
|
||||
}
|
||||
if(isset($_POST['image_id'])) {
|
||||
global $database;
|
||||
$image = $database->get_image($_POST['image_id']);
|
||||
if($image) {
|
||||
send_event(new ImageDeletionEvent($image));
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("post/list"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "remove") {
|
||||
@ -84,60 +82,48 @@ class Image_Hash_Ban extends Extension {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(is_a($event, 'AdminBuildingEvent')) {
|
||||
global $page;
|
||||
$this->theme->display_Image_hash_Bans($page, $this->get_image_hash_bans());
|
||||
}
|
||||
|
||||
if(is_a($event, 'AddImageHashBanEvent')) {
|
||||
|
||||
if(is_a($event, 'AddImageHashBanEvent')) {
|
||||
$this->add_image_hash_ban($event->hash, $event->reason);
|
||||
}
|
||||
|
||||
if(is_a($event, 'RemoveImageHashBanEvent')) {
|
||||
$this->remove_image_hash_ban($event->hash);
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent')) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
|
||||
$this->theme->display_image_banner($event->page, $event->image->hash);
|
||||
|
||||
if(is_a($event, 'ImageAdminBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
$database->Execute("CREATE TABLE image_bans (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
hash char(32) default NULL,
|
||||
date datetime default NULL,
|
||||
reason varchar(255) default NULL,
|
||||
PRIMARY KEY (id)
|
||||
)");
|
||||
hash char(32) default NULL,
|
||||
date datetime default NULL,
|
||||
reason varchar(255) default NULL,
|
||||
PRIMARY KEY (id)
|
||||
)");
|
||||
$config->set_int("ext_imageban_version", 1);
|
||||
}
|
||||
|
||||
|
||||
// DB funness
|
||||
|
||||
public function get_image_hash_bans() {
|
||||
|
||||
public function get_image_hash_bans() {
|
||||
// FIXME: many
|
||||
global $database;
|
||||
$bans = $database->get_all("SELECT * FROM image_bans");
|
||||
if($bans) {return $bans;}
|
||||
else {return array();}
|
||||
}
|
||||
|
||||
public function get_image_hash_ban($hash) {
|
||||
global $database;
|
||||
// yes, this is "? LIKE var", because ? is the thing with matching tokens
|
||||
// actually, slow
|
||||
// return $database->db->GetRow("SELECT * FROM bans WHERE ? LIKE ip AND date < now() AND (end > now() OR isnull(end))", array($ip));
|
||||
return $database->db->GetRow("SELECT * FROM image_bans WHERE hash = ? AND date < now()", array($hash));
|
||||
}
|
||||
|
||||
public function add_image_hash_ban($hash, $reason) {
|
||||
@ -151,7 +137,7 @@ class Image_Hash_Ban extends Extension {
|
||||
global $database;
|
||||
$database->Execute("DELETE FROM image_bans WHERE hash = ?", array($hash));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
add_event_listener(new Image_Hash_Ban(), 30); // in before resolution limit plugin
|
||||
?>
|
||||
|
@ -64,19 +64,16 @@ class ImageBanTheme extends Themelet {
|
||||
*
|
||||
* $image_id = the image to delete
|
||||
*/
|
||||
public function display_image_banner($page, $image_hash) {
|
||||
|
||||
/* I'll fix this soon
|
||||
$i_image_hash = int_escape($image_hash);
|
||||
public function get_buttons_html($image) {
|
||||
$html = "
|
||||
<form action='".make_link("admin/image_hash_ban")."' method='POST'>
|
||||
<input type='hidden' name='image_hash' value='$i_image_hash'>
|
||||
<input type='field' name='reason'>
|
||||
<form action='".make_link("image_hash_ban/add")."' method='POST'>
|
||||
<input type='hidden' name='hash' value='{$image->hash}'>
|
||||
<input type='hidden' name='image_id' value='{$image->id}'>
|
||||
<input type='text' name='reason'>
|
||||
<input type='submit' value='Ban and Delete'>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Image Ban", $html, "left"));
|
||||
*/
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
208
contrib/ipban/main.php
Normal file
208
contrib/ipban/main.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: IP Ban
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Ban IP addresses
|
||||
* Link: http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/IPBan
|
||||
*/
|
||||
|
||||
// RemoveIPBanEvent {{{
|
||||
class RemoveIPBanEvent extends Event {
|
||||
var $id;
|
||||
|
||||
public function RemoveIPBanEvent($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// AddIPBanEvent {{{
|
||||
class AddIPBanEvent extends Event {
|
||||
var $ip;
|
||||
var $reason;
|
||||
var $end;
|
||||
|
||||
public function AddIPBanEvent($ip, $reason, $end) {
|
||||
$this->ip = $ip;
|
||||
$this->reason = $reason;
|
||||
$this->end = $end;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
class IPBan extends Extension {
|
||||
var $theme;
|
||||
// event handler {{{
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("ipban", "IPBanTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
if($config->get_int("ext_ipban_version") < 5) {
|
||||
$this->install();
|
||||
}
|
||||
|
||||
$this->check_ip_ban();
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "ip_ban")) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if(isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) {
|
||||
if(empty($_POST['end'])) $end = null;
|
||||
else $end = $_POST['end'];
|
||||
send_event(new AddIPBanEvent($_POST['ip'], $_POST['reason'], $end));
|
||||
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("ip_ban/list"));
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "remove") {
|
||||
if(isset($_POST['id'])) {
|
||||
send_event(new RemoveIPBanEvent($_POST['id']));
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("ip_ban/list"));
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "list") {
|
||||
$this->theme->display_bans($event->page, $this->get_bans());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'UserBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_link("IP Bans", make_link("ip_ban/list"));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'AddIPBanEvent')) {
|
||||
global $user;
|
||||
$this->add_ip_ban($event->ip, $event->reason, $event->end, $user);
|
||||
}
|
||||
|
||||
if(is_a($event, 'RemoveIPBanEvent')) {
|
||||
global $database;
|
||||
$database->Execute("DELETE FROM bans WHERE id = ?", array($event->id));
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// installer {{{
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
// shortcut to latest
|
||||
if($config->get_int("ext_ipban_version") < 1) {
|
||||
$database->execute("
|
||||
CREATE TABLE bans (
|
||||
id {$database->engine->auto_increment},
|
||||
banner_id INTEGER NOT NULL,
|
||||
ip CHAR(15) NOT NULL,
|
||||
end_timestamp INTEGER,
|
||||
reason TEXT NOT NULL,
|
||||
INDEX (end_timestamp)
|
||||
) {$database->engine->create_table_extras};
|
||||
");
|
||||
$config->set_int("ext_ipban_version", 5);
|
||||
}
|
||||
|
||||
// ===
|
||||
|
||||
if($config->get_int("ext_ipban_version") < 1) {
|
||||
$database->Execute("CREATE TABLE bans (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
ip char(15) default NULL,
|
||||
date datetime default NULL,
|
||||
end datetime default NULL,
|
||||
reason varchar(255) default NULL,
|
||||
PRIMARY KEY (id)
|
||||
)");
|
||||
$config->set_int("ext_ipban_version", 1);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_ipban_version") == 1) {
|
||||
$database->execute("ALTER TABLE bans ADD COLUMN banner_id INTEGER NOT NULL AFTER id");
|
||||
$config->set_int("ext_ipban_version", 2);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_ipban_version") == 2) {
|
||||
$database->execute("ALTER TABLE bans DROP COLUMN date");
|
||||
$database->execute("ALTER TABLE bans CHANGE ip ip CHAR(20) NOT NULL");
|
||||
$database->execute("ALTER TABLE bans CHANGE reason reason TEXT NOT NULL");
|
||||
$database->execute("CREATE INDEX bans__end ON bans(end)");
|
||||
$config->set_int("ext_ipban_version", 3);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_ipban_version") == 3) {
|
||||
$database->execute("ALTER TABLE bans CHANGE end old_end DATE NOT NULL");
|
||||
$database->execute("ALTER TABLE bans ADD COLUMN end INTEGER");
|
||||
$database->execute("UPDATE bans SET end = UNIX_TIMESTAMP(old_end)");
|
||||
$database->execute("ALTER TABLE bans DROP COLUMN old_end");
|
||||
$database->execute("CREATE INDEX bans__end ON bans(end)");
|
||||
$config->set_int("ext_ipban_version", 4);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_ipban_version") == 4) {
|
||||
$database->execute("ALTER TABLE bans CHANGE end end_timestamp INTEGER");
|
||||
$config->set_int("ext_ipban_version", 5);
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// deal with banned person {{{
|
||||
private function check_ip_ban() {
|
||||
global $config;
|
||||
global $database;
|
||||
|
||||
$remote = $_SERVER['REMOTE_ADDR'];
|
||||
$bans = $this->get_active_bans();
|
||||
foreach($bans as $row) {
|
||||
if(
|
||||
(strstr($row['ip'], '/') && ip_in_range($remote, $row['ip'])) ||
|
||||
($row['ip'] == $remote)
|
||||
) {
|
||||
$admin = $database->get_user_by_id($row['banner_id']);
|
||||
$date = date("Y-m-d", $row['end_timestamp']);
|
||||
print "IP <b>{$row['ip']}</b> has been banned until <b>$date</b> by <b>{$admin->name}</b> because of <b>{$row['reason']}</b>";
|
||||
|
||||
$contact_link = $config->get_string("contact_link");
|
||||
if(!empty($contact_link)) {
|
||||
print "<p><a href='$contact_link'>Contact The Admin</a>";
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// database {{{
|
||||
private function get_bans() {
|
||||
global $database;
|
||||
$bans = $database->get_all("
|
||||
SELECT bans.*, users.name as banner_name
|
||||
FROM bans
|
||||
JOIN users ON banner_id = users.id
|
||||
ORDER BY end_timestamp, id");
|
||||
if($bans) {return $bans;}
|
||||
else {return array();}
|
||||
}
|
||||
|
||||
private function get_active_bans() {
|
||||
global $database;
|
||||
$bans = $database->get_all("
|
||||
SELECT * FROM bans
|
||||
WHERE (end_timestamp > now()) OR (end_timestamp IS NULL)
|
||||
");
|
||||
if($bans) {return $bans;}
|
||||
else {return array();}
|
||||
}
|
||||
|
||||
private function add_ip_ban($ip, $reason, $end, $user) {
|
||||
global $database;
|
||||
$sql = "INSERT INTO bans (ip, reason, end_timestamp, banner_id) VALUES (?, ?, ?, ?)";
|
||||
$database->Execute($sql, array($ip, $reason, strtotime($end), $user->id));
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
add_event_listener(new IPBan(), 10);
|
||||
?>
|
@ -12,13 +12,16 @@ class IPBanTheme extends Themelet {
|
||||
* )
|
||||
*/
|
||||
public function display_bans($page, $bans) {
|
||||
global $user;
|
||||
$h_bans = "";
|
||||
foreach($bans as $ban) {
|
||||
$end_human = date('Y-m-d', $ban['end_timestamp']);
|
||||
$h_bans .= "
|
||||
<tr>
|
||||
<td>{$ban['ip']}</td>
|
||||
<td>{$ban['reason']}</td>
|
||||
<td>{$ban['end']}</td>
|
||||
<td>{$ban['banner_name']}</td>
|
||||
<td>{$end_human}</td>
|
||||
<td>
|
||||
<form action='".make_link("ip_ban/remove")."' method='POST'>
|
||||
<input type='hidden' name='id' value='{$ban['id']}'>
|
||||
@ -30,18 +33,22 @@ class IPBanTheme extends Themelet {
|
||||
}
|
||||
$html = "
|
||||
<table border='1'>
|
||||
<thead><td>IP</td><td>Reason</td><td>Until</td><td>Action</td></thead>
|
||||
<thead><td>IP</td><td>Reason</td><td>By</td><td>Until</td><td>Action</td></thead>
|
||||
$h_bans
|
||||
<tr>
|
||||
<form action='".make_link("ip_ban/add")."' method='POST'>
|
||||
<td><input type='text' name='ip'></td>
|
||||
<td><input type='text' name='reason'></td>
|
||||
<td>{$user->name}</td>
|
||||
<td><input type='text' name='end'></td>
|
||||
<td><input type='submit' value='Ban'></td>
|
||||
</form>
|
||||
</tr>
|
||||
</table>
|
||||
";
|
||||
$page->set_title("IP Bans");
|
||||
$page->set_heading("IP Bans");
|
||||
$page->add_block(new NavBlock());
|
||||
$page->add_block(new Block("Edit IP Bans", $html));
|
||||
}
|
||||
}
|
@ -1,4 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: News
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Show a short amonut of text in a block on the post list
|
||||
*/
|
||||
|
||||
class News extends Extension {
|
||||
var $theme;
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Image Notes
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Adds notes overlaid on the images
|
||||
*/
|
||||
|
||||
class Notes extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("notes", "NotesTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
if($config->get_int("ext_notes_version") < 1) {
|
||||
$this->install();
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent')) {
|
||||
global $database;
|
||||
$notes = $database->get_all("SELECT * FROM image_notes WHERE image_id = ?", array($event->image->id));
|
||||
$this->theme->display_notes($event->page, $notes);
|
||||
}
|
||||
}
|
||||
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
$database->Execute("CREATE TABLE image_notes (
|
||||
id int(11) NOT NULL auto_increment PRIMARY KEY,
|
||||
image_id int(11) NOT NULL,
|
||||
user_id int(11) NOT NULL,
|
||||
owner_ip char(15) NOT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
updated_at datetime NOT NULL,
|
||||
version int(11) DEFAULT 1 NOT NULL,
|
||||
is_active enum('Y', 'N') DEFAULT 'Y' NOT NULL,
|
||||
x int(11) NOT NULL,
|
||||
y int(11) NOT NULL,
|
||||
w int(11) NOT NULL,
|
||||
h int(11) NOT NULL,
|
||||
body text NOT NULL
|
||||
)");
|
||||
$config->set_int("ext_notes_version", 1);
|
||||
}
|
||||
}
|
||||
add_event_listener(new Notes());
|
||||
?>
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
class NotesTheme extends Themelet {
|
||||
public function display_notes($page, $notes) {
|
||||
$html = <<<EOD
|
||||
<script type="text/javascript">
|
||||
img = byId("main_image");
|
||||
</script>
|
||||
EOD;
|
||||
$page->add_block(new Block(null, $html));
|
||||
}
|
||||
}
|
||||
?>
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: Image Scores (Numeric)
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Allow users to score images
|
||||
*/
|
||||
@ -28,52 +27,59 @@ class NumericScore extends Extension {
|
||||
if($config->get_int("ext_numeric_score_version", 0) < 1) {
|
||||
$this->install();
|
||||
}
|
||||
$config->set_default_bool("numeric_score_anon", true);
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && $event->page_name == "numeric_score" &&
|
||||
$event->get_arg(0) == "vote" &&
|
||||
isset($_POST['score']) && isset($_POST['image_id'])) {
|
||||
$i_score = int_escape($_POST['score']);
|
||||
$i_image_id = int_escape($_POST['image_id']);
|
||||
|
||||
if($i_score >= -1 || $i_score <= 1) {
|
||||
send_event(new NumericScoreSetEvent($i_image_id, $event->user, $i_score));
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent')) {
|
||||
global $user;
|
||||
if(!$user->is_anonymous()) {
|
||||
$html = $this->theme->get_voter_html($event->image);
|
||||
$event->page->add_block(new Block("Image Score", $html, "left", 20));
|
||||
}
|
||||
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("post/view/$i_image_id"));
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "numeric_score_vote")) {
|
||||
if(!$event->user->is_anonymous()) {
|
||||
$image_id = int_escape($_POST['image_id']);
|
||||
$char = $_POST['vote'];
|
||||
$score = 0;
|
||||
if($char == "up") $score = 1;
|
||||
else if($char == "down") $score = -1;
|
||||
if($score != 0) send_event(new NumericScoreSetEvent($image_id, $event->user, $score));
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("post/view/$image_id"));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageInfoSetEvent')) {
|
||||
global $user;
|
||||
$char = $_POST['numeric_score'];
|
||||
$score = 0;
|
||||
if($char == "u") $score = 1;
|
||||
else if($char == "d") $score = -1;
|
||||
if($score != 0) send_event(new NumericScoreSetEvent($event->image_id, $user, $score));
|
||||
}
|
||||
|
||||
if(is_a($event, 'NumericScoreSetEvent')) {
|
||||
if(!$event->user->is_anonymous() || $config->get_bool("numeric_score_anon")) {
|
||||
$this->add_vote($event->image_id, $event->user->id, $event->score);
|
||||
}
|
||||
$this->add_vote($event->image_id, $event->user->id, $event->score);
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageInfoBoxBuildingEvent')) {
|
||||
global $user;
|
||||
global $config;
|
||||
if(!$user->is_anonymous() || $config->get_bool("numeric_score_anon")) {
|
||||
$event->add_part($this->theme->get_voter_html($event->image));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(is_a($event, 'ImageDeletionEvent')) {
|
||||
global $database;
|
||||
$database->execute("DELETE FROM numeric_score_votes WHERE image_id=?", array($event->image->id));
|
||||
}
|
||||
|
||||
if(is_a($event, 'SetupBuildingEvent')) {
|
||||
$sb = new SetupBlock("Numeric Score");
|
||||
$sb->add_bool_option("numeric_score_anon", "Allow anonymous votes: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
if(is_a($event, 'ParseLinkTemplateEvent')) {
|
||||
$event->replace('$score', $event->image->numeric_score);
|
||||
}
|
||||
|
||||
if(is_a($event, 'SearchTermParseEvent')) {
|
||||
$matches = array();
|
||||
if(preg_match("/score(<|<=|=|>=|>)(\d+)/", $event->term, $matches)) {
|
||||
$cmp = $matches[1];
|
||||
$score = $matches[2];
|
||||
$event->set_querylet(new Querylet("numeric_score $cmp $score"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function install() {
|
||||
@ -99,7 +105,10 @@ class NumericScore extends Extension {
|
||||
private function add_vote($image_id, $user_id, $score) {
|
||||
global $database;
|
||||
$database->Execute(
|
||||
"REPLACE INTO numeric_score_votes(image_id, user_id, score) VALUES(?, ?, ?)",
|
||||
"DELETE FROM numeric_score_votes WHERE image_id=? AND user_id=?",
|
||||
array($image_id, $user_id));
|
||||
$database->Execute(
|
||||
"INSERT INTO numeric_score_votes(image_id, user_id, score) VALUES(?, ?, ?)",
|
||||
array($image_id, $user_id, $score));
|
||||
$database->Execute(
|
||||
"UPDATE images SET numeric_score=(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=?) WHERE id=?",
|
||||
|
@ -6,26 +6,19 @@ class NumericScoreTheme extends Themelet {
|
||||
$i_score = int_escape($image->numeric_score);
|
||||
|
||||
$html = "
|
||||
<table style='width: 400px;'>
|
||||
<tr>
|
||||
<td>Current score is $i_score</td>
|
||||
<td>
|
||||
<!--
|
||||
<form action='".make_link("numeric_score/vote")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$i_image_id' />
|
||||
<input type='hidden' name='score' value='1'>
|
||||
<input type='submit' value='Vote Up' />
|
||||
Current Score: $i_score
|
||||
|
||||
<p><form action='".make_link("numeric_score_vote")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='hidden' name='vote' value='up'>
|
||||
<input type='submit' value='Vote Up'>
|
||||
</form>
|
||||
|
||||
<p><form action='".make_link("numeric_score_vote")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$i_image_id'>
|
||||
<input type='hidden' name='vote' value='down'>
|
||||
<input type='submit' value='Vote Down'>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action='".make_link("numeric_score/vote")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$i_image_id' />
|
||||
<input type='hidden' name='score' value='-1'>
|
||||
<input type='submit' value='Vote Down' />
|
||||
</form>-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
|
24
contrib/piclens/main.php
Normal file
24
contrib/piclens/main.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: PicLens Button
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Adds a link to piclensify the gallery
|
||||
*/
|
||||
class PicLens extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_a($event, 'PageRequestEvent')) {
|
||||
$event->page->add_header("<script type=\"text/javascript\" src=\"http://lite.piclens.com/current/piclens.js\"></script>");
|
||||
}
|
||||
if(is_a($event, 'PostListBuildingEvent')) {
|
||||
$foo='
|
||||
<a href="javascript:PicLensLite.start();">Start Slideshow
|
||||
<img src="http://lite.piclens.com/images/PicLensButton.png"
|
||||
alt="PicLens" width="16" height="12" border="0"
|
||||
align="absmiddle"></a>';
|
||||
$event->page->add_block(new Block("PicLens", $foo, "left", 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new PicLens());
|
||||
?>
|
41
contrib/random_image/main.php
Normal file
41
contrib/random_image/main.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Random Image
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Do things with a random image
|
||||
* Link: http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/RandomImage
|
||||
*/
|
||||
|
||||
class RandomImage extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "random_image")) {
|
||||
global $database;
|
||||
|
||||
if($event->count_args() == 1) {
|
||||
$action = $event->get_arg(0);
|
||||
$search_terms = array();
|
||||
}
|
||||
else if($event->count_args() == 2) {
|
||||
$action = $event->get_arg(0);
|
||||
$search_terms = explode(' ', $event->get_arg(1));
|
||||
}
|
||||
$image = $database->get_random_image($search_terms);
|
||||
|
||||
if($event->get_arg(0) == "download") {
|
||||
if(!is_null($image)) {
|
||||
$event->page->set_mode("data");
|
||||
$event->page->set_type("image/jpeg");
|
||||
$event->page->set_data(file_get_contents($image->get_image_filename()));
|
||||
}
|
||||
}
|
||||
if($event->get_arg(0) == "view") {
|
||||
if(!is_null($image)) {
|
||||
send_event(new DisplayingImageEvent($image, $event->page));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new RandomImage());
|
||||
?>
|
@ -1,106 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Image Ratings
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Allow users to rate images
|
||||
*/
|
||||
|
||||
class RatingSetEvent extends Event {
|
||||
var $image_id, $user, $rating;
|
||||
|
||||
public function RatingSetEvent($image_id, $user, $rating) {
|
||||
$this->image_id = $image_id;
|
||||
$this->user = $user;
|
||||
$this->rating = $rating;
|
||||
}
|
||||
}
|
||||
|
||||
class Ratings extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("rating", "RatingsTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
if($config->get_int("ext_ratings2_version") < 2) {
|
||||
$this->install();
|
||||
}
|
||||
|
||||
global $config;
|
||||
$config->set_default_string("ext_rating_anon_privs", 'sq');
|
||||
$config->set_default_string("ext_rating_user_privs", 'sq');
|
||||
}
|
||||
|
||||
if(is_a($event, 'RatingSetEvent')) {
|
||||
$this->set_rating($event->image_id, $event->rating);
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageInfoBoxBuildingEvent')) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$event->add_part($this->theme->get_rater_html($event->image->id, $event->image->rating), 80);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageInfoSetEvent')) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
send_event(new RatingSetEvent($event->image_id, $user, $_POST['rating']));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'SetupBuildingEvent')) {
|
||||
$privs = array();
|
||||
$privs['Safe Only'] = 's';
|
||||
$privs['Safe and Questionable'] = 'sq';
|
||||
$privs['All'] = 'sqe';
|
||||
|
||||
$sb = new SetupBlock("Image Ratings");
|
||||
$sb->add_choice_option("ext_rating_anon_privs", $privs, "Anonymous: ");
|
||||
$sb->add_choice_option("ext_rating_user_privs", $privs, "<br>Logged in: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
if(is_a($event, 'ParseLinkTemplateEvent')) {
|
||||
$event->replace('$rating', $this->theme->rating_to_name($event->image->rating));
|
||||
}
|
||||
|
||||
if(is_a($event, 'SearchTermParseEvent')) {
|
||||
$matches = array();
|
||||
if(preg_match("/rating=([sqe]+)/", $event->term, $matches)) {
|
||||
$sqes = $matches[1];
|
||||
$arr = array();
|
||||
for($i=0; $i<strlen($sqes); $i++) {
|
||||
$arr[] = "'" . $sqes[$i] . "'";
|
||||
}
|
||||
$set = join(', ', $arr);
|
||||
$event->set_querylet(new Querylet("AND (rating IN ($set))"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
if($config->get_int("ext_ratings2_version") < 1) {
|
||||
$database->Execute("ALTER TABLE images ADD COLUMN rating ENUM('s', 'q', 'e') NOT NULL DEFAULT 'q'");
|
||||
$config->set_int("ext_ratings2_version", 1);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_ratings2_version") < 2) {
|
||||
$database->Execute("CREATE INDEX images__rating ON images(rating)");
|
||||
$config->set_int("ext_ratings2_version", 2);
|
||||
}
|
||||
}
|
||||
|
||||
private function set_rating($image_id, $rating) {
|
||||
global $database;
|
||||
$database->Execute("UPDATE images SET rating=? WHERE id=?", array($rating, $image_id));
|
||||
}
|
||||
}
|
||||
add_event_listener(new Ratings());
|
||||
?>
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
class RatingsTheme extends Themelet {
|
||||
public function get_rater_html($image_id, $rating) {
|
||||
$i_image_id = int_escape($image_id);
|
||||
$s_checked = $rating == 's' ? " checked" : "";
|
||||
$q_checked = $rating == 'q' ? " checked" : "";
|
||||
$e_checked = $rating == 'e' ? " checked" : "";
|
||||
$html = "
|
||||
<input type='hidden' name='image_id' value='$i_image_id' />
|
||||
<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>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function rating_to_name($rating) {
|
||||
switch($rating) {
|
||||
case 's': return "Safe";
|
||||
case 'q': return "Questionable";
|
||||
case 'e': return "Explicit";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,4 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Regen Thumb
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Regenerate a thumbnail image
|
||||
*/
|
||||
|
||||
class RegenThumb extends Extension {
|
||||
var $theme;
|
||||
@ -16,10 +22,9 @@ class RegenThumb extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent')) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$this->theme->display_buttons($event->page, $event->image->id);
|
||||
if(is_a($event, 'ImageAdminBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image->id));
|
||||
}
|
||||
}
|
||||
}
|
@ -4,14 +4,13 @@ class RegenThumbTheme extends Themelet {
|
||||
/*
|
||||
* Show a form which offers to regenerate the thumb of an image with ID #$image_id
|
||||
*/
|
||||
public function display_buttons($page, $image_id) {
|
||||
$html = "
|
||||
public function get_buttons_html($image_id) {
|
||||
return "
|
||||
<form action='".make_link("regen_thumb")."' method='POST'>
|
||||
<input type='hidden' name='image_id' value='$image_id'>
|
||||
<input type='submit' value='Regenerate'>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Regen Thumb", $html, "left"));
|
||||
}
|
||||
|
||||
/*
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Name: Report Images
|
||||
* Author: ATravelingGeek <atg@atravelinggeek.com>
|
||||
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: Resolution Limiter
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Allows the admin to set min / max image dimentions
|
||||
*/
|
||||
@ -15,6 +14,7 @@ class ResolutionLimit extends Extension {
|
||||
$max_w = $config->get_int("upload_max_width", -1);
|
||||
$max_h = $config->get_int("upload_max_height", -1);
|
||||
$ratios = explode(" ", $config->get_string("upload_ratios", ""));
|
||||
$ratios = array_filter($ratios, "strlen");
|
||||
|
||||
$image = $event->image;
|
||||
|
||||
@ -27,6 +27,7 @@ class ResolutionLimit extends Extension {
|
||||
$ok = false;
|
||||
foreach($ratios as $ratio) {
|
||||
$parts = explode(":", $ratio);
|
||||
if(count($parts) < 2) continue;
|
||||
$width = $parts[0];
|
||||
$height = $parts[1];
|
||||
if($image->width / $width == $image->height / $height) {
|
||||
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: RSS for Comments
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Self explanitory
|
||||
*/
|
||||
|
@ -1,4 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: RSS for Images
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Self explanitory
|
||||
*/
|
||||
|
||||
|
||||
class RSS_Images extends Extension {
|
||||
// event handling {{{
|
||||
@ -43,6 +50,8 @@ class RSS_Images extends Extension {
|
||||
$link = make_link("post/view/{$image->id}");
|
||||
$tags = $image->get_tag_list();
|
||||
$owner = $image->get_owner();
|
||||
$thumb_url = $image->get_thumb_link();
|
||||
$image_url = $image->get_image_link();
|
||||
$posted = strftime("%a, %d %b %Y %T %Z", $image->posted_timestamp);
|
||||
$content = html_escape(
|
||||
"<p>" . Themelet::build_thumb_html($image) . "</p>" .
|
||||
@ -56,6 +65,8 @@ class RSS_Images extends Extension {
|
||||
<guid isPermaLink=\"true\">$link</guid>
|
||||
<pubDate>$posted</pubDate>
|
||||
<description>$content</description>
|
||||
<media:thumbnail url=\"$thumb_url\"/>
|
||||
<media:content url=\"$image_url\"/>
|
||||
</item>
|
||||
";
|
||||
}
|
||||
@ -64,7 +75,7 @@ class RSS_Images extends Extension {
|
||||
$base_href = $config->get_string('base_href');
|
||||
$version = VERSION;
|
||||
$xml = "<"."?xml version=\"1.0\" encoding=\"utf-8\" ?".">
|
||||
<rss version=\"2.0\">
|
||||
<rss version=\"2.0\" xmlns:media=\"http://search.yahoo.com/mrss\">
|
||||
<channel>
|
||||
<title>$title</title>
|
||||
<description>The latest uploads to the image board</description>
|
@ -2,12 +2,9 @@
|
||||
/**
|
||||
* Name: Site Description
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Sets the "description" meta-info in the page header, for
|
||||
* eg search engines to read
|
||||
*
|
||||
* This is currently the only example of a user-contributed extension~
|
||||
*/
|
||||
class SiteDescription extends Extension {
|
||||
public function receive_event($event) {
|
||||
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Spoiler Filter
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Adds a [spoiler] tag to rot13 text inside it
|
||||
*/
|
||||
class Spoiler extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_a($event, 'TextFormattingEvent')) {
|
||||
$event->formatted = $this->filter($event->formatted);
|
||||
$event->stripped = $this->strip($event->stripped);
|
||||
}
|
||||
}
|
||||
|
||||
private function filter($text) {
|
||||
return str_replace(
|
||||
array("[spoiler]","[/spoiler]"),
|
||||
array("<span style=\"background-color:#000; color:#000;\">","</span>"),
|
||||
$text);
|
||||
}
|
||||
|
||||
private function strip($text) {
|
||||
$l1 = strlen("[spoiler]");
|
||||
$l2 = strlen("[/spoiler]");
|
||||
while(true) {
|
||||
$start = strpos($text, "[spoiler]");
|
||||
if($start === false) break;
|
||||
|
||||
$end = strpos($text, "[/spoiler]");
|
||||
if($end === false) break;
|
||||
|
||||
$beginning = substr($text, 0, $start);
|
||||
$middle = str_rot13(substr($text, $start+$l1, ($end-$start-$l1)));
|
||||
$ending = substr($text, $end + $l2, (strlen($text)-$end+$l2));
|
||||
|
||||
$text = $beginning . $middle . $ending;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
add_event_listener(new Spoiler(), 45); // before bbcode, so before <br>s are inserted
|
||||
?>
|
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Subversion Updater
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Provides a button to check for updates
|
||||
*/
|
||||
|
||||
class SVNUpdate extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("svn_update", "SVNUpdateTheme");
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "update")) {
|
||||
if($event->user->is_admin()) {
|
||||
if($event->get_arg(0) == "view_changes") {
|
||||
$this->theme->display_update_todo($event->page,
|
||||
$this->get_update_log(),
|
||||
$this->get_branches());
|
||||
}
|
||||
if($event->get_arg(0) == "update") {
|
||||
$this->theme->display_update_log($event->page, $this->run_update());
|
||||
}
|
||||
if($event->get_arg(0) == "dump") {
|
||||
$this->theme->display_update_log($event->page, $this->run_dump());
|
||||
}
|
||||
//if($event->get_arg(0) == "switch") {
|
||||
// $this->theme->display_update_log($event->page, $this->run_update());
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'AdminBuildingEvent')) {
|
||||
global $page;
|
||||
$this->theme->display_form($page);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_update_log() {
|
||||
return shell_exec("svn log -r HEAD:BASE .");
|
||||
}
|
||||
private function run_update() {
|
||||
return shell_exec("svn update");
|
||||
}
|
||||
private function run_dump() {
|
||||
global $database_dsn;
|
||||
$matches = array();
|
||||
|
||||
// FIXME: MySQL specific
|
||||
if(preg_match("#^mysql://([^:]+):([^@]+)@([^/]+)/([^\?]+)#", $database_dsn, $matches)) {
|
||||
$date = date("Ymd");
|
||||
return
|
||||
shell_exec("mysqldump -uUSER -pPASS -hHOST DATABASE | gzip > db-$date.sql.gz") .
|
||||
"\n\nDatabase dump should now be sitting in db-$date.sql.gz in the shimmie folder";
|
||||
}
|
||||
else {
|
||||
return "Couldn't parse database connection string";
|
||||
}
|
||||
}
|
||||
private function get_branches() {
|
||||
$data = shell_exec("svn ls http://svn.shishnet.org/shimmie2/branches/");
|
||||
$list = array();
|
||||
foreach(split("\n", $data) as $line) {
|
||||
$matches = array();
|
||||
if(preg_match("/branch_(\d.\d+)/", $line, $matches)) {
|
||||
$ver = $matches[1];
|
||||
$list["branch_$ver"] = "Stable ($ver.X)";
|
||||
}
|
||||
}
|
||||
ksort($list);
|
||||
$list = array_reverse($list, true);
|
||||
$list["trunk"] = "Unstable (Trunk)";
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
add_event_listener(new SVNUpdate());
|
||||
?>
|
@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
class SVNUpdateTheme extends Themelet {
|
||||
public function display_form($page) {
|
||||
$html = "
|
||||
<a href='".make_link("update/view_changes")."'>Check for Updates</a>
|
||||
";
|
||||
$page->add_block(new Block("Update", $html));
|
||||
}
|
||||
|
||||
public function display_update_todo($page, $log, $branches) {
|
||||
$h_log = html_escape($log);
|
||||
$updates = "
|
||||
<textarea rows='20' cols='80'>$h_log</textarea>
|
||||
<br/>
|
||||
<form action='".make_link("update/update")."' method='POST'>
|
||||
<input type='submit' value='Install Updates'>
|
||||
</form>
|
||||
";
|
||||
$options = "";
|
||||
foreach($branches as $name => $nice) {
|
||||
$options .= "<option value='$name'>$nice</option>";
|
||||
}
|
||||
$branches = "
|
||||
<form action='".make_link("update/switch")."' method='POST'>
|
||||
<select name='branch'>
|
||||
$options
|
||||
</select>
|
||||
<input type='submit' value='Change Branch'>
|
||||
</form>
|
||||
";
|
||||
|
||||
$page->set_title("Updates Available");
|
||||
$page->set_heading("Updates Available");
|
||||
$page->add_block(new NavBlock());
|
||||
$page->add_block(new Block("Updates For Current Branch", $updates));
|
||||
$page->add_block(new Block("Available Branches", $branches));
|
||||
}
|
||||
|
||||
public function display_update_log($page, $log) {
|
||||
$h_log = html_escape($log);
|
||||
$html = "
|
||||
<textarea rows='20' cols='80'>$h_log</textarea>
|
||||
";
|
||||
|
||||
$page->set_title("Update Log");
|
||||
$page->set_heading("Update Log");
|
||||
$page->add_block(new NavBlock());
|
||||
$page->add_block(new Block("Update Log", $html));
|
||||
}
|
||||
}
|
||||
?>
|
Binary file not shown.
Before Width: | Height: | Size: 183 B |
Binary file not shown.
Before Width: | Height: | Size: 227 B |
Binary file not shown.
Before Width: | Height: | Size: 170 B |
Binary file not shown.
Before Width: | Height: | Size: 198 B |
Binary file not shown.
@ -1,174 +0,0 @@
|
||||
<?php
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Name: Tagger *
|
||||
* Description: Advanced Tagging v2 *
|
||||
* Author: Artanis (Erik Youngren) <artanis.00@gmail.com> *
|
||||
* Do not remove this notice. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
class Tagger extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event ($event) {
|
||||
if(is_null($this->theme))
|
||||
$this->theme = get_theme_object("tagger", "taggerTheme");
|
||||
|
||||
if(is_a($event,'DisplayingImageEvent')) {
|
||||
global $page, $config, $user;
|
||||
|
||||
if($config->get_bool("tag_edit_anon")
|
||||
|| ($user->id != $config->get_int("anon_id"))
|
||||
&& $config->get_bool("ext_tagger_enabled"))
|
||||
{
|
||||
$this->theme->build_tagger($page,$event);
|
||||
}
|
||||
}
|
||||
if(is_a($event,'SetupBuildingEvent')) {
|
||||
$sb = new SetupBlock("Tagger");
|
||||
$sb->add_bool_option("ext_tagger_enabled","Enable Tagger");
|
||||
$sb->add_int_option("ext_tagger_search_delay","<br/>Delay queries by ");
|
||||
$sb->add_label(" milliseconds.");
|
||||
$sb->add_label("<br/>Limit queries returning more than ");
|
||||
$sb->add_int_option("ext_tagger_tag_max");
|
||||
$sb->add_label(" tags to ");
|
||||
$sb->add_int_option("ext_tagger_limit");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
}
|
||||
} add_event_listener( new tagger());
|
||||
|
||||
// Tagger AJAX back-end
|
||||
class TaggerXML extends Extension {
|
||||
public function receive_event($event) {
|
||||
if(is_a($event,'PageRequestEvent')
|
||||
&& $event->page_name == "tagger"
|
||||
&& $event->get_arg(0) == "tags")
|
||||
{
|
||||
global $page;
|
||||
|
||||
//$match_tags = null;
|
||||
//$image_tags = null;
|
||||
$tags=null;
|
||||
if (isset($_GET['s'])) { // tagger/tags[/...]?s=$string
|
||||
// return matching tags in XML form
|
||||
$tags = $this->match_tag_list($_GET['s']);
|
||||
} else if($event->get_arg(1)) { // tagger/tags/$int
|
||||
// return arg[1] AS image_id's tag list in XML form
|
||||
$tags = $this->image_tag_list($event->get_arg(1));
|
||||
}
|
||||
|
||||
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".
|
||||
"<tags>".
|
||||
$tags.
|
||||
"</tags>";
|
||||
|
||||
$page->set_mode("data");
|
||||
$page->set_type("text/xml");
|
||||
$page->set_data($xml);
|
||||
}
|
||||
}
|
||||
|
||||
private function match_tag_list ($s) {
|
||||
global $database, $config, $event;
|
||||
|
||||
$max_rows = $config->get_int("ext_tagger_tag_max",30);
|
||||
$limit_rows = $config->get_int("ext_tagger_limit",30);
|
||||
|
||||
$values = array();
|
||||
|
||||
// Match
|
||||
$p = strlen($s) == 1? " ":"\_";
|
||||
$sq = "%".$p.mysql_real_escape_string($s)."%";
|
||||
$match = "concat(?,tag) LIKE ?";
|
||||
array_push($values,$p,$sq);
|
||||
// Exclude
|
||||
// $exclude = $event->get_arg(1)? "AND NOT IN ".$this->image_tags($event->get_arg(1)) : null;
|
||||
|
||||
// Hidden Tags
|
||||
$hidden = $config->get_string('ext-tagger_show-hidden','N')=='N' ?
|
||||
"AND substring(tag,1,1) != '.'" : null;
|
||||
|
||||
$q_where = "WHERE {$match} {$hidden} AND count > 0";
|
||||
|
||||
// FROM based on return count
|
||||
$q_from = null;
|
||||
$count = $this->count($q_where,$values);
|
||||
if ($count > $max_rows) {
|
||||
$q_from = "FROM (SELECT * FROM `tags` {$q_where} ".
|
||||
"ORDER BY count DESC LIMIT 0, {$limit_rows}) AS `c_tags`";
|
||||
$q_where = null;
|
||||
$count = array("max"=>$count);
|
||||
} else {
|
||||
$q_from = "FROM `tags`";
|
||||
$count = null;
|
||||
}
|
||||
|
||||
$tags = $database->Execute("
|
||||
SELECT *
|
||||
{$q_from}
|
||||
{$q_where}
|
||||
ORDER BY tag",
|
||||
$values);
|
||||
|
||||
return $this->list_to_xml($tags,"search",$s,$count);
|
||||
}
|
||||
|
||||
private function image_tag_list ($image_id) {
|
||||
global $database;
|
||||
$tags = $database->Execute("
|
||||
SELECT tags.*
|
||||
FROM image_tags JOIN tags ON image_tags.tag_id = tags.id
|
||||
WHERE image_id=? ORDER BY tag", array($image_id));
|
||||
return $this->list_to_xml($tags,"image",$image_id);
|
||||
}
|
||||
|
||||
private function list_to_xml ($tags,$type,$query,$misc=null) {
|
||||
$r = $tags->_numOfRows;
|
||||
|
||||
$s_misc = "";
|
||||
if(!is_null($misc))
|
||||
foreach($misc as $attr => $val) $s_misc .= " ".$attr."=\"".$val."\"";
|
||||
|
||||
$result = "<list id=\"$type\" query=\"$query\" rows=\"$r\"{$s_misc}>";
|
||||
foreach($tags as $tag) {
|
||||
$result .= $this->tag_to_xml($tag);
|
||||
}
|
||||
return $result."</list>";
|
||||
}
|
||||
|
||||
private function tag_to_xml ($tag) {
|
||||
return
|
||||
"<tag ".
|
||||
"id=\"".$tag['id']."\" ".
|
||||
"count=\"".$tag['count']."\">".
|
||||
html_escape($tag['tag']).
|
||||
"</tag>";
|
||||
}
|
||||
|
||||
private function count($query,$values) {
|
||||
global $database;
|
||||
return $database->Execute(
|
||||
"SELECT COUNT(*) FROM `tags` $query",$values)->fields['COUNT(*)'];
|
||||
}
|
||||
|
||||
private function image_tags ($image_id) {
|
||||
global $database;
|
||||
$list = "(";
|
||||
$i_tags = $database->Execute(
|
||||
"SELECT tag_id FROM `image_tags` WHERE image_id=?",
|
||||
array($image_id));
|
||||
|
||||
$b = false;
|
||||
foreach($i_tags as $tag) {
|
||||
if($b)
|
||||
$list .= ",";
|
||||
$b = true;
|
||||
$list .= $tag['tag_id'];
|
||||
|
||||
}
|
||||
$list .= ")";
|
||||
|
||||
return $list;
|
||||
}
|
||||
} add_event_listener( new taggerXML(),10);
|
||||
?>
|
@ -1,218 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
||||
* Tagger - Advanced Tagging v2 *
|
||||
* Author: Artanis (Erik Youngren <artanis.00@gmail.com>) *
|
||||
* Do not remove this notice. *
|
||||
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
var Tagger = {
|
||||
initialize : function (image_id) {
|
||||
// object navigation
|
||||
this.tag.parent = this;
|
||||
this.position.parent = this;
|
||||
// components
|
||||
this.editor.container = byId('tagger_parent');
|
||||
this.editor.titlebar = byId('tagger_titlebar');
|
||||
this.editor.toolbar = byId('tagger_toolbar');
|
||||
//this.editor.menu = byId('tagger_p-menu');
|
||||
this.editor.body = byId('tagger_body');
|
||||
this.editor.tags = byId('tagger_tags');
|
||||
this.editor.form = this.editor.tags.parentNode;
|
||||
this.editor.statusbar = byId('tagger_statusbar');
|
||||
// initial data
|
||||
this.tag.image = image_id;
|
||||
this.tag.query = config.make_link("tagger/tags");
|
||||
this.tag.list = null;
|
||||
this.tag.suggest = null;
|
||||
this.tag.image_tags();
|
||||
|
||||
// reveal
|
||||
this.editor.container.style.display = "";
|
||||
|
||||
// dragging
|
||||
DragHandler.attach(this.editor.titlebar);
|
||||
|
||||
// positioning
|
||||
this.position.load();
|
||||
|
||||
// events
|
||||
window.onunload = function () { Tagger.position.save(); };
|
||||
},
|
||||
|
||||
alert : function (type,text,timeout) {
|
||||
var id = "tagger_alert-"+type
|
||||
var t_alert = byId(id);
|
||||
if (t_alert) {
|
||||
if(text == false) {
|
||||
// remove
|
||||
t_alert.parentNode.removeChild(t_alert);
|
||||
} else {
|
||||
// update
|
||||
t_alert.innerHTML = text;
|
||||
}
|
||||
} else if (text) {
|
||||
// create
|
||||
var t_alert = document.createElement("div");
|
||||
t_alert.setAttribute("id",id);
|
||||
t_alert.appendChild(document.createTextNode(text));
|
||||
this.editor.statusbar.appendChild(t_alert);
|
||||
if(timeout>1) {
|
||||
console.log("Tagger.alert('"+type+"',false,0)");
|
||||
setTimeout("Tagger.alert('"+type+"',false,0)",timeout);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
editor : {},
|
||||
|
||||
tag : {
|
||||
submit : function () {
|
||||
var l = this.list.childNodes.length;
|
||||
var tags = Array();
|
||||
for(var i=0; i<l; i++) {
|
||||
var s_tag = this.list.childNodes[i].firstChild.data;
|
||||
tags.push(s_tag);
|
||||
}
|
||||
tags = tags.join(" ");
|
||||
this.parent.editor.tags.value = tags;
|
||||
return true;
|
||||
},
|
||||
|
||||
search : function(s,ms) {
|
||||
clearTimeout(Tagger.tag.timer);
|
||||
Tagger.tag.timer = setTimeout(
|
||||
"Tagger.tag.ajax('"+Tagger.tag.query+"?s="+s+"',Tagger.tag.receive)",
|
||||
ms);
|
||||
},
|
||||
|
||||
receive : function (xml) {
|
||||
if(xml) {
|
||||
Tagger.tag.suggest = document.importNode(
|
||||
xml.responseXML.getElementsByTagName("list")[0],true);
|
||||
Tagger.tag.publish(Tagger.tag.suggest,byId("tagger_p-search"));
|
||||
}
|
||||
if(Tagger.tag.suggest.getAttribute("max")) {
|
||||
var rows = Tagger.tag.suggest.getAttribute("rows");
|
||||
var max = Tagger.tag.suggest.getAttribute("max");
|
||||
Tagger.alert("maxout","Showing "+rows+" of "+max+" tags",0);
|
||||
} else {
|
||||
Tagger.alert("maxout",false);
|
||||
}
|
||||
},
|
||||
|
||||
image_tags : function(xml) {
|
||||
if (!xml) {
|
||||
this.ajax(this.query+"/"+this.image,this.image_tags);
|
||||
return true;
|
||||
} else {
|
||||
Tagger.tag.list = document.importNode(
|
||||
xml.responseXML.getElementsByTagName("list")[0],true);
|
||||
Tagger.tag.publish(Tagger.tag.list,byId("tagger_p-applied"));
|
||||
}
|
||||
},
|
||||
|
||||
publish : function (list, page) {
|
||||
list.setAttribute("xmlns","http://www.w3.org/1999/xhtml");
|
||||
|
||||
var l = list.childNodes.length;
|
||||
for(var i=0; i<l; i++) {
|
||||
var tag = list.childNodes[i];
|
||||
tag.onclick = function () {
|
||||
Tagger.tag.toggle(this);
|
||||
byId("tagger_filter").select();
|
||||
};
|
||||
tag.setAttribute("title",tag.getAttribute("count")+" uses");
|
||||
}
|
||||
|
||||
page.innerHTML = "";
|
||||
page.appendChild(list);
|
||||
},
|
||||
|
||||
create : function (tag_name) {
|
||||
if(tag_name.length > 0) {
|
||||
var tag = document.createElement("tag");
|
||||
tag.setAttribute("count","0");
|
||||
tag.setAttribute("id","newTag_"+tag_name);
|
||||
tag.setAttribute("title","New - 0 uses");
|
||||
tag.onclick = function() {
|
||||
Tagger.tag.toggle(this);
|
||||
};
|
||||
tag.appendChild(document.createTextNode(tag_name));
|
||||
Tagger.tag.list.appendChild(tag);
|
||||
}
|
||||
},
|
||||
|
||||
toggle : function (tag) {
|
||||
if(tag.parentNode == this.list) {
|
||||
this.list.removeChild(tag);
|
||||
} else {
|
||||
this.list.appendChild(tag);
|
||||
}
|
||||
},
|
||||
|
||||
ajax : function (url, callback) {
|
||||
var http = (new XMLHttpRequest || new ActiveXObject("Microsoft.XMLHTTP"));
|
||||
http.open("GET",url,true);
|
||||
http.onreadystatechange = function () {
|
||||
if(http.readyState == 4) callback(http);
|
||||
};
|
||||
http.send(null);
|
||||
}
|
||||
},
|
||||
|
||||
position : {
|
||||
set : function (x,y) {
|
||||
if (!x || !y) {
|
||||
with(this.parent.editor.container.style) {
|
||||
top = "25px";
|
||||
left = "";
|
||||
right = "25px";
|
||||
bottom = "";
|
||||
}
|
||||
var xy = this.get();
|
||||
x = xy[0];
|
||||
y = xy[1];
|
||||
}
|
||||
with(this.parent.editor.container.style) {
|
||||
top = y+"px";
|
||||
left = x+"px";
|
||||
right = "";
|
||||
bottom = "";
|
||||
}
|
||||
},
|
||||
|
||||
get : function () {
|
||||
// http://www.quirksmode.org/js/findpos.html
|
||||
var left = 0;
|
||||
var top = 0;
|
||||
var obj = this.parent.editor.container;
|
||||
if(obj.offsetParent) {
|
||||
left = obj.offsetLeft;
|
||||
top = obj.offsetTop;
|
||||
while (obj = obj.offsetParent) {
|
||||
left += obj.offsetLeft;
|
||||
top += obj.offsetTop;
|
||||
}
|
||||
}
|
||||
return [left,top];
|
||||
},
|
||||
|
||||
save : function (x,y) {
|
||||
if (!x || !y) {
|
||||
var xy = this.get();
|
||||
x = xy[0];
|
||||
y = xy[1];
|
||||
}
|
||||
setCookie(config.title+"_tagger-position",x+" "+y,14);
|
||||
},
|
||||
|
||||
load : function () {
|
||||
var p = getCookie(config.title+"_tagger-position");
|
||||
if(p) {
|
||||
var xy = p.split(" ");
|
||||
this.set(xy[0],xy[1]);
|
||||
} else {
|
||||
this.set();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -1,104 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Tagger - Advanced Tagging v2 *
|
||||
* Author: Artanis (Erik Youngren <artanis.00@gmail.com>) *
|
||||
* Do not remove this notice. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#tagger_parent {
|
||||
text-align:left;
|
||||
position:fixed;
|
||||
max-width:300px;
|
||||
|
||||
}
|
||||
#tagger_parent * {
|
||||
background-color:#EEE;
|
||||
}
|
||||
|
||||
#tagger_titlebar {
|
||||
background-color:#ddd;
|
||||
border:2px solid;
|
||||
cursor:move;
|
||||
font-weight:bold;
|
||||
-moz-border-radius:5px 5px 0 0;
|
||||
padding:.25em;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
#tagger_toolbar, #tagger_body {
|
||||
padding:2px 2px 0 2px;
|
||||
border-style:solid;
|
||||
border-width: 0px 2px 0px 2px;
|
||||
}
|
||||
#tagger_body {
|
||||
max-height:175px;
|
||||
overflow:auto;
|
||||
overflow-x:hidden;
|
||||
overflow-y:auto;
|
||||
}
|
||||
|
||||
#tagger_statusbar {
|
||||
background-color:#ddd;
|
||||
border:2px solid;
|
||||
font-weight: bold;
|
||||
min-height:16px;
|
||||
-moz-border-radius:0 0 5px 5px;
|
||||
padding:.25em;
|
||||
} #tagger_statusbar * { background-color:#ddd; }
|
||||
|
||||
#tagger_body div {
|
||||
padding-top:2px;
|
||||
margin-top:2px;
|
||||
border-top:1px solid;
|
||||
}
|
||||
|
||||
/* Tagger Styling
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
#tagger_parent form {
|
||||
display:inline;
|
||||
}
|
||||
#tagger_parent input {
|
||||
width:auto;
|
||||
}
|
||||
#tagger_parent input[type=text] {
|
||||
background-color:white;
|
||||
}
|
||||
|
||||
/* Custom Element Base Styles
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#tagger_parent list {
|
||||
display: block;
|
||||
}
|
||||
#tagger_parent tag {
|
||||
font-size:1.25em;
|
||||
display:block;
|
||||
}
|
||||
|
||||
#tagger_parent list[id=image] tag:before {
|
||||
content:url('./images/active.png');
|
||||
}
|
||||
|
||||
#tagger_parent list[id=search] tag:before {
|
||||
content:url('./images/inactive.png');
|
||||
}
|
||||
/* Hovering */
|
||||
#tagger_parent tag:hover {
|
||||
cursor:pointer;
|
||||
background-color:#ddd;
|
||||
}
|
||||
|
||||
/*#tagger_parent list[id=image] tag:hover {
|
||||
background-color:#faa;
|
||||
}
|
||||
|
||||
#tagger_parent list[id=search] tag:hover {
|
||||
background-color:#afa;
|
||||
}*/
|
||||
|
||||
#tagger_parent list[id=image] tag:hover:before {
|
||||
content:url('./images/rem-tag.png');
|
||||
}
|
||||
|
||||
#tagger_parent list[id=search] tag:hover:before {
|
||||
content:url('./images/add-tag.png');
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
<?php
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Tagger - Advanced Tagging v2 *
|
||||
* Author: Artanis (Erik Youngren <artanis.00@gmail.com>) *
|
||||
* Do not remove this notice. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
class taggerTheme extends Themelet {
|
||||
public function build_tagger ($page, $event) {
|
||||
global $config;
|
||||
// Initialization code
|
||||
$base_href = $config->get_string('base_href');
|
||||
// TODO: AJAX test and fallback.
|
||||
$page->add_header("<script src='$base_href/ext/tagger/webtoolkit.drag.js' type='text/javascript'></script>");
|
||||
$page->add_block(new Block(null,
|
||||
"<script type='text/javascript'>Tagger.initialize("
|
||||
.$event->get_image()->id.");</script>","main",1000));
|
||||
|
||||
// Tagger block
|
||||
$page->add_block( new Block(
|
||||
null,
|
||||
$this->html($event->get_image()),
|
||||
"main"));
|
||||
}
|
||||
private function html($image) {
|
||||
global $config;
|
||||
$i_image_id = int_escape($image->id);
|
||||
$h_source = html_escape($image->source);
|
||||
$h_query = isset($_GET['search'])? $h_query= "search=".url_escape($_GET['search']) : "";
|
||||
|
||||
$delay = $config->get_string("ext_tagger_search_delay","250");
|
||||
|
||||
$url_form = make_link("tag_edit/set");
|
||||
|
||||
// TODO: option for initial Tagger window placement.
|
||||
$html = <<< EOD
|
||||
<div id="tagger_parent" style="display:none; top:25px; right:25px;">
|
||||
<div id="tagger_titlebar">Tagger</div>
|
||||
|
||||
<div id="tagger_toolbar">
|
||||
<input type="text" value="" id="tagger_filter" onkeyup="Tagger.tag.search(this.value, $delay);"></input>
|
||||
<input type="button" value="Add" onclick="Tagger.tag.create(byId('tagger_filter').value);"></input>
|
||||
<form action="$url_form" method="POST" onsubmit="Tagger.tag.submit();">
|
||||
<input type='hidden' name='image_id' value='$i_image_id' id="image_id"></input>
|
||||
<input type='hidden' name='query' value='$h_query'></input>
|
||||
<input type='hidden' name='source' value='$h_source'></input>
|
||||
<input type="hidden" name="tags" value="" id="tagger_tags"></input>
|
||||
|
||||
<input type="submit" value="Set"></input>
|
||||
</form>
|
||||
<!--<ul id="tagger_p-menu"></ul>
|
||||
<br style="clear:both;"/>-->
|
||||
</div>
|
||||
|
||||
<div id="tagger_body">
|
||||
<div id="tagger_p-search" name="Searched Tags"></div>
|
||||
<div id="tagger_p-applied" name="Applied Tags"></div>
|
||||
</div>
|
||||
<div id="tagger_statusbar"></div>
|
||||
</div>
|
||||
EOD;
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,85 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Crossbrowser Drag Handler
|
||||
* http://www.webtoolkit.info/
|
||||
*
|
||||
* Modified by Erik Youngren to move parent node
|
||||
**/
|
||||
|
||||
var DragHandler = {
|
||||
|
||||
|
||||
// private property.
|
||||
_oElem : null,
|
||||
|
||||
|
||||
// public method. Attach drag handler to an element.
|
||||
attach : function(oElem) {
|
||||
oElem.onmousedown = DragHandler._dragBegin;
|
||||
|
||||
// callbacks
|
||||
oElem.dragBegin = new Function();
|
||||
oElem.drag = new Function();
|
||||
oElem.dragEnd = new Function();
|
||||
|
||||
return oElem;
|
||||
},
|
||||
|
||||
|
||||
// private method. Begin drag process.
|
||||
_dragBegin : function(e) {
|
||||
var oElem = DragHandler._oElem = this;
|
||||
|
||||
if (isNaN(parseInt(oElem.parentNode.style.left))) { oElem.parentNode.style.left = '0px'; }
|
||||
if (isNaN(parseInt(oElem.parentNode.style.top))) { oElem.parentNode.style.top = '0px'; }
|
||||
|
||||
var x = parseInt(oElem.parentNode.style.left);
|
||||
var y = parseInt(oElem.parentNode.style.top);
|
||||
|
||||
e = e ? e : window.event;
|
||||
oElem.mouseX = e.clientX;
|
||||
oElem.mouseY = e.clientY;
|
||||
|
||||
oElem.dragBegin(oElem, x, y);
|
||||
|
||||
document.onmousemove = DragHandler._drag;
|
||||
document.onmouseup = DragHandler._dragEnd;
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
// private method. Drag (move) element.
|
||||
_drag : function(e) {
|
||||
var oElem = DragHandler._oElem;
|
||||
|
||||
var x = parseInt(oElem.parentNode.style.left);
|
||||
var y = parseInt(oElem.parentNode.style.top);
|
||||
|
||||
e = e ? e : window.event;
|
||||
oElem.parentNode.style.left = x + (e.clientX - oElem.mouseX) + 'px';
|
||||
oElem.parentNode.style.top = y + (e.clientY - oElem.mouseY) + 'px';
|
||||
|
||||
oElem.mouseX = e.clientX;
|
||||
oElem.mouseY = e.clientY;
|
||||
|
||||
oElem.drag(oElem, x, y);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
// private method. Stop drag process.
|
||||
_dragEnd : function() {
|
||||
var oElem = DragHandler._oElem;
|
||||
|
||||
var x = parseInt(oElem.parentNode.style.left);
|
||||
var y = parseInt(oElem.parentNode.style.top);
|
||||
|
||||
oElem.dragEnd(oElem, x, y);
|
||||
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
DragHandler._oElem = null;
|
||||
}
|
||||
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Image Scores (Text)
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Allow users to score images
|
||||
*/
|
||||
|
||||
class TextScoreSetEvent extends Event {
|
||||
var $image_id, $user, $score;
|
||||
|
||||
public function TextScoreSetEvent($image_id, $user, $score) {
|
||||
$this->image_id = $image_id;
|
||||
$this->user = $user;
|
||||
$this->score = $score;
|
||||
}
|
||||
}
|
||||
|
||||
class TextScore extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("text_score", "TextScoreTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
if($config->get_int("ext_text_score_version", 0) < 1) {
|
||||
$this->install();
|
||||
}
|
||||
$config->set_default_bool("text_score_anon", true);
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageInfoBoxBuildingEvent')) {
|
||||
global $user;
|
||||
global $config;
|
||||
if(!$user->is_anonymous() || $config->get_bool("text_score_anon")) {
|
||||
$event->add_part($this->theme->get_scorer_html($event->image));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageInfoSetEvent')) {
|
||||
global $user;
|
||||
$i_score = int_escape($_POST['text_score__score']);
|
||||
|
||||
if($i_score >= -2 || $i_score <= 2) {
|
||||
send_event(new TextScoreSetEvent($event->image_id, $user, $i_score));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'TextScoreSetEvent')) {
|
||||
if(!$event->user->is_anonymous() || $config->get_bool("text_score_anon")) {
|
||||
$this->add_vote($event->image_id, $event->user->id, $event->score);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageDeletionEvent')) {
|
||||
global $database;
|
||||
$database->execute("DELETE FROM text_score_votes WHERE image_id=?", array($event->image->id));
|
||||
}
|
||||
|
||||
if(is_a($event, 'SetupBuildingEvent')) {
|
||||
$sb = new SetupBlock("Text Score");
|
||||
$sb->add_bool_option("text_score_anon", "Allow anonymous votes: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
if(is_a($event, 'ParseLinkTemplateEvent')) {
|
||||
$event->replace('$text_score', $this->theme->score_to_name($event->image->text_score));
|
||||
}
|
||||
}
|
||||
|
||||
private function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
if($config->get_int("ext_text_score_version") < 1) {
|
||||
$database->Execute("ALTER TABLE images ADD COLUMN text_score INTEGER NOT NULL DEFAULT 0");
|
||||
$database->Execute("CREATE INDEX images__text_score ON images(text_score)");
|
||||
$database->Execute("
|
||||
CREATE TABLE text_score_votes (
|
||||
image_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
score INTEGER NOT NULL,
|
||||
UNIQUE(image_id, user_id),
|
||||
INDEX(image_id)
|
||||
)
|
||||
");
|
||||
$config->set_int("ext_text_score_version", 1);
|
||||
}
|
||||
}
|
||||
|
||||
private function add_vote($image_id, $user_id, $score) {
|
||||
global $database;
|
||||
// TODO: update if already voted
|
||||
$database->Execute(
|
||||
"REPLACE INTO text_score_votes(image_id, user_id, score) VALUES(?, ?, ?)",
|
||||
array($image_id, $user_id, $score));
|
||||
$database->Execute(
|
||||
"UPDATE images SET text_score=(SELECT AVG(score) FROM text_score_votes WHERE image_id=?) WHERE id=?",
|
||||
array($image_id, $image_id));
|
||||
}
|
||||
}
|
||||
add_event_listener(new TextScore());
|
||||
?>
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
class TextScoreTheme extends Themelet {
|
||||
public function get_scorer_html($image) {
|
||||
$i_image_id = int_escape($image->id);
|
||||
|
||||
$s_score = $this->score_to_name($image->text_score);
|
||||
$html = "
|
||||
Current score is \"$s_score\"
|
||||
<br/>
|
||||
<input type='hidden' name='image_id' value='$i_image_id' />
|
||||
<input type='radio' name='text_score__score' value='-2' id='-2'><label for='-2'>Delete</label>
|
||||
<input type='radio' name='text_score__score' value='-1' id='-1'><label for='-1'>Bad</label>
|
||||
<input type='radio' name='text_score__score' value='0' id='0' ><label for='0' >Ok</label>
|
||||
<input type='radio' name='text_score__score' value='1' id='1' ><label for='1' >Good</label>
|
||||
<input type='radio' name='text_score__score' value='2' id='2' ><label for='2' >Favourite</label>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function score_to_name($score) {
|
||||
$words = array();
|
||||
$words[-2] = "Delete";
|
||||
$words[-1] = "Bad";
|
||||
$words[ 0] = "Ok";
|
||||
$words[ 1] = "Good";
|
||||
$words[ 2] = "Favourite";
|
||||
$s_score = $words[$score];
|
||||
return $s_score;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -2,10 +2,8 @@
|
||||
/**
|
||||
* Name: Simple Wiki
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: A simple wiki, for those who don't want the
|
||||
* hugeness of mediawiki
|
||||
* Description: A simple wiki, for those who don't want the hugeness of mediawiki
|
||||
*/
|
||||
|
||||
// WikiUpdateEvent {{{
|
||||
|
@ -2,7 +2,6 @@
|
||||
/**
|
||||
* Name: Word Filter
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: Simple search and replace
|
||||
*/
|
||||
|
@ -1,4 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Image Zoom
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* License: GPLv2
|
||||
* Description: Scales down too-large images using browser based scaling
|
||||
*/
|
||||
|
||||
class Zoom extends Extension {
|
||||
var $theme;
|
@ -12,15 +12,19 @@ class ZoomTheme extends Themelet {
|
||||
<script type="text/javascript">
|
||||
img = document.getElementById("main_image");
|
||||
|
||||
img.onclick = function() {scale(img);};
|
||||
if(img) {
|
||||
img.onclick = function() {scale(img);};
|
||||
|
||||
msg_div = document.createElement("div");
|
||||
msg_div.id = "msg_div";
|
||||
msg_div.appendChild(document.createTextNode("Note: Image has been scaled to fit the screen; click to enlarge"));
|
||||
msg_div.style.display="none";
|
||||
img.parentNode.insertBefore(msg_div, img);
|
||||
msg_div = document.createElement("div");
|
||||
msg_div.id = "msg_div";
|
||||
msg_div.appendChild(document.createTextNode("Note: Image has been scaled to fit the screen; click to enlarge"));
|
||||
msg_div.style.display="none";
|
||||
img.parentNode.insertBefore(msg_div, img);
|
||||
|
||||
orig_width = $image_width;
|
||||
orig_width = $image_width;
|
||||
|
||||
$default
|
||||
}
|
||||
|
||||
function scale(img) {
|
||||
if(orig_width >= img.parentNode.clientWidth * 0.9) {
|
||||
@ -34,8 +38,6 @@ function scale(img) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$default
|
||||
</script>
|
||||
EOD;
|
||||
}
|
93
core/compat.inc.php
Normal file
93
core/compat.inc.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/*
|
||||
* Functions which are only in some versions of PHP,
|
||||
* or only implemented on some platforms
|
||||
*/
|
||||
|
||||
# (PHP 5 >= 5.2.1)
|
||||
# Based on http://www.phpit.net/
|
||||
# article/creating-zip-tar-archives-dynamically-php/2/
|
||||
if(!function_exists('sys_get_temp_dir')) {
|
||||
function sys_get_temp_dir() {
|
||||
// Try to get from environment variable
|
||||
if(!empty($_ENV['TMP'])) {
|
||||
return realpath($_ENV['TMP']);
|
||||
}
|
||||
else if(!empty($_ENV['TMPDIR'])) {
|
||||
return realpath($_ENV['TMPDIR']);
|
||||
}
|
||||
else if(!empty($_ENV['TEMP'])) {
|
||||
return realpath($_ENV['TEMP']);
|
||||
}
|
||||
|
||||
// Detect by creating a temporary file
|
||||
else {
|
||||
// Try to use system's temporary directory
|
||||
// as random name shouldn't exist
|
||||
$temp_file = tempnam(md5(uniqid(rand(), TRUE)), '');
|
||||
if($temp_file) {
|
||||
$temp_dir = realpath(dirname($temp_file));
|
||||
unlink($temp_file);
|
||||
return $temp_dir;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# (PHP >= 5.1)
|
||||
# from http://www.php.net/inet_pton
|
||||
if(!function_exists('inet_pton')) {
|
||||
function inet_pton($ip) {
|
||||
# ipv4
|
||||
if(strpos($ip, '.') !== FALSE) {
|
||||
$ip = pack('N',ip2long($ip));
|
||||
}
|
||||
# ipv6
|
||||
else if(strpos($ip, ':') !== FALSE) {
|
||||
$ip = explode(':', $ip);
|
||||
$res = str_pad('', (4*(8-count($ip))), '0000', STR_PAD_LEFT);
|
||||
foreach($ip as $seg) {
|
||||
$res .= str_pad($seg, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
$ip = pack('H'.strlen($res), $res);
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
|
||||
# (PHP >= 5.1)
|
||||
# from http://www.php.net/inet_ntop
|
||||
if(!function_exists('inet_ntop')) {
|
||||
function inet_ntop($ip) {
|
||||
if (strlen($ip)==4) {
|
||||
// ipv4
|
||||
list(,$ip)=unpack('N',$ip);
|
||||
$ip=long2ip($ip);
|
||||
} elseif(strlen($ip)==16) {
|
||||
// ipv6
|
||||
$ip=bin2hex($ip);
|
||||
$ip=substr(chunk_split($ip,4,':'),0,-1);
|
||||
$ip=explode(':',$ip);
|
||||
$res='';
|
||||
foreach($ip as $seg) {
|
||||
while($seg{0}=='0') $seg=substr($seg,1);
|
||||
if ($seg!='') {
|
||||
$res.=($res==''?'':':').$seg;
|
||||
} else {
|
||||
if (strpos($res,'::')===false) {
|
||||
if (substr($res,-1)==':') continue;
|
||||
$res.=':';
|
||||
continue;
|
||||
}
|
||||
$res.=($res==''?'':':').'0';
|
||||
}
|
||||
}
|
||||
$ip=$res;
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
$ADODB_CACHE_DIR="./data";
|
||||
require_once "compat.inc.php";
|
||||
$ADODB_CACHE_DIR=sys_get_temp_dir();
|
||||
require_once "lib/adodb/adodb.inc.php";
|
||||
|
||||
/* Querylet {{{
|
||||
@ -27,7 +28,42 @@ class Querylet {
|
||||
public function add_variable($var) {
|
||||
$this->variables[] = $var;
|
||||
}
|
||||
} // }}}
|
||||
}
|
||||
class TagQuerylet {
|
||||
var $tag;
|
||||
var $positive;
|
||||
|
||||
public function TagQuerylet($tag, $positive) {
|
||||
$this->tag = $tag;
|
||||
$this->positive = $positive;
|
||||
}
|
||||
}
|
||||
class ImgQuerylet {
|
||||
var $qlet;
|
||||
var $positive;
|
||||
|
||||
public function ImgQuerylet($qlet, $positive) {
|
||||
$this->qlet = $qlet;
|
||||
$this->positive = $positive;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// {{{ dbengines
|
||||
class DBEngine {
|
||||
var $name = null;
|
||||
var $auto_increment = null;
|
||||
var $create_table_extras = "";
|
||||
}
|
||||
class MySQL extends DBEngine {
|
||||
var $name = "mysql";
|
||||
var $auto_increment = "INTEGER PRIMARY KEY auto_increment";
|
||||
var $create_table_extras = "TYPE=INNODB DEFAULT CHARSET='utf8'";
|
||||
}
|
||||
class PostgreSQL extends DBEngine {
|
||||
var $name = "pgsql";
|
||||
var $auto_increment = "SERIAL PRIMARY KEY";
|
||||
}
|
||||
//}}}
|
||||
|
||||
/*
|
||||
* A class for controlled database access, available through "global $database"
|
||||
@ -36,6 +72,8 @@ class Database {
|
||||
var $db;
|
||||
var $extensions;
|
||||
var $get_images = "SELECT images.*,UNIX_TIMESTAMP(posted) AS posted_timestamp FROM images ";
|
||||
var $engine = null;
|
||||
var $cache_hits = 0, $cache_misses = 0;
|
||||
|
||||
/*
|
||||
* Create a new database object using connection info
|
||||
@ -44,7 +82,9 @@ class Database {
|
||||
public function Database() {
|
||||
if(is_readable("config.php")) {
|
||||
require_once "config.php";
|
||||
$this->engine = new MySQL();
|
||||
$this->db = @NewADOConnection($database_dsn);
|
||||
$this->use_memcache = isset($memcache);
|
||||
if($this->db) {
|
||||
$this->db->SetFetchMode(ADODB_FETCH_ASSOC);
|
||||
$this->db->Execute("SET NAMES utf8"); // FIXME: mysql specific :|
|
||||
@ -63,6 +103,10 @@ class Database {
|
||||
";
|
||||
exit;
|
||||
}
|
||||
if($this->use_memcache) {
|
||||
$this->memcache = new Memcache;
|
||||
$this->memcache->pconnect('localhost', 11211) or ($this->use_memcache = false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
header("Location: install.php");
|
||||
@ -70,20 +114,54 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
// memcache {{{
|
||||
public function cache_get($key) {
|
||||
assert(!is_null($key));
|
||||
if($this->use_memcache) {
|
||||
$val = $this->memcache->get($key);
|
||||
if($val) {
|
||||
$this->cache_hits++;
|
||||
return $val;
|
||||
}
|
||||
else {
|
||||
$this->cache_misses++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function cache_set($key, $val, $time=0) {
|
||||
assert(!is_null($key));
|
||||
if($this->use_memcache) {
|
||||
$this->memcache->set($key, $val, false, $time);
|
||||
}
|
||||
}
|
||||
|
||||
public function cache_delete($key) {
|
||||
assert(!is_null($key));
|
||||
if($this->use_memcache) {
|
||||
$this->memcache->delete($key);
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// misc {{{
|
||||
public function count_pages($tags=array()) {
|
||||
global $config;
|
||||
$images_per_page = $config->get_int('index_width') * $config->get_int('index_height');
|
||||
public function count_images($tags=array()) {
|
||||
if(count($tags) == 0) {
|
||||
return ceil($this->db->GetOne("SELECT COUNT(*) FROM images") / $images_per_page);
|
||||
return $this->db->GetOne("SELECT COUNT(*) FROM images");
|
||||
}
|
||||
else {
|
||||
$querylet = $this->build_search_querylet($tags);
|
||||
$result = $this->execute($querylet->sql, $querylet->variables);
|
||||
return ceil($result->RecordCount() / $images_per_page);
|
||||
return $result->RecordCount();
|
||||
}
|
||||
}
|
||||
|
||||
public function count_pages($tags=array()) {
|
||||
global $config;
|
||||
$images_per_page = $config->get_int('index_width') * $config->get_int('index_height');
|
||||
return ceil($this->count_images($tags) / $images_per_page);
|
||||
}
|
||||
|
||||
public function execute($query, $args=array()) {
|
||||
$result = $this->db->Execute($query, $args);
|
||||
if($result === False) {
|
||||
@ -107,6 +185,10 @@ class Database {
|
||||
}
|
||||
|
||||
public function upgrade_schema($filename) {
|
||||
$this->install_schema($filename);
|
||||
}
|
||||
|
||||
public function install_schema($filename) {
|
||||
//print "<br>upgrading $filename";
|
||||
|
||||
global $config;
|
||||
@ -128,6 +210,7 @@ class Database {
|
||||
// }}}
|
||||
// tags {{{
|
||||
public function resolve_alias($tag) {
|
||||
assert(is_string($tag));
|
||||
$newtag = $this->db->GetOne("SELECT newtag FROM aliases WHERE oldtag=?", array($tag));
|
||||
if(!empty($newtag)) {
|
||||
return $newtag;
|
||||
@ -137,6 +220,7 @@ class Database {
|
||||
}
|
||||
|
||||
public function sanitise($tag) {
|
||||
assert(is_string($tag));
|
||||
$tag = preg_replace("/[\s?*]/", "", $tag);
|
||||
$tag = preg_replace("/\.+/", ".", $tag);
|
||||
$tag = preg_replace("/^(\.+[\/\\\\])+/", "", $tag);
|
||||
@ -144,11 +228,13 @@ class Database {
|
||||
}
|
||||
|
||||
private function build_search_querylet($terms) {
|
||||
$tag_search = new Querylet("0");
|
||||
$tag_querylets = array();
|
||||
$img_querylets = array();
|
||||
$positive_tag_count = 0;
|
||||
$negative_tag_count = 0;
|
||||
$img_search = new Querylet("");
|
||||
|
||||
|
||||
// turn each term into a specific type of querylet
|
||||
foreach($terms as $term) {
|
||||
$negative = false;
|
||||
if((strlen($term) > 0) && ($term[0] == '-')) {
|
||||
@ -161,28 +247,54 @@ class Database {
|
||||
$stpe = new SearchTermParseEvent($term);
|
||||
send_event($stpe);
|
||||
if($stpe->is_querylet_set()) {
|
||||
$img_search->append($stpe->get_querylet());
|
||||
$img_querylets[] = new ImgQuerylet($stpe->get_querylet(), !$negative);
|
||||
}
|
||||
else {
|
||||
$term = str_replace("*", "%", $term);
|
||||
$term = str_replace("?", "_", $term);
|
||||
if(!preg_match("/^[%_]+$/", $term)) {
|
||||
$sign = $negative ? "-" : "+";
|
||||
if($sign == "+") $positive_tag_count++;
|
||||
else $negative_tag_count++;
|
||||
$tag_search->append(new Querylet(" $sign (tag LIKE ?)", array($term)));
|
||||
$tag_querylets[] = new TagQuerylet($term, !$negative);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge all the tag querylets into one generic one
|
||||
$sql = "0";
|
||||
$terms = array();
|
||||
foreach($tag_querylets as $tq) {
|
||||
$sign = $tq->positive ? "+" : "-";
|
||||
$sql .= " $sign (tag LIKE ?)";
|
||||
$terms[] = $tq->tag;
|
||||
|
||||
if($sign == "+") $positive_tag_count++;
|
||||
else $negative_tag_count++;
|
||||
}
|
||||
$tag_search = new Querylet($sql, $terms);
|
||||
|
||||
// merge all the image metadata searches into one generic querylet
|
||||
$n = 0;
|
||||
$sql = "";
|
||||
$terms = array();
|
||||
foreach($img_querylets as $iq) {
|
||||
if($n++ > 0) $sql .= " AND";
|
||||
if(!$iq->positive) $sql .= " NOT";
|
||||
$sql .= " (" . $iq->qlet->sql . ")";
|
||||
$terms = array_merge($terms, $iq->qlet->variables);
|
||||
}
|
||||
$img_search = new Querylet($sql, $terms);
|
||||
|
||||
|
||||
// no tags, do a simple search (+image metadata if we have any)
|
||||
if($positive_tag_count + $negative_tag_count == 0) {
|
||||
$query = new Querylet($this->get_images);
|
||||
|
||||
if(strlen($img_search->sql) > 0) {
|
||||
$query->append_sql("WHERE 1=1 ");
|
||||
$query->append_sql(" WHERE ");
|
||||
$query->append($img_search);
|
||||
}
|
||||
}
|
||||
|
||||
// one positive tag (a common case), do an optimised search
|
||||
else if($positive_tag_count == 1 && $negative_tag_count == 0) {
|
||||
$query = new Querylet(
|
||||
// MySQL is braindead, and does a full table scan on images, running the subquery once for each row -_-
|
||||
@ -198,9 +310,12 @@ class Database {
|
||||
$tag_search->variables);
|
||||
|
||||
if(strlen($img_search->sql) > 0) {
|
||||
$query->append_sql(" AND ");
|
||||
$query->append($img_search);
|
||||
}
|
||||
}
|
||||
|
||||
// more than one positive tag, or more than zero negative tags
|
||||
else {
|
||||
$s_tag_array = array_map("sql_escape", $tag_search->variables);
|
||||
$s_tag_list = join(', ', $s_tag_array);
|
||||
@ -232,38 +347,38 @@ class Database {
|
||||
$query = new Querylet("
|
||||
SELECT *, UNIX_TIMESTAMP(posted) AS posted_timestamp
|
||||
FROM ({$subquery->sql}) AS images ", $subquery->variables);
|
||||
|
||||
if(strlen($img_search->sql) > 0) {
|
||||
$query->append_sql(" WHERE ");
|
||||
$query->append($img_search);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# there are no results, "where 1=0" should shortcut things
|
||||
$query = new Querylet("
|
||||
SELECT images.*
|
||||
FROM images
|
||||
LEFT JOIN image_tags ON image_tags.image_id = images.id
|
||||
JOIN tags ON image_tags.tag_id = tags.id
|
||||
WHERE 1=0
|
||||
");
|
||||
}
|
||||
|
||||
if(strlen($img_search->sql) > 0) {
|
||||
$query->append_sql("WHERE 1=1 ");
|
||||
$query->append($img_search);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function delete_tags_from_image($image_id) {
|
||||
assert(is_numeric($image_id));
|
||||
$this->execute("UPDATE tags SET count = count - 1 WHERE id IN (SELECT tag_id FROM image_tags WHERE image_id = ?)", array($image_id));
|
||||
$this->execute("DELETE FROM image_tags WHERE image_id=?", array($image_id));
|
||||
}
|
||||
|
||||
public function set_tags($image_id, $tags) {
|
||||
assert(is_numeric($image_id));
|
||||
$tags = tag_explode($tags);
|
||||
|
||||
$tags = array_map(array($this, 'resolve_alias'), $tags);
|
||||
$tags = array_map(array($this, 'sanitise'), $tags);
|
||||
$tags = array_unique($tags); // remove any duplicate tags
|
||||
$tags = array_iunique($tags); // remove any duplicate tags
|
||||
|
||||
// delete old
|
||||
$this->delete_tags_from_image($image_id);
|
||||
@ -277,6 +392,7 @@ class Database {
|
||||
}
|
||||
|
||||
public function set_source($image_id, $source) {
|
||||
assert(is_numeric($image_id));
|
||||
if(empty($source)) $source = null;
|
||||
$this->execute("UPDATE images SET source=? WHERE id=?", array($source, $image_id));
|
||||
}
|
||||
@ -285,8 +401,8 @@ class Database {
|
||||
public function get_images($start, $limit, $tags=array()) {
|
||||
$images = array();
|
||||
|
||||
assert($start >= 0);
|
||||
assert($limit > 0);
|
||||
assert(is_numeric($start) && $start >= 0);
|
||||
assert(is_numeric($limit) && $limit > 0);
|
||||
if($start < 0) $start = 0;
|
||||
if($limit < 1) $limit = 1;
|
||||
|
||||
@ -307,6 +423,10 @@ class Database {
|
||||
}
|
||||
|
||||
public function get_next_image($id, $tags=array(), $next=true) {
|
||||
assert(is_numeric($id));
|
||||
assert(is_array($tags));
|
||||
assert(is_bool($next));
|
||||
|
||||
if($next) {
|
||||
$gtlt = "<";
|
||||
$dir = "DESC";
|
||||
@ -334,12 +454,22 @@ class Database {
|
||||
}
|
||||
|
||||
public function get_image($id) {
|
||||
assert(is_numeric($id));
|
||||
$image = null;
|
||||
$row = $this->db->GetRow("{$this->get_images} WHERE images.id=?", array($id));
|
||||
return ($row ? new Image($row) : null);
|
||||
}
|
||||
|
||||
public function get_random_image($tags=array()) {
|
||||
$max = $this->count_images($tags);
|
||||
$rand = mt_rand(0, $max);
|
||||
$set = $this->get_images($rand, 1, $tags);
|
||||
if(count($set) > 0) return $set[0];
|
||||
else return null;
|
||||
}
|
||||
|
||||
public function get_image_by_hash($hash) {
|
||||
assert(is_string($hash));
|
||||
$image = null;
|
||||
$row = $this->db->GetRow("{$this->get_images} WHERE hash=?", array($hash));
|
||||
return ($row ? new Image($row) : null);
|
||||
@ -354,21 +484,26 @@ class Database {
|
||||
|
||||
public function get_user_session($name, $session) {
|
||||
$row = $this->db->GetRow("{$this->SELECT_USER} WHERE name LIKE ? AND md5(concat(pass, ?)) = ?",
|
||||
array($name, $_SERVER['REMOTE_ADDR'], $session));
|
||||
array($name, get_session_ip(), $session));
|
||||
return $row ? new User($row) : null;
|
||||
}
|
||||
|
||||
public function get_user_by_id($id) {
|
||||
assert(is_numeric($id));
|
||||
$row = $this->db->GetRow("{$this->SELECT_USER} WHERE id=?", array($id));
|
||||
return $row ? new User($row) : null;
|
||||
}
|
||||
|
||||
public function get_user_by_name($name) {
|
||||
assert(is_string($name));
|
||||
$row = $this->db->GetRow("{$this->SELECT_USER} WHERE name=?", array($name));
|
||||
return $row ? new User($row) : null;
|
||||
}
|
||||
|
||||
public function get_user_by_name_and_hash($name, $hash) {
|
||||
assert(is_string($name));
|
||||
assert(is_string($hash));
|
||||
assert(strlen($hash) == 32);
|
||||
$row = $this->db->GetRow("{$this->SELECT_USER} WHERE name LIKE ? AND pass = ?", array($name, $hash));
|
||||
return $row ? new User($row) : null;
|
||||
}
|
||||
|
@ -16,10 +16,15 @@ class GenericPage {
|
||||
|
||||
// data
|
||||
var $data = "";
|
||||
var $filename = null;
|
||||
|
||||
public function set_data($data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function set_filename($filename) {
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
|
||||
// ==============================================
|
||||
@ -66,8 +71,6 @@ class GenericPage {
|
||||
// ==============================================
|
||||
|
||||
public function display() {
|
||||
global $config;
|
||||
|
||||
header("Content-type: {$this->type}");
|
||||
|
||||
switch($this->mode) {
|
||||
@ -78,6 +81,9 @@ class GenericPage {
|
||||
$layout->display_page($this);
|
||||
break;
|
||||
case "data":
|
||||
if(!is_null($this->filename)) {
|
||||
header('Content-Disposition: attachment; filename='.$this->filename);
|
||||
}
|
||||
print $this->data;
|
||||
break;
|
||||
case "redirect":
|
||||
|
@ -8,7 +8,6 @@ class User {
|
||||
var $email;
|
||||
var $join_date;
|
||||
var $days_old;
|
||||
var $enabled;
|
||||
var $admin;
|
||||
|
||||
public function User($row) {
|
||||
@ -17,7 +16,6 @@ class User {
|
||||
$this->email = $row['email'];
|
||||
$this->join_date = $row['joindate'];
|
||||
$this->days_old = $row['days_old'];
|
||||
$this->enabled = ($row['enabled'] == 'Y');
|
||||
$this->admin = ($row['admin'] == 'Y');
|
||||
}
|
||||
|
||||
@ -26,17 +24,6 @@ class User {
|
||||
return ($this->id == $config->get_int('anon_id'));
|
||||
}
|
||||
|
||||
public function is_enabled() {
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
public function set_enabled($enabled) {
|
||||
global $database;
|
||||
|
||||
$yn = $enabled ? 'Y' : 'N';
|
||||
$database->Execute("UPDATE users SET enabled=? WHERE id=?", array($yn, $this->id));
|
||||
}
|
||||
|
||||
public function is_admin() {
|
||||
return $this->admin;
|
||||
}
|
||||
|
@ -117,6 +117,25 @@ function make_link($page=null, $query=null) {
|
||||
* Misc *
|
||||
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
function version_check() {
|
||||
if(version_compare(PHP_VERSION, "5.0.0") == -1) {
|
||||
print <<<EOD
|
||||
Currently Shimmie 2 doesn't support versions of PHP lower than 5.0.0. Please
|
||||
either upgrade your PHP, or tell Shish that PHP 4 support is a big deal for
|
||||
you...
|
||||
EOD;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function check_cli() {
|
||||
if(isset($_SERVER['REMOTE_ADDR'])) {
|
||||
print "This script is to be run from the command line only.";
|
||||
exit;
|
||||
}
|
||||
$_SERVER['REMOTE_ADDR'] = "127.0.0.1";
|
||||
}
|
||||
|
||||
function get_thumbnail_size($orig_width, $orig_height) {
|
||||
global $config;
|
||||
|
||||
@ -155,8 +174,6 @@ function _count_execs($db, $sql, $inputarray) {
|
||||
}
|
||||
|
||||
function get_theme_object($file, $class) {
|
||||
global $config;
|
||||
$theme = $config->get_string("theme", "default");
|
||||
if(class_exists("Custom$class")) {
|
||||
$class = "Custom$class";
|
||||
return new $class();
|
||||
@ -199,6 +216,21 @@ function get_memory_limit() {
|
||||
return $memory;
|
||||
}
|
||||
|
||||
function get_session_ip() {
|
||||
global $config;
|
||||
|
||||
$mask = $config->get_string("session_hash_mask");
|
||||
if(!$mask) {
|
||||
$config->set_string("session_hash_mask", "255.255.0.0");
|
||||
$mask = "255.255.0.0";
|
||||
}
|
||||
|
||||
$addr = $_SERVER['REMOTE_ADDR'];
|
||||
$addr = inet_ntop(inet_pton($addr) & inet_pton($mask));
|
||||
|
||||
return $addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* PHP really, really sucks.
|
||||
*/
|
||||
@ -218,6 +250,17 @@ function get_base_href() {
|
||||
}
|
||||
|
||||
|
||||
function move_upload_to_archive($event) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
if(!@copy($event->tmpname, "images/$ha/$hash")) {
|
||||
$event->veto("Failed to copy file from uploads ({$event->tmpname}) to archive (images/$ha/$hash)");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
||||
* Debugging functions *
|
||||
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
@ -317,6 +360,115 @@ function array_contains($array, $target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// case insensetive uniqueness
|
||||
function array_iunique($array) {
|
||||
$ok = array();
|
||||
foreach($array as $element) {
|
||||
$found = false;
|
||||
foreach($ok as $existing) {
|
||||
if(strtolower($element) == strtolower($existing)) {
|
||||
$found = true; break;
|
||||
}
|
||||
}
|
||||
if(!$found) {
|
||||
$ok[] = $element;
|
||||
}
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
||||
// from http://uk.php.net/network
|
||||
function ip_in_range($IP, $CIDR) {
|
||||
list ($net, $mask) = split ("/", $CIDR);
|
||||
|
||||
$ip_net = ip2long ($net);
|
||||
$ip_mask = ~((1 << (32 - $mask)) - 1);
|
||||
|
||||
$ip_ip = ip2long ($IP);
|
||||
|
||||
$ip_ip_net = $ip_ip & $ip_mask;
|
||||
|
||||
return ($ip_ip_net == $ip_net);
|
||||
}
|
||||
|
||||
// from a patch by Christian Walde; only intended for use in the
|
||||
// "extension manager" extension, but it seems to fit better here
|
||||
function deltree($f) {
|
||||
if (is_link($f)) {
|
||||
unlink($f);
|
||||
}
|
||||
else if(is_dir($f)) {
|
||||
foreach(glob($f.'/*') as $sf) {
|
||||
if (is_dir($sf) && !is_link($sf)) {
|
||||
deltree($sf);
|
||||
} else {
|
||||
unlink($sf);
|
||||
}
|
||||
}
|
||||
rmdir($f);
|
||||
}
|
||||
}
|
||||
|
||||
// from a comment on http://uk.php.net/copy
|
||||
function full_copy($source, $target) {
|
||||
if(is_dir($source)) {
|
||||
@mkdir($target);
|
||||
|
||||
$d = dir($source);
|
||||
|
||||
while(FALSE !== ($entry = $d->read())) {
|
||||
if($entry == '.' || $entry == '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$Entry = $source . '/' . $entry;
|
||||
if(is_dir($Entry)) {
|
||||
full_copy($Entry, $target . '/' . $entry);
|
||||
continue;
|
||||
}
|
||||
copy($Entry, $target . '/' . $entry);
|
||||
}
|
||||
$d->close();
|
||||
}
|
||||
else {
|
||||
copy($source, $target);
|
||||
}
|
||||
}
|
||||
|
||||
function stripslashes_r($arr) {
|
||||
return is_array($arr) ? array_map('stripslashes_r', $arr) : stripslashes($arr);
|
||||
}
|
||||
|
||||
function sanitise_environment() {
|
||||
if(DEBUG) {
|
||||
error_reporting(E_ALL);
|
||||
assert_options(ASSERT_ACTIVE, 1);
|
||||
assert_options(ASSERT_BAIL, 1);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if(get_magic_quotes_gpc()) {
|
||||
$_GET = stripslashes_r($_GET);
|
||||
$_POST = stripslashes_r($_POST);
|
||||
$_COOKIE = stripslashes_r($_COOKIE);
|
||||
}
|
||||
}
|
||||
|
||||
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 *
|
||||
@ -424,7 +576,7 @@ function _get_user() {
|
||||
$user = null;
|
||||
if(isset($_COOKIE["shm_user"]) && isset($_COOKIE["shm_session"])) {
|
||||
$tmp_user = $database->get_user_session($_COOKIE["shm_user"], $_COOKIE["shm_session"]);
|
||||
if(!is_null($tmp_user) && $tmp_user->is_enabled()) {
|
||||
if(!is_null($tmp_user)) {
|
||||
$user = $tmp_user;
|
||||
}
|
||||
|
||||
|
@ -40,15 +40,45 @@ class AdminPage extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'DisplayingImageEvent')) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
$this->theme->display_deleter($event->page, $event->image->id);
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "admin_utils")) {
|
||||
if($event->user->is_admin()) {
|
||||
set_time_limit(0);
|
||||
$redirect = false;
|
||||
|
||||
switch($_POST['action']) {
|
||||
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 'database dump':
|
||||
$this->dbdump($event->page);
|
||||
break;
|
||||
}
|
||||
|
||||
if($redirect) {
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("admin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'ImageAdminBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_part($this->theme->get_deleter_html($event->image->id));
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'AdminBuildingEvent')) {
|
||||
$this->theme->display_page($event->page);
|
||||
$this->theme->display_form($event->page);
|
||||
}
|
||||
|
||||
if(is_a($event, 'UserBlockBuildingEvent')) {
|
||||
@ -57,6 +87,57 @@ class AdminPage extends Extension {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function lowercase_all_tags() {
|
||||
global $database;
|
||||
$database->execute("UPDATE tags SET tag=lower(tag)");
|
||||
}
|
||||
|
||||
private function recount_tag_use() {
|
||||
global $database;
|
||||
$database->Execute("UPDATE tags SET count=(SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id)");
|
||||
}
|
||||
|
||||
private function purge_unused_tags() {
|
||||
global $database;
|
||||
$this->recount_tag_use();
|
||||
$database->Execute("DELETE FROM tags WHERE count=0");
|
||||
}
|
||||
|
||||
private function dbdump($page) {
|
||||
include "config.php";
|
||||
|
||||
$matches = array();
|
||||
preg_match("#(\w+)://(\w+):(\w+)@([\w\.\-]+)/([\w_]+)(\?.*)?#", $database_dsn, $matches);
|
||||
$software = $matches[1];
|
||||
$username = $matches[2];
|
||||
$password = $matches[3];
|
||||
$hostname = $matches[4];
|
||||
$database = $matches[5];
|
||||
|
||||
switch($software) {
|
||||
case 'mysql':
|
||||
$cmd = "mysqldump -h$hostname -u$username -p$password $database";
|
||||
break;
|
||||
}
|
||||
|
||||
$page->set_mode("data");
|
||||
$page->set_type("application/x-unknown");
|
||||
$page->set_filename('shimmie-'.date('Ymd').'.sql');
|
||||
$page->set_data(shell_exec($cmd));
|
||||
}
|
||||
|
||||
private function check_for_orphanned_images() {
|
||||
$orphans = array();
|
||||
foreach(glob("images/*") as $dir) {
|
||||
foreach(glob("$dir/*") as $file) {
|
||||
$hash = str_replace("$dir/", "", $file);
|
||||
if(!$this->db_has_hash($hash)) {
|
||||
$orphans[] = $hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new AdminPage());
|
||||
?>
|
||||
|
@ -15,7 +15,7 @@ class AdminPageTheme extends Themelet {
|
||||
*
|
||||
* $image_id = the image to delete
|
||||
*/
|
||||
public function display_deleter($page, $image_id) {
|
||||
public function get_deleter_html($image_id) {
|
||||
$i_image_id = int_escape($image_id);
|
||||
$html = "
|
||||
<form action='".make_link("admin/delete_image")."' method='POST'>
|
||||
@ -23,7 +23,28 @@ class AdminPageTheme extends Themelet {
|
||||
<input type='submit' value='Delete'>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Admin", $html, "left"));
|
||||
return $html;
|
||||
}
|
||||
|
||||
/*
|
||||
* Show a form which links to admin_utils with POST[action] set to one of:
|
||||
* 'lowercase all tags'
|
||||
* 'recount tag use'
|
||||
* 'purge unused tags'
|
||||
*/
|
||||
public function display_form($page) {
|
||||
$html = "
|
||||
<p><form action='".make_link("admin_utils")."' method='POST'>
|
||||
<select name='action'>
|
||||
<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>
|
||||
</select>
|
||||
<input type='submit' value='Go'>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Misc Admin Tools", $html));
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -40,7 +40,9 @@ class AliasEditor extends Extension {
|
||||
}
|
||||
else if($event->get_arg(0) == "list") {
|
||||
global $database;
|
||||
$this->theme->display_aliases($event->page, $database->db->GetAssoc("SELECT oldtag, newtag FROM aliases"), $event->user->is_admin());
|
||||
$this->theme->display_aliases($event->page,
|
||||
$database->db->GetAssoc("SELECT oldtag, newtag FROM aliases ORDER BY newtag"),
|
||||
$event->user->is_admin());
|
||||
}
|
||||
else if($event->get_arg(0) == "export") {
|
||||
global $database;
|
||||
@ -71,7 +73,8 @@ class AliasEditor extends Extension {
|
||||
|
||||
if(is_a($event, 'AddAliasEvent')) {
|
||||
global $database;
|
||||
$database->Execute("INSERT INTO aliases(oldtag, newtag) VALUES(?, ?)", array($event->oldtag, $event->newtag));
|
||||
$database->Execute("INSERT INTO aliases(oldtag, newtag) VALUES(?, ?)",
|
||||
array($event->oldtag, $event->newtag));
|
||||
}
|
||||
|
||||
if(is_a($event, 'UserBlockBuildingEvent')) {
|
||||
|
@ -45,6 +45,7 @@ class AliasEditorTheme extends Themelet {
|
||||
$html = "
|
||||
<table border='1'>
|
||||
<thead><td>From</td><td>To</td>$action</thead>
|
||||
$add
|
||||
$h_aliases
|
||||
$add
|
||||
</table>
|
||||
|
@ -12,12 +12,14 @@ class BBCode extends Extension {
|
||||
$text = preg_replace("/\[b\](.*?)\[\/b\]/s", "<b>\\1</b>", $text);
|
||||
$text = preg_replace("/\[i\](.*?)\[\/i\]/s", "<i>\\1</i>", $text);
|
||||
$text = preg_replace("/\[u\](.*?)\[\/u\]/s", "<u>\\1</u>", $text);
|
||||
$text = preg_replace("/\[s\](.*?)\[\/s\]/s", "<s>\\1</s>", $text);
|
||||
$text = preg_replace("/\[code\](.*?)\[\/code\]/s", "<pre>\\1</pre>", $text);
|
||||
$text = preg_replace("/>>(\d+)/s", "<a href='".make_link("post/view/\\1")."'>>>\\1</a>", $text);
|
||||
$text = preg_replace("/>>([^\d].+)/", "<blockquote><small>\\1</small></blockquote>", $text);
|
||||
$text = preg_replace("/\[url=((?:https?|ftp|irc):\/\/.*?)\](.*?)\[\/url\]/s", "<a href='\\1'>\\2</a>", $text);
|
||||
$text = preg_replace("/\[url\]((?:https?|ftp|irc):\/\/.*?)\[\/url\]/s", "<a href='\\1'>\\1</a>", $text);
|
||||
$text = preg_replace("/\[\[(.*?)\]\]/s", "<a href='".make_link("wiki/\\1")."'>\\1</a>", $text);
|
||||
$text = preg_replace("/\[url=((?:https?|ftp|irc|mailto):\/\/.*?)\](.*?)\[\/url\]/s", "<a href='\\1'>\\2</a>", $text);
|
||||
$text = preg_replace("/\[url\]((?:https?|ftp|irc|mailto):\/\/.*?)\[\/url\]/s", "<a href='\\1'>\\1</a>", $text);
|
||||
$text = preg_replace("/\[\[([^\|\]]+)\|([^\]]+)\]\]/s", "<a href='".make_link("wiki/\\1")."'>\\2</a>", $text);
|
||||
$text = preg_replace("/\[\[([^\]]+)\]\]/s", "<a href='".make_link("wiki/\\1")."'>\\1</a>", $text);
|
||||
$text = str_replace("\n", "\n<br>", $text);
|
||||
$text = preg_replace("/\[quote\](.*?)\[\/quote\]/s", "<blockquote><small>\\1</small></blockquote>", $text);
|
||||
$text = preg_replace("/\[quote=(.*?)\](.*?)\[\/quote\]/s", "<small><small>Quoting \\1</small></small><blockquote><small>\\2</small></blockquote>", $text);
|
||||
@ -25,11 +27,13 @@ class BBCode extends Extension {
|
||||
$text = preg_replace("/\[h2\](.*?)\[\/h2\]/s", "<h2>\\1</h2>", $text);
|
||||
$text = preg_replace("/\[h3\](.*?)\[\/h3\]/s", "<h3>\\1</h3>", $text);
|
||||
$text = preg_replace("/\[h4\](.*?)\[\/h4\]/s", "<h4>\\1</h4>", $text);
|
||||
$text = preg_replace("/\[list\](.*?)\[\/list\]/s", "<ul>\\1</ul>", $text);
|
||||
$text = preg_replace("/\[ul\](.*?)\[\/ul\]/s", "<ul>\\1</ul>", $text);
|
||||
$text = preg_replace("/\[ol\](.*?)\[\/ol\]/s", "<ol>\\1</ol>", $text);
|
||||
$text = preg_replace("/\[li\](.*?)\[\/li\]/s", "<li>\\1</li>", $text);
|
||||
$text = preg_replace("#\[\*\]#s", "<li>", $text);
|
||||
$text = preg_replace("#<br><(li|ul|ol|/ul|/ol)>#s", "<\\1>", $text);
|
||||
$text = $this->filter_spoiler($text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
@ -37,20 +41,50 @@ class BBCode extends Extension {
|
||||
$text = preg_replace("/\[b\](.*?)\[\/b\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[i\](.*?)\[\/i\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[u\](.*?)\[\/u\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[s\](.*?)\[\/s\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[code\](.*?)\[\/code\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/s", "\\2", $text);
|
||||
$text = preg_replace("/\[url\](.*?)\[\/url\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[\[(.*?)\]\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[\[([^\|\]]+)\|([^\]]+)\]\]/s", "\\2", $text);
|
||||
$text = preg_replace("/\[\[([^\]]+)\]\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[quote\](.*?)\[\/quote\]/s", "", $text);
|
||||
$text = preg_replace("/\[quote=(.*?)\](.*?)\[\/quote\]/s", "", $text);
|
||||
$text = preg_replace("/\[h1\](.*?)\[\/h1\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[h2\](.*?)\[\/h2\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[h3\](.*?)\[\/h3\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[h4\](.*?)\[\/h4\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[list\](.*?)\[\/list\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[ul\](.*?)\[\/ul\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[ol\](.*?)\[\/ol\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[li\](.*?)\[\/li\]/s", "\\1", $text);
|
||||
$text = preg_replace("/\[\*\](.*?)/s", "\\1", $text);
|
||||
$text = $this->strip_spoiler($text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function filter_spoiler($text) {
|
||||
return str_replace(
|
||||
array("[spoiler]","[/spoiler]"),
|
||||
array("<span style=\"background-color:#000; color:#000;\">","</span>"),
|
||||
$text);
|
||||
}
|
||||
|
||||
private function strip_spoiler($text) {
|
||||
$l1 = strlen("[spoiler]");
|
||||
$l2 = strlen("[/spoiler]");
|
||||
while(true) {
|
||||
$start = strpos($text, "[spoiler]");
|
||||
if($start === false) break;
|
||||
|
||||
$end = strpos($text, "[/spoiler]");
|
||||
if($end === false) break;
|
||||
|
||||
$beginning = substr($text, 0, $start);
|
||||
$middle = str_rot13(substr($text, $start+$l1, ($end-$start-$l1)));
|
||||
$ending = substr($text, $end + $l2, (strlen($text)-$end+$l2));
|
||||
|
||||
$text = $beginning . $middle . $ending;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,12 @@ class CommentList extends Extension {
|
||||
if($event->count_args() == 3) {
|
||||
send_event(new CommentDeletionEvent($event->get_arg(1)));
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("post/view/".$event->get_arg(2)));
|
||||
if(!empty($_SERVER['HTTP_REFERER'])) {
|
||||
$event->page->set_redirect($_SERVER['HTTP_REFERER']);
|
||||
}
|
||||
else {
|
||||
$event->page->set_redirect(make_link("post/view/".$event->get_arg(2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -144,8 +149,40 @@ class CommentList extends Extension {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
// shortcut to latest
|
||||
if($config->get_int("ext_comments_version") < 1) {
|
||||
$database->upgrade_schema("ext/comment/schema.xml");
|
||||
$database->Execute("CREATE TABLE comments (
|
||||
id {$database->engine->auto_increment},
|
||||
image_id INTEGER NOT NULL,
|
||||
owner_id INTEGER NOT NULL,
|
||||
owner_ip CHAR(16) NOT NULL,
|
||||
posted DATETIME DEFAULT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
INDEX (image_id),
|
||||
INDEX (owner_ip),
|
||||
INDEX (posted)
|
||||
) {$database->engine->create_table_extras}");
|
||||
$config->set_int("ext_comments_version", 2);
|
||||
}
|
||||
|
||||
// ===
|
||||
if($config->get_int("ext_comments_version") < 1) {
|
||||
$database->Execute("CREATE TABLE comments (
|
||||
id {$database->engine->auto_increment},
|
||||
image_id INTEGER NOT NULL,
|
||||
owner_id INTEGER NOT NULL,
|
||||
owner_ip CHAR(16) NOT NULL,
|
||||
posted DATETIME DEFAULT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
INDEX (image_id)
|
||||
) {$database->engine->create_table_extras}");
|
||||
$config->set_int("ext_comments_version", 1);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_comments_version") == 1) {
|
||||
$database->Execute("CREATE INDEX comments_owner_ip ON comments(owner_ip)");
|
||||
$database->Execute("CREATE INDEX comments_posted ON comments(posted)");
|
||||
$config->set_int("ext_comments_version", 2);
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
@ -166,9 +203,9 @@ class CommentList extends Extension {
|
||||
FROM comments
|
||||
GROUP BY image_id
|
||||
ORDER BY latest DESC
|
||||
LIMIT ?,?
|
||||
LIMIT ? OFFSET ?
|
||||
";
|
||||
$result = $database->Execute($get_threads, array($start, $threads_per_page));
|
||||
$result = $database->Execute($get_threads, array($threads_per_page, $start));
|
||||
|
||||
$total_pages = (int)($database->db->GetOne("SELECT COUNT(distinct image_id) AS count FROM comments") / 10);
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<schema version="0.3">
|
||||
<!-- FIXME: mysql utf8ness -->
|
||||
<table name="comments">
|
||||
<field name="id" type="I"><key/><autoincrement/></field>
|
||||
<field name="image_id" type="I"><notnull/></field><!-- references -->
|
||||
<field name="owner_id" type="I"><notnull/></field><!-- references -->
|
||||
<field name="owner_ip" type="C" size="15"><notnull/></field>
|
||||
<field name="posted" type="T"><notnull/></field>
|
||||
<field name="comment" type="X" size="4000"><notnull/></field>
|
||||
<index name="comments__image_id"><col>image_id</col></index>
|
||||
<index name="comments__owner_ip"><col>owner_ip</col></index>
|
||||
<index name="comments__posted"><col>posted</col></index>
|
||||
<opt platform="mysql">DEFAULT CHARSET='utf8'</opt>
|
||||
</table>
|
||||
|
||||
<sql>
|
||||
<query>DELETE FROM config WHERE name='ext_comments_version'</query>
|
||||
<query>INSERT INTO config(name, value) VALUES('ext_comments_version', 3)</query>
|
||||
</sql>
|
||||
</schema>
|
128
ext/ext_manager/main.php
Normal file
128
ext/ext_manager/main.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* Name: Extension Manager
|
||||
* Author: Shish <webmaster@shishnet.org>
|
||||
* Link: http://trac.shishnet.org/shimmie2/
|
||||
* License: GPLv2
|
||||
* Description: A thing for point & click extension management
|
||||
*/
|
||||
|
||||
class ExtensionInfo { // {{{
|
||||
var $ext_name, $name, $link, $author, $email, $description;
|
||||
|
||||
function ExtensionInfo($main) {
|
||||
$matches = array();
|
||||
$lines = file($main);
|
||||
preg_match("#contrib/(.*)/main.php#", $main, $matches);
|
||||
$this->ext_name = $matches[1];
|
||||
$this->name = $this->ext_name;
|
||||
$this->enabled = $this->is_enabled($this->ext_name);
|
||||
|
||||
for($i=0; $i<count($lines); $i++) {
|
||||
$line = $lines[$i];
|
||||
if(preg_match("/Name: (.*)/", $line, $matches)) {
|
||||
$this->name = $matches[1];
|
||||
}
|
||||
if(preg_match("/Link: (.*)/", $line, $matches)) {
|
||||
$this->link = $matches[1];
|
||||
}
|
||||
if(preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) {
|
||||
$this->author = $matches[1];
|
||||
$this->email = $matches[2];
|
||||
}
|
||||
else if(preg_match("/Author: (.*)/", $line, $matches)) {
|
||||
$this->author = $matches[1];
|
||||
}
|
||||
if(preg_match("/(.*)Description: (.*)/", $line, $matches)) {
|
||||
$this->description = $matches[2];
|
||||
$start = $matches[1]." ";
|
||||
$start_len = strlen($start);
|
||||
while(substr($lines[$i+1], 0, $start_len) == $start) {
|
||||
$this->description .= substr($lines[$i+1], $start_len);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
if(preg_match("/\*\//", $line, $matches)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function is_enabled($fname) {
|
||||
return file_exists("ext/$fname");
|
||||
}
|
||||
} // }}}
|
||||
|
||||
class ExtManager extends Extension {
|
||||
var $theme;
|
||||
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("ext_manager", "ExtManagerTheme");
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "ext_manager")) {
|
||||
if($event->user->is_admin()) {
|
||||
if($event->get_arg(0) == "set") {
|
||||
if(is_writable("ext")) {
|
||||
$this->set_things($_POST);
|
||||
$event->page->set_mode("redirect");
|
||||
$event->page->set_redirect(make_link("ext_manager"));
|
||||
}
|
||||
else {
|
||||
$this->theme->display_error($event->page, "File Operation Failed",
|
||||
"The extension folder isn't writable by the web server :(");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->theme->display_table($event->page, $this->get_extensions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'UserBlockBuildingEvent')) {
|
||||
if($event->user->is_admin()) {
|
||||
$event->add_link("Extension Manager", make_link("ext_manager"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function get_extensions() {
|
||||
$extensions = array();
|
||||
foreach(glob("contrib/*/main.php") as $main) {
|
||||
$extensions[] = new ExtensionInfo($main);
|
||||
}
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
private function set_things($settings) {
|
||||
foreach(glob("contrib/*/main.php") as $main) {
|
||||
$matches = array();
|
||||
preg_match("#contrib/(.*)/main.php#", $main, $matches);
|
||||
$fname = $matches[1];
|
||||
|
||||
if(!isset($settings["ext_$fname"])) $settings["ext_$fname"] = 0;
|
||||
$this->set_enabled($fname, $settings["ext_$fname"]);
|
||||
}
|
||||
}
|
||||
|
||||
private function set_enabled($fname, $enabled) {
|
||||
if($enabled) {
|
||||
// enable if currently disabled
|
||||
if(!file_exists("ext/$fname")) {
|
||||
if(function_exists("symlink")) {
|
||||
symlink("../contrib/$fname", "ext/$fname");
|
||||
}
|
||||
else {
|
||||
full_copy("contrib/$fname", "ext/$fname");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// disable if currently enabled
|
||||
if(file_exists("ext/$fname")) {
|
||||
deltree("ext/$fname");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_event_listener(new ExtManager());
|
||||
?>
|
@ -8,13 +8,13 @@ class ExtManagerTheme extends Themelet {
|
||||
<tr><th>Name</th><th>Author</th><th>Description</th><th>Enabled</th></tr>
|
||||
";
|
||||
foreach($extensions as $extension) {
|
||||
$ext_name = $extension["ext_name"];
|
||||
$h_name = empty($extension["name"]) ? $ext_name : html_escape($extension["name"]);
|
||||
$h_email = html_escape($extension["email"]);
|
||||
$h_link = isset($extension["link"]) ? html_escape($extension["link"]) : "";
|
||||
$h_author = html_escape($extension["author"]);
|
||||
$h_description = html_escape($extension["description"]);
|
||||
$h_enabled = $extension["enabled"] ? " checked='checked'" : "";
|
||||
$ext_name = $extension->ext_name;
|
||||
$h_name = empty($extension->name) ? $ext_name : html_escape($extension->name);
|
||||
$h_email = html_escape($extension->email);
|
||||
$h_link = isset($extension->link) ? html_escape($extension->link) : "";
|
||||
$h_author = html_escape($extension->author);
|
||||
$h_description = html_escape($extension->description);
|
||||
$h_enabled = $extension->enabled ? " checked='checked'" : "";
|
||||
$html .= "
|
||||
<tr>
|
||||
" . (
|
@ -14,10 +14,7 @@ class PixelFileHandler extends Extension {
|
||||
if(is_a($event, 'DataUploadEvent') && $this->supported_ext($event->type) && $this->check_contents($event->tmpname)) {
|
||||
$hash = $event->hash;
|
||||
$ha = substr($hash, 0, 2);
|
||||
if(!copy($event->tmpname, "images/$ha/$hash")) {
|
||||
$event->veto("Pixel Handler failed to copy file from uploads ({$event->tmpname}) to archive (images/$ha/$hash)");
|
||||
return;
|
||||
}
|
||||
if(!move_upload_to_archive($event)) return;
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
$image = $this->create_image_from_data("images/$ha/$hash", $event->metadata);
|
||||
if(is_null($image)) {
|
||||
@ -44,10 +41,7 @@ class PixelFileHandler extends Extension {
|
||||
|
||||
private function supported_ext($ext) {
|
||||
$exts = array("jpg", "jpeg", "gif", "png");
|
||||
foreach($exts as $supported) {
|
||||
if($ext == $supported) return true;
|
||||
}
|
||||
return false;
|
||||
return array_contains($exts, strtolower($ext));
|
||||
}
|
||||
|
||||
private function create_image_from_data($filename, $metadata) {
|
||||
@ -72,7 +66,12 @@ class PixelFileHandler extends Extension {
|
||||
}
|
||||
|
||||
private function check_contents($file) {
|
||||
return (file_exists($file) && !is_null(getimagesize($file)));
|
||||
$valid = Array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG);
|
||||
if(!file_exists($file)) return false;
|
||||
$info = getimagesize($file);
|
||||
if(is_null($info)) return false;
|
||||
if(array_contains($valid, $info[2])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private function create_thumb($hash) {
|
||||
|
@ -164,7 +164,7 @@ class ImageIO extends Extension {
|
||||
else {
|
||||
$page->set_title("Not Found");
|
||||
$page->set_heading("Not Found");
|
||||
$page->add_block(new Block("Navigation", "<a href='".index."'>Index</a>", "left", 0));
|
||||
$page->add_block(new Block("Navigation", "<a href='".make_link()."'>Index</a>", "left", 0));
|
||||
$page->add_block(new Block("Image not in database",
|
||||
"The requested image was not found in the database"));
|
||||
}
|
||||
|
@ -85,26 +85,30 @@ class Index extends Extension {
|
||||
if(preg_match("/size(<|>|<=|>=|=)(\d+)x(\d+)/", $event->term, $matches)) {
|
||||
$cmp = $matches[1];
|
||||
$args = array(int_escape($matches[2]), int_escape($matches[3]));
|
||||
$event->set_querylet(new Querylet("AND (width $cmp ? AND height $cmp ?)", $args));
|
||||
$event->set_querylet(new Querylet("width $cmp ? AND height $cmp ?", $args));
|
||||
}
|
||||
else if(preg_match("/ratio(<|>|<=|>=|=)(\d+):(\d+)/", $event->term, $matches)) {
|
||||
$cmp = $matches[1];
|
||||
$args = array(int_escape($matches[2]), int_escape($matches[3]));
|
||||
$event->set_querylet(new Querylet("AND (width / height $cmp ? / ?)", $args));
|
||||
$event->set_querylet(new Querylet("width / height $cmp ? / ?", $args));
|
||||
}
|
||||
else if(preg_match("/(filesize|id)(<|>|<=|>=|=)(\d+[kmg]?b?)/i", $event->term, $matches)) {
|
||||
$col = $matches[1];
|
||||
$cmp = $matches[2];
|
||||
$val = parse_shorthand_int($matches[3]);
|
||||
$event->set_querylet(new Querylet("AND (images.$col $cmp $val)"));
|
||||
$event->set_querylet(new Querylet("images.$col $cmp ?", array($val)));
|
||||
}
|
||||
else if(preg_match("/hash=([0-9a-fA-F]*)/i", $event->term, $matches)) {
|
||||
$hash = strtolower($matches[2]);
|
||||
$event->set_querylet(new Querylet("AND (images.hash = '$hash')"));
|
||||
$event->set_querylet(new Querylet("images.hash = '$hash'"));
|
||||
}
|
||||
else if(preg_match("/(filetype|ext)=([a-zA-Z0-9]*)/i", $event->term, $matches)) {
|
||||
$ext = strtolower($matches[2]);
|
||||
$event->set_querylet(new Querylet("AND (images.ext = '$ext')"));
|
||||
$event->set_querylet(new Querylet("images.ext = '$ext'"));
|
||||
}
|
||||
else if(preg_match("/(filename|name)=([a-zA-Z0-9]*)/i", $event->term, $matches)) {
|
||||
$filename = strtolower($matches[2]);
|
||||
$event->set_querylet(new Querylet("images.filename LIKE '%$filename%'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ class IndexTheme extends Themelet {
|
||||
<p><form action='$h_search_link' method='GET'>
|
||||
<input id='search_input' name='search' type='text'
|
||||
value='$h_search_string' autocomplete='off' />
|
||||
<input type='hidden' name='q' value='/post/list'>
|
||||
<input type='submit' value='Find' style='display: none;' />
|
||||
</form>
|
||||
<div id='search_completions'></div>";
|
||||
|
@ -1,136 +0,0 @@
|
||||
<?php
|
||||
|
||||
// RemoveIPBanEvent {{{
|
||||
class RemoveIPBanEvent extends Event {
|
||||
var $id;
|
||||
|
||||
public function RemoveIPBanEvent($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// AddIPBanEvent {{{
|
||||
class AddIPBanEvent extends Event {
|
||||
var $ip;
|
||||
var $reason;
|
||||
var $end;
|
||||
|
||||
public function AddIPBanEvent($ip, $reason, $end) {
|
||||
$this->ip = $ip;
|
||||
$this->reason = $reason;
|
||||
$this->end = $end;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
class IPBan extends Extension {
|
||||
var $theme;
|
||||
// event handler {{{
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("ipban", "IPBanTheme");
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
if($config->get_int("ext_ipban_version") < 2) {
|
||||
$this->install();
|
||||
}
|
||||
|
||||
$this->check_ip_ban();
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "ip_ban")) {
|
||||
global $user;
|
||||
if($user->is_admin()) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if(isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) {
|
||||
if(empty($_POST['end'])) $end = null;
|
||||
else $end = $_POST['end'];
|
||||
send_event(new AddIPBanEvent($_POST['ip'], $_POST['reason'], $end));
|
||||
|
||||
global $page;
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
}
|
||||
}
|
||||
else if($event->get_arg(0) == "remove") {
|
||||
if(isset($_POST['id'])) {
|
||||
send_event(new RemoveIPBanEvent($_POST['id']));
|
||||
|
||||
global $page;
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'AddIPBanEvent')) {
|
||||
global $user;
|
||||
$this->add_ip_ban($event->ip, $event->reason, $event->end, $user);
|
||||
}
|
||||
|
||||
if(is_a($event, 'RemoveIPBanEvent')) {
|
||||
$this->remove_ip_ban($event->id);
|
||||
}
|
||||
|
||||
if(is_a($event, 'AdminBuildingEvent')) {
|
||||
global $page;
|
||||
$this->theme->display_bans($page, $this->get_bans());
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// installer {{{
|
||||
protected function install() {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
if($config->get_int("ext_ipban_version") < 3) {
|
||||
$database->upgrade_schema("ext/ipban/schema.xml");
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// deal with banned person {{{
|
||||
private function check_ip_ban() {
|
||||
$row = $this->get_ip_ban($_SERVER['REMOTE_ADDR']);
|
||||
if($row) {
|
||||
global $config;
|
||||
global $database;
|
||||
$admin = $database->get_user_by_id($row['banner_id']);
|
||||
print "IP <b>{$row['ip']}</b> has been banned by <b>{$admin->name}</b> because of <b>{$row['reason']}</b>";
|
||||
|
||||
$contact_link = $config->get_string("contact_link");
|
||||
if(!empty($contact_link)) {
|
||||
print "<p><a href='$contact_link'>Contact The Admin</a>";
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// database {{{
|
||||
private function get_bans() {
|
||||
global $database;
|
||||
$bans = $database->get_all("SELECT * FROM bans");
|
||||
if($bans) {return $bans;}
|
||||
else {return array();}
|
||||
}
|
||||
|
||||
private function get_ip_ban($ip) {
|
||||
global $database;
|
||||
return $database->db->GetRow("SELECT * FROM bans WHERE ip = ? AND date < now() AND (end > now() OR isnull(end))", array($ip));
|
||||
}
|
||||
|
||||
private function add_ip_ban($ip, $reason, $end, $user) {
|
||||
global $database;
|
||||
$database->Execute(
|
||||
"INSERT INTO bans (ip, reason, date, end, banner_id) VALUES (?, ?, now(), ?, ?)",
|
||||
array($ip, $reason, $end, $user->id));
|
||||
}
|
||||
|
||||
private function remove_ip_ban($id) {
|
||||
global $database;
|
||||
$database->Execute("DELETE FROM bans WHERE id = ?", array($id));
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
add_event_listener(new IPBan(), 10);
|
||||
?>
|
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<schema version="0.3">
|
||||
<!-- FIXME: mysql utf8ness -->
|
||||
<table name="bans">
|
||||
<field name="id" type="I"><key/><autoincrement/></field>
|
||||
<field name="banner_id" type="I"><notnull/></field>
|
||||
<field name="ip" type="C" size="15"><notnull/></field>
|
||||
<field name="date" type="T"><notnull/></field>
|
||||
<field name="end" type="T"><notnull/></field>
|
||||
<field name="reason" type="C" size="255"><notnull/></field>
|
||||
<index name="bans__ip"><col>ip</col></index>
|
||||
<opt platform="mysql">DEFAULT CHARSET='utf8'</opt>
|
||||
</table>
|
||||
|
||||
<sql>
|
||||
<query>DELETE FROM config WHERE name='ext_ipban_version'</query>
|
||||
<query>INSERT INTO config(name, value) VALUES('ext_ipban_version', 3)</query>
|
||||
</sql>
|
||||
</schema>
|
@ -152,6 +152,10 @@ class Setup extends Extension {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("setup"));
|
||||
}
|
||||
else if($event->get_arg(0) == "advanced") {
|
||||
global $config;
|
||||
$this->theme->display_advanced($event->page, $config->values);
|
||||
}
|
||||
else {
|
||||
$panel = new SetupPanel();
|
||||
send_event(new SetupBuildingEvent($panel));
|
||||
|
@ -52,10 +52,41 @@ class SetupTheme extends Themelet {
|
||||
$page->add_block(new Block("Setup", $table));
|
||||
}
|
||||
|
||||
public function display_advanced($page, $options) {
|
||||
$rows = "";
|
||||
foreach($options as $name => $value) {
|
||||
$h_value = html_escape($value);
|
||||
$len = strlen($h_value);
|
||||
$box = "";
|
||||
if($len < 50) {
|
||||
$box .= "<input type='text' name='_config_$name' value='$h_value'>";
|
||||
}
|
||||
else {
|
||||
$box .= "<textarea cols='50' rows='4' name='_config_$name'>$h_value</textarea>";
|
||||
}
|
||||
$box .= "<input type='hidden' name='_type_$name' value='string'>";
|
||||
$rows .= "<tr><td>$name</td><td>$box</td></tr>";
|
||||
}
|
||||
|
||||
$table = "
|
||||
<form action='".make_link("setup/save")."' method='POST'><table>
|
||||
<tr><th width='25%'>Name</th><th>Value</th></tr>
|
||||
$rows
|
||||
<tr><td colspan='2'><input type='submit' value='Save Settings'></td></tr>
|
||||
</table></form>
|
||||
";
|
||||
|
||||
$page->set_title("Shimmie Setup");
|
||||
$page->set_heading("Shimmie Setup");
|
||||
$page->add_block(new Block("Navigation", $this->build_navigation(), "left", 0));
|
||||
$page->add_block(new Block("Setup", $table));
|
||||
}
|
||||
|
||||
protected function build_navigation() {
|
||||
return "
|
||||
<a href='".make_link()."'>Index</a>
|
||||
<br><a href='http://trac.shishnet.org/shimmie2/wiki/Settings'>Help</a>
|
||||
<br><a href='".make_link("setup/advanced")."'>Advanced</a>
|
||||
";
|
||||
}
|
||||
|
||||
|
@ -34,10 +34,8 @@ class TagEditTheme extends Themelet {
|
||||
}
|
||||
|
||||
$html .= "
|
||||
<table style='width: 500px;'>
|
||||
<tr><td width='50px'>Tags</td><td width='300px'><input type='text' name='tag_edit__tags' value='$h_tags'></td></tr>
|
||||
$source_edit
|
||||
</table>
|
||||
";
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ class TagList extends Extension {
|
||||
$config->set_default_int("tag_list_length", 15);
|
||||
$config->set_default_int("tags_min", 3);
|
||||
$config->set_default_string("info_link", 'http://en.wikipedia.org/wiki/$tag');
|
||||
$config->set_default_string("tag_list_image_type", 'related');
|
||||
}
|
||||
|
||||
if(is_a($event, 'PageRequestEvent') && ($event->page_name == "tags")) {
|
||||
@ -51,18 +52,27 @@ class TagList extends Extension {
|
||||
if(is_a($event, 'DisplayingImageEvent')) {
|
||||
global $config;
|
||||
if($config->get_int('tag_list_length') > 0) {
|
||||
$this->add_related_block($event->page, $event->image);
|
||||
if($config->get_string('tag_list_image_type') == 'related') {
|
||||
$this->add_related_block($event->page, $event->image);
|
||||
}
|
||||
else {
|
||||
$this->add_tags_block($event->page, $event->image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_a($event, 'SetupBuildingEvent')) {
|
||||
$sb = new SetupBlock("Tag Map Options");
|
||||
$sb->add_int_option("tags_min", "Ignore tags used fewer than "); $sb->add_label(" times");
|
||||
$sb->add_int_option("tags_min", "Only show tags used at least "); $sb->add_label(" times");
|
||||
$event->panel->add_block($sb);
|
||||
|
||||
$sb = new SetupBlock("Popular / Related Tag List");
|
||||
$sb->add_int_option("tag_list_length", "Show top "); $sb->add_label(" tags");
|
||||
$sb->add_text_option("info_link", "<br>Tag info link: ");
|
||||
$sb->add_choice_option("tag_list_image_type", array(
|
||||
"Image's tags only" => "tags",
|
||||
"Show related" => "related"
|
||||
), "<br>Image tag list: ");
|
||||
$sb->add_bool_option("tag_list_numbers", "<br>Show tag counts: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
@ -91,9 +101,9 @@ class TagList extends Extension {
|
||||
$result = $database->execute("
|
||||
SELECT
|
||||
tag,
|
||||
FLOOR(LOG(LOG(count - ? + 1)+1)*1.5*100)/100 AS scaled
|
||||
FLOOR(LOG(2.7, LOG(2.7, count - ? + 1)+1)*1.5*100)/100 AS scaled
|
||||
FROM tags
|
||||
WHERE count > ?
|
||||
WHERE count >= ?
|
||||
ORDER BY tag
|
||||
", array($tags_min, $tags_min));
|
||||
$tag_data = $result->GetArray();
|
||||
@ -114,7 +124,7 @@ class TagList extends Extension {
|
||||
|
||||
$tags_min = $config->get_int('tags_min');
|
||||
$result = $database->execute(
|
||||
"SELECT tag,count FROM tags WHERE count > ? ORDER BY tag",
|
||||
"SELECT tag,count FROM tags WHERE count >= ? ORDER BY tag",
|
||||
array($tags_min));
|
||||
$tag_data = $result->GetArray();
|
||||
|
||||
@ -140,7 +150,7 @@ class TagList extends Extension {
|
||||
|
||||
$tags_min = $config->get_int('tags_min');
|
||||
$result = $database->execute(
|
||||
"SELECT tag,count,FLOOR(LOG(count)) AS scaled FROM tags WHERE count > ? ORDER BY count DESC, tag ASC",
|
||||
"SELECT tag,count,FLOOR(LOG(count)) AS scaled FROM tags WHERE count >= ? ORDER BY count DESC, tag ASC",
|
||||
array($tags_min));
|
||||
$tag_data = $result->GetArray();
|
||||
|
||||
@ -194,6 +204,26 @@ class TagList extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
private function add_tags_block($page, $image) {
|
||||
global $database;
|
||||
global $config;
|
||||
|
||||
$query = "
|
||||
SELECT tags.tag, tags.count
|
||||
FROM tags, image_tags
|
||||
WHERE tags.id = image_tags.tag_id
|
||||
AND image_tags.image_id = ?
|
||||
ORDER BY count DESC
|
||||
LIMIT ?
|
||||
";
|
||||
$args = array($image->id, $config->get_int('tag_list_length'));
|
||||
|
||||
$tags = $database->get_all($query, $args);
|
||||
if(count($tags) > 0) {
|
||||
$this->theme->display_related_block($page, $tags);
|
||||
}
|
||||
}
|
||||
|
||||
private function add_popular_block($page) {
|
||||
global $database;
|
||||
global $config;
|
||||
|
@ -2,14 +2,14 @@
|
||||
<schema version="0.3">
|
||||
<!-- FIXME: mysql utf8ness -->
|
||||
<table name="aliases">
|
||||
<field name="oldtag" type="C" size="255"><key/><notnull/></field>
|
||||
<field name="newtag" type="C" size="255"><notnull/></field>
|
||||
<field name="oldtag" type="C" size="128"><key/><notnull/></field>
|
||||
<field name="newtag" type="C" size="128"><notnull/></field>
|
||||
<index name="aliases__unique"><col>oldtag</col><col>newtag</col><unique/></index>
|
||||
<opt platform="mysql">DEFAULT CHARSET='utf8'</opt>
|
||||
</table>
|
||||
|
||||
<table name="config">
|
||||
<field name="name" type="C" size="255"><key/><notnull/></field>
|
||||
<field name="name" type="C" size="128"><key/><notnull/></field>
|
||||
<field name="value" type="X" size="4000"></field>
|
||||
<opt platform="mysql">DEFAULT CHARSET='utf8'</opt>
|
||||
</table>
|
||||
@ -22,7 +22,7 @@
|
||||
<field name="filesize" type="I"><notnull/></field>
|
||||
<field name="hash" type="C" size="32"><notnull/></field>
|
||||
<field name="ext" type="C" size="4"><notnull/></field>
|
||||
<field name="source" type="C" size="255"></field>
|
||||
<field name="source" type="C" size="249"></field>
|
||||
<field name="width" type="I"><notnull/></field>
|
||||
<field name="height" type="I"><notnull/></field>
|
||||
<field name="posted" type="T"><notnull/></field>
|
||||
@ -38,9 +38,8 @@
|
||||
<field name="name" type="C" size="32"><notnull/></field>
|
||||
<field name="pass" type="C" size="32"></field>
|
||||
<field name="joindate" type="T"><notnull/></field>
|
||||
<field name="enabled" type="C" size="1"><notnull/><default value="Y"/></field>
|
||||
<field name="admin" type="C" size="1"><notnull/><default value="N"/></field>
|
||||
<field name="email" type="C" size="255"></field>
|
||||
<field name="email" type="C" size="249"></field>
|
||||
<index name="users__name"><col>name</col><unique/></index>
|
||||
<opt platform="mysql">DEFAULT CHARSET='utf8'</opt>
|
||||
</table>
|
||||
|
@ -5,6 +5,8 @@ class Upload extends Extension {
|
||||
// event handling {{{
|
||||
public function receive_event($event) {
|
||||
if(is_null($this->theme)) $this->theme = get_theme_object("upload", "UploadTheme");
|
||||
|
||||
$is_full = (disk_free_space(realpath("./images/")) < 100*1024*1024);
|
||||
|
||||
if(is_a($event, 'InitExtEvent')) {
|
||||
global $config;
|
||||
@ -16,7 +18,12 @@ class Upload extends Extension {
|
||||
if(is_a($event, 'PostListBuildingEvent')) {
|
||||
global $user;
|
||||
if($this->can_upload($user)) {
|
||||
$this->theme->display_block($event->page);
|
||||
if($is_full) {
|
||||
$this->theme->display_full($event->page);
|
||||
}
|
||||
else {
|
||||
$this->theme->display_block($event->page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +65,9 @@ class Upload extends Extension {
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->theme->display_page($event->page);
|
||||
if(!$is_full) {
|
||||
$this->theme->display_page($event->page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +88,9 @@ class Upload extends Extension {
|
||||
|
||||
if(is_a($event, "DataUploadEvent")) {
|
||||
global $config;
|
||||
if($is_full) {
|
||||
$event->veto("Upload failed; disk nearly full");
|
||||
}
|
||||
if(filesize($event->tmpname) > $config->get_int('upload_size')) {
|
||||
$event->veto("File too large (".filesize($event->tmpname)." > ".($config->get_int('upload_size')).")");
|
||||
}
|
||||
@ -188,7 +200,7 @@ class Upload extends Extension {
|
||||
$event = new DataUploadEvent($user, $tmp_filename, $metadata);
|
||||
send_event($event);
|
||||
if($event->vetoed) {
|
||||
$this->theme->display_upload_error($page, "Error with ".html_escape($file['name']),
|
||||
$this->theme->display_upload_error($page, "Error with ".html_escape($url),
|
||||
$event->veto_reason);
|
||||
$ok = false;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user