From 0836574070be966d905912593f65081c244e4257 Mon Sep 17 00:00:00 2001 From: shish Date: Sun, 9 Dec 2007 04:18:58 +0000 Subject: [PATCH] more extensions git-svn-id: file:///home/shish/svn/shimmie2/branches/branch_2.1@649 7f39781d-f577-437e-ae19-be835c7a54ca --- contrib/browser_search/main.php | 114 +++++++++ contrib/danbooru_api/main.php | 427 ++++++++++++++++++++++++++++++++ contrib/link_image/_style.css | 41 +++ contrib/link_image/main.php | 45 ++++ contrib/link_image/readme.txt | 82 ++++++ contrib/link_image/theme.php | 71 ++++++ contrib/tag_history/main.php | 205 +++++++++++++++ contrib/tag_history/theme.php | 85 +++++++ 8 files changed, 1070 insertions(+) create mode 100755 contrib/browser_search/main.php create mode 100644 contrib/danbooru_api/main.php create mode 100644 contrib/link_image/_style.css create mode 100644 contrib/link_image/main.php create mode 100644 contrib/link_image/readme.txt create mode 100644 contrib/link_image/theme.php create mode 100644 contrib/tag_history/main.php create mode 100644 contrib/tag_history/theme.php diff --git a/contrib/browser_search/main.php b/contrib/browser_search/main.php new file mode 100755 index 00000000..e345ae4d --- /dev/null +++ b/contrib/browser_search/main.php @@ -0,0 +1,114 @@ + + * Some code (and lots of help) by Artanis (Erik Youngren ) from the 'tagger' extention - Used with permission + * Link: http://atravelinggeek.com/ + * License: GPLv2 + * Description: Allows the user to add a browser 'plugin' to search the site with real-time suggestions + * Version 0.1c + * October 26, 2007 + * + */ + +class BrowserSearch extends Extension { + public function receive_event($event) { + global $page; + global $config; + + if(is_a($event, 'InitExtEvent')) { + $config->set_default_string("search_suggestions_results_order", 'a'); + } + + // Add in header code to let the browser know that the search plugin exists + if(is_a($event, 'PageRequestEvent')) { + // We need to build the data for the header + global $config; + $search_title = $config->get_string('title'); + $search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml'); + $page->add_header(""); + } + + // The search.xml file that is generated on the fly + if(is_a($event, 'PageRequestEvent') && ($event->page_name == "browser_search") && $event->get_arg(0) == "please_dont_use_this_tag_as_it_would_break_stuff__search.xml") { + // First, we need to build all the variables we'll need + + $search_title = $config->get_string('title'); + //$search_form_url = $config->get_string('base_href'); //make_link('post/list'); + $search_form_url = make_link('post/list/{searchTerms}'); + $suggenton_url = make_link('browser_search/')."{searchTerms}"; + + + // Now for the XML + $xml = " + + $search_title + UTF-8 + data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFhYAfX19AH9/fwCAgIAAgYGBAIODgwCEhIQAhoaGAIeHhwCJiYkAioqKAIyMjACPj48AkJCQAJKSkgCTk5MAlZWVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEREREREREREREREREREREREAAAAAAAAAAAAAABERERERAAEBAQEBAQEBAQAAEREREQABAQEBAQEBAQEAAQAREREAAQEBAQEBAQEBAAEBABERAAEBAQEBAQEBAQABAQAREQAGBgUEAwIBAQEAAgEAEREADAwMCwsKCQkHAAkIABERABAQEBAQEA4ODAANDQAREQAQEBAQEBAQEBAAEBAAEREAEBAQEBAQEBAQABAQABERAAAAAAAAAAAAAAAQEAAREREAEBAPEBAQEBAQABAAEREREQAQEBAQEBAPEBAAABERERERAAAAAAAAAAAAAAAREREREREREREREREREREREf//AACADwAAgAcAAIADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMABAADgAQAA8AEAAP//AAA= + $search_form_url + + + + + + "; + + // And now to send it to the browser + $page->set_mode("data"); + $page->set_type("text/xml"); + $page->set_data($xml); + } else if(is_a($event, 'PageRequestEvent') && ($event->page_name == "browser_search") && !$config->get_bool("disable_search_suggestions")) { // We need to return results! + global $database; + + // We have to build some json stuff + $tag_search = $event->get_arg(0); + + // Now to get DB results + if($config->get_string("search_suggestions_results_order") == "a") { + $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30",array($tag_search."%")); + } else { + $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30",array($tag_search."%")); + } + + + // And to do stuff with it. We want our output to look like: + // ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]] + $json_tag_list = ""; + + $tags_array = array(); + foreach($tags as $tag) { + array_push($tags_array,$tag['tag']); + } + + + $json_tag_list .= implode("\",\"", $tags_array); +// $json_tag_list = implode($tags_array,", "); +// $json_tag_list = "\"".implode($tags_array,"\", \"")."\""; + + + // And now for the final output + $json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]"; + $page->set_mode("data"); + $page->set_data($json_string); + } + + if(is_a($event, 'SetupBuildingEvent')) { + + $sort_by = array(); + $sort_by['Alphabetical'] = 'a'; + $sort_by['Tag Count'] = 't'; + + $sb = new SetupBlock("Browser Search"); + $sb->add_bool_option("disable_search_suggestions", "Disable search suggestions when using browser-based search: "); + $sb->add_label("
"); + $sb->add_choice_option("search_suggestions_results_order", $sort_by, "Sort the suggestions by:"); + $event->panel->add_block($sb); + } + + } + + +} +add_event_listener(new BrowserSearch()); +?> diff --git a/contrib/danbooru_api/main.php b/contrib/danbooru_api/main.php new file mode 100644 index 00000000..8f0b6c4a --- /dev/null +++ b/contrib/danbooru_api/main.php @@ -0,0 +1,427 @@ + +Notes: +danbooru API based on documentation from danbooru 1.0 - http://attachr.com/7569 +I've only been able to test add_post and find_tags because I use the old danbooru firefox extension for firefox 1.5 + +Functions currently implemented: +add_comment - NOT DONE YET, waiting on some backend shimmie code :) +add_post - title and rating are currently ignored because shimmie does not support them +find_posts - sort of works, filename is returned as the original filename and probably won't help when it comes to actually downloading it +find_tags - id, name, and after_id all work but the tags parameter is ignored just like danbooru 1.0 ignores it + +CHANGELOG +21-OCT-07 9:07PM CST - JJS +Turns out I actually did need to implement the new parameter names +for danbooru api v1.8.1. Now danbooruup should work when used with /api/danbooru/post/create.xml +Also correctly redirects the url provided by danbooruup in the event +of a duplicate image. + +19-OCT-07 4:46PM CST - JJS +Add compatibility with danbooru api v1.8.1 style urls +for find_posts and add_post. NOTE: This does not implement +the changes to the parameter names, it is simply a +workaround for the latest danbooruup firefox extension. +Completely compatibility will probably involve a rewrite with a different URL + +*/ + +class DanbooruApi extends Extension +{ + // Receive the event + public function receive_event($event) + { + // Check if someone is accessing /api/danbooru (us) + if(is_a($event, 'PageRequestEvent') && ($event->page_name == "api") && ($event->get_arg(0) == 'danbooru')) + { + // execute the danbooru processing code + $this->api_danbooru($event); + } + } + + // Danbooru API + private function api_danbooru($event) + { + global $page; + global $config; + global $database; + global $user; + $page->set_mode("data"); + $page->set_type("application/xml"); + //debug + //$page->set_type("text/plain"); + + $results = array(); + + /* + add_comment() + Adds a comment to a post. + Parameters + * body: the body of the comment + * post_id: the post id + * login: your login + * password: your password Response + * 200: success + * 500: error. response body will the the error message. + */ + if($event->get_arg(1) == 'add_comment') + { + // On error the response body is the error message so plain text is fine + $page->set_type("text/plain"); + // We do wish to auth the user if possible, if it fails treat as anonymous + $this->authenticate_user(); + // Check if anonymous commenting is allowed before proceeding + if($config->get_bool("comment_anon") || !$user->is_anonymous()) + { + // Did the user supply a post_id and a comment body? + if(isset($_REQUEST['post_id']) && isset($_REQUEST['body']) && trim($_REQUEST['body']) != "") + { + // waiting for someone to write an event handler for the comments extension :) + } else + { + // User didn't supply necessary parameters, tell them that + header("HTTP/1.0 500 Internal Server Error"); + $page->set_data("You forgot to supply either a post id or the body of your comment"); + } + } else + { + header("HTTP/1.0 500 Internal Server Error"); + $page->set_data("You supplied an invalid login or password or anonymous commenting is currently disabled"); + } + } + + /* + add_post() + Adds a post to the database. + Parameters + * login: login + * password: password + * file: file as a multipart form + * source: source url + * title: title **IGNORED** + * tags: list of tags as a string, delimited by whitespace + * md5: MD5 hash of upload in hexadecimal format + * rating: rating of the post. can be explicit, questionable, or safe. **IGNORED** + Notes + * The only necessary parameter is tags and either file or source. + * If you want to sign your post, you need a way to authenticate your account, either by supplying login and password, or by supplying a cookie. + * If an account is not supplied or if it doesn‘t authenticate, he post will be added anonymously. + * If the md5 parameter is supplied and does not match the hash of what‘s on the server, the post is rejected. + Response + The response depends on the method used: + Post + * X-Danbooru-Location set to the URL for newly uploaded post. + Get + * Redirected to the newly uploaded post. + */ + if(($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) + { + // No XML data is returned from this function + $page->set_type("text/plain"); + // Check first if a login was supplied, if it wasn't check if the user is logged in via cookie + // If all that fails, it's an anonymous upload + $this->authenticate_user(); + // Now we check if a file was uploaded or a url was provided to transload + // Much of this code is borrowed from /ext/upload + if($config->get_bool("upload_anon") || !$user->is_anonymous()) + { + $file = null; + $filename = ""; + $source = ""; + if(isset($_FILES['file'])) + { // A file was POST'd in + $file = $_FILES['file']['tmp_name']; + $filename = $_FILES['file']['name']; + // If both a file is posted and a source provided, I'm assuming source is the source of the file + if(isset($_REQUEST['source']) && !empty($_REQUEST['source'])) + { + $source = $_REQUEST['source']; + } else + { + $source = null; + } + } elseif(isset($_FILES['post'])) + { + $file = $_FILES['post']['tmp_name']['file']; + $filename = $_FILES['post']['name']['file']; + if(isset($_REQUEST['post']['source']) && !empty($_REQUEST['post']['source'])) + { + $source = $_REQUEST['post']['source']; + } else + { + $source = null; + } + } elseif(isset($_REQUEST['source']) || isset($_REQUEST['post']['source'])) + { // A url was provided + $url = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source']; + $source = $url; + $tmp_filename = tempnam("/tmp", "shimmie_transload"); + + // Are we using fopen wrappers or curl? + if($config->get_string("transload_engine") == "fopen") + { + $fp = fopen($url, "r"); + if(!$fp) { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: fopen read error"); + } + + $data = ""; + $length = 0; + while(!feof($fp) && $length <= $config->get_int('upload_size')) + { + $data .= fread($fp, 8192); + $length = strlen($data); + } + fclose($fp); + + $fp = fopen($tmp_filename, "w"); + fwrite($fp, $data); + fclose($fp); + } + + if($config->get_string("transload_engine") == "curl") + { + $ch = curl_init($url); + $fp = fopen($tmp_filename, "w"); + + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + + curl_exec($ch); + curl_close($ch); + fclose($fp); + } + $file = $tmp_filename; + $filename = basename($url); + } else + { // Nothing was specified at all + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: no input files"); + return; + } + + // Get tags out of url + $posttags = isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']; + + // Now that we have some sort of physical file, process it + $image = new Image($file, $filename, $posttags, $source); + // This occurs if the uploaded file is not an image + if(!$image->is_ok()) + { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: unknown"); + return; + } + // Was an md5 supplied? Does it match the image hash? + if(isset($_REQUEST['md5'])) + { + if($_REQUEST['md5'] != $image->hash) + { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: md5 mismatch"); + return; + } + } + // Is the image too large? + if(filesize($file['tmp_name']) > $config->get_int('upload_size')) + { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: too large"); + return; + } + // Does it exist already? + $existing = $database->get_image_by_hash($image->hash); + if(!is_null($existing)) { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: duplicate"); + $existinglink = make_link("post/view/" . $existing->id); + header("X-Danbooru-Location: $existinglink"); + } + + // Fire off an event which should process the new image and add it to the db + $nevent = new UploadingImageEvent($user, $image); + send_event($nevent); + // Did something screw up? + if($event->vetoed) { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: $event->veto_reason"); + return; + } else + { // If it went ok, grab the id for the newly uploaded image and pass it in the header + $newimg = $database->get_image_by_hash($image->hash); + $newid = make_link("post/view/" . $newimg->id); + // Did we POST or GET this call? + if($_SERVER['REQUEST_METHOD'] == 'POST') + { + header("X-Danbooru-Location: $newid"); + } + else + header("Location: $newid"); + } + } else + { + header("HTTP/1.0 409 Conflict"); + header("X-Danbooru-Errors: authentication error"); + return; + } + } + + /* + find_posts() + Find all posts that match the search criteria. Posts will be ordered by id descending. + Parameters + * md5: md5 hash to search for (comma delimited) + * id: id to search for (comma delimited) + * tags: what tags to search for + * limit: limit + * offset: offset + * after_id: limit results to posts added after this id + */ + if(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) + { + if(isset($_GET['md5'])) + { + $md5list = explode(",",$_GET['md5']); + foreach($md5list as $md5) + { + $results[] = $database->get_image_by_hash($md5); + } + } elseif(isset($_GET['id'])) + { + $idlist = explode(",",$_GET['id']); + foreach($idlist as $id) + { + $results[] = $database->get_image($id); + } + } else + { + $limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100; + $start = isset($_GET['offset']) ? int_escape($_GET['offset']) : 0; + $tags = isset($_GET['tags']) ? tag_explode($_GET['tags']) : array(); + $results = $database->get_images($start,$limit,$tags); + } + + // Now we have the array $results filled with Image objects + // Let's display them + $xml = "\n"; + foreach($results as $img) + { + // Sanity check to see if $img is really an image object + // If it isn't (e.g. someone requested an invalid md5 or id), break out of the this + if(!is_object($img)) + continue; + $taglist = $img->get_tag_list(); + $owner = $img->get_owner(); + $xml .= "hash\" rating=\"Questionable\" date=\"$img->posted\" is_warehoused=\"false\" file_name=\"$img->filename\" tags=\"$taglist\" source=\"$img->source\" score=\"0\" id=\"$img->id\" author=\"$owner->name\"/>\n"; + } + $xml .= ""; + $page->set_data($xml); + } + + /* + find_tags() Find all tags that match the search criteria. + Parameters + * id: A comma delimited list of tag id numbers. + * name: A comma delimited list of tag names. + * tags: any typical tag query. See Tag#parse_query for details. + * after_id: limit results to tags with an id number after after_id. Useful if you only want to refresh + */ + if($event->get_arg(1) == 'find_tags') + { + if(isset($_GET['id'])) + { + $idlist = explode(",",$_GET['id']); + foreach($idlist as $id) + { + $sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE id = ?", array($id)); + if(!$sqlresult->EOF) + { + $results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']); + } + } + } elseif(isset($_GET['name'])) + { + $namelist = explode(",",$_GET['name']); + foreach($namelist as $name) + { + $sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE tag = ?", array($name)); + if(!$sqlresult->EOF) + { + $results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']); + } + } + } + /* Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags + elseif(isset($_GET['tags'])) + { + $start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; + $tags = tag_explode($_GET['tags']); + + } + */ + else + { + $start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; + $sqlresult = $database->execute("SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC",array($start)); + while(!$sqlresult->EOF) + { + $results[] = array($sqlresult->fields['count'], $sqlresult->fields['tag'], $sqlresult->fields['id']); + $sqlresult->MoveNext(); + } + } + + // Tag results collected, build XML output + $xml = "\n"; + foreach($results as $tag) + { + $xml .= "\n"; + } + $xml .= ""; + $page->set_data($xml); + } + + // Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper + // Shimmie view page + // Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123 + // This redirects that to http://shimmie/post/view/123 + if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) + { + $fixedlocation = make_link("post/view/" . $event->get_arg(3)); + header("Location: $fixedlocation"); + } + } + + // Turns out I use this a couple times so let's make it a utility function + // Authenticates a user based on the contents of the login and password parameters + // or makes them anonymous. Does not set any cookies or anything permanent. + private function authenticate_user() + { + global $database; + global $user; + + if(isset($_REQUEST['login']) && isset($_REQUEST['password'])) + { + // Get this user from the db, if it fails the user becomes anonymous + // Code borrowed from /ext/user + $name = $_REQUEST['login']; + $pass = $_REQUEST['password']; + $hash = md5( strtolower($name) . $pass ); + $duser = $database->get_user_by_name_and_hash($name, $hash); + if(!is_null($duser)) { + $user = $duser; + } else + { + $user = $database->get_user_by_id($config->get_int("anon_id", 0)); + } + } + } +} + +add_event_listener(new DanbooruApi()); +?> diff --git a/contrib/link_image/_style.css b/contrib/link_image/_style.css new file mode 100644 index 00000000..472620f6 --- /dev/null +++ b/contrib/link_image/_style.css @@ -0,0 +1,41 @@ +/* + * This file may not be distributed without its readme.txt +**/ + +/* * * Link to Image * * */ +#Link_to_Image { + /* allows borders to encompass the content; */ + overflow:hidden; +} + +#Link_to_Image fieldset { + width: 32%; + float:left; + min-width:25em; +} + +#Link_to_Image input, #Link_to_Image label { + display:block; + width:66%; + float:left; + margin-bottom:2.5px; +} + +#Link_to_Image label { + width:30%; + text-align:left; + padding-right:2%; + cursor:pointer; +} +#Link_to_Image input { + font-size:0.7em; + font-family:courier, fixed, monospace; +} + +#Link_to_Image br { + clear:both; +} + +#Link_to_Image label:hover { + border-bottom:1px dashed; +} \ No newline at end of file diff --git a/contrib/link_image/main.php b/contrib/link_image/main.php new file mode 100644 index 00000000..229c3dc1 --- /dev/null +++ b/contrib/link_image/main.php @@ -0,0 +1,45 @@ + + * Description: Show various forms of link to each image, for copy & paste + */ +class LinkImage extends Extension { + var $theme; + + public function receive_event($event) { + if(is_null($this->theme)) $this->theme = get_theme_object("link_image", "LinkImageTheme"); + if(is_a($event, 'DisplayingImageEvent')) { + global $config; + $data_href = get_base_href(); + $event->page->add_header("",0); + + $this->theme->links_block($event->page,$this->data($event->image)); + } + if(is_a($event, 'SetupBuildingEvent')) { + $sb = new SetupBlock("Link to Image"); + $sb->add_text_option("ext_link-img_text-link_format", "Text Link Format: "); + $event->panel->add_block($sb); + } + if(is_a($event, 'InitExtEvent')) { + global $config; + //just set default if empty. + $config->set_default_string("ext_link-img_text-link_format", + '$title - $id ($ext $size $filesize)'); + } + } + private function data($image) { + global $config; + + $text_link = $image->parse_link_template($config->get_string("ext_link-img_text-link_format")); + $text_link = $text_link==" "? null : $text_link; // null blank setting so the url gets filled in on the text links. + + return array( + 'thumb_src' => $image->get_thumb_link(), + 'image_src' => $image->get_image_link(), + 'post_link' => $image->get_short_link(), + 'text_link' => $text_link); + } +} +add_event_listener(new LinkImage()); +?> diff --git a/contrib/link_image/readme.txt b/contrib/link_image/readme.txt new file mode 100644 index 00000000..6051103a --- /dev/null +++ b/contrib/link_image/readme.txt @@ -0,0 +1,82 @@ +Link to Image adds BBCode and HTML link codes to the image view. Offers code for a customizable text link, thumbnail links, and full image inline. + +Author: Erik Youngren + +License: GPLv2 + +Submit a Bug Report or Suggestion for Link to Image: + * http://trac.shishnet.org/shimmie2/newticket?owner=artanis.00@gmail.com&component=third%20party%20extensions&keywords=link_to_image + += Use = +There is one option in Board Config: Text Link Format. +It takes the following arguments as well as plain text. +|| arguments || replacement || +|| $id || The image ID. || +|| $hash || The MD5 hash of the image. || +|| $tags || The image's tag list. || +|| $base || The base HREF as set in Config. || +|| $ext || The image's extension. || +|| $size || The image's display size. || +|| $filesize || The image's size in KB. || +|| $filename || The image's original filename. || +|| $title || The site title as set in Config. || +Link to Image will default this option to '$title - $id ($ext $size $filesize)'. +To reset to the default, simply clear the current setting. Link to Image will then fill in the default value after the save. + +To leave the setting blank for any reason, leave a space (' ') in it. + += Install = + 1. Copy the folder {{{contrib/link_image/}}} to {{{ext/}}}. + 2. In the Config panel, make sure Base URL is set (you may as well set Data URL while you're there, if you haven't already.) + 3. Make sure Image Link, Thumb Link, and Short Link all contain the full path ("http://" and onward,) either by using $base or plain text. Link to Image will not be able to retrieve the correct paths without these variables. + += Change Log = +== Version 0.3.0 == + * Moved Link to Image over to the official theme engine. This functions basically the same as what the prototype was, but it's more thought out and nicer. + * Cleaned up the insides a bit. + +== Version 0.2.0 == + * Changed the HTML generation to use a prototype theme engine. All HTML generation is now contained within {{{link_image.html.php}}}, which may be copied to the current theme folder and edited from there. + +== Version 0.1.4 - 20070510 == + * Style changes. + * Added output containing only the locations of the thumb, image and post. + * Added a link to wikipedia's HTML page, just as BBCode has a wikipedia link. + +== Version 0.1.3b - 20070509 == + * Renamed style.css to _style.css to avoid the auto loader. + +== Version 0.1.3 - 20070508 == + * Created Readme.txt + * Merged 0.1.2 into 0.1.2b + * Removed uneeded documentation from main.php + * Rewrote the css to be unique. Previously used CSS I wrote for elsewhere. Styled to reduce space consumption. + * Added code to insert the CSS import. + * Updated Nice URLs to allow access to the /ext/ folder. (Why is my stylesheet returning HTML instead of CSS?) + * First SVN update. + +== Version 0.1.2b - 20070507 == +(fairly simultaneous with 0.1.2) + * shish: + * Updated to new extension format + * Created folder link_image in trunk/contrib + * Renamed link_image.ext.php to main.php and moved to /link_image/ + * Created style.css {{{ /* 404'd :|*/ }}}. + * Documentation (different from mine.) + * Changed add_text_option() and added add_label() in SetupBuildingEvent because I was using an edited version of the function that shish didn't know about. It was a wonder that didn't throw massive errors. + * Published on SVN. + +== Version 0.1.2 - 20070506 == + * Textboxes now select-all when they gain focus. + * Commenting and documentation. + +== Version 0.1.1 - 20070506 == + * Fixed HTML thumbnail link code. (image tag was being html_escaped twice, resulting in "$gt;" and "<" from the first escape becoming "&gt;" and "&lt;") It turns out that html_escape was completely unnecessary, all I had to do was replace the single-quotes around the attributes with escaped double-quotes ('\"'.) + +== Version 0.1.0 - 20070506 == + * Release. + += Links = + * http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/LinkToImage - Home + * http://forum.shishnet.org/viewtopic.php?p=153 - Discussion + * http://trac.shishnet.org/shimmie2/browser/trunk/contrib/link_image - Shimmie2 Trac SVN diff --git a/contrib/link_image/theme.php b/contrib/link_image/theme.php new file mode 100644 index 00000000..48e68cd2 --- /dev/null +++ b/contrib/link_image/theme.php @@ -0,0 +1,71 @@ +add_block( new Block( + "Link to Image", + "
". + "BBCode". + $this->link_code("Text Link",$this->url($post_link, $text_link,"ubb"),"ubb_text-link"). + $this->link_code("Thumbnail Link",$this->url($post_link, $this->img($thumb_src,"ubb"),"ubb"),"ubb_thumb-link"). + $this->link_code("Inline Image", $this->img($image_src,"ubb"), "ubb_full-img"). + "
". + + "
". + "HTML". + $this->link_code("Text Link", $this->url($post_link, $text_link,"html"), "html_text-link"). + $this->link_code("Thumbnail Link", $this->url($post_link,$this->img($thumb_src,"html"),"html"), "html_thumb-link"). + $this->link_code("Inline Image", $this->img($image_src,"html"), "html_full-image"). + "
". + + "
". + "Plain Text". + $this->link_code("Post URL",$post_link,"text_post-link"). + $this->link_code("Thumbnail URL",$thumb_src,"text_thumb-url"). + $this->link_code("Image URL",$image_src,"text_image-src"). + "
", + "main", + 50)); + } + + protected function url ($url,$content,$type) { + if ($content == NULL) {$content=$url;} + + switch ($type) { + case "html": + $text = "".$content.""; + break; + case "ubb": + $text = "[url=".$url."]".$content."[/url]"; + break; + default: + $text = $link." - ".$content; + } + return $text; + } + + protected function img ($src,$type) { + switch ($type) { + case "html": + $text = ""; + break; + case "ubb": + $text = "[img]".$src."[/img]"; + break; + default: + $text = $src; + } + return $text; + } + + protected function link_code($label,$content,$id=NULL) { + return "\n". + "\n
\n"; + } +} +?> diff --git a/contrib/tag_history/main.php b/contrib/tag_history/main.php new file mode 100644 index 00000000..71b599f3 --- /dev/null +++ b/contrib/tag_history/main.php @@ -0,0 +1,205 @@ + + * Description: Keep a record of tag changes + */ + +class Tag_History extends Extension { + var $theme; + + public function receive_event($event) { + if(is_null($this->theme)) $this->theme = get_theme_object("tag_history", "Tag_HistoryTheme"); + + if(is_a($event, 'InitExtEvent')) { + // shimmie is being installed so call install to create the table. + global $config; + if($config->get_int("ext_tag_history_version") < 3) { + $this->install(); + } + } + + if(is_a($event, 'PageRequestEvent') && ($event->page_name == "tag_history")) + { + if($event->get_arg(0) == "revert") + { + // this is a request to revert to a previous version of the tags + $this->process_revert_request($_POST['revert']); + } + else if($event->count_args() == 1) + { + // must be an attempt to view a tag history + $image_id = int_escape($event->get_arg(0)); + $this->theme->display_history_page($event->page, $image_id, $this->get_tag_history_from_id($image_id)); + } + else { + $this->theme->display_global_page($event->page, $this->get_global_tag_history()); + } + } + if(is_a($event, 'DisplayingImageEvent')) + { + // handle displaying a link on the view page + $this->theme->display_history_link($event->page, $event->image->id); + } + if(is_a($event, 'ImageDeletionEvent')) + { + // handle removing of history when an image is deleted + $this->delete_all_tag_history($event->image->id); + } + if(is_a($event, 'SetupBuildingEvent')) { + $sb = new SetupBlock("Tag History"); + $sb->add_label("Limit to "); + $sb->add_int_option("history_limit"); + $sb->add_label(" entires per image"); + $event->panel->add_block($sb); + } + if(is_a($event, 'TagSetEvent')) { + $this->add_tag_history($event->image_id, $event->tags); + } + } + + protected function install() + { + global $database; + global $config; + + if($config->get_int("ext_tag_history_version") < 1) { + $database->Execute("CREATE TABLE tag_histories + ( + id integer NOT NULL auto_increment PRIMARY KEY, + image_id integer NOT NULL, + tags text NOT NULL + )"); + $config->set_int("ext_tag_history_version", 1); + } + + if($config->get_int("ext_tag_history_version") == 1) { + $database->Execute("ALTER TABLE tag_histories ADD COLUMN user_id INTEGER NOT NULL"); + $database->Execute("ALTER TABLE tag_histories ADD COLUMN date_set DATETIME NOT NULL"); + $config->set_int("ext_tag_history_version", 2); + } + + if($config->get_int("ext_tag_history_version") == 2) { + $database->Execute("ALTER TABLE tag_histories ADD COLUMN user_ip CHAR(15) NOT NULL"); + $config->set_int("ext_tag_history_version", 3); + } + } + + /* + * this function is called when a revert request is received + */ + private function process_revert_request($revert_id) + { + global $page; + // check for the nothing case + if($revert_id=="nothing") + { + // tried to set it too the same thing so ignore it (might be a bot) + // go back to the index page with you + $page->set_mode("redirect"); + $page->set_redirect(make_link()); + return; + } + + $revert_id = int_escape($revert_id); + + // lets get this revert id assuming it exists + $result = $this->get_tag_history_from_revert($revert_id); + + if($result==null) + { + // there is no history entry with that id so either the image was deleted + // while the user was viewing the history, someone is playing with form + // variables or we have messed up in code somewhere. + die("Error: No tag history with specified id was found."); + } + + // lets get the values out of the result + $stored_result_id = $result->fields['id']; + $stored_image_id = $result->fields['image_id']; + $stored_tags = $result->fields['tags']; + + // all should be ok so we can revert by firing the SetUserTags event. + send_event(new TagSetEvent($stored_image_id, $stored_tags)); + + // all should be done now so redirect the user back to the image + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$stored_image_id")); + } + + public function get_tag_history_from_revert($revert_id) + { + global $database; + $row = $database->execute(" + SELECT tag_histories.*, users.name + FROM tag_histories + JOIN users ON tag_histories.user_id = users.id + WHERE tag_histories.id = ?", array($revert_id)); + return ($row ? $row : null); + } + + public function get_tag_history_from_id($image_id) + { + global $database; + $row = $database->db->GetAll(" + SELECT tag_histories.*, users.name + FROM tag_histories + JOIN users ON tag_histories.user_id = users.id + WHERE image_id = ? + ORDER BY tag_histories.id DESC", + array($image_id)); + return ($row ? $row : array()); + } + + public function get_global_tag_history() + { + global $database; + $row = $database->db->GetAll(" + SELECT tag_histories.*, users.name + FROM tag_histories + JOIN users ON tag_histories.user_id = users.id + ORDER BY tag_histories.id DESC + LIMIT 100"); + return ($row ? $row : array()); + } + + /* + * this function is called when an image has been deleted + */ + private function delete_all_tag_history($image_id) + { + global $database; + $database->execute("DELETE FROM tag_histories WHERE image_id = ?", array($image_id)); + } + + /* + * this function is called just before an images tag are changed + */ + private function add_tag_history($image_id, $tags) + { + global $database; + global $config; + global $user; + + if(is_array($tags)) $tags = implode(' ', $tags); + + // add a history entry + $allowed = $config->get_int("history_limit",10); + if($allowed<=0) return; + $row = $database->execute(" + INSERT INTO tag_histories(image_id, tags, user_id, user_ip, date_set) + VALUES (?, ?, ?, ?, now())", + array($image_id, $tags, $user->id, $_SERVER['REMOTE_ADDR'])); + $entries = $database->db->GetOne("SELECT COUNT(*) FROM `tag_histories` WHERE image_id = ?", array($image_id)); + + // if needed remove oldest one + if($entries > $allowed) + { + // TODO: Make these queries better + $min_id = $database->db->GetOne("SELECT MIN(id) FROM tag_histories WHERE image_id = ?", array($image_id)); + $database->execute("DELETE FROM tag_histories WHERE id = ?", array($min_id)); + } + } +} +add_event_listener(new Tag_History()); +?> diff --git a/contrib/tag_history/theme.php b/contrib/tag_history/theme.php new file mode 100644 index 00000000..a8fa60dc --- /dev/null +++ b/contrib/tag_history/theme.php @@ -0,0 +1,85 @@ + +
+
    + "; + + global $user; + $history_list = ""; + foreach($history as $fields) + { + $current_id = $fields['id']; + $current_tags = html_escape($fields['tags']); + $name = $fields['name']; + $setter = "".html_escape($name).""; + if($user->is_admin()) { + $setter .= " / " . $fields['user_ip']; + } + $history_list .= "
  • $current_tags (Set by $setter)
  • \n"; + } + + $end_string = " +
+ +
+ + "; + $history_html = $start_string . $history_list . $end_string; + + $page->set_title("Image $image_id Tag History"); + $page->set_heading("Tag History: $image_id"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Tag History", $history_html, "main", 10)); + } + + public function display_global_page($page, $history) { + $start_string = " +
+
+
    + "; + $end_string = " +
+ +
+
+ "; + + global $user; + $history_list = ""; + foreach($history as $fields) + { + $current_id = $fields['id']; + $image_id = $fields['image_id']; + $current_tags = html_escape($fields['tags']); + $name = $fields['name']; + $setter = "".html_escape($name).""; + if($user->is_admin()) { + $setter .= " / " . $fields['user_ip']; + } + $history_list .= " +
  • + + $image_id: + $current_tags (Set by $setter) +
  • + "; + } + + $history_html = $start_string . $history_list . $end_string; + $page->set_title("Global Tag History"); + $page->set_heading("Global Tag History"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Tag History", $history_html, "main", 10)); + } + + public function display_history_link($page, $image_id) { + $link = "Tag History\n"; + $page->add_block(new Block(null, $link, "main", 5)); + } +} +?>