Merge pull request #1 from shish/develop

MERGE ALL THE THINGS
This commit is contained in:
Daniel Løvbrøtte Olsen 2015-12-17 11:26:51 +01:00
commit de87bdbb0d
115 changed files with 2789 additions and 2063 deletions

2
.gitignore vendored
View File

@ -3,6 +3,8 @@ data
images images
thumbs thumbs
!lib/images !lib/images
*.phar
*.sqlite
# Created by http://www.gitignore.io # Created by http://www.gitignore.io

View File

@ -3,7 +3,12 @@
</IfModule> </IfModule>
<FilesMatch "\.(sqlite|sdb|s3db|db)$"> <FilesMatch "\.(sqlite|sdb|s3db|db)$">
<IfModule mod_authz_host.c>
Require all denied
</IfModule>
<IfModule !mod_authz_host.c>
Deny from all Deny from all
</IfModule>
</FilesMatch> </FilesMatch>
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>

View File

@ -4,3 +4,6 @@ imports:
filter: filter:
excluded_paths: [lib/*,ext/tagger/script.js,ext/chatbox/*] excluded_paths: [lib/*,ext/tagger/script.js,ext/chatbox/*]
tools:
external_code_coverage: true

View File

@ -2,21 +2,18 @@ language: php
sudo: false sudo: false
php: php:
# Here is where we can list the versions of PHP you want to test against
# using major version aliases
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- nightly - nightly
# optionally specify a list of environments, for example to test different RDBMS
env: env:
matrix:
- DB=mysql - DB=mysql
- DB=pgsql - DB=pgsql
- DB=sqlite - DB=sqlite
install: install:
# Enable logging of all queries (for debugging) and create the database schema for shimmie.
- mkdir -p data/config - mkdir -p data/config
- if [[ "$DB" == "pgsql" ]]; then psql -c "SELECT set_config('log_statement', 'all', false);" -U postgres; fi - if [[ "$DB" == "pgsql" ]]; then psql -c "SELECT set_config('log_statement', 'all', false);" -U postgres; fi
- if [[ "$DB" == "pgsql" ]]; then psql -c "CREATE DATABASE shimmie;" -U postgres; fi - if [[ "$DB" == "pgsql" ]]; then psql -c "CREATE DATABASE shimmie;" -U postgres; fi
@ -25,12 +22,12 @@ install:
- if [[ "$DB" == "mysql" ]]; then mysql -e "CREATE DATABASE shimmie;" -uroot; fi - if [[ "$DB" == "mysql" ]]; then mysql -e "CREATE DATABASE shimmie;" -uroot; fi
- if [[ "$DB" == "mysql" ]]; then echo '<?php define("DATABASE_DSN", "mysql:user=root;password=;host=localhost;dbname=shimmie");' > data/config/auto_install.conf.php ; fi - if [[ "$DB" == "mysql" ]]; then echo '<?php define("DATABASE_DSN", "mysql:user=root;password=;host=localhost;dbname=shimmie");' > data/config/auto_install.conf.php ; fi
- if [[ "$DB" == "sqlite" ]]; then echo '<?php define("DATABASE_DSN", "sqlite:shimmie.sqlite");' > data/config/auto_install.conf.php ; fi - if [[ "$DB" == "sqlite" ]]; then echo '<?php define("DATABASE_DSN", "sqlite:shimmie.sqlite");' > data/config/auto_install.conf.php ; fi
- wget https://scrutinizer-ci.com/ocular.phar
script: script:
- php install.php - php install.php
- phpunit --configuration tests/phpunit.xml - phpunit --configuration tests/phpunit.xml --coverage-clover=data/coverage.clover
# If a failure occured then dump out a bunch of logs for debugging purposes.
after_failure: after_failure:
- head -n 100 data/config/* - head -n 100 data/config/*
- ls /var/run/mysql* - ls /var/run/mysql*
@ -42,7 +39,5 @@ after_failure:
- ls /var/log/postgresql - ls /var/log/postgresql
- cat /var/log/postgresql/postgresql* - cat /var/log/postgresql/postgresql*
# configure notifications (email, IRC, campfire etc) after_script:
#notifications: - php ocular.phar code-coverage:upload --format=php-clover data/coverage.clover
# irc: "irc.freenode.org#shimmie"
#

View File

@ -96,7 +96,7 @@ class BaseThemelet {
* *
* @param string $base_url * @param string $base_url
* @param string $query * @param string $query
* @param int|string $page * @param string $page
* @param string $name * @param string $name
* @return string * @return string
*/ */
@ -108,8 +108,8 @@ class BaseThemelet {
/** /**
* @param string $base_url * @param string $base_url
* @param string $query * @param string $query
* @param int|string $page * @param string $page
* @param int|string $current_page * @param int $current_page
* @param string $name * @param string $name
* @return string * @return string
*/ */

View File

@ -58,7 +58,7 @@ class Block {
$this->body = $body; $this->body = $body;
$this->section = $section; $this->section = $section;
$this->position = $position; $this->position = $position;
$this->id = str_replace(' ', '_', is_null($id) ? (is_null($header) ? md5($body) : $header) . $section : $id); $this->id = preg_replace('/[^\w]/', '',str_replace(' ', '_', is_null($id) ? (is_null($header) ? md5($body) : $header) . $section : $id));
} }
/** /**
@ -94,4 +94,3 @@ class NavBlock extends Block {
parent::__construct("Navigation", "<a href='".make_link()."'>Index</a>", "left", 0); parent::__construct("Navigation", "<a href='".make_link()."'>Index</a>", "left", 0);
} }
} }

View File

@ -639,7 +639,7 @@ class Database {
* *
* @param string $query * @param string $query
* @param array $args * @param array $args
* @return mixed|null * @return array|null
*/ */
public function get_row($query, $args=array()) { public function get_row($query, $args=array()) {
$_start = microtime(true); $_start = microtime(true);
@ -702,7 +702,7 @@ class Database {
* Get the ID of the last inserted row. * Get the ID of the last inserted row.
* *
* @param string|null $seq * @param string|null $seq
* @return string * @return int
*/ */
public function get_last_insert_id($seq) { public function get_last_insert_id($seq) {
if($this->engine->name == "pgsql") { if($this->engine->name == "pgsql") {

View File

@ -282,7 +282,7 @@ abstract class DataHandlerExtension extends Extension {
abstract protected function supported_ext($ext); abstract protected function supported_ext($ext);
/** /**
* @param $tmpname * @param string $tmpname
* @return bool * @return bool
*/ */
abstract protected function check_contents($tmpname); abstract protected function check_contents($tmpname);

View File

@ -63,7 +63,7 @@ class Image {
public $tag_array; public $tag_array;
public $owner_id, $owner_ip; public $owner_id, $owner_ip;
public $posted, $posted_timestamp; public $posted;
public $source; public $source;
public $locked; public $locked;
@ -82,7 +82,6 @@ class Image {
$name = str_replace("images.", "", $name); $name = str_replace("images.", "", $name);
$this->$name = $value; // hax, this is likely the cause of much scrutinizer-ci complaints. $this->$name = $value; // hax, this is likely the cause of much scrutinizer-ci complaints.
} }
$this->posted_timestamp = strtotime($this->posted); // pray
$this->locked = bool_escape($this->locked); $this->locked = bool_escape($this->locked);
assert(is_numeric($this->id)); assert(is_numeric($this->id));
@ -192,6 +191,13 @@ class Image {
return ($yays > 1 || $nays > 0); return ($yays > 1 || $nays > 0);
} }
/**
* @param string[] $tags
* @param int $offset
* @param int $limit
* @return null|PDOStatement
* @throws SCoreException
*/
public function get_accelerated_result($tags, $offset, $limit) { public function get_accelerated_result($tags, $offset, $limit) {
global $database; global $database;
@ -244,7 +250,7 @@ class Image {
* Count the number of image results for a given search * Count the number of image results for a given search
* *
* @param string[] $tags * @param string[] $tags
* @return mixed * @return int
*/ */
public static function count_images($tags=array()) { public static function count_images($tags=array()) {
assert('is_array($tags)'); assert('is_array($tags)');
@ -284,7 +290,6 @@ class Image {
return ceil(Image::count_images($tags) / $config->get_int('index_images')); return ceil(Image::count_images($tags) / $config->get_int('index_images'));
} }
/* /*
* Accessors & mutators * Accessors & mutators
*/ */
@ -401,9 +406,9 @@ class Image {
/** /**
* Check configured template for a link, then try nice URL, then plain URL * Check configured template for a link, then try nice URL, then plain URL
* *
* @param $template * @param string $template
* @param $nice * @param string $nice
* @param $plain * @param string $plain
* @return string * @return string
*/ */
private function get_link($template, $nice, $plain) { private function get_link($template, $nice, $plain) {
@ -532,6 +537,10 @@ class Image {
return $this->locked; return $this->locked;
} }
/**
* @param bool $tf
* @throws SCoreException
*/
public function set_locked($tf) { public function set_locked($tf) {
global $database; global $database;
$ln = $tf ? "Y" : "N"; $ln = $tf ? "Y" : "N";
@ -551,9 +560,22 @@ class Image {
*/ */
public function delete_tags_from_image() { public function delete_tags_from_image() {
global $database; global $database;
$database->execute( if($database->get_driver_name() == "mysql") {
"UPDATE tags SET count = count - 1 WHERE id IN ". //mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this
"(SELECT tag_id FROM image_tags WHERE image_id = :id)", array("id"=>$this->id)); $database->execute("
UPDATE tags t
INNER JOIN image_tags it ON t.id = it.tag_id
SET count = count - 1
WHERE it.image_id = :id",
array("id"=>$this->id)
);
} else {
$database->execute("
UPDATE tags
SET count = count - 1
WHERE id IN (SELECT tag_id FROM image_tags WHERE image_id = :id)", array("id"=>$this->id)
);
}
$database->execute("DELETE FROM image_tags WHERE image_id=:id", array("id"=>$this->id)); $database->execute("DELETE FROM image_tags WHERE image_id=:id", array("id"=>$this->id));
} }
@ -567,9 +589,6 @@ class Image {
assert('is_array($tags) && count($tags) > 0', var_export($tags, true)); assert('is_array($tags) && count($tags) > 0', var_export($tags, true));
global $database; global $database;
$tags = array_map(array('Tag', 'sanitise'), $tags);
$tags = Tag::resolve_aliases($tags);
if(count($tags) <= 0) { if(count($tags) <= 0) {
throw new SCoreException('Tried to set zero tags'); throw new SCoreException('Tried to set zero tags');
} }
@ -579,12 +598,6 @@ class Image {
$this->delete_tags_from_image(); $this->delete_tags_from_image();
// insert each new tags // insert each new tags
foreach($tags as $tag) { foreach($tags as $tag) {
$ttpe = new TagTermParseEvent($tag, $this->id);
send_event($ttpe);
if($ttpe->is_metatag()) {
continue;
}
if(mb_strlen($tag, 'UTF-8') > 255){ if(mb_strlen($tag, 'UTF-8') > 255){
flash_message("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n"); flash_message("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n");
continue; continue;
@ -623,6 +636,19 @@ class Image {
} }
} }
/**
* Send list of metatags to be parsed.
*
* @param [] $metatags
* @param int $image_id
*/
public function parse_metatags($metatags, $image_id) {
foreach($metatags as $tag) {
$ttpe = new TagTermParseEvent($tag, $image_id, TRUE);
send_event($ttpe);
}
}
/** /**
* Delete this image from the database and disk * Delete this image from the database and disk
*/ */
@ -736,9 +762,58 @@ class Image {
return Image::build_accurate_search_querylet($terms); return Image::build_accurate_search_querylet($terms);
} }
/**
* @param string[] $terms
* @return ImgQuerylet[]
*/
private static function parse_meta_terms($terms) {
$img_querylets = array();
$stpe = new SearchTermParseEvent(null, $terms);
send_event($stpe);
if ($stpe->is_querylet_set()) {
foreach ($stpe->get_querylets() as $querylet) {
$img_querylets[] = new ImgQuerylet($querylet, true);
}
}
return $img_querylets;
}
/**
* @param ImgQuerylet[] $img_querylets
* @return Querylet
*/
private static function build_img_search($img_querylets) {
// 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);
}
return new Querylet($sql, $terms);
}
/**
* @param Querylet $img_search
* @return Querylet
*/
private static function build_simple_query($img_search) {
$query = new Querylet("SELECT images.* FROM images ");
if (!empty($img_search->sql)) {
$query->append_sql(" WHERE ");
$query->append($img_search);
return $query;
}
return $query;
}
/** /**
* WARNING: this description is no longer accurate, though it does get across * WARNING: this description is no longer accurate, though it does get across
* the general idea - the actual method has a few extra optimisiations * the general idea - the actual method has a few extra optimisations
* *
* "foo bar -baz user=foo" becomes * "foo bar -baz user=foo" becomes
* *
@ -752,7 +827,7 @@ class Image {
* A) Incredibly simple: * A) Incredibly simple:
* Each search term maps to a list of image IDs * Each search term maps to a list of image IDs
* B) Runs really fast on a good database: * B) Runs really fast on a good database:
* These lists are calucalted once, and the set intersection taken * These lists are calculated once, and the set intersection taken
* C) Runs really slow on bad databases: * C) Runs really slow on bad databases:
* All the subqueries are executed every time for every row in the * All the subqueries are executed every time for every row in the
* images table. Yes, MySQL does suck this much. * images table. Yes, MySQL does suck this much.
@ -764,21 +839,12 @@ class Image {
global $database; global $database;
$tag_querylets = array(); $tag_querylets = array();
$img_querylets = array(); $img_querylets = self::parse_meta_terms($terms);
$positive_tag_count = 0; $positive_tag_count = 0;
$stpe = new SearchTermParseEvent(null, $terms);
send_event($stpe);
if($stpe->is_querylet_set()) {
foreach($stpe->get_querylets() as $querylet) {
$img_querylets[] = new ImgQuerylet($querylet, true);
}
}
$terms = Tag::resolve_aliases($terms);
// parse the words that are searched for into // parse the words that are searched for into
// various types of querylet // various types of querylet
$terms = Tag::resolve_aliases($terms);
foreach($terms as $term) { foreach($terms as $term) {
$positive = true; $positive = true;
if(is_string($term) && !empty($term) && ($term[0] == '-')) { if(is_string($term) && !empty($term) && ($term[0] == '-')) {
@ -804,37 +870,21 @@ class Image {
} }
} }
} }
$img_search = self::build_img_search($img_querylets);
// 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);
// How many tag querylets are there? // How many tag querylets are there?
$count_tag_querylets = count($tag_querylets); $count_tag_querylets = count($tag_querylets);
// no tags, do a simple search (+image metadata if we have any) // no tags, do a simple search (+image metadata if we have any)
if($count_tag_querylets === 0) { if($count_tag_querylets === 0) {
$query = new Querylet("SELECT images.* FROM images "); $query = self::build_simple_query($img_search);
if(!empty($img_search->sql)) {
$query->append_sql(" WHERE ");
$query->append($img_search);
}
} }
// one positive tag (a common case), do an optimised search // one positive tag (a common case), do an optimised search
else if($count_tag_querylets === 1 && $tag_querylets[0]->positive) { else if($count_tag_querylets === 1 && $tag_querylets[0]->positive) {
$query = new Querylet($database->scoreql_to_sql(" $query = new Querylet($database->scoreql_to_sql("
SELECT images.* FROM images SELECT images.*
FROM images
JOIN image_tags ON images.id=image_tags.image_id JOIN image_tags ON images.id=image_tags.image_id
JOIN tags ON image_tags.tag_id=tags.id JOIN tags ON image_tags.tag_id=tags.id
WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag) WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)
@ -854,10 +904,12 @@ class Image {
foreach($tag_querylets as $tq) { foreach($tag_querylets as $tq) {
$tag_ids = $database->get_col( $tag_ids = $database->get_col(
$database->scoreql_to_sql( $database->scoreql_to_sql("
"SELECT id FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)" SELECT id
), FROM tags
array("tag"=>$tq->tag)); WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)
"), array("tag"=>$tq->tag)
);
if($tq->positive) { if($tq->positive) {
$positive_tag_id_array = array_merge($positive_tag_id_array, $tag_ids); $positive_tag_id_array = array_merge($positive_tag_id_array, $tag_ids);
$tags_ok = count($tag_ids) > 0; $tags_ok = count($tag_ids) > 0;
@ -872,7 +924,7 @@ class Image {
$have_pos = count($positive_tag_id_array) > 0; $have_pos = count($positive_tag_id_array) > 0;
$have_neg = count($negative_tag_id_array) > 0; $have_neg = count($negative_tag_id_array) > 0;
$sql = "SELECT images.* FROM images WHERE images.id IN ("; $sql = "";
if($have_pos) { if($have_pos) {
$positive_tag_id_list = join(', ', $positive_tag_id_array); $positive_tag_id_list = join(', ', $positive_tag_id_array);
$sql .= " $sql .= "
@ -894,8 +946,11 @@ class Image {
WHERE tag_id IN ($negative_tag_id_list) WHERE tag_id IN ($negative_tag_id_list)
"; ";
} }
$sql .= ")"; $query = new Querylet("
$query = new Querylet($sql); SELECT images.*
FROM images
WHERE images.id IN ($sql)
");
if(strlen($img_search->sql) > 0) { if(strlen($img_search->sql) > 0) {
$query->append_sql(" AND "); $query->append_sql(" AND ");
@ -921,23 +976,16 @@ class Image {
* build_accurate_search_querylet() for a full explanation * build_accurate_search_querylet() for a full explanation
* *
* @param array $terms * @param array $terms
* @return Querylet
*/ */
private static function build_ugly_search_querylet($terms) { private static function build_ugly_search_querylet($terms) {
global $database; global $database;
$tag_querylets = array(); $tag_querylets = array();
$img_querylets = array(); $img_querylets = self::parse_meta_terms($terms);
$positive_tag_count = 0; $positive_tag_count = 0;
$negative_tag_count = 0; $negative_tag_count = 0;
$stpe = new SearchTermParseEvent(null, $terms);
send_event($stpe);
if($stpe->is_querylet_set()) {
foreach($stpe->get_querylets() as $querylet) {
$img_querylets[] = new ImgQuerylet($querylet, true);
}
}
$terms = Tag::resolve_aliases($terms); $terms = Tag::resolve_aliases($terms);
reset($terms); // rewind to first element in array. reset($terms); // rewind to first element in array.
@ -979,44 +1027,24 @@ class Image {
else $negative_tag_count++; else $negative_tag_count++;
} }
$tag_search = new Querylet($sql, $terms); $tag_search = new Querylet($sql, $terms);
$img_search = self::build_img_search($img_querylets);
// 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) // no tags, do a simple search (+image metadata if we have any)
if($positive_tag_count + $negative_tag_count == 0) { if($positive_tag_count + $negative_tag_count == 0) {
$query = new Querylet("SELECT images.*,UNIX_TIMESTAMP(posted) AS posted_timestamp FROM images "); $query = self::build_simple_query($img_search);
if(!empty($img_search->sql)) {
$query->append_sql(" WHERE ");
$query->append($img_search);
}
} }
// one positive tag (a common case), do an optimised search // one positive tag (a common case), do an optimised search
else if($positive_tag_count === 1 && $negative_tag_count === 0) { 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 -_- // MySQL is braindead, and does a full table scan on images, running the subquery once for each row -_-
// "{$this->get_images} WHERE images.id IN (SELECT image_id FROM tags WHERE tag LIKE ?) ", // "{$this->get_images} WHERE images.id IN (SELECT image_id FROM tags WHERE tag LIKE ?) ",
" $query = new Querylet("
SELECT images.*, UNIX_TIMESTAMP(posted) AS posted_timestamp SELECT images.*
FROM tags, image_tags, images FROM images
WHERE JOIN image_tags ON images.id=image_tags.image_id
tag LIKE :tag0 JOIN tags ON image_tags.tag_id=tags.id
AND tags.id = image_tags.tag_id WHERE tag LIKE :tag0
AND image_tags.image_id = images.id ", $tag_search->variables);
",
$tag_search->variables);
if(!empty($img_search->sql)) { if(!empty($img_search->sql)) {
$query->append_sql(" AND "); $query->append_sql(" AND ");
@ -1031,7 +1059,10 @@ class Image {
$x = 0; $x = 0;
foreach($tag_search->variables as $tag) { foreach($tag_search->variables as $tag) {
$tag_ids = $database->get_col("SELECT id FROM tags WHERE tag LIKE :tag", array("tag"=>$tag)); $tag_ids = $database->get_col(
"SELECT id FROM tags WHERE tag LIKE :tag",
array("tag"=>$tag)
);
$tag_id_array = array_merge($tag_id_array, $tag_ids); $tag_id_array = array_merge($tag_id_array, $tag_ids);
$tags_ok = count($tag_ids) > 0 || !$tag_querylets[$x]->positive; $tags_ok = count($tag_ids) > 0 || !$tag_querylets[$x]->positive;
@ -1057,7 +1088,7 @@ class Image {
) )
); );
$query = new Querylet(' $query = new Querylet('
SELECT *, UNIX_TIMESTAMP(posted) AS posted_timestamp SELECT *
FROM ('.$subquery->sql.') AS images ', $subquery->variables); FROM ('.$subquery->sql.') AS images ', $subquery->variables);
if(!empty($img_search->sql)) { if(!empty($img_search->sql)) {
@ -1102,7 +1133,7 @@ class Tag {
* Remove any excess fluff from a user-input tag * Remove any excess fluff from a user-input tag
* *
* @param string $tag * @param string $tag
* @return mixed * @return string
*/ */
public static function sanitise($tag) { public static function sanitise($tag) {
assert('is_string($tag)'); assert('is_string($tag)');
@ -1118,7 +1149,7 @@ class Tag {
* *
* @param string|string[] $tags * @param string|string[] $tags
* @param bool $tagme * @param bool $tagme
* @return array * @return string[]
*/ */
public static function explode($tags, $tagme=true) { public static function explode($tags, $tagme=true) {
assert('is_string($tags) || is_array($tags)'); assert('is_string($tags) || is_array($tags)');
@ -1142,6 +1173,8 @@ class Tag {
$tag_array = array("tagme"); $tag_array = array("tagme");
} }
$tag_array = array_iunique($tag_array); //remove duplicate tags
sort($tag_array); sort($tag_array);
return $tag_array; return $tag_array;
@ -1213,7 +1246,10 @@ class Tag {
global $database; global $database;
$db_wild_tag = str_replace("%", "\%", $tag); $db_wild_tag = str_replace("%", "\%", $tag);
$db_wild_tag = str_replace("*", "%", $db_wild_tag); $db_wild_tag = str_replace("*", "%", $db_wild_tag);
$newtags = $database->get_col($database->scoreql_to_sql("SELECT tag FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(?)"), array($db_wild_tag)); $newtags = $database->get_col(
$database->scoreql_to_sql("SELECT tag FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(?)"),
array($db_wild_tag)
);
if(count($newtags) > 0) { if(count($newtags) > 0) {
$resolved = $newtags; $resolved = $newtags;
} else { } else {
@ -1237,7 +1273,7 @@ class Tag {
$i = 0; $i = 0;
$tag_count = count($tags); $tag_count = count($tags);
while($i<$tag_count) { while($i<$tag_count) {
$aliases = explode(' ', Tag::resolve_alias($tags[$i])); $aliases = Tag::explode(Tag::resolve_alias($tags[$i]), FALSE);
foreach($aliases as $alias){ foreach($aliases as $alias){
if(!in_array($alias, $new)){ if(!in_array($alias, $new)){
if($tags[$i] == $alias){ if($tags[$i] == $alias){
@ -1284,7 +1320,7 @@ function move_upload_to_archive(DataUploadEvent $event) {
* @param $base string * @param $base string
* @return array * @return array
*/ */
function add_dir(/*string*/ $base) { function add_dir($base) {
$results = array(); $results = array();
foreach(list_files($base) as $full_path) { foreach(list_files($base) as $full_path) {
@ -1307,12 +1343,12 @@ function add_dir(/*string*/ $base) {
} }
/** /**
* @param $tmpname * @param string $tmpname
* @param $filename * @param string $filename
* @param $tags * @param string $tags
* @throws UploadException * @throws UploadException
*/ */
function add_image(/*string*/ $tmpname, /*string*/ $filename, /*string*/ $tags) { function add_image($tmpname, $filename, $tags) {
assert(file_exists($tmpname)); assert(file_exists($tmpname));
$pathinfo = pathinfo($filename); $pathinfo = pathinfo($filename);
@ -1337,7 +1373,7 @@ function add_image(/*string*/ $tmpname, /*string*/ $filename, /*string*/ $tags)
* *
* @param int $orig_width * @param int $orig_width
* @param int $orig_height * @param int $orig_height
* @return array * @return int[]
*/ */
function get_thumbnail_size(/*int*/ $orig_width, /*int*/ $orig_height) { function get_thumbnail_size(/*int*/ $orig_width, /*int*/ $orig_height) {
global $config; global $config;
@ -1363,4 +1399,3 @@ function get_thumbnail_size(/*int*/ $orig_width, /*int*/ $orig_height) {
} }
} }

View File

@ -205,6 +205,10 @@ class Page {
$this->cookies[] = array($full_name, $value, $time, $path); $this->cookies[] = array($full_name, $value, $time, $path);
} }
/**
* @param string $name
* @return string|null
*/
public function get_cookie(/*string*/ $name) { public function get_cookie(/*string*/ $name) {
$full_name = COOKIE_PREFIX."_".$name; $full_name = COOKIE_PREFIX."_".$name;
if(isset($_COOKIE[$full_name])) { if(isset($_COOKIE[$full_name])) {

View File

@ -36,7 +36,7 @@ _d("COMPILE_ELS", false); // boolean pre-build the list of event listeners
_d("NICE_URLS", false); // boolean force niceurl mode _d("NICE_URLS", false); // boolean force niceurl mode
_d("SEARCH_ACCEL", false); // boolean use search accelerator _d("SEARCH_ACCEL", false); // boolean use search accelerator
_d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse
_d("VERSION", '2.5.4+'); // string shimmie version _d("VERSION", '2.5.5+'); // string shimmie version
_d("TIMEZONE", null); // string timezone _d("TIMEZONE", null); // string timezone
_d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable
_d("EXTRA_EXTS", ""); // optional extra extensions _d("EXTRA_EXTS", ""); // optional extra extensions

View File

@ -124,6 +124,25 @@ function no_escape($input) {
return $input; return $input;
} }
/**
* @param int $val
* @param int|null $min
* @param int|null $max
* @return int
*/
function clamp($val, $min, $max) {
if(!is_numeric($val) || (!is_null($min) && $val < $min)) {
$val = $min;
}
if(!is_null($max) && $val > $max) {
$val = $max;
}
if(!is_null($min) && !is_null($max)) {
assert('$val >= $min && $val <= $max', "$min <= $val <= $max");
}
return $val;
}
/** /**
* @param string $name * @param string $name
* @param array $attrs * @param array $attrs
@ -268,17 +287,22 @@ function validate_input($inputs) {
foreach($inputs as $key => $validations) { foreach($inputs as $key => $validations) {
$flags = explode(',', $validations); $flags = explode(',', $validations);
if(in_array('bool', $flags) && !isset($_POST[$key])) {
$_POST[$key] = 'off';
}
if(in_array('optional', $flags)) { if(in_array('optional', $flags)) {
if(!isset($_POST[$key])) { if(!isset($_POST[$key]) || trim($_POST[$key]) == "") {
$outputs[$key] = null;
continue; continue;
} }
} }
if(!isset($_POST[$key]) || trim($_POST[$key]) == "") {
if(!isset($_POST[$key])) {
throw new InvalidInput("Input '$key' not set"); throw new InvalidInput("Input '$key' not set");
} }
$value = $_POST[$key]; $value = trim($_POST[$key]);
if(in_array('user_id', $flags)) { if(in_array('user_id', $flags)) {
$id = int_escape($value); $id = int_escape($value);
@ -308,11 +332,36 @@ function validate_input($inputs) {
$outputs[$key] = $value; $outputs[$key] = $value;
} }
else if(in_array('email', $flags)) { else if(in_array('email', $flags)) {
$outputs[$key] = $value; $outputs[$key] = trim($value);
} }
else if(in_array('password', $flags)) { else if(in_array('password', $flags)) {
$outputs[$key] = $value; $outputs[$key] = $value;
} }
else if(in_array('int', $flags)) {
$value = trim($value);
if(empty($value) || !is_numeric($value)) {
throw new InvalidInput("Invalid int: ".html_escape($value));
}
$outputs[$key] = (int)$value;
}
else if(in_array('bool', $flags)) {
$outputs[$key] = bool_escape($value);
}
else if(in_array('string', $flags)) {
if(in_array('trim', $flags)) {
$value = trim($value);
}
if(in_array('lower', $flags)) {
$value = strtolower($value);
}
if(in_array('not-empty', $flags)) {
throw new InvalidInput("$key must not be blank");
}
if(in_array('nullify', $flags)) {
if(empty($value)) $value = null;
}
$outputs[$key] = $value;
}
else { else {
throw new InvalidInput("Unknown validation '$validations'"); throw new InvalidInput("Unknown validation '$validations'");
} }
@ -910,21 +959,19 @@ function transload($url, $mfile) {
} }
if($config->get_string("transload_engine") === "fopen") { if($config->get_string("transload_engine") === "fopen") {
$fp = @fopen($url, "r"); $fp_in = @fopen($url, "r");
if(!$fp) { $fp_out = fopen($mfile, "w");
if(!$fp_in || !$fp_out) {
return false; return false;
} }
$data = "";
$length = 0; $length = 0;
while(!feof($fp) && $length <= $config->get_int('upload_size')) { while(!feof($fp_in) && $length <= $config->get_int('upload_size')) {
$data .= fread($fp, 8192); $data = fread($fp_in, 8192);
$length = strlen($data); $length += strlen($data);
fwrite($fp_out, $data);
} }
fclose($fp); fclose($fp_in);
fclose($fp_out);
$fp = fopen($mfile, "w");
fwrite($fp, $data);
fclose($fp);
$headers = http_parse_headers(implode("\n", $http_response_header)); $headers = http_parse_headers(implode("\n", $http_response_header));

View File

@ -1,6 +1,6 @@
<?php <?php
class AdminPageTest extends ShimmiePHPUnitTestCase { class AdminPageTest extends ShimmiePHPUnitTestCase {
function testAuth() { public function testAuth() {
$this->get_page('admin'); $this->get_page('admin');
$this->assert_response(403); $this->assert_response(403);
$this->assert_title("Permission Denied"); $this->assert_title("Permission Denied");
@ -16,7 +16,7 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
$this->assert_title("Admin Tools"); $this->assert_title("Admin Tools");
} }
function testLowercase() { public function testLowercase() {
$ts = time(); // we need a tag that hasn't been used before $ts = time(); // we need a tag that hasn't been used before
$this->log_in_as_admin(); $this->log_in_as_admin();
@ -37,7 +37,7 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
} }
# FIXME: make sure the admin tools actually work # FIXME: make sure the admin tools actually work
function testRecount() { public function testRecount() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page('admin'); $this->get_page('admin');
$this->assert_title("Admin Tools"); $this->assert_title("Admin Tools");
@ -46,7 +46,7 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
send_event(new AdminActionEvent('recount_tag_use')); send_event(new AdminActionEvent('recount_tag_use'));
} }
function testDump() { public function testDump() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page('admin'); $this->get_page('admin');
$this->assert_title("Admin Tools"); $this->assert_title("Admin Tools");
@ -57,7 +57,7 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
//$this->assert_response(200); //$this->assert_response(200);
} }
function testDBQ() { public function testDBQ() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test");
$image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2");

View File

@ -1,12 +1,12 @@
<?php <?php
class AliasEditorTest extends ShimmiePHPUnitTestCase { class AliasEditorTest extends ShimmiePHPUnitTestCase {
function testAliasList() { public function testAliasList() {
$this->get_page('alias/list'); $this->get_page('alias/list');
$this->assert_response(200); $this->assert_response(200);
$this->assert_title("Alias List"); $this->assert_title("Alias List");
} }
function testAliasListReadOnly() { public function testAliasListReadOnly() {
// Check that normal users can't add aliases. // Check that normal users can't add aliases.
$this->log_in_as_user(); $this->log_in_as_user();
$this->get_page('alias/list'); $this->get_page('alias/list');
@ -14,7 +14,7 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text("Add"); $this->assert_no_text("Add");
} }
function testAliasEditor() { public function testAliasEditor() {
/* /*
********************************************************************** **********************************************************************
* FIXME: TODO: * FIXME: TODO:
@ -26,6 +26,8 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase {
* dig into this and determine exactly what is happening. * dig into this and determine exactly what is happening.
* *
********************************************************************* *********************************************************************
*/
$this->markTestIncomplete();
$this->log_in_as_admin(); $this->log_in_as_admin();
@ -97,8 +99,6 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase {
$this->get_page('alias/list'); $this->get_page('alias/list');
$this->assert_title("Alias List"); $this->assert_title("Alias List");
$this->assert_no_text("Add"); $this->assert_no_text("Add");
*/
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<?php <?php
class ArtistTest extends ShimmiePHPUnitTestCase { class ArtistTest extends ShimmiePHPUnitTestCase {
function testSearch() { public function testSearch() {
# FIXME: check that the results are there # FIXME: check that the results are there
$this->get_page("post/list/author=bob/1"); $this->get_page("post/list/author=bob/1");
#$this->assert_response(200); #$this->assert_response(200);

View File

@ -77,8 +77,7 @@ class ArtistsTheme extends Themelet {
if($html) $page->add_block(new Block("Manage Artists", $html, "left", 10)); if($html) $page->add_block(new Block("Manage Artists", $html, "left", 10));
} }
public function show_artist_editor($artist, $aliases, $members, $urls) public function show_artist_editor($artist, $aliases, $members, $urls) {
{
global $user; global $user;
$artistName = $artist['name']; $artistName = $artist['name'];
@ -88,8 +87,7 @@ class ArtistsTheme extends Themelet {
// aliases // aliases
$aliasesString = ""; $aliasesString = "";
$aliasesIDsString = ""; $aliasesIDsString = "";
foreach ($aliases as $alias) foreach ($aliases as $alias) {
{
$aliasesString .= $alias["alias_name"]." "; $aliasesString .= $alias["alias_name"]." ";
$aliasesIDsString .= $alias["alias_id"]." "; $aliasesIDsString .= $alias["alias_id"]." ";
} }
@ -99,8 +97,7 @@ class ArtistsTheme extends Themelet {
// members // members
$membersString = ""; $membersString = "";
$membersIDsString = ""; $membersIDsString = "";
foreach ($members as $member) foreach ($members as $member) {
{
$membersString .= $member["name"]." "; $membersString .= $member["name"]." ";
$membersIDsString .= $member["id"]." "; $membersIDsString .= $member["id"]." ";
} }
@ -110,16 +107,14 @@ class ArtistsTheme extends Themelet {
// urls // urls
$urlsString = ""; $urlsString = "";
$urlsIDsString = ""; $urlsIDsString = "";
foreach ($urls as $url) foreach ($urls as $url) {
{
$urlsString .= $url["url"]."\n"; $urlsString .= $url["url"]."\n";
$urlsIDsString .= $url["id"]." "; $urlsIDsString .= $url["id"]." ";
} }
$urlsString = substr($urlsString, 0, strlen($urlsString) -1); $urlsString = substr($urlsString, 0, strlen($urlsString) -1);
$urlsIDsString = rtrim($urlsIDsString); $urlsIDsString = rtrim($urlsIDsString);
$html = $html = '
'
<form method="POST" action="'.make_link("artist/edited/".$artist['id']).'"> <form method="POST" action="'.make_link("artist/edited/".$artist['id']).'">
'.$user->get_auth_html().' '.$user->get_auth_html().'
<table> <table>
@ -135,15 +130,13 @@ class ArtistsTheme extends Themelet {
<tr><td colspan="2"><input type="submit" value="Submit" /></td></tr> <tr><td colspan="2"><input type="submit" value="Submit" /></td></tr>
</table> </table>
</form> </form>
'; ';
global $page; global $page;
$page->add_block(new Block("Edit artist", $html, "main", 10)); $page->add_block(new Block("Edit artist", $html, "main", 10));
} }
public function new_artist_composer() public function new_artist_composer() {
{
global $page, $user; global $page, $user;
$html = "<form action=".make_link("artist/create")." method='POST'> $html = "<form action=".make_link("artist/create")." method='POST'>
@ -163,8 +156,7 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artists", $html, "main", 10)); $page->add_block(new Block("Artists", $html, "main", 10));
} }
public function list_artists($artists, $pageNumber, $totalPages) public function list_artists($artists, $pageNumber, $totalPages) {
{
global $user, $page; global $user, $page;
$html = "<table id='poolsList' class='zebra'>". $html = "<table id='poolsList' class='zebra'>".
@ -174,27 +166,26 @@ class ArtistsTheme extends Themelet {
"<th>Last updater</th>". "<th>Last updater</th>".
"<th>Posts</th>"; "<th>Posts</th>";
if(!$user->is_anonymous()) $html .= "<th colspan='2'>Action</th>"; // space for edit link if(!$user->is_anonymous()) $html .= "<th colspan='2'>Action</th>"; // space for edit link
$html .= "</tr></thead>"; $html .= "</tr></thead>";
$deletionLinkActionArray = $deletionLinkActionArray = array(
array('artist' => 'artist/nuke/' 'artist' => 'artist/nuke/',
, 'alias' => 'artist/alias/delete/' 'alias' => 'artist/alias/delete/',
, 'member' => 'artist/member/delete/' 'member' => 'artist/member/delete/',
); );
$editionLinkActionArray = $editionLinkActionArray = array(
array('artist' => 'artist/edit/' 'artist' => 'artist/edit/',
, 'alias' => 'artist/alias/edit/' 'alias' => 'artist/alias/edit/',
, 'member' => 'artist/member/edit/' 'member' => 'artist/member/edit/',
); );
$typeTextArray = $typeTextArray = array(
array('artist' => 'Artist' 'artist' => 'Artist',
, 'alias' => 'Alias' 'alias' => 'Alias',
, 'member' => 'Member' 'member' => 'Member',
); );
foreach ($artists as $artist) { foreach ($artists as $artist) {
@ -236,12 +227,11 @@ class ArtistsTheme extends Themelet {
$this->display_paginator($page, "artist/list", null, $pageNumber, $totalPages); $this->display_paginator($page, "artist/list", null, $pageNumber, $totalPages);
} }
public function show_new_alias_composer($artistID) public function show_new_alias_composer($artistID) {
{
global $user; global $user;
$html = $html = '
'<form method="POST" action='.make_link("artist/alias/add").'> <form method="POST" action='.make_link("artist/alias/add").'>
'.$user->get_auth_html().' '.$user->get_auth_html().'
<table> <table>
<tr><td>Alias:</td><td><input type="text" name="aliases" /> <tr><td>Alias:</td><td><input type="text" name="aliases" />
@ -254,12 +244,12 @@ class ArtistsTheme extends Themelet {
global $page; global $page;
$page->add_block(new Block("Artist Aliases", $html, "main", 20)); $page->add_block(new Block("Artist Aliases", $html, "main", 20));
} }
public function show_new_member_composer($artistID)
{ public function show_new_member_composer($artistID) {
global $user; global $user;
$html = $html = '
' <form method="POST" action='.make_link("artist/member/add").'> <form method="POST" action='.make_link("artist/member/add").'>
'.$user->get_auth_html().' '.$user->get_auth_html().'
<table> <table>
<tr><td>Members:</td><td><input type="text" name="members" /> <tr><td>Members:</td><td><input type="text" name="members" />
@ -273,12 +263,11 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artist members", $html, "main", 30)); $page->add_block(new Block("Artist members", $html, "main", 30));
} }
public function show_new_url_composer($artistID) public function show_new_url_composer($artistID) {
{
global $user; global $user;
$html = $html = '
' <form method="POST" action='.make_link("artist/url/add").'> <form method="POST" action='.make_link("artist/url/add").'>
'.$user->get_auth_html().' '.$user->get_auth_html().'
<table> <table>
<tr><td>URL:</td><td><textarea name="urls"></textarea> <tr><td>URL:</td><td><textarea name="urls"></textarea>
@ -292,12 +281,10 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artist URLs", $html, "main", 40)); $page->add_block(new Block("Artist URLs", $html, "main", 40));
} }
public function show_alias_editor($alias) public function show_alias_editor($alias) {
{
global $user; global $user;
$html = $html = '
'
<form method="POST" action="'.make_link("artist/alias/edited/".$alias['id']).'"> <form method="POST" action="'.make_link("artist/alias/edited/".$alias['id']).'">
'.$user->get_auth_html().' '.$user->get_auth_html().'
<label for="alias">Alias:</label> <label for="alias">Alias:</label>
@ -311,12 +298,10 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Edit Alias", $html, "main", 10)); $page->add_block(new Block("Edit Alias", $html, "main", 10));
} }
public function show_url_editor($url) public function show_url_editor($url) {
{
global $user; global $user;
$html = $html = '
'
<form method="POST" action="'.make_link("artist/url/edited/".$url['id']).'"> <form method="POST" action="'.make_link("artist/url/edited/".$url['id']).'">
'.$user->get_auth_html().' '.$user->get_auth_html().'
<label for="url">URL:</label> <label for="url">URL:</label>
@ -330,12 +315,10 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Edit URL", $html, "main", 10)); $page->add_block(new Block("Edit URL", $html, "main", 10));
} }
public function show_member_editor($member) public function show_member_editor($member) {
{
global $user; global $user;
$html = $html = '
'
<form method="POST" action="'.make_link("artist/member/edited/".$member['id']).'"> <form method="POST" action="'.make_link("artist/member/edited/".$member['id']).'">
'.$user->get_auth_html().' '.$user->get_auth_html().'
<label for="member">Member name:</label> <label for="member">Member name:</label>
@ -349,8 +332,7 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Edit Member", $html, "main", 10)); $page->add_block(new Block("Edit Member", $html, "main", 10));
} }
public function show_artist($artist, $aliases, $members, $urls, $images, $userIsLogged, $userIsAdmin) public function show_artist($artist, $aliases, $members, $urls, $images, $userIsLogged, $userIsAdmin) {
{
global $page; global $page;
$artist_link = "<a href='".make_link("post/list/".$artist['name']."/1")."'>".str_replace("_", " ", $artist['name'])."</a>"; $artist_link = "<a href='".make_link("post/list/".$artist['name']."/1")."'>".str_replace("_", " ", $artist['name'])."</a>";
@ -361,11 +343,8 @@ class ArtistsTheme extends Themelet {
<th></th> <th></th>
<th></th>"; <th></th>";
if ($userIsLogged) if ($userIsLogged) $html .= "<th></th>";
$html .= "<th></th>"; if ($userIsAdmin) $html .= "<th></th>";
if ($userIsAdmin)
$html .= "<th></th>";
$html .= " <tr> $html .= " <tr>
</thead> </thead>
@ -377,124 +356,11 @@ class ArtistsTheme extends Themelet {
if ($userIsAdmin) $html .= "<td></td>"; if ($userIsAdmin) $html .= "<td></td>";
$html .= "</tr>"; $html .= "</tr>";
if (count($aliases) > 0) $html .= $this->render_aliases($aliases, $userIsLogged, $userIsAdmin);
{ $html .= $this->render_members($members, $userIsLogged, $userIsAdmin);
$aliasViewLink = str_replace("_", " ", $aliases[0]['alias_name']); // no link anymore $html .= $this->render_urls($urls, $userIsLogged, $userIsAdmin);
$aliasEditLink = "<a href='".make_link("artist/alias/edit/".$aliases[0]['alias_id'])."'>Edit</a>";
$aliasDeleteLink = "<a href='".make_link("artist/alias/delete/".$aliases[0]['alias_id'])."'>Delete</a>";
$html .= "<tr> $html .= "<tr>
<td class='left'>Aliases:</td>
<td class='left'>".$aliasViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$aliasEditLink."</td>";
if ($userIsAdmin)
$html .= "<td class='left'>".$aliasDeleteLink."</td>";
$html .= "</tr>";
if (count($aliases) > 1)
{
for ($i = 1; $i < count($aliases); $i++)
{
$aliasViewLink = str_replace("_", " ", $aliases[$i]['alias_name']); // no link anymore
$aliasEditLink = "<a href='".make_link("artist/alias/edit/".$aliases[$i]['alias_id'])."'>Edit</a>";
$aliasDeleteLink = "<a href='".make_link("artist/alias/delete/".$aliases[$i]['alias_id'])."'>Delete</a>";
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>".$aliasViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$aliasEditLink."</td>";
if ($userIsAdmin)
$html .= "<td class='left'>".$aliasDeleteLink."</td>";
$html .= "</tr>";
}
}
}
if (count($members) > 0)
{
$memberViewLink = str_replace("_", " ", $members[0]['name']); // no link anymore
$memberEditLink = "<a href='".make_link("artist/member/edit/".$members[0]['id'])."'>Edit</a>";
$memberDeleteLink = "<a href='".make_link("artist/member/delete/".$members[0]['id'])."'>Delete</a>";
$html .= "<tr>
<td class='left'>Members:</td>
<td class='left'>".$memberViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$memberEditLink."</td>";
if ($userIsAdmin)
$html .= "<td class='left'>".$memberDeleteLink."</td>";
$html .= "</tr>";
if (count($members) > 1)
{
for ($i = 1; $i < count($members); $i++)
{
$memberViewLink = str_replace("_", " ", $members[$i]['name']); // no link anymore
$memberEditLink = "<a href='".make_link("artist/member/edit/".$members[$i]['id'])."'>Edit</a>";
$memberDeleteLink = "<a href='".make_link("artist/member/delete/".$members[$i]['id'])."'>Delete</a>";
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>".$memberViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$memberEditLink."</td>";
if ($userIsAdmin)
$html .= "<td class='left'>".$memberDeleteLink."</td>";
$html .= "</tr>";
}
}
}
if (count($urls) > 0)
{
$urlViewLink = "<a href='".str_replace("_", " ", $urls[0]['url'])."' target='_blank'>".str_replace("_", " ", $urls[0]['url'])."</a>";
$urlEditLink = "<a href='".make_link("artist/url/edit/".$urls[0]['id'])."'>Edit</a>";
$urlDeleteLink = "<a href='".make_link("artist/url/delete/".$urls[0]['id'])."'>Delete</a>";
$html .= "<tr>
<td class='left'>URLs:</td>
<td class='left'>".$urlViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$urlEditLink."</td>";
if ($userIsAdmin)
$html .= "<td class='left'>".$urlDeleteLink."</td>";
$html .= "</tr>";
if (count($urls) > 1)
{
for ($i = 1; $i < count($urls); $i++)
{
$urlViewLink = "<a href='".str_replace("_", " ", $urls[$i]['url'])."' target='_blank'>".str_replace("_", " ", $urls[$i]['url'])."</a>";
$urlEditLink = "<a href='".make_link("artist/url/edit/".$urls[$i]['id'])."'>Edit</a>";
$urlDeleteLink = "<a href='".make_link("artist/url/delete/".$urls[$i]['id'])."'>Delete</a>";
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>".$urlViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$urlEditLink."</td>";
if ($userIsAdmin)
$html .= "<td class='left'>".$urlDeleteLink."</td>";
$html .= "</tr>";
}
}
}
$html .=
"<tr>
<td class='left'>Notes:</td> <td class='left'>Notes:</td>
<td class='left'>".$artist["notes"]."</td>"; <td class='left'>".$artist["notes"]."</td>";
if ($userIsLogged) $html .= "<td></td>"; if ($userIsLogged) $html .= "<td></td>";
@ -511,7 +377,6 @@ class ArtistsTheme extends Themelet {
//we show the images for the artist //we show the images for the artist
$artist_images = ""; $artist_images = "";
foreach($images as $image) { foreach($images as $image) {
$thumb_html = $this->build_thumb_html($image); $thumb_html = $this->build_thumb_html($image);
$artist_images .= '<span class="thumb">'. $artist_images .= '<span class="thumb">'.
@ -522,5 +387,143 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artist Images", $artist_images, "main", 20)); $page->add_block(new Block("Artist Images", $artist_images, "main", 20));
} }
/**
* @param $aliases
* @param $userIsLogged
* @param $userIsAdmin
* @return string
*/
private function render_aliases($aliases, $userIsLogged, $userIsAdmin) {
$html = "";
if(count($aliases) > 0) {
$aliasViewLink = str_replace("_", " ", $aliases[0]['alias_name']); // no link anymore
$aliasEditLink = "<a href='" . make_link("artist/alias/edit/" . $aliases[0]['alias_id']) . "'>Edit</a>";
$aliasDeleteLink = "<a href='" . make_link("artist/alias/delete/" . $aliases[0]['alias_id']) . "'>Delete</a>";
$html .= "<tr>
<td class='left'>Aliases:</td>
<td class='left'>" . $aliasViewLink . "</td>";
if ($userIsLogged)
$html .= "<td class='left'>" . $aliasEditLink . "</td>";
if ($userIsAdmin)
$html .= "<td class='left'>" . $aliasDeleteLink . "</td>";
$html .= "</tr>";
if (count($aliases) > 1) {
for ($i = 1; $i < count($aliases); $i++) {
$aliasViewLink = str_replace("_", " ", $aliases[$i]['alias_name']); // no link anymore
$aliasEditLink = "<a href='" . make_link("artist/alias/edit/" . $aliases[$i]['alias_id']) . "'>Edit</a>";
$aliasDeleteLink = "<a href='" . make_link("artist/alias/delete/" . $aliases[$i]['alias_id']) . "'>Delete</a>";
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>" . $aliasViewLink . "</td>";
if ($userIsLogged)
$html .= "<td class='left'>" . $aliasEditLink . "</td>";
if ($userIsAdmin)
$html .= "<td class='left'>" . $aliasDeleteLink . "</td>";
$html .= "</tr>";
}
}
}
return $html;
}
/**
* @param $members
* @param $userIsLogged
* @param $userIsAdmin
* @return string
*/
private function render_members($members, $userIsLogged, $userIsAdmin) {
$html = "";
if(count($members) > 0) {
$memberViewLink = str_replace("_", " ", $members[0]['name']); // no link anymore
$memberEditLink = "<a href='" . make_link("artist/member/edit/" . $members[0]['id']) . "'>Edit</a>";
$memberDeleteLink = "<a href='" . make_link("artist/member/delete/" . $members[0]['id']) . "'>Delete</a>";
$html .= "<tr>
<td class='left'>Members:</td>
<td class='left'>" . $memberViewLink . "</td>";
if ($userIsLogged)
$html .= "<td class='left'>" . $memberEditLink . "</td>";
if ($userIsAdmin)
$html .= "<td class='left'>" . $memberDeleteLink . "</td>";
$html .= "</tr>";
if (count($members) > 1) {
for ($i = 1; $i < count($members); $i++) {
$memberViewLink = str_replace("_", " ", $members[$i]['name']); // no link anymore
$memberEditLink = "<a href='" . make_link("artist/member/edit/" . $members[$i]['id']) . "'>Edit</a>";
$memberDeleteLink = "<a href='" . make_link("artist/member/delete/" . $members[$i]['id']) . "'>Delete</a>";
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>" . $memberViewLink . "</td>";
if ($userIsLogged)
$html .= "<td class='left'>" . $memberEditLink . "</td>";
if ($userIsAdmin)
$html .= "<td class='left'>" . $memberDeleteLink . "</td>";
$html .= "</tr>";
}
}
}
return $html;
}
/**
* @param $urls
* @param $userIsLogged
* @param $userIsAdmin
* @return string
*/
private function render_urls($urls, $userIsLogged, $userIsAdmin) {
$html = "";
if(count($urls) > 0) {
$urlViewLink = "<a href='" . str_replace("_", " ", $urls[0]['url']) . "' target='_blank'>" . str_replace("_", " ", $urls[0]['url']) . "</a>";
$urlEditLink = "<a href='" . make_link("artist/url/edit/" . $urls[0]['id']) . "'>Edit</a>";
$urlDeleteLink = "<a href='" . make_link("artist/url/delete/" . $urls[0]['id']) . "'>Delete</a>";
$html .= "<tr>
<td class='left'>URLs:</td>
<td class='left'>" . $urlViewLink . "</td>";
if ($userIsLogged)
$html .= "<td class='left'>" . $urlEditLink . "</td>";
if ($userIsAdmin)
$html .= "<td class='left'>" . $urlDeleteLink . "</td>";
$html .= "</tr>";
if (count($urls) > 1) {
for ($i = 1; $i < count($urls); $i++) {
$urlViewLink = "<a href='" . str_replace("_", " ", $urls[$i]['url']) . "' target='_blank'>" . str_replace("_", " ", $urls[$i]['url']) . "</a>";
$urlEditLink = "<a href='" . make_link("artist/url/edit/" . $urls[$i]['id']) . "'>Edit</a>";
$urlDeleteLink = "<a href='" . make_link("artist/url/delete/" . $urls[$i]['id']) . "'>Delete</a>";
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>" . $urlViewLink . "</td>";
if ($userIsLogged)
$html .= "<td class='left'>" . $urlEditLink . "</td>";
if ($userIsAdmin)
$html .= "<td class='left'>" . $urlDeleteLink . "</td>";
$html .= "</tr>";
}
return $html;
}
}
return $html;
}
} }

View File

@ -1,6 +1,6 @@
<?php <?php
class BanWordsTest extends ShimmiePHPUnitTestCase { class BanWordsTest extends ShimmiePHPUnitTestCase {
function check_blocked($image_id, $words) { public function check_blocked($image_id, $words) {
global $user; global $user;
try { try {
send_event(new CommentPostingEvent($image_id, $user, $words)); send_event(new CommentPostingEvent($image_id, $user, $words));
@ -11,7 +11,7 @@ class BanWordsTest extends ShimmiePHPUnitTestCase {
} }
} }
function testWordBan() { public function testWordBan() {
global $config; global $config;
$config->set_string("banned_words", "viagra\nporn\n\n/http:.*\.cn\//"); $config->set_string("banned_words", "viagra\nporn\n\n/http:.*\.cn\//");

View File

@ -93,7 +93,7 @@ class BBCode extends FormatterExtension {
/** /**
* @param string $text * @param string $text
* @return mixed * @return string
*/ */
private function filter_spoiler(/*string*/ $text) { private function filter_spoiler(/*string*/ $text) {
return str_replace( return str_replace(

View File

@ -1,6 +1,6 @@
<?php <?php
class BlocksTest extends ShimmiePHPUnitTestCase { class BlocksTest extends ShimmiePHPUnitTestCase {
function testBlocks() { public function testBlocks() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("blocks/list"); $this->get_page("blocks/list");
$this->assert_response(200); $this->assert_response(200);

View File

@ -1,13 +1,13 @@
<?php <?php
class BlotterTest extends ShimmiePHPUnitTestCase { class BlotterTest extends ShimmiePHPUnitTestCase {
function testLogin() { public function testLogin() {
$this->log_in_as_admin(); $this->log_in_as_admin();
//$this->assert_text("Blotter Editor"); //$this->assert_text("Blotter Editor");
//$this->click("Blotter Editor"); //$this->click("Blotter Editor");
//$this->log_out(); //$this->log_out();
} }
function testDenial() { public function testDenial() {
$this->get_page("blotter/editor"); $this->get_page("blotter/editor");
$this->assert_response(403); $this->assert_response(403);
$this->get_page("blotter/add"); $this->get_page("blotter/add");
@ -16,7 +16,7 @@ class BlotterTest extends ShimmiePHPUnitTestCase {
$this->assert_response(403); $this->assert_response(403);
} }
function testAddViewRemove() { public function testAddViewRemove() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("blotter/editor"); $this->get_page("blotter/editor");

View File

@ -176,4 +176,3 @@ class BlotterTheme extends Themelet {
return $html; return $html;
} }
} }

View File

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

View File

@ -1,6 +1,6 @@
<?php <?php
class BrowserSearchTest extends ShimmiePHPUnitTestCase { class BrowserSearchTest extends ShimmiePHPUnitTestCase {
function testBasic() { public function testBasic() {
$this->get_page("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml"); $this->get_page("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml");
$this->get_page("browser_search/test"); $this->get_page("browser_search/test");
} }

View File

@ -1,6 +1,6 @@
<?php <?php
class BulkAddTest extends ShimmiePHPUnitTestCase { class BulkAddTest extends ShimmiePHPUnitTestCase {
function testBulkAdd() { public function testBulkAdd() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page('admin'); $this->get_page('admin');
@ -11,7 +11,8 @@ class BulkAddTest extends ShimmiePHPUnitTestCase {
$this->assertContains("Error, asdf is not a readable directory", $this->assertContains("Error, asdf is not a readable directory",
$bae->results, implode("\n", $bae->results)); $bae->results, implode("\n", $bae->results));
return; // FIXME: have BAE return a list of successes as well as errors? // FIXME: have BAE return a list of successes as well as errors?
$this->markTestIncomplete();
$this->get_page('admin'); $this->get_page('admin');
$this->assert_title("Admin Tools"); $this->assert_title("Admin Tools");

View File

@ -54,6 +54,14 @@ class BulkAddCSV extends Extension {
/** /**
* Generate the necessary DataUploadEvent for a given image and tags. * Generate the necessary DataUploadEvent for a given image and tags.
*
* @param string $tmpname
* @param string $filename
* @param string $tags
* @param string $source
* @param string $rating
* @param string $thumbfile
* @throws UploadException
*/ */
private function add_image($tmpname, $filename, $tags, $source, $rating, $thumbfile) { private function add_image($tmpname, $filename, $tags, $source, $rating, $thumbfile) {
assert(file_exists($tmpname)); assert(file_exists($tmpname));

View File

@ -71,12 +71,16 @@ class Comment {
} }
/** /**
* @param \User $user * @param User $user
* @return mixed * @return mixed
*/ */
public static function count_comments_by_user($user) { public static function count_comments_by_user($user) {
global $database; global $database;
return $database->get_one("SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id", array("owner_id"=>$user->id)); return $database->get_one("
SELECT COUNT(*) AS count
FROM comments
WHERE owner_id=:owner_id
", array("owner_id"=>$user->id));
} }
/** /**
@ -89,6 +93,9 @@ class Comment {
} }
class CommentList extends Extension { class CommentList extends Extension {
/** @var CommentListTheme $theme */
var $theme;
public function onInitExt(InitExtEvent $event) { public function onInitExt(InitExtEvent $event) {
global $config, $database; global $config, $database;
$config->set_default_int('comment_window', 5); $config->set_default_int('comment_window', 5);
@ -147,9 +154,19 @@ class CommentList extends Extension {
} }
public function onPageRequest(PageRequestEvent $event) { public function onPageRequest(PageRequestEvent $event) {
global $page, $user, $database;
if($event->page_matches("comment")) { if($event->page_matches("comment")) {
if($event->get_arg(0) === "add") { switch($event->get_arg(0)) {
case "add": $this->onPageRequest_add(); break;
case "delete": $this->onPageRequest_delete($event); break;
case "bulk_delete": $this->onPageRequest_bulk_delete(); break;
case "list": $this->onPageRequest_list($event); break;
case "beta-search": $this->onPageRequest_beta_search($event); break;
}
}
}
private function onPageRequest_add() {
global $user, $page;
if (isset($_POST['image_id']) && isset($_POST['comment'])) { if (isset($_POST['image_id']) && isset($_POST['comment'])) {
try { try {
$i_iid = int_escape($_POST['image_id']); $i_iid = int_escape($_POST['image_id']);
@ -157,13 +174,14 @@ class CommentList extends Extension {
send_event($cpe); send_event($cpe);
$page->set_mode("redirect"); $page->set_mode("redirect");
$page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid")); $page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid"));
} } catch (CommentPostingException $ex) {
catch(CommentPostingException $ex) {
$this->theme->display_error(403, "Comment Blocked", $ex->getMessage()); $this->theme->display_error(403, "Comment Blocked", $ex->getMessage());
} }
} }
} }
else if($event->get_arg(0) === "delete") {
private function onPageRequest_delete(PageRequestEvent $event) {
global $user, $page;
if ($user->can("delete_comment")) { if ($user->can("delete_comment")) {
// FIXME: post, not args // FIXME: post, not args
if ($event->count_args() === 3) { if ($event->count_args() === 3) {
@ -172,52 +190,55 @@ class CommentList extends Extension {
$page->set_mode("redirect"); $page->set_mode("redirect");
if (!empty($_SERVER['HTTP_REFERER'])) { if (!empty($_SERVER['HTTP_REFERER'])) {
$page->set_redirect($_SERVER['HTTP_REFERER']); $page->set_redirect($_SERVER['HTTP_REFERER']);
} } else {
else {
$page->set_redirect(make_link("post/view/" . $event->get_arg(2))); $page->set_redirect(make_link("post/view/" . $event->get_arg(2)));
} }
} }
} } else {
else {
$this->theme->display_permission_denied(); $this->theme->display_permission_denied();
} }
} }
else if($event->get_arg(0) === "bulk_delete") {
private function onPageRequest_bulk_delete() {
global $user, $database, $page;
if ($user->can("delete_comment") && !empty($_POST["ip"])) { if ($user->can("delete_comment") && !empty($_POST["ip"])) {
$ip = $_POST['ip']; $ip = $_POST['ip'];
$cids = $database->get_col("SELECT id FROM comments WHERE owner_ip=:ip", array("ip"=>$ip)); $comment_ids = $database->get_col("
$num = count($cids); SELECT id
FROM comments
WHERE owner_ip=:ip
", array("ip" => $ip));
$num = count($comment_ids);
log_warning("comment", "Deleting $num comments from $ip"); log_warning("comment", "Deleting $num comments from $ip");
foreach($cids as $cid) { foreach($comment_ids as $cid) {
send_event(new CommentDeletionEvent($cid)); send_event(new CommentDeletionEvent($cid));
} }
flash_message("Deleted $num comments"); flash_message("Deleted $num comments");
$page->set_mode("redirect"); $page->set_mode("redirect");
$page->set_redirect(make_link("admin")); $page->set_redirect(make_link("admin"));
} } else {
else {
$this->theme->display_permission_denied(); $this->theme->display_permission_denied();
} }
} }
else if($event->get_arg(0) === "list") {
private function onPageRequest_list(PageRequestEvent $event) {
$page_num = int_escape($event->get_arg(1)); $page_num = int_escape($event->get_arg(1));
$this->build_page($page_num); $this->build_page($page_num);
} }
else if($event->get_arg(0) === "beta-search") {
private function onPageRequest_beta_search(PageRequestEvent $event) {
$search = $event->get_arg(1); $search = $event->get_arg(1);
$page_num = int_escape($event->get_arg(2)); $page_num = int_escape($event->get_arg(2));
$duser = User::by_name($search); $duser = User::by_name($search);
$i_comment_count = Comment::count_comments_by_user($duser); $i_comment_count = Comment::count_comments_by_user($duser);
$com_per_page = 50; $com_per_page = 50;
$total_pages = ceil($i_comment_count / $com_per_page); $total_pages = ceil($i_comment_count / $com_per_page);
$page_num = $this->sanity_check_pagenumber($page_num, $total_pages); $page_num = clamp($page_num, 1, $total_pages);
$comments = $this->get_user_comments($duser->id, $com_per_page, ($page_num - 1) * $com_per_page); $comments = $this->get_user_comments($duser->id, $com_per_page, ($page_num - 1) * $com_per_page);
$this->theme->display_all_user_comments($comments, $page_num, $total_pages, $duser); $this->theme->display_all_user_comments($comments, $page_num, $total_pages, $duser);
} }
}
}
public function onAdminBuilding(AdminBuildingEvent $event) { public function onAdminBuilding(AdminBuildingEvent $event) {
$this->theme->display_admin_block(); $this->theme->display_admin_block();
@ -264,7 +285,10 @@ class CommentList extends Extension {
public function onCommentDeletion(CommentDeletionEvent $event) { public function onCommentDeletion(CommentDeletionEvent $event) {
global $database; global $database;
$database->Execute("DELETE FROM comments WHERE id=:comment_id", array("comment_id"=>$event->comment_id)); $database->Execute("
DELETE FROM comments
WHERE id=:comment_id
", array("comment_id"=>$event->comment_id));
log_info("comment", "Deleting Comment #{$event->comment_id}"); log_info("comment", "Deleting Comment #{$event->comment_id}");
} }
@ -328,39 +352,33 @@ class CommentList extends Extension {
") / 10); ") / 10);
$database->cache->set("comment_pages", $total_pages, 600); $database->cache->set("comment_pages", $total_pages, 600);
} }
$total_pages = max($total_pages, 1);
if(is_null($current_page) || $current_page <= 0) { $current_page = clamp($current_page, 1, $total_pages);
$current_page = 1;
}
$current_page = $this->sanity_check_pagenumber($current_page, $total_pages);
$threads_per_page = 10; $threads_per_page = 10;
$start = $threads_per_page * ($current_page - 1); $start = $threads_per_page * ($current_page - 1);
$get_threads = " $result = $database->Execute("
SELECT image_id,MAX(posted) AS latest SELECT image_id,MAX(posted) AS latest
FROM comments FROM comments
$where $where
GROUP BY image_id GROUP BY image_id
ORDER BY latest DESC ORDER BY latest DESC
LIMIT :limit OFFSET :offset LIMIT :limit OFFSET :offset
"; ", array("limit"=>$threads_per_page, "offset"=>$start));
$result = $database->Execute($get_threads, array("limit"=>$threads_per_page, "offset"=>$start));
if(ext_is_live("Ratings")) { $user_ratings = ext_is_live("Ratings") ? Ratings::get_user_privs($user) : "";
$user_ratings = Ratings::get_user_privs($user);
} else {
$user_ratings = "";
}
$images = array(); $images = array();
while($row = $result->fetch()) { while($row = $result->fetch()) {
$image = Image::by_id($row["image_id"]); $image = Image::by_id($row["image_id"]);
if(ext_is_live("Ratings") && !is_null($image)) { if(
if(strpos($user_ratings, $image->rating) === FALSE) { ext_is_live("Ratings") && !is_null($image) &&
strpos($user_ratings, $image->rating) === FALSE
) {
$image = null; // this is "clever", I may live to regret it $image = null; // this is "clever", I may live to regret it
} }
}
if(!is_null($image)) { if(!is_null($image)) {
$comments = $this->get_comments($image->id); $comments = $this->get_comments($image->id);
$images[] = array($image, $comments); $images[] = array($image, $comments);
@ -372,13 +390,27 @@ class CommentList extends Extension {
// }}} // }}}
// get comments {{{ // get comments {{{
/**
* @param string $query
* @param array $args
* @return Comment[]
*/
private function get_generic_comments($query, $args) {
global $database;
$rows = $database->get_all($query, $args);
$comments = array();
foreach($rows as $row) {
$comments[] = new Comment($row);
}
return $comments;
}
/** /**
* @param int $count * @param int $count
* @return array * @return Comment[]
*/ */
private function get_recent_comments($count) { private function get_recent_comments($count) {
global $database; return $this->get_generic_comments("
$rows = $database->get_all("
SELECT SELECT
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
comments.comment as comment, comments.id as comment_id, comments.comment as comment, comments.id as comment_id,
@ -389,22 +421,16 @@ class CommentList extends Extension {
ORDER BY comments.id DESC ORDER BY comments.id DESC
LIMIT :limit LIMIT :limit
", array("limit"=>$count)); ", array("limit"=>$count));
$comments = array();
foreach($rows as $row) {
$comments[] = new Comment($row);
}
return $comments;
} }
/** /**
* @param int $user_id * @param int $user_id
* @param int $count * @param int $count
* @param int $offset * @param int $offset
* @return array * @return Comment[]
*/ */
private function get_user_comments(/*int*/ $user_id, /*int*/ $count, /*int*/ $offset=0) { private function get_user_comments(/*int*/ $user_id, /*int*/ $count, /*int*/ $offset=0) {
global $database; return $this->get_generic_comments("
$rows = $database->get_all("
SELECT SELECT
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
comments.comment as comment, comments.id as comment_id, comments.comment as comment, comments.id as comment_id,
@ -416,21 +442,14 @@ class CommentList extends Extension {
ORDER BY comments.id DESC ORDER BY comments.id DESC
LIMIT :limit OFFSET :offset LIMIT :limit OFFSET :offset
", array("user_id"=>$user_id, "offset"=>$offset, "limit"=>$count)); ", array("user_id"=>$user_id, "offset"=>$offset, "limit"=>$count));
$comments = array();
foreach($rows as $row) {
$comments[] = new Comment($row);
}
return $comments;
} }
/** /**
* @param int $image_id * @param int $image_id
* @return array * @return Comment[]
*/ */
private function get_comments(/*int*/ $image_id) { private function get_comments(/*int*/ $image_id) {
global $database; return $this->get_generic_comments("
$i_image_id = int_escape($image_id);
$rows = $database->get_all("
SELECT SELECT
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
comments.comment as comment, comments.id as comment_id, comments.comment as comment, comments.id as comment_id,
@ -440,16 +459,14 @@ class CommentList extends Extension {
LEFT JOIN users ON comments.owner_id=users.id LEFT JOIN users ON comments.owner_id=users.id
WHERE comments.image_id=:image_id WHERE comments.image_id=:image_id
ORDER BY comments.id ASC ORDER BY comments.id ASC
", array("image_id"=>$i_image_id)); ", array("image_id"=>$image_id));
$comments = array();
foreach($rows as $row) {
$comments[] = new Comment($row);
}
return $comments;
} }
// }}} // }}}
// add / remove / edit comments {{{ // add / remove / edit comments {{{
/**
* @return bool
*/
private function is_comment_limit_hit() { private function is_comment_limit_hit() {
global $config, $database; global $config, $database;
@ -463,9 +480,11 @@ class CommentList extends Extension {
else $window_sql = "interval '$window minute'"; else $window_sql = "interval '$window minute'";
// window doesn't work as an SQL param because it's inside quotes >_< // window doesn't work as an SQL param because it's inside quotes >_<
$result = $database->get_all("SELECT * FROM comments WHERE owner_ip = :remote_ip ". $result = $database->get_all("
"AND posted > now() - $window_sql", SELECT *
Array("remote_ip"=>$_SERVER['REMOTE_ADDR'])); FROM comments
WHERE owner_ip = :remote_ip AND posted > now() - $window_sql
", array("remote_ip"=>$_SERVER['REMOTE_ADDR']));
return (count($result) >= $max); return (count($result) >= $max);
} }
@ -483,6 +502,8 @@ class CommentList extends Extension {
* many times. * many times.
* *
* FIXME: assumes comments are posted via HTTP... * FIXME: assumes comments are posted via HTTP...
*
* @return string
*/ */
public static function get_hash() { public static function get_hash() {
return md5($_SERVER['REMOTE_ADDR'] . date("%Y%m%d")); return md5($_SERVER['REMOTE_ADDR'] . date("%Y%m%d"));
@ -537,28 +558,14 @@ class CommentList extends Extension {
*/ */
private function is_dupe(/*int*/ $image_id, /*string*/ $comment) { private function is_dupe(/*int*/ $image_id, /*string*/ $comment) {
global $database; global $database;
return ($database->get_row("SELECT * FROM comments WHERE image_id=:image_id AND comment=:comment", array("image_id"=>$image_id, "comment"=>$comment))); return $database->get_row("
SELECT *
FROM comments
WHERE image_id=:image_id AND comment=:comment
", array("image_id"=>$image_id, "comment"=>$comment));
} }
// do some checks // do some checks
/**
* @param int $pagenum
* @param int $maxpage
* @return int
*/
private function sanity_check_pagenumber(/*int*/ $pagenum, /*int*/ $maxpage){
if (!is_numeric($pagenum)){
$pagenum=1;
}
if ($pagenum>$maxpage){
$pagenum=$maxpage;
}
if ($pagenum<=0){
$pagenum=1;
}
return $pagenum;
}
/** /**
* @param int $image_id * @param int $image_id
* @param User $user * @param User $user
@ -588,6 +595,12 @@ class CommentList extends Extension {
log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", false, array("image_id"=>$image_id, "comment_id"=>$cid)); log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", false, array("image_id"=>$image_id, "comment_id"=>$cid));
} }
/**
* @param int $image_id
* @param User $user
* @param string $comment
* @throws CommentPostingException
*/
private function comment_checks(/*int*/ $image_id, User $user, /*string*/ $comment) { private function comment_checks(/*int*/ $image_id, User $user, /*string*/ $comment) {
global $config, $page; global $config, $page;

View File

@ -1,19 +1,19 @@
<?php <?php
class CommentListTest extends ShimmiePHPUnitTestCase { class CommentListTest extends ShimmiePHPUnitTestCase {
function setUp() { public function setUp() {
global $config; global $config;
parent::setUp(); parent::setUp();
$config->set_int("comment_limit", 100); $config->set_int("comment_limit", 100);
$this->log_out(); $this->log_out();
} }
function tearDown() { public function tearDown() {
global $config; global $config;
$config->set_int("comment_limit", 10); $config->set_int("comment_limit", 10);
parent::tearDown(); parent::tearDown();
} }
function testCommentsPage() { public function testCommentsPage() {
global $user; global $user;
$this->log_in_as_user(); $this->log_in_as_user();
@ -85,8 +85,9 @@ class CommentListTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text('ASDFASDF'); $this->assert_no_text('ASDFASDF');
} }
/* public function testSingleDel() {
function testSingleDel() { $this->markTestIncomplete();
$this->log_in_as_admin(); $this->log_in_as_admin();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
@ -106,5 +107,4 @@ class CommentListTest extends ShimmiePHPUnitTestCase {
$this->delete_image($image_id); $this->delete_image($image_id);
$this->log_out(); $this->log_out();
} }
*/
} }

View File

@ -55,232 +55,162 @@ class DanbooruApi extends Extension {
} }
// Danbooru API // Danbooru API
private function api_danbooru(PageRequestEvent $event) private function api_danbooru(PageRequestEvent $event) {
{
global $page; global $page;
global $config;
global $database;
global $user;
$page->set_mode("data"); $page->set_mode("data");
$page->set_type("application/xml");
//debug
//$page->set_type("text/plain");
$results = array(); if(($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) {
$danboorup_kludge=1; // danboorup for firefox makes broken links out of location: /path
/*
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 // No XML data is returned from this function
$page->set_type("text/plain"); $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 $this->api_add_post();
// 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($user->can("create_image"))
{
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) {
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: fopen read error");
} }
$data = ""; elseif(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) {
$length = 0; $page->set_type("application/xml");
while(!feof($fp) && $length <= $config->get_int('upload_size')) $page->set_data($this->api_find_posts());
{
$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") elseif($event->get_arg(1) == 'find_tags') {
{ $page->set_type("application/xml");
$ch = curl_init($url); $page->set_data($this->api_find_tags());
$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
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: no input files");
return;
} }
// Get tags out of url // Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper
$posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']); // Shimmie view page
$hash = md5_file($file); // Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123
// Was an md5 supplied? Does it match the file hash? // This redirects that to http://shimmie/post/view/123
if(isset($_REQUEST['md5'])) elseif(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) {
{ $fixedlocation = make_link("post/view/" . $event->get_arg(3));
if(strtolower($_REQUEST['md5']) != $hash) $page->set_mode("redirect");
{ $page->set_redirect($fixedlocation);
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: md5 mismatch");
return;
}
}
// Upload size checking is now performed in the upload extension
// It is also currently broken due to some confusion over file variable ($tmp_filename?)
// Does it exist already?
$existing = Image::by_hash($hash);
if(!is_null($existing)) {
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: duplicate");
$existinglink = make_link("post/view/" . $existing->id);
if($danboorup_kludge) $existinglink=make_http($existinglink);
$page->add_http_header("X-Danbooru-Location: $existinglink");
return; // wut!
}
// Fire off an event which should process the new file and add it to the db
$fileinfo = pathinfo($filename);
$metadata = array();
$metadata['filename'] = $fileinfo['basename'];
$metadata['extension'] = $fileinfo['extension'];
$metadata['tags'] = $posttags;
$metadata['source'] = $source;
//log_debug("danbooru_api","========== NEW($filename) =========");
//log_debug("danbooru_api", "upload($filename): fileinfo(".var_export($fileinfo,TRUE)."), metadata(".var_export($metadata,TRUE).")...");
try {
$nevent = new DataUploadEvent($file, $metadata);
//log_debug("danbooru_api", "send_event(".var_export($nevent,TRUE).")");
send_event($nevent);
// If it went ok, grab the id for the newly uploaded image and pass it in the header
$newimg = Image::by_hash($hash); // FIXME: Unsupported file doesn't throw an error?
$newid = make_link("post/view/" . $newimg->id);
if($danboorup_kludge) $newid=make_http($newid);
// Did we POST or GET this call?
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$page->add_http_header("X-Danbooru-Location: $newid");
}
else
$page->add_http_header("Location: $newid");
}
catch(UploadException $ex) {
// Did something screw up?
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: exception - " . $ex->getMessage());
return;
}
} else
{
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: authentication error");
return;
} }
} }
/* /**
find_posts() * Turns out I use this a couple times so let's make it a utility function
Find all posts that match the search criteria. Posts will be ordered by id descending. * Authenticates a user based on the contents of the login and password parameters
Parameters * or makes them anonymous. Does not set any cookies or anything permanent.
* md5: md5 hash to search for (comma delimited)
* id: id to search for (comma delimited)
* tags: what tags to search for
* limit: limit
* page: page number
* 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'))) private function authenticate_user() {
{ global $config, $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'];
$duser = User::by_name_and_pass($name, $pass);
if(!is_null($duser)) {
$user = $duser;
}
else {
$user = User::by_id($config->get_int("anon_id", 0));
}
}
}
/**
* 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
*
* @return string
*/
private function api_find_tags() {
global $database;
$results = array();
if(isset($_GET['id'])) {
$idlist = explode(",", $_GET['id']);
foreach ($idlist as $id) {
$sqlresult = $database->get_all(
"SELECT id,tag,count FROM tags WHERE id = ?",
array($id));
foreach ($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
}
}
}
elseif(isset($_GET['name'])) {
$namelist = explode(",", $_GET['name']);
foreach ($namelist as $name) {
$sqlresult = $database->get_all(
"SELECT id,tag,count FROM tags WHERE tag = ?",
array($name));
foreach ($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
}
}
}
// Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags
elseif(false && 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->get_all(
"SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC",
array($start));
foreach ($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
}
}
// Tag results collected, build XML output
$xml = "<tags>\n";
foreach ($results as $tag) {
$xml .= xml_tag("tag", array(
"type" => "0",
"counts" => $tag[0],
"name" => $tag[1],
"id" => $tag[2],
));
}
$xml .= "</tags>";
return $xml;
}
/**
* 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
* - page: page number
* - after_id: limit results to posts added after this id
*
* @return string
* @throws SCoreException
*/
private function api_find_posts() {
$results = array();
$this->authenticate_user(); $this->authenticate_user();
$start = 0; $start = 0;
if(isset($_GET['md5'])) if(isset($_GET['md5'])) {
{
$md5list = explode(",", $_GET['md5']); $md5list = explode(",", $_GET['md5']);
foreach($md5list as $md5) foreach ($md5list as $md5) {
{
$results[] = Image::by_hash($md5); $results[] = Image::by_hash($md5);
} }
$count = count($results); $count = count($results);
} elseif(isset($_GET['id'])) }
{ elseif(isset($_GET['id'])) {
$idlist = explode(",", $_GET['id']); $idlist = explode(",", $_GET['id']);
foreach($idlist as $id) foreach ($idlist as $id) {
{
$results[] = Image::by_id($id); $results[] = Image::by_id($id);
} }
$count = count($results); $count = count($results);
} else }
{ else {
$limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100; $limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100;
// Calculate start offset. // Calculate start offset.
@ -299,8 +229,7 @@ class DanbooruApi extends Extension {
// Now we have the array $results filled with Image objects // Now we have the array $results filled with Image objects
// Let's display them // Let's display them
$xml = "<posts count=\"{$count}\" offset=\"{$start}\">\n"; $xml = "<posts count=\"{$count}\" offset=\"{$start}\">\n";
foreach($results as $img) foreach ($results as $img) {
{
// Sanity check to see if $img is really an image object // 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 it isn't (e.g. someone requested an invalid md5 or id), break out of the this
if (!is_object($img)) if (!is_object($img))
@ -328,107 +257,140 @@ class DanbooruApi extends Extension {
)); ));
} }
$xml .= "</posts>"; $xml .= "</posts>";
$page->set_data($xml); return $xml;
} }
/* /**
find_tags() Find all tags that match the search criteria. * add_post()
Parameters * Adds a post to the database.
* id: A comma delimited list of tag id numbers. *
* name: A comma delimited list of tag names. * Parameters:
* tags: any typical tag query. See Tag#parse_query for details. * - login: login
* after_id: limit results to tags with an id number after after_id. Useful if you only want to refresh * - 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) == 'find_tags') { private function api_add_post() {
if(isset($_GET['id'])) { global $user, $config, $page;
$idlist = explode(",",$_GET['id']); $danboorup_kludge = 1; // danboorup for firefox makes broken links out of location: /path
foreach($idlist as $id) {
$sqlresult = $database->get_all(
"SELECT id,tag,count FROM tags WHERE id = ?",
array($id));
foreach($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
}
}
}
elseif(isset($_GET['name'])) {
$namelist = explode(",",$_GET['name']);
foreach($namelist as $name) {
$sqlresult = $database->get_all(
"SELECT id,tag,count FROM tags WHERE tag = ?",
array($name));
foreach($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['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']);
} // 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
else { $this->authenticate_user();
$start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; // Now we check if a file was uploaded or a url was provided to transload
$sqlresult = $database->get_all( // Much of this code is borrowed from /ext/upload
"SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC",
array($start)); if (!$user->can("create_image")) {
foreach($sqlresult as $row) { $page->set_code(409);
$results[] = array($row['count'], $row['tag'], $row['id']); $page->add_http_header("X-Danbooru-Errors: authentication error");
} return;
} }
// Tag results collected, build XML output if (isset($_FILES['file'])) { // A file was POST'd in
$xml = "<tags>\n"; $file = $_FILES['file']['tmp_name'];
foreach($results as $tag) { $filename = $_FILES['file']['name'];
$xml .= "<tag type=\"0\" count=\"$tag[0]\" name=\"" . $this->xmlspecialchars($tag[1]) . "\" id=\"$tag[2]\"/>\n"; // 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;
} }
$xml .= "</tags>"; } elseif (isset($_FILES['post'])) {
$page->set_data($xml); $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
$source = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source'];
$file = tempnam("/tmp", "shimmie_transload");
$ok = transload($source, $file);
if (!$ok) {
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: fopen read error");
return;
}
$filename = basename($source);
} else { // Nothing was specified at all
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: no input files");
return;
} }
// Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper // Get tags out of url
// Shimmie view page $posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']);
// Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123
// This redirects that to http://shimmie/post/view/123 // Was an md5 supplied? Does it match the file hash?
if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) { $hash = md5_file($file);
$fixedlocation = make_link("post/view/" . $event->get_arg(3)); if (isset($_REQUEST['md5']) && strtolower($_REQUEST['md5']) != $hash) {
$page->set_mode("redirect"); $page->set_code(409);
$page->set_redirect($fixedlocation); $page->add_http_header("X-Danbooru-Errors: md5 mismatch");
return;
} }
// Upload size checking is now performed in the upload extension
// It is also currently broken due to some confusion over file variable ($tmp_filename?)
// Does it exist already?
$existing = Image::by_hash($hash);
if (!is_null($existing)) {
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: duplicate");
$existinglink = make_link("post/view/" . $existing->id);
if ($danboorup_kludge) $existinglink = make_http($existinglink);
$page->add_http_header("X-Danbooru-Location: $existinglink");
return;
} }
// Turns out I use this a couple times so let's make it a utility function // Fire off an event which should process the new file and add it to the db
// Authenticates a user based on the contents of the login and password parameters $fileinfo = pathinfo($filename);
// or makes them anonymous. Does not set any cookies or anything permanent. $metadata = array();
private function authenticate_user() $metadata['filename'] = $fileinfo['basename'];
{ $metadata['extension'] = $fileinfo['extension'];
global $config; $metadata['tags'] = $posttags;
global $user; $metadata['source'] = $source;
//log_debug("danbooru_api","========== NEW($filename) =========");
//log_debug("danbooru_api", "upload($filename): fileinfo(".var_export($fileinfo,TRUE)."), metadata(".var_export($metadata,TRUE).")...");
if(isset($_REQUEST['login']) && isset($_REQUEST['password'])) try {
{ $nevent = new DataUploadEvent($file, $metadata);
// Get this user from the db, if it fails the user becomes anonymous //log_debug("danbooru_api", "send_event(".var_export($nevent,TRUE).")");
// Code borrowed from /ext/user send_event($nevent);
$name = $_REQUEST['login']; // If it went ok, grab the id for the newly uploaded image and pass it in the header
$pass = $_REQUEST['password']; $newimg = Image::by_hash($hash); // FIXME: Unsupported file doesn't throw an error?
$duser = User::by_name_and_pass($name, $pass); $newid = make_link("post/view/" . $newimg->id);
if(!is_null($duser)) { if ($danboorup_kludge) $newid = make_http($newid);
$user = $duser;
} else // Did we POST or GET this call?
{ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$user = User::by_id($config->get_int("anon_id", 0)); $page->add_http_header("X-Danbooru-Location: $newid");
} else {
$page->add_http_header("Location: $newid");
}
} catch (UploadException $ex) {
// Did something screw up?
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: exception - " . $ex->getMessage());
} }
} }
} }
// From htmlspecialchars man page on php.net comments
// If tags contain quotes they need to be htmlified
private function xmlspecialchars($text)
{
return str_replace('&#039;', '&apos;', htmlspecialchars($text, ENT_QUOTES));
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
class DanbooruApiTest extends ShimmiePHPUnitTestCase { class DanbooruApiTest extends ShimmiePHPUnitTestCase {
function testSearch() { public function testSearch() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$image_id = $this->post_image("tests/bedroom_workshop.jpg", "data"); $image_id = $this->post_image("tests/bedroom_workshop.jpg", "data");

View File

@ -1,11 +1,11 @@
<?php <?php
class DowntimeTest extends ShimmiePHPUnitTestCase { class DowntimeTest extends ShimmiePHPUnitTestCase {
function tearDown() { public function tearDown() {
global $config; global $config;
$config->set_bool("downtime", false); $config->set_bool("downtime", false);
} }
function testDowntime() { public function testDowntime() {
global $config; global $config;
$config->set_string("downtime_message", "brb, unit testing"); $config->set_string("downtime_message", "brb, unit testing");

View File

@ -1,6 +1,6 @@
<?php <?php
class EmoticonTest extends ShimmiePHPUnitTestCase { class EmoticonTest extends ShimmiePHPUnitTestCase {
function testEmoticons() { public function testEmoticons() {
global $user; global $user;
$this->log_in_as_user(); $this->log_in_as_user();

View File

@ -1,6 +1,6 @@
<?php <?php
class ETTest extends ShimmiePHPUnitTestCase { class ETTest extends ShimmiePHPUnitTestCase {
function testET() { public function testET() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("system_info"); $this->get_page("system_info");
$this->assert_title("System Info"); $this->assert_title("System Info");

View File

@ -23,7 +23,7 @@ class ExtensionInfo {
var $description, $documentation, $version, $visibility; var $description, $documentation, $version, $visibility;
var $enabled; var $enabled;
function __construct($main) { public function __construct($main) {
$matches = array(); $matches = array();
$lines = file($main); $lines = file($main);
$number_of_lines = count($lines); $number_of_lines = count($lines);
@ -37,26 +37,26 @@ class ExtensionInfo {
if(preg_match("/Name: (.*)/", $line, $matches)) { if(preg_match("/Name: (.*)/", $line, $matches)) {
$this->name = $matches[1]; $this->name = $matches[1];
} }
if(preg_match("/Visibility: (.*)/", $line, $matches)) { else if(preg_match("/Visibility: (.*)/", $line, $matches)) {
$this->visibility = $matches[1]; $this->visibility = $matches[1];
} }
if(preg_match("/Link: (.*)/", $line, $matches)) { else if(preg_match("/Link: (.*)/", $line, $matches)) {
$this->link = $matches[1]; $this->link = $matches[1];
if($this->link[0] == "/") { if($this->link[0] == "/") {
$this->link = make_link(substr($this->link, 1)); $this->link = make_link(substr($this->link, 1));
} }
} }
if(preg_match("/Version: (.*)/", $line, $matches)) { else if(preg_match("/Version: (.*)/", $line, $matches)) {
$this->version = $matches[1]; $this->version = $matches[1];
} }
if(preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) { else if(preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) {
$this->author = $matches[1]; $this->author = $matches[1];
$this->email = $matches[2]; $this->email = $matches[2];
} }
else if(preg_match("/Author: (.*)/", $line, $matches)) { else if(preg_match("/Author: (.*)/", $line, $matches)) {
$this->author = $matches[1]; $this->author = $matches[1];
} }
if(preg_match("/(.*)Description: ?(.*)/", $line, $matches)) { else if(preg_match("/(.*)Description: ?(.*)/", $line, $matches)) {
$this->description = $matches[2]; $this->description = $matches[2];
$start = $matches[1]." "; $start = $matches[1]." ";
$start_len = strlen($start); $start_len = strlen($start);
@ -65,7 +65,7 @@ class ExtensionInfo {
$i++; $i++;
} }
} }
if(preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) { else if(preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) {
$this->documentation = $matches[2]; $this->documentation = $matches[2];
$start = $matches[1]." "; $start = $matches[1]." ";
$start_len = strlen($start); $start_len = strlen($start);
@ -75,7 +75,7 @@ class ExtensionInfo {
} }
$this->documentation = str_replace('$site', make_http(get_base_href()), $this->documentation); $this->documentation = str_replace('$site', make_http(get_base_href()), $this->documentation);
} }
if(preg_match("/\*\//", $line, $matches)) { else if(preg_match("/\*\//", $line, $matches)) {
break; break;
} }
} }
@ -156,7 +156,7 @@ class ExtManager extends Extension {
/** /**
* @param bool $all * @param bool $all
* @return array * @return ExtensionInfo[]
*/ */
private function get_extensions(/*bool*/ $all) { private function get_extensions(/*bool*/ $all) {
$extensions = array(); $extensions = array();

View File

@ -1,6 +1,6 @@
<?php <?php
class ExtManagerTest extends ShimmiePHPUnitTestCase { class ExtManagerTest extends ShimmiePHPUnitTestCase {
function testAuth() { public function testAuth() {
$this->get_page('ext_manager'); $this->get_page('ext_manager');
$this->assert_title("Extensions"); $this->assert_title("Extensions");

View File

@ -204,7 +204,7 @@ class Favorites extends Extension {
/** /**
* @param Image $image * @param Image $image
* @return array * @return string[]
*/ */
private function list_persons_who_have_favorited(Image $image) { private function list_persons_who_have_favorited(Image $image) {
global $database; global $database;

View File

@ -1,6 +1,6 @@
<?php <?php
class FavoritesTest extends ShimmiePHPUnitTestCase { class FavoritesTest extends ShimmiePHPUnitTestCase {
function testFavorites() { public function testFavorites() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test");

View File

@ -1,6 +1,6 @@
<?php <?php
class FeaturedTest extends ShimmiePHPUnitTestCase { class FeaturedTest extends ShimmiePHPUnitTestCase {
function testFeatured() { public function testFeatured() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");

View File

@ -1,6 +1,6 @@
<?php <?php
class Handle404Test extends ShimmiePHPUnitTestCase { class Handle404Test extends ShimmiePHPUnitTestCase {
function test404Handler() { public function test404Handler() {
$this->get_page('not/a/page'); $this->get_page('not/a/page');
// most descriptive error first // most descriptive error first
$this->assert_text("No handler could be found for the page 'not/a/page'"); $this->assert_text("No handler could be found for the page 'not/a/page'");

View File

@ -44,7 +44,7 @@ class ArchiveFileHandler extends Extension {
} }
/** /**
* @param $ext * @param string $ext
* @return bool * @return bool
*/ */
private function supported_ext($ext) { private function supported_ext($ext) {

View File

@ -50,7 +50,7 @@ class FlashFileHandler extends DataHandlerExtension {
} }
/** /**
* @param $file * @param string $file
* @return bool * @return bool
*/ */
protected function check_contents(/*string*/ $file) { protected function check_contents(/*string*/ $file) {

View File

@ -50,7 +50,7 @@ class IcoFileHandler extends Extension {
} }
/** /**
* @param $ext * @param string $ext
* @return bool * @return bool
*/ */
private function supported_ext($ext) { private function supported_ext($ext) {
@ -59,8 +59,8 @@ class IcoFileHandler extends Extension {
} }
/** /**
* @param $filename * @param string $filename
* @param $metadata * @param mixed[] $metadata
* @return Image * @return Image
*/ */
private function create_image_from_data($filename, $metadata) { private function create_image_from_data($filename, $metadata) {

View File

@ -1,6 +1,6 @@
<?php <?php
class IcoHandlerTest extends ShimmiePHPUnitTestCase { class IcoHandlerTest extends ShimmiePHPUnitTestCase {
function testIcoHander() { public function testIcoHander() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("lib/static/favicon.ico", "shimmie favicon"); $image_id = $this->post_image("lib/static/favicon.ico", "shimmie favicon");
$this->get_page("post/view/$image_id"); // test for no crash $this->get_page("post/view/$image_id"); // test for no crash

View File

@ -26,7 +26,7 @@ class MP3FileHandler extends DataHandlerExtension {
/** /**
* @param string $filename * @param string $filename
* @param array $metadata * @param mixed[] $metadata
* @return Image|null * @return Image|null
*/ */
protected function create_image_from_data($filename, $metadata) { protected function create_image_from_data($filename, $metadata) {

View File

@ -67,7 +67,7 @@ class PixelFileHandler extends DataHandlerExtension {
} }
/** /**
* @param $hash * @param string $hash
* @return bool * @return bool
*/ */
protected function create_thumb_force(/*string*/ $hash) { protected function create_thumb_force(/*string*/ $hash) {
@ -91,9 +91,6 @@ class PixelFileHandler extends DataHandlerExtension {
return $ok; return $ok;
} }
/**
* @param ImageAdminBlockBuildingEvent $event
*/
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
$event->add_part(" $event->add_part("
<form> <form>

View File

@ -1,6 +1,6 @@
<?php <?php
class PixelHandlerTest extends ShimmiePHPUnitTestCase { class PixelHandlerTest extends ShimmiePHPUnitTestCase {
function testPixelHander() { public function testPixelHander() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
//$this->assert_response(302); //$this->assert_response(302);

View File

@ -51,7 +51,7 @@ class SVGFileHandler extends Extension {
} }
/** /**
* @param $ext * @param string $ext
* @return bool * @return bool
*/ */
private function supported_ext($ext) { private function supported_ext($ext) {
@ -60,8 +60,8 @@ class SVGFileHandler extends Extension {
} }
/** /**
* @param $filename * @param string $filename
* @param $metadata * @param mixed[] $metadata
* @return Image * @return Image
*/ */
private function create_image_from_data($filename, $metadata) { private function create_image_from_data($filename, $metadata) {
@ -82,7 +82,7 @@ class SVGFileHandler extends Extension {
} }
/** /**
* @param $file * @param string $file
* @return bool * @return bool
*/ */
private function check_contents($file) { private function check_contents($file) {

View File

@ -1,6 +1,6 @@
<?php <?php
class SVGHandlerTest extends ShimmiePHPUnitTestCase { class SVGHandlerTest extends ShimmiePHPUnitTestCase {
function testSVGHander() { public function testSVGHander() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/test.svg", "something"); $image_id = $this->post_image("tests/test.svg", "something");
$this->get_page("post/view/$image_id"); // test for no crash $this->get_page("post/view/$image_id"); // test for no crash

View File

@ -118,7 +118,7 @@ class VideoFileHandler extends DataHandlerExtension {
/** /**
* @param string $filename * @param string $filename
* @param array $metadata * @param mixed[] $metadata
* @return Image|null * @return Image|null
*/ */
protected function create_image_from_data($filename, $metadata) { protected function create_image_from_data($filename, $metadata) {
@ -162,7 +162,7 @@ class VideoFileHandler extends DataHandlerExtension {
} }
/** /**
* @param $file * @param string $file
* @return bool * @return bool
*/ */
protected function check_contents($file) { protected function check_contents($file) {

View File

@ -1,6 +1,6 @@
<?php <?php
class HomeTest extends ShimmiePHPUnitTestCase { class HomeTest extends ShimmiePHPUnitTestCase {
function testHomePage() { public function testHomePage() {
$this->get_page('home'); $this->get_page('home');
// FIXME: this page doesn't use blocks; need assert_data_contains // FIXME: this page doesn't use blocks; need assert_data_contains

View File

@ -32,6 +32,9 @@ class ImageAdditionEvent extends Event {
class ImageAdditionException extends SCoreException { class ImageAdditionException extends SCoreException {
var $error; var $error;
/**
* @param string $error
*/
public function __construct($error) { public function __construct($error) {
$this->error = $error; $this->error = $error;
} }

View File

@ -13,6 +13,9 @@
class RemoveImageHashBanEvent extends Event { class RemoveImageHashBanEvent extends Event {
var $hash; var $hash;
/**
* @param string $hash
*/
public function __construct($hash) { public function __construct($hash) {
$this->hash = $hash; $this->hash = $hash;
} }
@ -23,6 +26,10 @@ class AddImageHashBanEvent extends Event {
var $hash; var $hash;
var $reason; var $reason;
/**
* @param string $hash
* @param string $reason
*/
public function __construct($hash, $reason) { public function __construct($hash, $reason) {
$this->hash = $hash; $this->hash = $hash;
$this->reason = $reason; $this->reason = $reason;
@ -126,6 +133,11 @@ class ImageBan extends Extension {
// DB funness // DB funness
/**
* @param int $page
* @param int $size
* @return array
*/
public function get_image_hash_bans($page, $size=100) { public function get_image_hash_bans($page, $size=100) {
global $database; global $database;

View File

@ -1,6 +1,6 @@
<?php <?php
class HashBanTest extends ShimmiePHPUnitTestCase { class HashBanTest extends ShimmiePHPUnitTestCase {
function testBan() { public function testBan() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
$this->log_out(); $this->log_out();

View File

@ -322,8 +322,17 @@ class Index extends Extension {
// check for tags first as tag based searches are more common. // check for tags first as tag based searches are more common.
if(preg_match("/^tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { if(preg_match("/^tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
$cmp = ltrim($matches[1], ":") ?: "="; $cmp = ltrim($matches[1], ":") ?: "=";
$tags = $matches[2]; $count = $matches[2];
$event->add_querylet(new Querylet('images.id IN (SELECT DISTINCT image_id FROM image_tags GROUP BY image_id HAVING count(image_id) '.$cmp.' '.$tags.')')); $event->add_querylet(
new Querylet("EXISTS (
SELECT 1
FROM image_tags it
LEFT JOIN tags t ON it.tag_id = t.id
WHERE images.id = it.image_id
GROUP BY image_id
HAVING COUNT(*) $cmp $count
)")
);
} }
else if(preg_match("/^ratio([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+):(\d+)$/i", $event->term, $matches)) { else if(preg_match("/^ratio([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+):(\d+)$/i", $event->term, $matches)) {
$cmp = preg_replace('/^:/', '=', $matches[1]); $cmp = preg_replace('/^:/', '=', $matches[1]);
@ -394,4 +403,3 @@ class Index extends Extension {
$this->stpen++; $this->stpen++;
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
class IndexTest extends ShimmiePHPUnitTestCase { class IndexTest extends ShimmiePHPUnitTestCase {
function testIndexPage() { public function testIndexPage() {
$this->get_page('post/list'); $this->get_page('post/list');
$this->assert_title("Welcome to Shimmie ".VERSION); $this->assert_title("Welcome to Shimmie ".VERSION);
$this->assert_no_text("Prev | Index | Next"); $this->assert_no_text("Prev | Index | Next");
@ -29,7 +29,7 @@ class IndexTest extends ShimmiePHPUnitTestCase {
# FIXME: test search box # FIXME: test search box
} }
function testSearches() { public function testSearches() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
$image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "computer bedroom workshop"); $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "computer bedroom workshop");

View File

@ -3,6 +3,11 @@
class IndexTheme extends Themelet { class IndexTheme extends Themelet {
var $page_number, $total_pages, $search_terms; var $page_number, $total_pages, $search_terms;
/**
* @param int $page_number
* @param int $total_pages
* @param string[] $search_terms
*/
public function set_page($page_number, $total_pages, $search_terms) { public function set_page($page_number, $total_pages, $search_terms) {
$this->page_number = $page_number; $this->page_number = $page_number;
$this->total_pages = $total_pages; $this->total_pages = $total_pages;
@ -27,6 +32,10 @@ and of course start organising your images :-)
$page->add_block(new Block("Installation Succeeded!", $text, "main", 0)); $page->add_block(new Block("Installation Succeeded!", $text, "main", 0));
} }
/**
* @param Page $page
* @param Image[] $images
*/
public function display_page(Page $page, $images) { public function display_page(Page $page, $images) {
$this->display_page_header($page, $images); $this->display_page_header($page, $images);
@ -41,12 +50,21 @@ and of course start organising your images :-)
} }
} }
public function display_admin_block(/*array(string)*/ $parts) { /**
* @param string[] $parts
*/
public function display_admin_block($parts) {
global $page; global $page;
$page->add_block(new Block("List Controls", join("<br>", $parts), "left", 50)); $page->add_block(new Block("List Controls", join("<br>", $parts), "left", 50));
} }
/**
* @param int $page_number
* @param int $total_pages
* @param string[] $search_terms
* @return string
*/
protected function build_navigation($page_number, $total_pages, $search_terms) { protected function build_navigation($page_number, $total_pages, $search_terms) {
$prev = $page_number - 1; $prev = $page_number - 1;
$next = $page_number + 1; $next = $page_number + 1;
@ -72,6 +90,11 @@ and of course start organising your images :-)
return $h_prev.' | '.$h_index.' | '.$h_next.'<br>'.$h_search; return $h_prev.' | '.$h_index.' | '.$h_next.'<br>'.$h_search;
} }
/**
* @param Image[] $images
* @param string $query
* @return string
*/
protected function build_table($images, $query) { protected function build_table($images, $query) {
$h_query = html_escape($query); $h_query = html_escape($query);
$table = "<div class='shm-image-list' data-query='$h_query'>"; $table = "<div class='shm-image-list' data-query='$h_query'>";
@ -82,6 +105,10 @@ and of course start organising your images :-)
return $table; return $table;
} }
/**
* @param Page $page
* @param Image[] $images
*/
protected function display_page_header(Page $page, $images) { protected function display_page_header(Page $page, $images) {
global $config; global $config;
@ -102,6 +129,10 @@ and of course start organising your images :-)
$page->set_heading($page_title); $page->set_heading($page_title);
} }
/**
* @param Page $page
* @param Image[] $images
*/
protected function display_page_images(Page $page, $images) { protected function display_page_images(Page $page, $images) {
if (count($this->search_terms) > 0) { if (count($this->search_terms) > 0) {
$query = url_escape(implode(' ', $this->search_terms)); $query = url_escape(implode(' ', $this->search_terms));

View File

@ -1,6 +1,6 @@
<?php <?php
class IPBanTest extends ShimmiePHPUnitTestCase { class IPBanTest extends ShimmiePHPUnitTestCase {
function testIPBan() { public function testIPBan() {
$this->get_page('ip_ban/list'); $this->get_page('ip_ban/list');
$this->assert_response(403); $this->assert_response(403);
$this->assert_title("Permission Denied"); $this->assert_title("Permission Denied");
@ -9,7 +9,9 @@ class IPBanTest extends ShimmiePHPUnitTestCase {
$this->get_page('ip_ban/list'); $this->get_page('ip_ban/list');
$this->assert_no_text("42.42.42.42"); $this->assert_no_text("42.42.42.42");
/*
$this->markTestIncomplete();
$this->set_field('ip', '42.42.42.42'); $this->set_field('ip', '42.42.42.42');
$this->set_field('reason', 'unit testing'); $this->set_field('reason', 'unit testing');
$this->set_field('end', '1 week'); $this->set_field('end', '1 week');
@ -18,7 +20,6 @@ class IPBanTest extends ShimmiePHPUnitTestCase {
$this->assert_text("42.42.42.42"); $this->assert_text("42.42.42.42");
$this->click("Remove"); // FIXME: remove which ban? :S $this->click("Remove"); // FIXME: remove which ban? :S
$this->assert_no_text("42.42.42.42"); $this->assert_no_text("42.42.42.42");
*/
$this->get_page('ip_ban/list?all=on'); // just test it doesn't crash for now $this->get_page('ip_ban/list?all=on'); // just test it doesn't crash for now

View File

@ -1,6 +1,6 @@
<?php <?php
class LinkImageTest extends ShimmiePHPUnitTestCase { class LinkImageTest extends ShimmiePHPUnitTestCase {
function testLinkImage() { public function testLinkImage() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pie"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pie");
@ -9,7 +9,8 @@ class LinkImageTest extends ShimmiePHPUnitTestCase {
# in there, see if it takes us to the right page # in there, see if it takes us to the right page
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");
/* $this->markTestIncomplete();
// FIXME // FIXME
$matches = array(); $matches = array();
preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $raw, $matches); preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $raw, $matches);
@ -18,7 +19,6 @@ class LinkImageTest extends ShimmiePHPUnitTestCase {
$this->get($matches[1]); $this->get($matches[1]);
$this->assert_title("Image $image_id: pie"); $this->assert_title("Image $image_id: pie");
} }
*/
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
class LogDatabaseTest extends ShimmiePHPUnitTestCase { class LogDatabaseTest extends ShimmiePHPUnitTestCase {
function testLog() { public function testLog() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("log/view"); $this->get_page("log/view");
$this->get_page("log/view?module=core-image"); $this->get_page("log/view?module=core-image");

View File

@ -16,20 +16,14 @@ class MassTagger extends Extension {
public function onPostListBuilding(PostListBuildingEvent $event) { public function onPostListBuilding(PostListBuildingEvent $event) {
global $config, $page, $user; global $config, $page, $user;
if( !$user->is_admin() ) return; if($user->is_admin()) {
$this->theme->display_mass_tagger( $page, $event, $config ); $this->theme->display_mass_tagger( $page, $event, $config );
} }
}
public function onPageRequest(PageRequestEvent $event) { public function onPageRequest(PageRequestEvent $event) {
global $config, $page, $user; global $page, $user;
if( !$event->page_matches("mass_tagger") ) return; if($event->page_matches("mass_tagger/tag") && $user->is_admin()) {
if( !$user->is_admin() ) return;
if($event->get_arg(0) == "tag") $this->_apply_mass_tags( $config, $page, $user, $event );
}
private function _apply_mass_tags( $config, Page $page, $user, $event ) {
if( !isset($_POST['ids']) or !isset($_POST['tag']) ) return; if( !isset($_POST['ids']) or !isset($_POST['tag']) ) return;
$tag = $_POST['tag']; $tag = $_POST['tag'];
@ -49,15 +43,12 @@ class MassTagger extends Extension {
$images = array_map( "Image::by_id", $ids ); $images = array_map( "Image::by_id", $ids );
if(isset($_POST['setadd']) && if(isset($_POST['setadd']) && $_POST['setadd'] == 'set') {
$_POST['setadd'] == 'set')
{
foreach($images as $image) { foreach($images as $image) {
$image->set_tags(Tag::explode($tag)); $image->set_tags(Tag::explode($tag));
} }
} }
else else {
{
foreach($images as $image) { foreach($images as $image) {
if (!empty($neg_tag_array)) { if (!empty($neg_tag_array)) {
$img_tags = array_merge($pos_tag_array, explode(" ",$image->get_tag_list())); $img_tags = array_merge($pos_tag_array, explode(" ",$image->get_tag_list()));
@ -74,4 +65,5 @@ class MassTagger extends Extension {
$page->set_redirect($_SERVER['HTTP_REFERER']); $page->set_redirect($_SERVER['HTTP_REFERER']);
} }
} }
}

View File

@ -28,7 +28,10 @@ class NotATag extends Extension {
$this->scan($event->tags); $this->scan($event->tags);
} }
private function scan(/*array*/ $tags_mixed) { /**
* @param string[] $tags_mixed
*/
private function scan($tags_mixed) {
global $database; global $database;
$tags = array(); $tags = array();
@ -90,6 +93,11 @@ class NotATag extends Extension {
} }
} }
/**
* @param int $page
* @param int $size
* @return array
*/
public function get_untags($page, $size=100) { public function get_untags($page, $size=100) {
global $database; global $database;

View File

@ -276,8 +276,7 @@ class Notes extends Extension {
(?, ?, ?, ?, now(), ?, ?, ?, ?, ?)", (?, ?, ?, ?, now(), ?, ?, ?, ?, ?)",
array(1, $imageID, $user_id, $_SERVER['REMOTE_ADDR'], $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText)); array(1, $imageID, $user_id, $_SERVER['REMOTE_ADDR'], $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText));
$result = $database->get_row("SELECT LAST_INSERT_ID() AS noteID", array()); $noteID = $database->get_last_insert_id('notes_id_seq');
$noteID = $result["noteID"];
log_info("notes", "Note added {$noteID} by {$user->name}"); log_info("notes", "Note added {$noteID} by {$user->name}");
@ -304,9 +303,9 @@ class Notes extends Extension {
(?, ?, now())", (?, ?, now())",
array($image_id, $user_id)); array($image_id, $user_id));
$result = $database->get_row("SELECT LAST_INSERT_ID() AS requestID", array()); $resultID = $database->get_last_insert_id('note_request_id_seq');
log_info("notes", "Note requested {$result["requestID"]} by {$user->name}"); log_info("notes", "Note requested {$requestID} by {$user->name}");
} }

View File

@ -13,7 +13,12 @@
class NumericScoreSetEvent extends Event { class NumericScoreSetEvent extends Event {
var $image_id, $user, $score; var $image_id, $user, $score;
public function __construct(/*int*/ $image_id, User $user, /*int*/ $score) { /**
* @param int $image_id
* @param User $user
* @param int $score
*/
public function __construct($image_id, User $user, $score) {
$this->image_id = $image_id; $this->image_id = $image_id;
$this->user = $user; $this->user = $user;
$this->score = $score; $this->score = $score;
@ -102,15 +107,15 @@ class NumericScore extends Extension {
if(!empty($_GET['day'])){ if(!empty($_GET['day'])){
$D = (int) $_GET['day']; $D = (int) $_GET['day'];
if($D >= 1 && $D <= 31) $day = $D; $day = clamp($D, 1, 31);
} }
if(!empty($_GET['month'])){ if(!empty($_GET['month'])){
$M = (int) $_GET['month']; $M = (int) $_GET['month'];
if($M >= 1 && $M <= 12) $month = $M; $month = clamp($M, 1 ,12);
} }
if(!empty($_GET['year'])){ if(!empty($_GET['year'])){
$Y = (int) $_GET['year']; $Y = (int) $_GET['year'];
if($Y >= 1970 && $Y < 2100) $year = $Y; $year = clamp($Y, 1970, 2100);
} }
$totaldate = $year."/".$month."/".$day; $totaldate = $year."/".$month."/".$day;
@ -168,7 +173,10 @@ class NumericScore extends Extension {
$this->delete_votes_by($event->id); $this->delete_votes_by($event->id);
} }
public function delete_votes_by(/*int*/ $user_id) { /**
* @param int $user_id
*/
public function delete_votes_by($user_id) {
global $database; global $database;
$image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", array($user_id)); $image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", array($user_id));
@ -239,9 +247,9 @@ class NumericScore extends Extension {
"images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)", "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)",
array("ns_user_id"=>$iid))); array("ns_user_id"=>$iid)));
} }
else if(preg_match("/^order[=|:](numeric_)?(score)[_]?(desc|asc)?$/i", $event->term, $matches)){ else if(preg_match("/^order[=|:](?:numeric_)?(score)(?:_(desc|asc))?$/i", $event->term, $matches)){
$default_order_for_column = "DESC"; $default_order_for_column = "DESC";
$sort = isset($matches[3]) ? strtoupper($matches[3]) : $default_order_for_column; $sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column;
Image::$order_sql = "images.numeric_score $sort"; Image::$order_sql = "images.numeric_score $sort";
$event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag
} }
@ -250,7 +258,7 @@ class NumericScore extends Extension {
public function onTagTermParse(TagTermParseEvent $event) { public function onTagTermParse(TagTermParseEvent $event) {
$matches = array(); $matches = array();
if(preg_match("/^vote[=|:](up|down|remove)$/", $event->term, $matches)) { if(preg_match("/^vote[=|:](up|down|remove)$/", $event->term, $matches) && $event->parse) {
global $user; global $user;
$score = ($matches[1] == "up" ? 1 : ($matches[1] == "down" ? -1 : 0)); $score = ($matches[1] == "up" ? 1 : ($matches[1] == "down" ? -1 : 0));
if(!$user->is_anonymous()) { if(!$user->is_anonymous()) {
@ -290,7 +298,7 @@ class NumericScore extends Extension {
* @param int $user_id * @param int $user_id
* @param int $score * @param int $score
*/ */
private function add_vote(/*int*/ $image_id, /*int*/ $user_id, /*int*/ $score) { private function add_vote($image_id, $user_id, $score) {
global $database; global $database;
$database->execute( $database->execute(
"DELETE FROM numeric_score_votes WHERE image_id=:imageid AND user_id=:userid", "DELETE FROM numeric_score_votes WHERE image_id=:imageid AND user_id=:userid",

View File

@ -1,6 +1,6 @@
<?php <?php
class NumericScoreTest extends ShimmiePHPUnitTestCase { class NumericScoreTest extends ShimmiePHPUnitTestCase {
function testNumericScore() { public function testNumericScore() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");

View File

@ -1,6 +1,6 @@
<?php <?php
class OekakiTest extends ShimmiePHPUnitTestCase { class OekakiTest extends ShimmiePHPUnitTestCase {
function testLog() { public function testLog() {
$this->log_in_as_user(); $this->log_in_as_user();
$this->get_page("oekaki/create"); $this->get_page("oekaki/create");
} }

View File

@ -208,7 +208,7 @@ class _SafeOuroborosImage
// meta // meta
$this->change = intval($img->id); //DaFug is this even supposed to do? ChangeID? $this->change = intval($img->id); //DaFug is this even supposed to do? ChangeID?
// Should be JSON specific, just strip this when converting to XML // Should be JSON specific, just strip this when converting to XML
$this->created_at = array('n' => 123456789, 's' => $img->posted_timestamp, 'json_class' => 'Time'); $this->created_at = array('n' => 123456789, 's' => strtotime($img->posted), 'json_class' => 'Time');
$this->id = intval($img->id); $this->id = intval($img->id);
$this->parent_id = null; $this->parent_id = null;
if (defined('ENABLED_EXTS')) { if (defined('ENABLED_EXTS')) {

View File

@ -1,6 +1,6 @@
<?php <?php
class PrivMsgTest extends ShimmiePHPUnitTestCase { class PrivMsgTest extends ShimmiePHPUnitTestCase {
function testPM() { public function testPM() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("user/test"); $this->get_page("user/test");
@ -31,7 +31,7 @@ class PrivMsgTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testAdminAccess() { public function testAdminAccess() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("user/test"); $this->get_page("user/test");

View File

@ -252,19 +252,21 @@ class Pools extends Extension {
* When displaying an image, optionally list all the pools that the * When displaying an image, optionally list all the pools that the
* image is currently a member of on a side panel, as well as a link * image is currently a member of on a side panel, as well as a link
* to the Next image in the pool. * to the Next image in the pool.
*
* @var DisplayingImageEvent $event
*/ */
public function onDisplayingImage(DisplayingImageEvent $event) { public function onDisplayingImage(DisplayingImageEvent $event) {
global $config; global $config;
if($config->get_bool("poolsInfoOnViewImage")) { if($config->get_bool("poolsInfoOnViewImage")) {
$imageID = $event->image->id; $imageID = $event->image->id;
$poolsIDs = $this->get_pool_id($imageID); $poolsIDs = $this->get_pool_ids($imageID);
$show_nav = $config->get_bool("poolsShowNavLinks", false); $show_nav = $config->get_bool("poolsShowNavLinks", false);
$navInfo = array(); $navInfo = array();
foreach($poolsIDs as $poolID) { foreach($poolsIDs as $poolID) {
$pool = $this->get_single_pool($poolID['pool_id']); $pool = $this->get_single_pool($poolID);
$navInfo[$pool['id']] = array(); $navInfo[$pool['id']] = array();
$navInfo[$pool['id']]['info'] = $pool; $navInfo[$pool['id']]['info'] = $pool;
@ -374,16 +376,10 @@ class Pools extends Extension {
private function list_pools(Page $page, /*int*/ $pageNumber) { private function list_pools(Page $page, /*int*/ $pageNumber) {
global $config, $database; global $config, $database;
if(is_null($pageNumber) || !is_numeric($pageNumber)) $pageNumber = clamp($pageNumber, 1, null) - 1;
$pageNumber = 0;
else if ($pageNumber <= 0)
$pageNumber = 0;
else
$pageNumber--;
$poolsPerPage = $config->get_int("poolsListsPerPage"); $poolsPerPage = $config->get_int("poolsListsPerPage");
$order_by = ""; $order_by = "";
$order = $page->get_cookie("ui-order-pool"); $order = $page->get_cookie("ui-order-pool");
if($order == "created" || is_null($order)){ if($order == "created" || is_null($order)){
@ -404,8 +400,7 @@ class Pools extends Extension {
ON p.user_id = u.id ON p.user_id = u.id
$order_by $order_by
LIMIT :l OFFSET :o LIMIT :l OFFSET :o
", array("l"=>$poolsPerPage, "o"=>$pageNumber * $poolsPerPage) ", array("l"=>$poolsPerPage, "o"=>$pageNumber * $poolsPerPage));
);
$totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pools") / $poolsPerPage); $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pools") / $poolsPerPage);
@ -416,7 +411,7 @@ class Pools extends Extension {
/** /**
* HERE WE CREATE A NEW POOL * HERE WE CREATE A NEW POOL
* *
* @return mixed * @return int
* @throws PoolCreationException * @throws PoolCreationException
*/ */
private function add_pool() { private function add_pool() {
@ -438,12 +433,9 @@ class Pools extends Extension {
VALUES (:uid, :public, :title, :desc, now())", VALUES (:uid, :public, :title, :desc, now())",
array("uid"=>$user->id, "public"=>$public, "title"=>$_POST["title"], "desc"=>$_POST["description"])); array("uid"=>$user->id, "public"=>$public, "title"=>$_POST["title"], "desc"=>$_POST["description"]));
$result = array(); $poolID = $database->get_last_insert_id('pools_id_seq');
$result['poolID'] = $database->get_last_insert_id('pools_id_seq'); log_info("pools", "Pool {$poolID} created by {$user->name}");
return $poolID;
log_info("pools", "Pool {$result["poolID"]} created by {$user->name}");
return $result["poolID"];
} }
/** /**
@ -482,11 +474,11 @@ class Pools extends Extension {
/** /**
* Get all of the pool IDs that an image is in, given an image ID. * Get all of the pool IDs that an image is in, given an image ID.
* @param int $imageID Integer ID for the image * @param int $imageID Integer ID for the image
* @return array * @return int[]
*/ */
private function get_pool_id(/*int*/ $imageID) { private function get_pool_ids(/*int*/ $imageID) {
global $database; global $database;
return $database->get_all("SELECT pool_id FROM pool_images WHERE image_id=:iid", array("iid"=>$imageID)); return $database->get_col("SELECT pool_id FROM pool_images WHERE image_id=:iid", array("iid"=>$imageID));
} }
/** /**

View File

@ -1,6 +1,6 @@
<?php <?php
class PoolsTest extends ShimmiePHPUnitTestCase { class PoolsTest extends ShimmiePHPUnitTestCase {
function testPools() { public function testPools() {
$this->get_page('pool/list'); $this->get_page('pool/list');
$this->assert_title("Pools"); $this->assert_title("Pools");

View File

@ -1,6 +1,6 @@
<?php <?php
class RandomTest extends ShimmiePHPUnitTestCase { class RandomTest extends ShimmiePHPUnitTestCase {
function testRandom() { public function testRandom() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test");
$this->log_out(); $this->log_out();
@ -15,7 +15,7 @@ class RandomTest extends ShimmiePHPUnitTestCase {
# FIXME: assert($raw == file(blah.jpg)) # FIXME: assert($raw == file(blah.jpg))
} }
function testPostListBlock() { public function testPostListBlock() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("setup"); $this->get_page("setup");

View File

@ -1,6 +1,6 @@
<?php <?php
class RatingTest extends ShimmiePHPUnitTestCase { class RatingTest extends ShimmiePHPUnitTestCase {
function testRating() { public function testRating() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");

View File

@ -1,6 +1,6 @@
<?php <?php
class RegenThumbTest extends ShimmiePHPUnitTestCase { class RegenThumbTest extends ShimmiePHPUnitTestCase {
function testRegenThumb() { public function testRegenThumb() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");

View File

@ -58,7 +58,7 @@ class Relationships extends Extension {
public function onTagTermParse(TagTermParseEvent $event) { public function onTagTermParse(TagTermParseEvent $event) {
$matches = array(); $matches = array();
if(preg_match("/^parent[=|:]([0-9]+|none)$/", $event->term, $matches)) { if(preg_match("/^parent[=|:]([0-9]+|none)$/", $event->term, $matches) && $event->parse) {
$parentID = $matches[1]; $parentID = $matches[1];
if($parentID == "none" || $parentID == "0"){ if($parentID == "none" || $parentID == "0"){
@ -67,7 +67,7 @@ class Relationships extends Extension {
$this->set_parent($event->id, $parentID); $this->set_parent($event->id, $parentID);
} }
} }
else if(preg_match("/^child[=|:]([0-9]+)$/", $event->term, $matches)) { else if(preg_match("/^child[=|:]([0-9]+)$/", $event->term, $matches) && $event->parse) {
$childID = $matches[1]; $childID = $matches[1];
$this->set_child($event->id, $childID); $this->set_child($event->id, $childID);

View File

@ -141,7 +141,10 @@ class ReportImage extends Extension {
$this->delete_reports_by($event->id); $this->delete_reports_by($event->id);
} }
public function delete_reports_by(/*int*/ $user_id) { /**
* @param int $user_id
*/
public function delete_reports_by($user_id) {
global $database; global $database;
$database->execute("DELETE FROM image_reports WHERE reporter_id=?", array($user_id)); $database->execute("DELETE FROM image_reports WHERE reporter_id=?", array($user_id));
$database->cache->delete("image-report-count"); $database->cache->delete("image-report-count");
@ -165,7 +168,7 @@ class ReportImage extends Extension {
/** /**
* @param Image $image * @param Image $image
* @return array * @return string[]
*/ */
public function get_reporters(Image $image) { public function get_reporters(Image $image) {
global $database; global $database;
@ -206,7 +209,7 @@ class ReportImage extends Extension {
} }
/** /**
* @return mixed * @return int
*/ */
public function count_reported_images() { public function count_reported_images() {
global $database; global $database;
@ -220,13 +223,3 @@ class ReportImage extends Extension {
return $count; return $count;
} }
} }
// ===== Changelog =====
// * Version 0.3a / 0.3a_rc - 11/06/07 - I can no longer use the same theme.php file for both SVN and RCx. Sorry.
// * Same deal with theme.php as it is with main.php
// * Version 0.3 / 0.3_rc - 11/06/07 - Added the option to display thumbnails, moved the reported image list to it's
// own page, and checked to make sure the user is an admin before letting them delete / view reported images.
// * Version 0.2c_rc2 - 10/27/07 - Now (really!) supports Shimmie2 RC2!
// * Version 0.2b - 10/27/07 - Now supports Shimmie2 RC2!
// * Version 0.2a - 10/24/07 - Fixed some SQL issues. I will make sure to test before commiting :)
// * Version 0.2 - 10/24/07 - First public release.

View File

@ -1,6 +1,6 @@
<?php <?php
class ReportImageTest extends ShimmiePHPUnitTestCase { class ReportImageTest extends ShimmiePHPUnitTestCase {
function testReportImage() { public function testReportImage() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");

View File

@ -1,6 +1,6 @@
<?php <?php
class ResLimitTest extends ShimmiePHPUnitTestCase { class ResLimitTest extends ShimmiePHPUnitTestCase {
function testResLimitOK() { public function testResLimitOK() {
global $config; global $config;
$config->set_int("upload_min_height", 0); $config->set_int("upload_min_height", 0);
$config->set_int("upload_min_width", 0); $config->set_int("upload_min_width", 0);
@ -16,7 +16,7 @@ class ResLimitTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text("ratio"); $this->assert_no_text("ratio");
} }
function testResLimitSmall() { public function testResLimitSmall() {
global $config; global $config;
$config->set_int("upload_min_height", 900); $config->set_int("upload_min_height", 900);
$config->set_int("upload_min_width", 900); $config->set_int("upload_min_width", 900);
@ -33,7 +33,7 @@ class ResLimitTest extends ShimmiePHPUnitTestCase {
} }
} }
function testResLimitLarge() { public function testResLimitLarge() {
global $config; global $config;
$config->set_int("upload_min_height", 0); $config->set_int("upload_min_height", 0);
$config->set_int("upload_min_width", 0); $config->set_int("upload_min_width", 0);
@ -50,7 +50,7 @@ class ResLimitTest extends ShimmiePHPUnitTestCase {
} }
function testResLimitRatio() { public function testResLimitRatio() {
global $config; global $config;
$config->set_int("upload_min_height", -1); $config->set_int("upload_min_height", -1);
$config->set_int("upload_min_width", -1); $config->set_int("upload_min_width", -1);

View File

@ -75,14 +75,14 @@ class ResizeImage extends Extension {
$isanigif = 0; $isanigif = 0;
if($image_obj->ext == "gif"){ if($image_obj->ext == "gif"){
$image_filename = warehouse_path("images", $image_obj->hash); $image_filename = warehouse_path("images", $image_obj->hash);
if(!($fh = @fopen($image_filename, 'rb'))){ //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) if(($fh = @fopen($image_filename, 'rb'))) {
return false; //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
}
while(!feof($fh) && $isanigif < 2) { while(!feof($fh) && $isanigif < 2) {
$chunk = fread($fh, 1024 * 100); $chunk = fread($fh, 1024 * 100);
$isanigif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); $isanigif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
} }
} }
}
if($isanigif == 0){ if($isanigif == 0){
try { try {
$this->resize_image($image_obj, $width, $height); $this->resize_image($image_obj, $width, $height);
@ -181,49 +181,13 @@ class ResizeImage extends Extension {
throw new ImageResizeException("The current image size does not match what is set in the database! - Aborting Resize."); throw new ImageResizeException("The current image size does not match what is set in the database! - Aborting Resize.");
} }
/* $memory_use = $this->calc_memory_use($info);
Check Memory usage limits
Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024);
New check: memory_use = width * height * (bits per channel) * channels * 2.5
It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4)
We need to consider the size that we are GOING TO instead.
The factor of 2.5 is simply a rough guideline.
http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize
*/
if (isset($info['bits']) && isset($info['channels']))
{
$memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024;
} else {
//
// If we don't have bits and channel info from the image then assume default values
// of 8 bits per color and 4 channels (R,G,B,A) -- ie: regular 24-bit color
//
$memory_use = ($info[0] * $info[1] * 1 * 4 * 2.5) / 1024;
}
$memory_limit = get_memory_limit(); $memory_limit = get_memory_limit();
if ($memory_use > $memory_limit) { if ($memory_use > $memory_limit) {
throw new ImageResizeException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)"); throw new ImageResizeException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)");
} }
/* Calculate the new size of the image */ list($new_height, $new_width) = $this->calc_new_size($image_obj, $width, $height);
if ( $height > 0 && $width > 0 ) {
$new_height = $height;
$new_width = $width;
} else {
// Scale the new image
if ($width == 0) $factor = $height/$image_obj->height;
elseif ($height == 0) $factor = $width/$image_obj->width;
else $factor = min( $width / $image_obj->width, $height / $image_obj->height );
$new_width = round( $image_obj->width * $factor );
$new_height = round( $image_obj->height * $factor );
}
/* Attempt to load the image */ /* Attempt to load the image */
switch ( $info[2] ) { switch ( $info[2] ) {
@ -303,19 +267,65 @@ class ResizeImage extends Extension {
send_event(new ThumbnailGenerationEvent($new_hash, $filetype)); send_event(new ThumbnailGenerationEvent($new_hash, $filetype));
/* Update the database */ /* Update the database */
$database->Execute( $database->Execute("
"UPDATE images SET UPDATE images SET filename = :filename, filesize = :filesize, hash = :hash, width = :width, height = :height
filename = :filename, filesize = :filesize, hash = :hash, width = :width, height = :height WHERE id = :id
WHERE ", array(
id = :id
",
array(
"filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash, "filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash,
"width"=>$new_width, "height"=>$new_height, "id"=>$image_obj->id "width"=>$new_width, "height"=>$new_height, "id"=>$image_obj->id
) ));
);
log_info("resize", "Resized Image #{$image_obj->id} - New hash: {$new_hash}"); log_info("resize", "Resized Image #{$image_obj->id} - New hash: {$new_hash}");
} }
/**
* Check Memory usage limits
*
* Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024);
* New check: memory_use = width * height * (bits per channel) * channels * 2.5
*
* It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4)
* We need to consider the size that we are GOING TO instead.
*
* The factor of 2.5 is simply a rough guideline.
* http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize
*
* @param $info
* @return int
*/
private function calc_memory_use($info) {
if (isset($info['bits']) && isset($info['channels'])) {
return $memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024;
}
else {
// If we don't have bits and channel info from the image then assume default values
// of 8 bits per color and 4 channels (R,G,B,A) -- ie: regular 24-bit color
return $memory_use = ($info[0] * $info[1] * 1 * 4 * 2.5) / 1024;
}
}
/**
* @param Image $image_obj
* @param $width
* @param $height
* @return int[]
*/
private function calc_new_size(Image $image_obj, $width, $height) {
/* Calculate the new size of the image */
if ($height > 0 && $width > 0) {
$new_height = $height;
$new_width = $width;
return array($new_height, $new_width);
} else {
// Scale the new image
if ($width == 0) $factor = $height / $image_obj->height;
elseif ($height == 0) $factor = $width / $image_obj->width;
else $factor = min($width / $image_obj->width, $height / $image_obj->height);
$new_width = round($image_obj->width * $factor);
$new_height = round($image_obj->height * $factor);
return array($new_height, $new_width);
}
}
} }

View File

@ -29,7 +29,7 @@ class RSS_Comments extends Extension {
users.id as user_id, users.name as user_name, users.id as user_id, users.name as user_name,
comments.comment as comment, comments.id as comment_id, comments.comment as comment, comments.id as comment_id,
comments.image_id as image_id, comments.owner_ip as poster_ip, comments.image_id as image_id, comments.owner_ip as poster_ip,
UNIX_TIMESTAMP(posted) AS posted_timestamp comments.posted as posted
FROM comments FROM comments
LEFT JOIN users ON comments.owner_id=users.id LEFT JOIN users ON comments.owner_id=users.id
ORDER BY comments.id DESC ORDER BY comments.id DESC
@ -42,7 +42,7 @@ class RSS_Comments extends Extension {
$comment_id = $comment['comment_id']; $comment_id = $comment['comment_id'];
$link = make_http(make_link("post/view/$image_id")); $link = make_http(make_link("post/view/$image_id"));
$owner = html_escape($comment['user_name']); $owner = html_escape($comment['user_name']);
$posted = date(DATE_RSS, $comment['posted_timestamp']); $posted = date(DATE_RSS, strtotime($comment['posted']));
$comment = html_escape($comment['comment']); $comment = html_escape($comment['comment']);
$content = html_escape("$owner: $comment"); $content = html_escape("$owner: $comment");

View File

@ -98,7 +98,7 @@ class RSS_Images extends Extension {
$owner = $image->get_owner(); $owner = $image->get_owner();
$thumb_url = $image->get_thumb_link(); $thumb_url = $image->get_thumb_link();
$image_url = $image->get_image_link(); $image_url = $image->get_image_link();
$posted = date(DATE_RSS, $image->posted_timestamp); $posted = date(DATE_RSS, strtotime($image->posted));
$content = html_escape( $content = html_escape(
"<p>" . $this->theme->build_thumb_html($image) . "</p>" . "<p>" . $this->theme->build_thumb_html($image) . "</p>" .
"<p>Uploaded by " . html_escape($owner->name) . "</p>" "<p>Uploaded by " . html_escape($owner->name) . "</p>"

View File

@ -1,6 +1,6 @@
<?php <?php
class SetupTest extends ShimmiePHPUnitTestCase { class SetupTest extends ShimmiePHPUnitTestCase {
function testNiceUrlsTest() { public function testNiceUrlsTest() {
# XXX: this only checks that the text is "ok", to check # XXX: this only checks that the text is "ok", to check
# for a bug where it was coming out as "\nok"; it doesn't # for a bug where it was coming out as "\nok"; it doesn't
# check that niceurls actually work # check that niceurls actually work
@ -9,27 +9,27 @@ class SetupTest extends ShimmiePHPUnitTestCase {
$this->assert_no_content("\n"); $this->assert_no_content("\n");
} }
function testAuthAnon() { public function testAuthAnon() {
$this->get_page('setup'); $this->get_page('setup');
$this->assert_response(403); $this->assert_response(403);
$this->assert_title("Permission Denied"); $this->assert_title("Permission Denied");
} }
function testAuthUser() { public function testAuthUser() {
$this->log_in_as_user(); $this->log_in_as_user();
$this->get_page('setup'); $this->get_page('setup');
$this->assert_response(403); $this->assert_response(403);
$this->assert_title("Permission Denied"); $this->assert_title("Permission Denied");
} }
function testAuthAdmin() { public function testAuthAdmin() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page('setup'); $this->get_page('setup');
$this->assert_title("Shimmie Setup"); $this->assert_title("Shimmie Setup");
$this->assert_text("General"); $this->assert_text("General");
} }
function testAdvanced() { public function testAdvanced() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page('setup/advanced'); $this->get_page('setup/advanced');
$this->assert_title("Shimmie Setup"); $this->assert_title("Shimmie Setup");

View File

@ -19,8 +19,6 @@
class _SafeImage { class _SafeImage {
#{"id":"2","height":"768","width":"1024","hash":"71cdfaabbcdad3f777e0b60418532e94","filesize":"439561","filename":"HeilAmu.png","ext":"png","owner_ip":"0.0.0.0","posted":"0000-00-00 00:00:00","source":null,"locked":"N","owner_id":"0","rating":"u","numeric_score":"0","text_score":"0","notes":"0","favorites":"0","posted_timestamp":-62169955200,"tag_array":["cat","kunimitsu"]}
public $id; public $id;
public $height; public $height;
public $width; public $width;
@ -39,7 +37,7 @@ class _SafeImage {
$this->hash = $img->hash; $this->hash = $img->hash;
$this->filesize = $img->filesize; $this->filesize = $img->filesize;
$this->ext = $img->ext; $this->ext = $img->ext;
$this->posted = $img->posted_timestamp; $this->posted = strtotime($img->posted);
$this->source = $img->source; $this->source = $img->source;
$this->owner_id = $img->owner_id; $this->owner_id = $img->owner_id;
$this->tags = $img->get_tag_array(); $this->tags = $img->get_tag_array();
@ -48,52 +46,30 @@ class _SafeImage {
class ShimmieApi extends Extension { class ShimmieApi extends Extension {
public function onPageRequest(PageRequestEvent $event) { public function onPageRequest(PageRequestEvent $event) {
global $database, $page, $user; global $page, $user;
if($event->page_matches("api/shimmie")) { if($event->page_matches("api/shimmie")) {
$page->set_mode("data"); $page->set_mode("data");
$page->set_type("text/plain"); $page->set_type("text/plain");
if(!$event->page_matches("api/shimmie/get_tags") && !$event->page_matches("api/shimmie/get_image") && !$event->page_matches("api/shimmie/find_images") && !$event->page_matches("api/shimmie/get_user")){
$page->set_mode("redirect");
$page->set_redirect(make_link("ext_doc/shimmie_api"));
}
if($event->page_matches("api/shimmie/get_tags")){ if($event->page_matches("api/shimmie/get_tags")){
$arg = $event->get_arg(0); $tag = $event->get_arg(0);
if(empty($tag) && isset($_GET['tag'])) $tag = $_GET['tag'];
if(!empty($arg)){ $res = $this->api_get_tags($tag);
$all = $database->get_all(
"SELECT tag FROM tags WHERE tag LIKE ?",
array($arg."%"));
}
elseif(isset($_GET['tag'])){
$all = $database->get_all(
"SELECT tag FROM tags WHERE tag LIKE ?",
array($_GET['tag']."%"));
}
else {
$all = $database->get_all("SELECT tag FROM tags");
}
$res = array();
foreach($all as $row) {$res[] = $row["tag"];}
$page->set_data(json_encode($res)); $page->set_data(json_encode($res));
} }
if($event->page_matches("api/shimmie/get_image")) { elseif($event->page_matches("api/shimmie/get_image")) {
$arg = $event->get_arg(0); $arg = $event->get_arg(0);
if(!empty($arg)){ if(empty($arg) && isset($_GET['id'])) $arg = $_GET['id'];
$image = Image::by_id(int_escape($event->get_arg(0))); $image = Image::by_id(int_escape($arg));
}
elseif(isset($_GET['id'])){
$image = Image::by_id(int_escape($_GET['id']));
}
// FIXME: handle null image // FIXME: handle null image
$image->get_tag_array(); // tag data isn't loaded into the object until necessary $image->get_tag_array(); // tag data isn't loaded into the object until necessary
$safe_image = new _SafeImage($image); $safe_image = new _SafeImage($image);
$page->set_data(json_encode($safe_image)); $page->set_data(json_encode($safe_image));
} }
if($event->page_matches("api/shimmie/find_images")) { elseif($event->page_matches("api/shimmie/find_images")) {
$search_terms = $event->get_search_terms(); $search_terms = $event->get_search_terms();
$page_number = $event->get_page_number(); $page_number = $event->get_page_number();
$page_size = $event->get_page_size(); $page_size = $event->get_page_size();
@ -106,7 +82,7 @@ class ShimmieApi extends Extension {
$page->set_data(json_encode($safe_images)); $page->set_data(json_encode($safe_images));
} }
if($event->page_matches("api/shimmie/get_user")) { elseif($event->page_matches("api/shimmie/get_user")) {
$query = $user->id; $query = $user->id;
$type = "id"; $type = "id";
if($event->count_args() == 1) { if($event->count_args() == 1) {
@ -121,9 +97,47 @@ class ShimmieApi extends Extension {
$type = "name"; $type = "name";
} }
$all = $this->api_get_user($type, $query);
$page->set_data(json_encode($all));
}
else {
$page->set_mode("redirect");
$page->set_redirect(make_link("ext_doc/shimmie_api"));
}
}
}
/**
* @param string $arg
* @return string[]
*/
private function api_get_tags($arg) {
global $database;
if (!empty($arg)) {
$all = $database->get_all("SELECT tag FROM tags WHERE tag LIKE ?", array($arg . "%"));
} else {
$all = $database->get_all("SELECT tag FROM tags");
}
$res = array();
foreach ($all as $row) {
$res[] = $row["tag"];
}
return $res;
}
/**
* @param $type
* @param $query
* @return array
*/
private function api_get_user($type, $query) {
global $database;
$all = $database->get_row( $all = $database->get_row(
"SELECT id, name, joindate, class FROM users WHERE $type=?", "SELECT id, name, joindate, class FROM users WHERE $type=?",
array($query)); array($query)
);
if (!empty($all)) { if (!empty($all)) {
//FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice.. //FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice..
@ -153,9 +167,7 @@ class ShimmieApi extends Extension {
} }
} }
} }
$page->set_data(json_encode($all)); return $all;
}
}
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
class ShimmieApiTest extends ShimmiePHPUnitTestCase { class ShimmieApiTest extends ShimmiePHPUnitTestCase {
function testAPI() { public function testAPI() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");

View File

@ -1,6 +1,6 @@
<?php <?php
class SiteDescriptionTest extends ShimmiePHPUnitTestCase { class SiteDescriptionTest extends ShimmiePHPUnitTestCase {
function testSiteDescription() { public function testSiteDescription() {
global $config, $page; global $config, $page;
$config->set_string("site_description", "A Shimmie testbed"); $config->set_string("site_description", "A Shimmie testbed");
$this->get_page("post/list"); $this->get_page("post/list");
@ -10,7 +10,7 @@ class SiteDescriptionTest extends ShimmiePHPUnitTestCase {
); );
} }
function testSiteKeywords() { public function testSiteKeywords() {
global $config, $page; global $config, $page;
$config->set_string("site_keywords", "foo,bar,baz"); $config->set_string("site_keywords", "foo,bar,baz");
$this->get_page("post/list"); $this->get_page("post/list");

View File

@ -56,7 +56,7 @@ class XMLSitemap extends Extension
$latestimages_urllist[$arrayid] = "post/view/$image->id"; $latestimages_urllist[$arrayid] = "post/view/$image->id";
} }
$this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", $image->posted_timestamp)); $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted)));
/* --- Display page --- */ /* --- Display page --- */
// when sitemap is ok, display it from the file // when sitemap is ok, display it from the file
@ -88,7 +88,7 @@ class XMLSitemap extends Extension
// create url from image id's // create url from image id's
$latestimages_urllist[$arrayid] = "post/view/$image->id"; $latestimages_urllist[$arrayid] = "post/view/$image->id";
} }
$this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", $image->posted_timestamp)); $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted)));
/* --- Add other tags --- */ /* --- Add other tags --- */
$other_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 21,10000000"); $other_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 21,10000000");
@ -106,7 +106,7 @@ class XMLSitemap extends Extension
// create url from image id's // create url from image id's
$otherimages[$arrayid] = "post/view/$image->id"; $otherimages[$arrayid] = "post/view/$image->id";
} }
$this->add_sitemap_queue($otherimages, "monthly", "0.6", date("Y-m-d", $image->posted_timestamp)); $this->add_sitemap_queue($otherimages, "monthly", "0.6", date("Y-m-d", strtotime($image->posted)));
/* --- Display page --- */ /* --- Display page --- */

View File

@ -1,6 +1,6 @@
<?php <?php
class XMLSitemapTest extends ShimmiePHPUnitTestCase { class XMLSitemapTest extends ShimmiePHPUnitTestCase {
function testBasic() { public function testBasic() {
# this will implicitly check that there are no # this will implicitly check that there are no
# PHP-level error messages # PHP-level error messages
$this->get_page('sitemap.xml'); $this->get_page('sitemap.xml');

View File

@ -95,11 +95,38 @@ class SourceSetEvent extends Event {
class TagSetEvent extends Event { class TagSetEvent extends Event {
/** @var \Image */ /** @var \Image */
public $image; public $image;
var $tags; public $tags;
public $metatags;
public function __construct(Image $image, $tags) { public function __construct(Image $image, $tags) {
$this->image = $image; $this->image = $image;
$this->tags = Tag::explode($tags);
$this->tags = array();
$this->metatags = array();
//tags need to be sanitised, alias checked & have metatags removed before being passed to onTagSet
$tag_array = Tag::explode($tags);
$tag_array = array_map(array('Tag', 'sanitise'), $tag_array);
$tag_array = Tag::resolve_aliases($tag_array);
foreach($tag_array as $tag) {
if((strpos($tag, ':') === FALSE) && (strpos($tag, '=') === FALSE)) {
//Tag doesn't contain : or =, meaning it can't possibly be a metatag.
//This should help speed wise, as it avoids running every single tag through a bunch of preg_match instead.
array_push($this->tags, $tag);
continue;
}
$ttpe = new TagTermParseEvent($tag, $this->image->id, FALSE); //Only check for metatags, don't parse. Parsing is done after set_tags.
send_event($ttpe);
//seperate tags from metatags
if(!$ttpe->is_metatag()) {
array_push($this->tags, $tag);
}else{
array_push($this->metatags, $tag);
}
}
} }
} }
@ -131,14 +158,17 @@ class LockSetEvent extends Event {
* Signal that a tag term needs parsing * Signal that a tag term needs parsing
*/ */
class TagTermParseEvent extends Event { class TagTermParseEvent extends Event {
var $term = null; public $term = NULL; //tag
var $id = null; public $id = NULL; //image_id
/** @var bool */ /** @var bool */
public $metatag = false; public $metatag = FALSE;
/** @var bool */
public $parse = TRUE; //marks the tag to be parsed, and not just checked if valid metatag
public function __construct($term, $id) { public function __construct($term, $id, $parse) {
$this->term = $term; $this->term = $term;
$this->id = $id; $this->id = $id;
$this->parse = $parse;
} }
/** /**
@ -215,6 +245,7 @@ class TagEdit extends Extension {
if($user->can("edit_image_tag") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { if($user->can("edit_image_tag") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) {
$event->image->set_tags($event->tags); $event->image->set_tags($event->tags);
} }
$event->image->parse_metatags($event->metatags, $event->image->id);
} }
public function onSourceSet(SourceSetEvent $event) { public function onSourceSet(SourceSetEvent $event) {
@ -257,7 +288,7 @@ class TagEdit extends Extension {
public function onTagTermParse(TagTermParseEvent $event) { public function onTagTermParse(TagTermParseEvent $event) {
$matches = array(); $matches = array();
if(preg_match("/^source[=|:](.*)$/i", $event->term, $matches)) { if(preg_match("/^source[=|:](.*)$/i", $event->term, $matches) && $event->parse) {
$source = ($matches[1] !== "none" ? $matches[1] : null); $source = ($matches[1] !== "none" ? $matches[1] : null);
send_event(new SourceSetEvent(Image::by_id($event->id), $source)); send_event(new SourceSetEvent(Image::by_id($event->id), $source));
} }

View File

@ -1,6 +1,6 @@
<?php <?php
class TagEditTest extends ShimmiePHPUnitTestCase { class TagEditTest extends ShimmiePHPUnitTestCase {
function testTagEdit() { public function testTagEdit() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");
@ -21,7 +21,7 @@ class TagEditTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testSourceEdit() { public function testSourceEdit() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");
@ -50,8 +50,10 @@ class TagEditTest extends ShimmiePHPUnitTestCase {
/* /*
* FIXME: Mass Tagger seems to be broken, and this test case always fails. * FIXME: Mass Tagger seems to be broken, and this test case always fails.
* */
function testMassEdit() { public function testMassEdit() {
$this->markTestIncomplete();
$this->log_in_as_admin(); $this->log_in_as_admin();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
@ -71,6 +73,5 @@ class TagEditTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
*/
} }

View File

@ -74,7 +74,7 @@ class TagEditCloud extends Extension {
$ignore_tags = Tag::explode($config->get_string("tageditcloud_ignoretags")); $ignore_tags = Tag::explode($config->get_string("tageditcloud_ignoretags"));
if(class_exists("TagCategories")){ if(ext_is_live("TagCategories")) {
$categories = $database->get_all("SELECT category, color FROM image_tag_categories"); $categories = $database->get_all("SELECT category, color FROM image_tag_categories");
$cat_color = array(); $cat_color = array();
foreach($categories as $row) { foreach($categories as $row) {
@ -82,8 +82,6 @@ class TagEditCloud extends Extension {
} }
} }
$tag_data = null;
switch($sort_method) { switch($sort_method) {
case 'a': case 'a':
case 'p': case 'p':
@ -99,7 +97,9 @@ class TagEditCloud extends Extension {
break; break;
case 'r': case 'r':
$relevant_tags = array_diff($image->get_tag_array(),$ignore_tags); $relevant_tags = array_diff($image->get_tag_array(),$ignore_tags);
if(count($relevant_tags) > 0) { if(count($relevant_tags) == 0) {
return null;
}
$relevant_tags = implode(",",array_map(array($database,"escape"),$relevant_tags)); $relevant_tags = implode(",",array_map(array($database,"escape"),$relevant_tags));
$tag_data = $database->get_all(" $tag_data = $database->get_all("
SELECT t2.tag AS tag, COUNT(image_id) AS count, FLOOR(LN(LN(COUNT(image_id) - :tag_min1 + 1)+1)*150)/200 AS scaled SELECT t2.tag AS tag, COUNT(image_id) AS count, FLOOR(LN(LN(COUNT(image_id) - :tag_min1 + 1)+1)*150)/200 AS scaled
@ -112,18 +112,14 @@ class TagEditCloud extends Extension {
ORDER BY count DESC ORDER BY count DESC
LIMIT :limit", LIMIT :limit",
array("tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count)); array("tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count));
}
break; break;
} }
if(is_null($tag_data)) {
return null;
}
$counter = 1; $counter = 1;
foreach($tag_data as $row) { foreach($tag_data as $row) {
$full_tag = $row['tag']; $full_tag = $row['tag'];
if(class_exists("TagCategories")){ if(ext_is_live("TagCategories")){
$tc = explode(':',$row['tag']); $tc = explode(':',$row['tag']);
if(isset($tc[1]) && isset($cat_color[$tc[0]])){ if(isset($tc[1]) && isset($cat_color[$tc[0]])){
$h_tag = html_escape($tc[1]); $h_tag = html_escape($tc[1]);

View File

@ -329,7 +329,8 @@ class Tag_History extends Extension {
$image = Image::by_id($stored_image_id); $image = Image::by_id($stored_image_id);
if ( ! $image instanceof Image) { if ( ! $image instanceof Image) {
throw new ImageDoesNotExist("Error: cannot find any image with the ID = ". $stored_image_id); continue;
//throw new ImageDoesNotExist("Error: cannot find any image with the ID = ". $stored_image_id);
} }
log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']');

View File

@ -1,12 +1,13 @@
<?php <?php
class TagHistoryTest extends ShimmiePHPUnitTestCase { class TagHistoryTest extends ShimmiePHPUnitTestCase {
function testTagHistory() { public function testTagHistory() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id"); $this->get_page("post/view/$image_id");
$this->assert_title("Image $image_id: pbx"); $this->assert_title("Image $image_id: pbx");
/* $this->markTestIncomplete();
// FIXME // FIXME
$this->set_field("tag_edit__tags", "new"); $this->set_field("tag_edit__tags", "new");
$this->click("Set"); $this->click("Set");
@ -15,7 +16,6 @@ class TagHistoryTest extends ShimmiePHPUnitTestCase {
$this->assert_text("new (Set by demo"); $this->assert_text("new (Set by demo");
$this->click("Revert To"); $this->click("Revert To");
$this->assert_title("Image $image_id: pbx"); $this->assert_title("Image $image_id: pbx");
*/
$this->get_page("tag_history/all/1"); $this->get_page("tag_history/all/1");
$this->assert_title("Global Tag History"); $this->assert_title("Global Tag History");

View File

@ -2,7 +2,7 @@
class TagListTest extends ShimmiePHPUnitTestCase { class TagListTest extends ShimmiePHPUnitTestCase {
var $pages = array("map", "alphabetic", "popularity", "categories"); var $pages = array("map", "alphabetic", "popularity", "categories");
function testTagList() { public function testTagList() {
$this->get_page('tags/map'); $this->get_page('tags/map');
$this->assert_title('Tag List'); $this->assert_title('Tag List');
@ -18,7 +18,7 @@ class TagListTest extends ShimmiePHPUnitTestCase {
# FIXME: test that these show the right stuff # FIXME: test that these show the right stuff
} }
function testMinCount() { public function testMinCount() {
foreach($this->pages as $page) { foreach($this->pages as $page) {
$this->get_page("tags/$page?mincount=999999"); $this->get_page("tags/$page?mincount=999999");
$this->assert_title("Tag List"); $this->assert_title("Tag List");

View File

@ -1,6 +1,6 @@
<?php <?php
class TipsTest extends ShimmiePHPUnitTestCase { class TipsTest extends ShimmiePHPUnitTestCase {
function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->log_in_as_admin(); $this->log_in_as_admin();
@ -15,7 +15,7 @@ class TipsTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testImageless() { public function testImageless() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("tips/list"); $this->get_page("tips/list");
@ -37,7 +37,7 @@ class TipsTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testImaged() { public function testImaged() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("tips/list"); $this->get_page("tips/list");
@ -59,7 +59,7 @@ class TipsTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testDisabled() { public function testDisabled() {
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("tips/list"); $this->get_page("tips/list");

View File

@ -234,8 +234,8 @@ class Upload extends Extension {
} }
/** /**
* @param string|int $id * @param int $id
* @return array * @return string[]
*/ */
private function tags_for_upload_slot($id) { private function tags_for_upload_slot($id) {
if(isset($_POST["tags$id"])) { if(isset($_POST["tags$id"])) {

View File

@ -1,18 +1,18 @@
<?php <?php
class UploadTest extends ShimmiePHPUnitTestCase { class UploadTest extends ShimmiePHPUnitTestCase {
function testUploadPage() { public function testUploadPage() {
$this->log_in_as_user(); $this->log_in_as_user();
$this->get_page("upload"); $this->get_page("upload");
$this->assert_title("Upload"); $this->assert_title("Upload");
} }
function testUpload() { public function testUpload() {
$this->log_in_as_user(); $this->log_in_as_user();
$this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
} }
function testRejectDupe() { public function testRejectDupe() {
$this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
try { try {
@ -23,7 +23,7 @@ class UploadTest extends ShimmiePHPUnitTestCase {
} }
} }
function testRejectUnknownFiletype() { public function testRejectUnknownFiletype() {
try { try {
$this->post_image("index.php", "test"); $this->post_image("index.php", "test");
} }
@ -32,16 +32,16 @@ class UploadTest extends ShimmiePHPUnitTestCase {
} }
} }
function testRejectHuge() { public function testRejectHuge() {
/* $this->markTestIncomplete();
// FIXME: huge.dat is rejected for other reasons; manual testing shows that this works // FIXME: huge.dat is rejected for other reasons; manual testing shows that this works
file_put_contents("huge.dat", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024*1024*3)); file_put_contents("huge.dat", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024*1024*3));
$image_id_4 = $this->post_image("index.php", "test"); $this->post_image("index.php", "test");
$this->assert_response(200); $this->assert_response(200);
$this->assert_title("Upload Status"); $this->assert_title("Upload Status");
$this->assert_text("File too large"); $this->assert_text("File too large");
unlink("huge.dat"); unlink("huge.dat");
*/
} }
} }

View File

@ -80,6 +80,9 @@ class UserCreationException extends SCoreException {}
class NullUserException extends SCoreException {} class NullUserException extends SCoreException {}
class UserPage extends Extension { class UserPage extends Extension {
/** @var UserPageTheme $theme */
var $theme;
public function onInitExt(InitExtEvent $event) { public function onInitExt(InitExtEvent $event) {
global $config; global $config;
$config->set_default_bool("login_signup_enabled", true); $config->set_default_bool("login_signup_enabled", true);
@ -94,64 +97,22 @@ class UserPage extends Extension {
public function onPageRequest(PageRequestEvent $event) { public function onPageRequest(PageRequestEvent $event) {
global $config, $page, $user; global $config, $page, $user;
// user info is shown on all pages $this->show_user_info();
if($user->is_anonymous()) {
$this->theme->display_login_block($page);
}
else {
$ubbe = new UserBlockBuildingEvent();
send_event($ubbe);
ksort($ubbe->parts);
$this->theme->display_user_block($page, $user, $ubbe->parts);
}
if($event->page_matches("user_admin")) { if($event->page_matches("user_admin")) {
if($event->get_arg(0) == "login") { if($event->get_arg(0) == "login") {
if(isset($_POST['user']) && isset($_POST['pass'])) { if(isset($_POST['user']) && isset($_POST['pass'])) {
$this->login($page); $this->page_login($_POST['user'], $_POST['pass']);
} }
else { else {
$this->theme->display_login_page($page); $this->theme->display_login_page($page);
} }
} }
else if($event->get_arg(0) == "recover") { else if($event->get_arg(0) == "recover") {
$user = User::by_name($_POST['username']); $this->page_recover($_POST['username']);
if(is_null($user)) {
$this->theme->display_error(404, "Error", "There's no user with that name");
}
else if(is_null($user->email)) {
$this->theme->display_error(400, "Error", "That user has no registered email address");
}
else {
// send email
}
} }
else if($event->get_arg(0) == "create") { else if($event->get_arg(0) == "create") {
if(!$config->get_bool("login_signup_enabled")) { $this->page_create();
$this->theme->display_signups_disabled($page);
}
else if(!isset($_POST['name'])) {
$this->theme->display_signup_page($page);
}
else if($_POST['pass1'] != $_POST['pass2']) {
$this->theme->display_error(400, "Password Mismatch", "Passwords don't match");
}
else {
try {
if(!captcha_check()) {
throw new UserCreationException("Error in captcha");
}
$uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email']);
send_event($uce);
$this->set_login_cookie($uce->username, $uce->password);
$page->set_mode("redirect");
$page->set_redirect(make_link("user"));
}
catch(UserCreationException $ex) {
$this->theme->display_error(400, "User Creation Error", $ex->getMessage());
}
}
} }
else if($event->get_arg(0) == "list") { else if($event->get_arg(0) == "list") {
// select users.id,name,joindate,admin, // select users.id,name,joindate,admin,
@ -165,24 +126,7 @@ class UserPage extends Extension {
$this->theme->display_user_list($page, User::by_list(0), $user); $this->theme->display_user_list($page, User::by_list(0), $user);
} }
else if($event->get_arg(0) == "logout") { else if($event->get_arg(0) == "logout") {
$page->add_cookie("session", "", time()+60*60*24*$config->get_int('login_memory'), "/"); $this->page_logout();
if(CACHE_HTTP || SPEED_HAX) {
# to keep as few versions of content as possible,
# make cookies all-or-nothing
$page->add_cookie("user", "", time()+60*60*24*$config->get_int('login_memory'), "/");
}
log_info("user", "Logged out");
$page->set_mode("redirect");
// Try forwarding to same page on logout unless user comes from registration page
if ($config->get_int("user_loginshowprofile",0) == 0 &&
isset($_SERVER['HTTP_REFERER']) &&
strstr($_SERVER['HTTP_REFERER'], "post/"))
{
$page->set_redirect ($_SERVER['HTTP_REFERER']);
} else {
$page->set_redirect(make_link());
}
} }
if(!$user->check_auth_token()) { if(!$user->check_auth_token()) {
@ -369,8 +313,8 @@ class UserPage extends Extension {
global $user; global $user;
$matches = array(); $matches = array();
if(preg_match("/^(poster|user)[=|:](.*)$/i", $event->term, $matches)) { if(preg_match("/^(?:poster|user)[=|:](.*)$/i", $event->term, $matches)) {
$duser = User::by_name($matches[2]); $duser = User::by_name($matches[1]);
if(!is_null($duser)) { if(!is_null($duser)) {
$user_id = $duser->id; $user_id = $duser->id;
} }
@ -379,25 +323,33 @@ class UserPage extends Extension {
} }
$event->add_querylet(new Querylet("images.owner_id = $user_id")); $event->add_querylet(new Querylet("images.owner_id = $user_id"));
} }
else if(preg_match("/^(poster|user)_id[=|:]([0-9]+)$/i", $event->term, $matches)) { else if(preg_match("/^(?:poster|user)_id[=|:]([0-9]+)$/i", $event->term, $matches)) {
$user_id = int_escape($matches[2]); $user_id = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.owner_id = $user_id")); $event->add_querylet(new Querylet("images.owner_id = $user_id"));
} }
else if($user->can("view_ip") && preg_match("/^(poster|user)_ip[=|:]([0-9\.]+)$/i", $event->term, $matches)) { else if($user->can("view_ip") && preg_match("/^(?:poster|user)_ip[=|:]([0-9\.]+)$/i", $event->term, $matches)) {
$user_ip = $matches[2]; // FIXME: ip_escape? $user_ip = $matches[1]; // FIXME: ip_escape?
$event->add_querylet(new Querylet("images.owner_ip = '$user_ip'")); $event->add_querylet(new Querylet("images.owner_ip = '$user_ip'"));
} }
} }
private function show_user_info() {
global $user, $page;
// user info is shown on all pages
if ($user->is_anonymous()) {
$this->theme->display_login_block($page);
} else {
$ubbe = new UserBlockBuildingEvent();
send_event($ubbe);
ksort($ubbe->parts);
$this->theme->display_user_block($page, $user, $ubbe->parts);
}
}
// }}} // }}}
// Things done *with* the user {{{ // Things done *with* the user {{{
/** private function page_login($name, $pass) {
* @param Page $page global $config, $user, $page;
*/
private function login(Page $page) {
global $config, $user;
$name = $_POST['user'];
$pass = $_POST['pass'];
if(empty($name) || empty($pass)) { if(empty($name) || empty($pass)) {
$this->theme->display_error(400, "Error", "Username or password left blank"); $this->theme->display_error(400, "Error", "Username or password left blank");
@ -427,12 +379,72 @@ class UserPage extends Extension {
} }
} }
private function page_logout() {
global $page, $config;
$page->add_cookie("session", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/");
if (CACHE_HTTP || SPEED_HAX) {
# to keep as few versions of content as possible,
# make cookies all-or-nothing
$page->add_cookie("user", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/");
}
log_info("user", "Logged out");
$page->set_mode("redirect");
// Try forwarding to same page on logout unless user comes from registration page
if ($config->get_int("user_loginshowprofile", 0) == 0 &&
isset($_SERVER['HTTP_REFERER']) &&
strstr($_SERVER['HTTP_REFERER'], "post/")
) {
$page->set_redirect($_SERVER['HTTP_REFERER']);
} else {
$page->set_redirect(make_link());
}
}
/**
* @param string $username
*/
private function page_recover($username) {
$user = User::by_name($username);
if (is_null($user)) {
$this->theme->display_error(404, "Error", "There's no user with that name");
} else if (is_null($user->email)) {
$this->theme->display_error(400, "Error", "That user has no registered email address");
} else {
// send email
}
}
private function page_create() {
global $config, $page;
if (!$config->get_bool("login_signup_enabled")) {
$this->theme->display_signups_disabled($page);
} else if (!isset($_POST['name'])) {
$this->theme->display_signup_page($page);
} else if ($_POST['pass1'] != $_POST['pass2']) {
$this->theme->display_error(400, "Password Mismatch", "Passwords don't match");
} else {
try {
if (!captcha_check()) {
throw new UserCreationException("Error in captcha");
}
$uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email']);
send_event($uce);
$this->set_login_cookie($uce->username, $uce->password);
$page->set_mode("redirect");
$page->set_redirect(make_link("user"));
} catch (UserCreationException $ex) {
$this->theme->display_error(400, "User Creation Error", $ex->getMessage());
}
}
}
/** /**
* @param UserCreationEvent $event * @param UserCreationEvent $event
* @throws UserCreationException * @throws UserCreationException
*/ */
private function check_user_creation(UserCreationEvent $event) private function check_user_creation(UserCreationEvent $event) {
{
$name = $event->username; $name = $event->username;
//$pass = $event->password; //$pass = $event->password;
//$email = $event->email; //$email = $event->email;
@ -450,8 +462,7 @@ class UserPage extends Extension {
} }
} }
private function create_user(UserCreationEvent $event) private function create_user(UserCreationEvent $event) {
{
global $database, $user; global $database, $user;
$email = (!empty($event->email)) ? $event->email : null; $email = (!empty($event->email)) ? $event->email : null;

View File

@ -1,6 +1,6 @@
<?php <?php
class UserPageTest extends ShimmiePHPUnitTestCase { class UserPageTest extends ShimmiePHPUnitTestCase {
function testUserPage() { public function testUserPage() {
$this->get_page('user'); $this->get_page('user');
$this->assert_title("Not Logged In"); $this->assert_title("Not Logged In");
$this->assert_no_text("Options"); $this->assert_no_text("Options");

View File

@ -1,6 +1,11 @@
<?php <?php
class ViewTest extends ShimmiePHPUnitTestCase { class ViewTest extends ShimmiePHPUnitTestCase {
function testViewPage() { public function setUp() {
parent::setUp();
// FIXME: upload images
}
public function testViewPage() {
$this->log_in_as_user(); $this->log_in_as_user();
$image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test");
$image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2");
@ -9,8 +14,16 @@ class ViewTest extends ShimmiePHPUnitTestCase {
$this->get_page("post/view/$image_id_1"); $this->get_page("post/view/$image_id_1");
$this->assert_title("Image $image_id_1: test"); $this->assert_title("Image $image_id_1: test");
}
public function testPrevNext() {
$this->markTestIncomplete();
$this->log_in_as_user();
$image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test");
$image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2");
$image_id_3 = $this->post_image("tests/favicon.png", "test");
/*
$this->click("Prev"); $this->click("Prev");
$this->assert_title("Image $image_id_2: test2"); $this->assert_title("Image $image_id_2: test2");
@ -19,22 +32,35 @@ class ViewTest extends ShimmiePHPUnitTestCase {
$this->click("Next"); $this->click("Next");
$this->assert_title("Image not found"); $this->assert_title("Image not found");
*/ }
public function testView404() {
$this->log_in_as_user();
$image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test");
$image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2");
$image_id_3 = $this->post_image("tests/favicon.png", "test");
$idp1 = $image_id_3 + 1;
$this->get_page("post/view/$idp1"); $this->get_page("post/view/$idp1");
$this->assert_title('Image not found'); $this->assert_title('Image not found');
$this->get_page('post/view/-1'); $this->get_page('post/view/-1');
$this->assert_title('Image not found'); $this->assert_title('Image not found');
}
/* public function testNextSearchResult() {
* FIXME: this assumes Nice URLs. $this->markTestIncomplete();
*
$this->log_in_as_user();
$image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test");
$image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2");
$image_id_3 = $this->post_image("tests/favicon.png", "test");
// FIXME: this assumes Nice URLs.
# note: skips image #2 # note: skips image #2
$this->get_page("post/view/$image_id_1?search=test"); // FIXME: assumes niceurls $this->get_page("post/view/$image_id_1?search=test"); // FIXME: assumes niceurls
$this->click("Prev"); $this->click("Prev");
$this->assert_title("Image $image_id_3: test"); $this->assert_title("Image $image_id_3: test");
*/
} }
} }

View File

@ -1,13 +1,14 @@
<?php <?php
class WikiTest extends ShimmiePHPUnitTestCase { class WikiTest extends ShimmiePHPUnitTestCase {
function testIndex() { public function testIndex() {
$this->get_page("wiki"); $this->get_page("wiki");
$this->assert_title("Index"); $this->assert_title("Index");
$this->assert_text("This is a default page"); $this->assert_text("This is a default page");
} }
/* public function testAccess() {
function testAccess() { $this->markTestIncomplete();
global $config; global $config;
foreach(array("anon", "user", "admin") as $user) { foreach(array("anon", "user", "admin") as $user) {
foreach(array(false, true) as $allowed) { foreach(array(false, true) as $allowed) {
@ -39,7 +40,9 @@ class WikiTest extends ShimmiePHPUnitTestCase {
} }
} }
function testLock() { public function testLock() {
$this->markTestIncomplete();
global $config; global $config;
$config->set_bool("wiki_edit_anon", true); $config->set_bool("wiki_edit_anon", true);
$config->set_bool("wiki_edit_user", false); $config->set_bool("wiki_edit_user", false);
@ -73,7 +76,9 @@ class WikiTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testDefault() { public function testDefault() {
$this->markTestIncomplete();
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("wiki/wiki:default"); $this->get_page("wiki/wiki:default");
$this->assert_title("wiki:default"); $this->assert_title("wiki:default");
@ -90,7 +95,9 @@ class WikiTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
function testRevisions() { public function testRevisions() {
$this->markTestIncomplete();
$this->log_in_as_admin(); $this->log_in_as_admin();
$this->get_page("wiki/test"); $this->get_page("wiki/test");
$this->assert_title("test"); $this->assert_title("test");
@ -111,6 +118,5 @@ class WikiTest extends ShimmiePHPUnitTestCase {
$this->click("Delete All"); $this->click("Delete All");
$this->log_out(); $this->log_out();
} }
*/
} }

View File

@ -1,12 +1,12 @@
<?php <?php
class WordFilterTest extends ShimmiePHPUnitTestCase { class WordFilterTest extends ShimmiePHPUnitTestCase {
function setUp() { public function setUp() {
global $config; global $config;
parent::setUp(); parent::setUp();
$config->set_string("word_filter", "whore,nice lady\na duck,a kitten\n white ,\tspace\ninvalid"); $config->set_string("word_filter", "whore,nice lady\na duck,a kitten\n white ,\tspace\ninvalid");
} }
function _doThings($in, $out) { public function _doThings($in, $out) {
global $user; global $user;
$this->log_in_as_user(); $this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot");
@ -15,49 +15,49 @@ class WordFilterTest extends ShimmiePHPUnitTestCase {
$this->assert_text($out); $this->assert_text($out);
} }
function testRegular() { public function testRegular() {
$this->_doThings( $this->_doThings(
"posted by a whore", "posted by a whore",
"posted by a nice lady" "posted by a nice lady"
); );
} }
function testReplaceAll() { public function testReplaceAll() {
$this->_doThings( $this->_doThings(
"a whore is a whore is a whore", "a whore is a whore is a whore",
"a nice lady is a nice lady is a nice lady" "a nice lady is a nice lady is a nice lady"
); );
} }
function testMixedCase() { public function testMixedCase() {
$this->_doThings( $this->_doThings(
"monkey WhorE", "monkey WhorE",
"monkey nice lady" "monkey nice lady"
); );
} }
function testOnlyWholeWords() { public function testOnlyWholeWords() {
$this->_doThings( $this->_doThings(
"my name is whoretta", "my name is whoretta",
"my name is whoretta" "my name is whoretta"
); );
} }
function testMultipleWords() { public function testMultipleWords() {
$this->_doThings( $this->_doThings(
"I would like a duck", "I would like a duck",
"I would like a kitten" "I would like a kitten"
); );
} }
function testWhitespace() { public function testWhitespace() {
$this->_doThings( $this->_doThings(
"A colour is white", "A colour is white",
"A colour is space" "A colour is space"
); );
} }
function testIgnoreInvalid() { public function testIgnoreInvalid() {
$this->_doThings( $this->_doThings(
"The word was invalid", "The word was invalid",
"The word was invalid" "The word was invalid"

9
tests/test-deep.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
php \
-d extension.dir=/usr/lib/php/extensions/no-debug-non-zts-20121212/ \
-d extension=xdebug.so \
-d xdebug.profiler_output_dir=./data/prof/ \
-d xdebug.profiler_enable=1 \
./phpunit.phar \
--config tests/phpunit.xml \
--coverage-clover data/coverage.clover

Some files were not shown because too many files have changed in this diff Show More