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)$">
Deny from all <IfModule mod_authz_host.c>
Require all denied
</IfModule>
<IfModule !mod_authz_host.c>
Deny from all
</IfModule>
</FilesMatch> </FilesMatch>
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>

View File

@ -1,6 +1,9 @@
imports: imports:
- javascript - javascript
- php - php
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,47 +2,42 @@ language: php
sudo: false sudo: false
php: php:
# Here is where we can list the versions of PHP you want to test against - 5.4
# using major version aliases - 5.5
- 5.4 - 5.6
- 5.5 - nightly
- 5.6
- 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 - if [[ "$DB" == "pgsql" ]]; then echo '<?php define("DATABASE_DSN", "pgsql:user=postgres;password=;host=;dbname=shimmie");' > data/config/auto_install.conf.php ; fi
- if [[ "$DB" == "pgsql" ]]; then echo '<?php define("DATABASE_DSN", "pgsql:user=postgres;password=;host=;dbname=shimmie");' > data/config/auto_install.conf.php ; fi - if [[ "$DB" == "mysql" ]]; then mysql -e "SET GLOBAL general_log = 'ON';" -uroot; fi
- if [[ "$DB" == "mysql" ]]; then mysql -e "SET GLOBAL general_log = 'ON';" -uroot; fi - 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*
- ls /var/log/*mysql* - ls /var/log/*mysql*
- cat /var/log/mysql.err - cat /var/log/mysql.err
- cat /var/log/mysql.log - cat /var/log/mysql.log
- cat /var/log/mysql/error.log - cat /var/log/mysql/error.log
- cat /var/log/mysql/slow.log - cat /var/log/mysql/slow.log
- 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

@ -20,7 +20,7 @@ class Block {
*/ */
public $body; public $body;
/** /**
* Where the block should be placed. The default theme supports * Where the block should be placed. The default theme supports
* "main" and "left", other themes can add their own areas. * "main" and "left", other themes can add their own areas.
* *
@ -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,41 +870,25 @@ 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)
"), array("tag"=>$tag_querylets[0]->tag)); "), array("tag"=>$tag_querylets[0]->tag));
if(!empty($img_search->sql)) { if(!empty($img_search->sql)) {
$query->append_sql(" AND "); $query->append_sql(" AND ");
@ -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.*
SELECT images.*, UNIX_TIMESTAMP(posted) AS posted_timestamp FROM images
FROM tags, image_tags, images JOIN image_tags ON images.id=image_tags.image_id
WHERE JOIN tags ON image_tags.tag_id=tags.id
tag LIKE :tag0 WHERE tag LIKE :tag0
AND tags.id = image_tags.tag_id ", $tag_search->variables);
AND image_tags.image_id = images.id
",
$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

@ -23,7 +23,7 @@ class ArtistsTheme extends Themelet {
* @param null|int $artistID * @param null|int $artistID
* @param bool $is_admin * @param bool $is_admin
*/ */
public function sidebar_options(/*string*/ $mode, $artistID=NULL, $is_admin=FALSE){ public function sidebar_options(/*string*/ $mode, $artistID=NULL, $is_admin=FALSE) {
global $page, $user; global $page, $user;
$html = ""; $html = "";
@ -77,49 +77,44 @@ 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'];
$artistNotes = $artist['notes']; $artistNotes = $artist['notes'];
$artistID = $artist['id']; $artistID = $artist['id'];
// 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"]." "; }
} $aliasesString = rtrim($aliasesString);
$aliasesString = rtrim($aliasesString); $aliasesIDsString = rtrim($aliasesIDsString);
$aliasesIDsString = rtrim($aliasesIDsString);
// 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"]." "; }
} $membersString = rtrim($membersString);
$membersString = rtrim($membersString); $membersIDsString = rtrim($membersIDsString);
$membersIDsString = rtrim($membersIDsString);
// 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,113 +130,108 @@ 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()
{
global $page, $user;
$html = "<form action=".make_link("artist/create")." method='POST'>
".$user->get_auth_html()."
<table>
<tr><td>Name:</td><td><input type='text' name='name' /></td></tr>
<tr><td>Aliases:</td><td><input type='text' name='aliases' /></td></tr>
<tr><td>Members:</td><td><input type='text' name='members' /></td></tr>
<tr><td>URLs:</td><td><textarea name='urls'></textarea></td></tr>
<tr><td>Notes:</td><td><textarea name='notes'></textarea></td></tr>
<tr><td colspan='2'><input type='submit' value='Submit' /></td></tr>
</table>
";
$page->set_title("Artists");
$page->set_heading("Artists");
$page->add_block(new Block("Artists", $html, "main", 10));
} }
public function list_artists($artists, $pageNumber, $totalPages) public function new_artist_composer() {
{ global $page, $user;
global $user, $page;
$html = "<table id='poolsList' class='zebra'>". $html = "<form action=".make_link("artist/create")." method='POST'>
"<thead><tr>". ".$user->get_auth_html()."
"<th>Name</th>". <table>
"<th>Type</th>". <tr><td>Name:</td><td><input type='text' name='name' /></td></tr>
"<th>Last updater</th>". <tr><td>Aliases:</td><td><input type='text' name='aliases' /></td></tr>
"<th>Posts</th>"; <tr><td>Members:</td><td><input type='text' name='members' /></td></tr>
<tr><td>URLs:</td><td><textarea name='urls'></textarea></td></tr>
<tr><td>Notes:</td><td><textarea name='notes'></textarea></td></tr>
<tr><td colspan='2'><input type='submit' value='Submit' /></td></tr>
</table>
";
$page->set_title("Artists");
$page->set_heading("Artists");
$page->add_block(new Block("Artists", $html, "main", 10));
}
public function list_artists($artists, $pageNumber, $totalPages) {
global $user, $page;
if(!$user->is_anonymous()) $html .= "<th colspan='2'>Action</th>"; // space for edit link $html = "<table id='poolsList' class='zebra'>".
"<thead><tr>".
"<th>Name</th>".
"<th>Type</th>".
"<th>Last updater</th>".
"<th>Posts</th>";
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) {
if ($artist['type'] != 'artist') if ($artist['type'] != 'artist')
$artist['name'] = str_replace("_", " ", $artist['name']); $artist['name'] = str_replace("_", " ", $artist['name']);
$elementLink = "<a href='".make_link("artist/view/".$artist['artist_id'])."'>".str_replace("_", " ", $artist['name'])."</a>"; $elementLink = "<a href='".make_link("artist/view/".$artist['artist_id'])."'>".str_replace("_", " ", $artist['name'])."</a>";
//$artist_link = "<a href='".make_link("artist/view/".$artist['artist_id'])."'>".str_replace("_", " ", $artist['artist_name'])."</a>"; //$artist_link = "<a href='".make_link("artist/view/".$artist['artist_id'])."'>".str_replace("_", " ", $artist['artist_name'])."</a>";
$user_link = "<a href='".make_link("user/".$artist['user_name'])."'>".$artist['user_name']."</a>"; $user_link = "<a href='".make_link("user/".$artist['user_name'])."'>".$artist['user_name']."</a>";
$edit_link = "<a href='".make_link($editionLinkActionArray[$artist['type']].$artist['id'])."'>Edit</a>"; $edit_link = "<a href='".make_link($editionLinkActionArray[$artist['type']].$artist['id'])."'>Edit</a>";
$del_link = "<a href='".make_link($deletionLinkActionArray[$artist['type']].$artist['id'])."'>Delete</a>"; $del_link = "<a href='".make_link($deletionLinkActionArray[$artist['type']].$artist['id'])."'>Delete</a>";
$html .= "<tr>". $html .= "<tr>".
"<td class='left'>".$elementLink; "<td class='left'>".$elementLink;
//if ($artist['type'] == 'member') //if ($artist['type'] == 'member')
// $html .= " (member of ".$artist_link.")"; // $html .= " (member of ".$artist_link.")";
//if ($artist['type'] == 'alias') //if ($artist['type'] == 'alias')
// $html .= " (alias for ".$artist_link.")"; // $html .= " (alias for ".$artist_link.")";
$html .= "</td>". $html .= "</td>".
"<td>".$typeTextArray[$artist['type']]."</td>". "<td>".$typeTextArray[$artist['type']]."</td>".
"<td>".$user_link."</td>". "<td>".$user_link."</td>".
"<td>".$artist['posts']."</td>"; "<td>".$artist['posts']."</td>";
if(!$user->is_anonymous()) $html .= "<td>".$edit_link."</td>"; if(!$user->is_anonymous()) $html .= "<td>".$edit_link."</td>";
if($user->is_admin()) $html .= "<td>".$del_link."</td>"; if($user->is_admin()) $html .= "<td>".$del_link."</td>";
$html .= "</tr>"; $html .= "</tr>";
} }
$html .= "</tbody></table>"; $html .= "</tbody></table>";
$page->set_title("Artists"); $page->set_title("Artists");
$page->set_heading("Artists"); $page->set_heading("Artists");
$page->add_block(new Block("Artists", $html, "main", 10)); $page->add_block(new Block("Artists", $html, "main", 10));
$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" />
@ -249,277 +239,290 @@ 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("Artist Aliases", $html, "main", 20)); $page->add_block(new Block("Artist Aliases", $html, "main", 20));
} }
public function show_new_member_composer($artistID)
{
global $user;
$html = public function show_new_member_composer($artistID) {
' <form method="POST" action='.make_link("artist/member/add").'> global $user;
$html = '
<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" />
<input type="hidden" name="artistID" value='.$artistID.' /></td></tr> <input type="hidden" name="artistID" value='.$artistID.' /></td></tr>
<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("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>
<input type="hidden" name="artistID" value='.$artistID.' /></td></tr> <input type="hidden" name="artistID" value='.$artistID.' /></td></tr>
<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("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> <input type="text" name="alias" value="'.$alias['alias'].'" />
<input type="text" name="alias" value="'.$alias['alias'].'" /> <input type="hidden" name="aliasID" value="'.$alias['id'].'" />
<input type="hidden" name="aliasID" value="'.$alias['id'].'" /> <input type="submit" value="Submit" />
<input type="submit" value="Submit" /> </form>
</form> ';
';
global $page; global $page;
$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> <input type="text" name="url" value="'.$url['url'].'" />
<input type="text" name="url" value="'.$url['url'].'" /> <input type="hidden" name="urlID" value="'.$url['id'].'" />
<input type="hidden" name="urlID" value="'.$url['id'].'" /> <input type="submit" value="Submit" />
<input type="submit" value="Submit" /> </form>
</form> ';
';
global $page; global $page;
$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> <input type="text" name="name" value="'.$member['name'].'" />
<input type="text" name="name" value="'.$member['name'].'" /> <input type="hidden" name="memberID" value="'.$member['id'].'" />
<input type="hidden" name="memberID" value="'.$member['id'].'" /> <input type="submit" value="Submit" />
<input type="submit" value="Submit" /> </form>
</form> ';
';
global $page; global $page;
$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>";
$html = "<table id='poolsList' class='zebra'> $html = "<table id='poolsList' class='zebra'>
<thead> <thead>
<tr> <tr>
<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 .= " <tr>
$html .= "<th></th>";
$html .= " <tr>
</thead> </thead>
<tr> <tr>
<td class='left'>Name:</td> <td class='left'>Name:</td>
<td class='left'>".$artist_link."</td>"; <td class='left'>".$artist_link."</td>";
if ($userIsLogged) $html .= "<td></td>"; if ($userIsLogged) $html .= "<td></td>";
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>
<td class='left'>Aliases:</td>
<td class='left'>".$aliasViewLink."</td>";
if ($userIsLogged)
$html .= "<td class='left'>".$aliasEditLink."</td>";
if ($userIsAdmin) $html .= "<tr>
$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>";
if ($userIsAdmin) $html .= "<td></td>"; if ($userIsAdmin) $html .= "<td></td>";
//TODO how will notes be edited? On edit artist? (should there be an editartist?) or on a editnotes? //TODO how will notes be edited? On edit artist? (should there be an editartist?) or on a editnotes?
//same question for deletion //same question for deletion
$html .= "</tr> $html .= "</tr>
</table>"; </table>";
$page->set_title("Artist"); $page->set_title("Artist");
$page->set_heading("Artist"); $page->set_heading("Artist");
$page->add_block(new Block("Artist", $html, "main", 10)); $page->add_block(new Block("Artist", $html, "main", 10));
//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">'.
'<a href="$image_link">'.$thumb_html.'</a>'. '<a href="$image_link">'.$thumb_html.'</a>'.
'</span>'; '</span>';
} }
$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

@ -62,7 +62,7 @@ class BlotterTheme extends Themelet {
if($entries[$i]['important'] == 'Y') { $important = 'Y'; } else { $important = 'N'; } if($entries[$i]['important'] == 'Y') { $important = 'Y'; } else { $important = 'N'; }
// Add the new table row(s) // Add the new table row(s)
$table_rows .= $table_rows .=
"<tr> "<tr>
<td>$entry_date</td> <td>$entry_date</td>
<td>$entry_text</td> <td>$entry_text</td>
@ -114,7 +114,7 @@ class BlotterTheme extends Themelet {
$i_open = "<font color='#{$i_color}'>"; $i_open = "<font color='#{$i_color}'>";
$i_close="</font>"; $i_close="</font>";
} }
$html .= "{$i_open}{$clean_date} - {$entry_text}{$i_close}<br /><br />"; $html .= "{$i_open}{$clean_date} - {$entry_text}{$i_close}<br /><br />";
} }
$html .= "</pre>"; $html .= "</pre>";
return $html; return $html;
@ -139,9 +139,9 @@ class BlotterTheme extends Themelet {
$entry_text = $entries[$i]['entry_text']; $entry_text = $entries[$i]['entry_text'];
if($entries[$i]['important'] == 'Y') { if($entries[$i]['important'] == 'Y') {
$i_open = "<font color='#{$i_color}'>"; $i_open = "<font color='#{$i_color}'>";
$i_close="</font>"; $i_close="</font>";
} }
$entries_list .= "<li>{$i_open}{$clean_date} - {$entry_text}{$i_close}</li>"; $entries_list .= "<li>{$i_open}{$clean_date} - {$entry_text}{$i_close}</li>";
} }
$pos_break = ""; $pos_break = "";
@ -149,7 +149,7 @@ class BlotterTheme extends Themelet {
if($position === "left") { if($position === "left") {
$pos_break = "<br />"; $pos_break = "<br />";
$pos_align = ""; $pos_align = "";
} }
if(count($entries) === 0) { if(count($entries) === 0) {
@ -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,78 +154,92 @@ 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)) {
if(isset($_POST['image_id']) && isset($_POST['comment'])) { case "add": $this->onPageRequest_add(); break;
try { case "delete": $this->onPageRequest_delete($event); break;
$i_iid = int_escape($_POST['image_id']); case "bulk_delete": $this->onPageRequest_bulk_delete(); break;
$cpe = new CommentPostingEvent($_POST['image_id'], $user, $_POST['comment']); case "list": $this->onPageRequest_list($event); break;
send_event($cpe); case "beta-search": $this->onPageRequest_beta_search($event); break;
$page->set_mode("redirect");
$page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid"));
}
catch(CommentPostingException $ex) {
$this->theme->display_error(403, "Comment Blocked", $ex->getMessage());
}
}
}
else if($event->get_arg(0) === "delete") {
if($user->can("delete_comment")) {
// FIXME: post, not args
if($event->count_args() === 3) {
send_event(new CommentDeletionEvent($event->get_arg(1)));
flash_message("Deleted comment");
$page->set_mode("redirect");
if(!empty($_SERVER['HTTP_REFERER'])) {
$page->set_redirect($_SERVER['HTTP_REFERER']);
}
else {
$page->set_redirect(make_link("post/view/".$event->get_arg(2)));
}
}
}
else {
$this->theme->display_permission_denied();
}
}
else if($event->get_arg(0) === "bulk_delete") {
if($user->can("delete_comment") && !empty($_POST["ip"])) {
$ip = $_POST['ip'];
$cids = $database->get_col("SELECT id FROM comments WHERE owner_ip=:ip", array("ip"=>$ip));
$num = count($cids);
log_warning("comment", "Deleting $num comments from $ip");
foreach($cids as $cid) {
send_event(new CommentDeletionEvent($cid));
}
flash_message("Deleted $num comments");
$page->set_mode("redirect");
$page->set_redirect(make_link("admin"));
}
else {
$this->theme->display_permission_denied();
}
}
else if($event->get_arg(0) === "list") {
$page_num = int_escape($event->get_arg(1));
$this->build_page($page_num);
}
else if($event->get_arg(0) === "beta-search") {
$search = $event->get_arg(1);
$page_num = int_escape($event->get_arg(2));
$duser = User::by_name($search);
$i_comment_count = Comment::count_comments_by_user($duser);
$com_per_page = 50;
$total_pages = ceil($i_comment_count/$com_per_page);
$page_num = $this->sanity_check_pagenumber($page_num, $total_pages);
$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);
} }
} }
} }
private function onPageRequest_add() {
global $user, $page;
if (isset($_POST['image_id']) && isset($_POST['comment'])) {
try {
$i_iid = int_escape($_POST['image_id']);
$cpe = new CommentPostingEvent($_POST['image_id'], $user, $_POST['comment']);
send_event($cpe);
$page->set_mode("redirect");
$page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid"));
} catch (CommentPostingException $ex) {
$this->theme->display_error(403, "Comment Blocked", $ex->getMessage());
}
}
}
private function onPageRequest_delete(PageRequestEvent $event) {
global $user, $page;
if ($user->can("delete_comment")) {
// FIXME: post, not args
if ($event->count_args() === 3) {
send_event(new CommentDeletionEvent($event->get_arg(1)));
flash_message("Deleted comment");
$page->set_mode("redirect");
if (!empty($_SERVER['HTTP_REFERER'])) {
$page->set_redirect($_SERVER['HTTP_REFERER']);
} else {
$page->set_redirect(make_link("post/view/" . $event->get_arg(2)));
}
}
} else {
$this->theme->display_permission_denied();
}
}
private function onPageRequest_bulk_delete() {
global $user, $database, $page;
if ($user->can("delete_comment") && !empty($_POST["ip"])) {
$ip = $_POST['ip'];
$comment_ids = $database->get_col("
SELECT id
FROM comments
WHERE owner_ip=:ip
", array("ip" => $ip));
$num = count($comment_ids);
log_warning("comment", "Deleting $num comments from $ip");
foreach($comment_ids as $cid) {
send_event(new CommentDeletionEvent($cid));
}
flash_message("Deleted $num comments");
$page->set_mode("redirect");
$page->set_redirect(make_link("admin"));
} else {
$this->theme->display_permission_denied();
}
}
private function onPageRequest_list(PageRequestEvent $event) {
$page_num = int_escape($event->get_arg(1));
$this->build_page($page_num);
}
private function onPageRequest_beta_search(PageRequestEvent $event) {
$search = $event->get_arg(1);
$page_num = int_escape($event->get_arg(2));
$duser = User::by_name($search);
$i_comment_count = Comment::count_comments_by_user($duser);
$com_per_page = 50;
$total_pages = ceil($i_comment_count / $com_per_page);
$page_num = clamp($page_num, 1, $total_pages);
$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);
}
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,38 +352,32 @@ 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 = 1; $current_page = clamp($current_page, 1, $total_pages);
}
$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) &&
$image = null; // this is "clever", I may live to regret it strpos($user_ratings, $image->rating) === FALSE
} ) {
$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);
@ -373,22 +391,13 @@ class CommentList extends Extension {
// get comments {{{ // get comments {{{
/** /**
* @param int $count * @param string $query
* @return array * @param array $args
* @return Comment[]
*/ */
private function get_recent_comments($count) { private function get_generic_comments($query, $args) {
global $database; global $database;
$rows = $database->get_all(" $rows = $database->get_all($query, $args);
SELECT
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.image_id as image_id, comments.owner_ip as poster_ip,
comments.posted as posted
FROM comments
LEFT JOIN users ON comments.owner_id=users.id
ORDER BY comments.id DESC
LIMIT :limit
", array("limit"=>$count));
$comments = array(); $comments = array();
foreach($rows as $row) { foreach($rows as $row) {
$comments[] = new Comment($row); $comments[] = new Comment($row);
@ -396,60 +405,68 @@ class CommentList extends Extension {
return $comments; return $comments;
} }
/**
* @param int $count
* @return Comment[]
*/
private function get_recent_comments($count) {
return $this->get_generic_comments("
SELECT
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.image_id as image_id, comments.owner_ip as poster_ip,
comments.posted as posted
FROM comments
LEFT JOIN users ON comments.owner_id=users.id
ORDER BY comments.id DESC
LIMIT :limit
", array("limit"=>$count));
}
/** /**
* @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,
comments.image_id as image_id, comments.owner_ip as poster_ip, comments.image_id as image_id, comments.owner_ip as poster_ip,
comments.posted as posted 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
WHERE users.id = :user_id WHERE users.id = :user_id
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); SELECT
$rows = $database->get_all("
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,
comments.image_id as image_id, comments.owner_ip as poster_ip, comments.image_id as image_id, comments.owner_ip as poster_ip,
comments.posted as posted 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
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,360 +55,46 @@ 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 = "";
$length = 0;
while(!feof($fp) && $length <= $config->get_int('upload_size'))
{
$data .= fread($fp, 8192);
$length = strlen($data);
}
fclose($fp);
$fp = fopen($tmp_filename, "w");
fwrite($fp, $data);
fclose($fp);
}
if($config->get_string("transload_engine") == "curl")
{
$ch = curl_init($url);
$fp = fopen($tmp_filename, "w");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
}
$file = $tmp_filename;
$filename = basename($url);
} else
{ // Nothing was specified at all
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: no input files");
return;
}
// Get tags out of url
$posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']);
$hash = md5_file($file);
// Was an md5 supplied? Does it match the file hash?
if(isset($_REQUEST['md5']))
{
if(strtolower($_REQUEST['md5']) != $hash)
{
$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;
}
} }
/* elseif(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) {
find_posts() $page->set_type("application/xml");
Find all posts that match the search criteria. Posts will be ordered by id descending. $page->set_data($this->api_find_posts());
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
*/
if(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml')))
{
$this->authenticate_user();
$start = 0;
if(isset($_GET['md5']))
{
$md5list = explode(",",$_GET['md5']);
foreach($md5list as $md5)
{
$results[] = Image::by_hash($md5);
}
$count = count($results);
} elseif(isset($_GET['id']))
{
$idlist = explode(",",$_GET['id']);
foreach($idlist as $id)
{
$results[] = Image::by_id($id);
}
$count = count($results);
} else
{
$limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100;
// Calculate start offset.
if (isset($_GET['page'])) // Danbooru API uses 'page' >= 1
$start = (int_escape($_GET['page'])-1) * $limit;
else if (isset($_GET['pid'])) // Gelbooru API uses 'pid' >= 0
$start = int_escape($_GET['pid']) * $limit;
else
$start = 0;
$tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : array();
$count = Image::count_images($tags);
$results = Image::find_images(max($start, 0), min($limit, 100), $tags);
}
// Now we have the array $results filled with Image objects
// Let's display them
$xml = "<posts count=\"{$count}\" offset=\"{$start}\">\n";
foreach($results as $img)
{
// Sanity check to see if $img is really an image object
// If it isn't (e.g. someone requested an invalid md5 or id), break out of the this
if(!is_object($img))
continue;
$taglist = $img->get_tag_list();
$owner = $img->get_owner();
$previewsize = get_thumbnail_size($img->width, $img->height);
$xml .= xml_tag("post", array(
"id" => $img->id,
"md5" => $img->hash,
"file_name" => $img->filename,
"file_url" => $img->get_image_link(),
"height" => $img->height,
"width" => $img->width,
"preview_url" => $img->get_thumb_link(),
"preview_height" => $previewsize[1],
"preview_width" => $previewsize[0],
"rating" => "u",
"date" => $img->posted,
"is_warehoused" => false,
"tags" => $taglist,
"source" => $img->source,
"score" => 0,
"author" => $owner->name
));
}
$xml .= "</posts>";
$page->set_data($xml);
} }
/* elseif($event->get_arg(1) == 'find_tags') {
find_tags() Find all tags that match the search criteria. $page->set_type("application/xml");
Parameters $page->set_data($this->api_find_tags());
* id: A comma delimited list of tag id numbers.
* name: A comma delimited list of tag names.
* tags: any typical tag query. See Tag#parse_query for details.
* after_id: limit results to tags with an id number after after_id. Useful if you only want to refresh
*/
if($event->get_arg(1) == 'find_tags') {
if(isset($_GET['id'])) {
$idlist = explode(",",$_GET['id']);
foreach($idlist as $id) {
$sqlresult = $database->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']);
}
*/
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 .= "<tag type=\"0\" count=\"$tag[0]\" name=\"" . $this->xmlspecialchars($tag[1]) . "\" id=\"$tag[2]\"/>\n";
}
$xml .= "</tags>";
$page->set_data($xml);
} }
// Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper // Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper
// Shimmie view page // Shimmie view page
// Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123 // Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123
// This redirects that to http://shimmie/post/view/123 // This redirects that to http://shimmie/post/view/123
if(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) { elseif(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) {
$fixedlocation = make_link("post/view/" . $event->get_arg(3)); $fixedlocation = make_link("post/view/" . $event->get_arg(3));
$page->set_mode("redirect"); $page->set_mode("redirect");
$page->set_redirect($fixedlocation); $page->set_redirect($fixedlocation);
} }
} }
// Turns out I use this a couple times so let's make it a utility function /**
// Authenticates a user based on the contents of the login and password parameters * Turns out I use this a couple times so let's make it a utility function
// or makes them anonymous. Does not set any cookies or anything permanent. * Authenticates a user based on the contents of the login and password parameters
private function authenticate_user() * or makes them anonymous. Does not set any cookies or anything permanent.
{ */
global $config; private function authenticate_user() {
global $user; global $config, $user;
if(isset($_REQUEST['login']) && isset($_REQUEST['password'])) if(isset($_REQUEST['login']) && isset($_REQUEST['password'])) {
{
// Get this user from the db, if it fails the user becomes anonymous // Get this user from the db, if it fails the user becomes anonymous
// Code borrowed from /ext/user // Code borrowed from /ext/user
$name = $_REQUEST['login']; $name = $_REQUEST['login'];
@ -416,18 +102,294 @@ class DanbooruApi extends Extension {
$duser = User::by_name_and_pass($name, $pass); $duser = User::by_name_and_pass($name, $pass);
if(!is_null($duser)) { if(!is_null($duser)) {
$user = $duser; $user = $duser;
} else }
{ else {
$user = User::by_id($config->get_int("anon_id", 0)); $user = User::by_id($config->get_int("anon_id", 0));
} }
} }
} }
// From htmlspecialchars man page on php.net comments /**
// If tags contain quotes they need to be htmlified * find_tags()
private function xmlspecialchars($text) * Find all tags that match the search criteria.
{ *
return str_replace('&#039;', '&apos;', htmlspecialchars($text, ENT_QUOTES)); * 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();
$start = 0;
if(isset($_GET['md5'])) {
$md5list = explode(",", $_GET['md5']);
foreach ($md5list as $md5) {
$results[] = Image::by_hash($md5);
}
$count = count($results);
}
elseif(isset($_GET['id'])) {
$idlist = explode(",", $_GET['id']);
foreach ($idlist as $id) {
$results[] = Image::by_id($id);
}
$count = count($results);
}
else {
$limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100;
// Calculate start offset.
if (isset($_GET['page'])) // Danbooru API uses 'page' >= 1
$start = (int_escape($_GET['page']) - 1) * $limit;
else if (isset($_GET['pid'])) // Gelbooru API uses 'pid' >= 0
$start = int_escape($_GET['pid']) * $limit;
else
$start = 0;
$tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : array();
$count = Image::count_images($tags);
$results = Image::find_images(max($start, 0), min($limit, 100), $tags);
}
// Now we have the array $results filled with Image objects
// Let's display them
$xml = "<posts count=\"{$count}\" offset=\"{$start}\">\n";
foreach ($results as $img) {
// Sanity check to see if $img is really an image object
// If it isn't (e.g. someone requested an invalid md5 or id), break out of the this
if (!is_object($img))
continue;
$taglist = $img->get_tag_list();
$owner = $img->get_owner();
$previewsize = get_thumbnail_size($img->width, $img->height);
$xml .= xml_tag("post", array(
"id" => $img->id,
"md5" => $img->hash,
"file_name" => $img->filename,
"file_url" => $img->get_image_link(),
"height" => $img->height,
"width" => $img->width,
"preview_url" => $img->get_thumb_link(),
"preview_height" => $previewsize[1],
"preview_width" => $previewsize[0],
"rating" => "u",
"date" => $img->posted,
"is_warehoused" => false,
"tags" => $taglist,
"source" => $img->source,
"score" => 0,
"author" => $owner->name
));
}
$xml .= "</posts>";
return $xml;
}
/**
* 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.
*/
private function api_add_post() {
global $user, $config, $page;
$danboorup_kludge = 1; // danboorup for firefox makes broken links out of location: /path
// Check first if a login was supplied, if it wasn't check if the user is logged in via cookie
// If all that fails, it's an anonymous upload
$this->authenticate_user();
// Now we check if a file was uploaded or a url was provided to transload
// Much of this code is borrowed from /ext/upload
if (!$user->can("create_image")) {
$page->set_code(409);
$page->add_http_header("X-Danbooru-Errors: authentication error");
return;
}
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
$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;
}
// Get tags out of url
$posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']);
// Was an md5 supplied? Does it match the file hash?
$hash = md5_file($file);
if (isset($_REQUEST['md5']) && strtolower($_REQUEST['md5']) != $hash) {
$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;
}
// 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());
}
} }
} }

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

@ -253,9 +253,9 @@ class Index extends Extension {
$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();
$count_search_terms = count($search_terms); $count_search_terms = count($search_terms);
try { try {
#log_debug("index", "Search for ".implode(" ", $search_terms), false, array("terms"=>$search_terms)); #log_debug("index", "Search for ".implode(" ", $search_terms), false, array("terms"=>$search_terms));
$total_pages = Image::count_pages($search_terms); $total_pages = Image::count_pages($search_terms);
@ -277,7 +277,7 @@ class Index extends Extension {
} }
$count_images = count($images); $count_images = count($images);
if($count_search_terms === 0 && $count_images === 0 && $page_number === 1) { if($count_search_terms === 0 && $count_images === 0 && $page_number === 1) {
$this->theme->display_intro($page); $this->theme->display_intro($page);
send_event(new PostListBuildingEvent($search_terms)); send_event(new PostListBuildingEvent($search_terms));
@ -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,62 +16,54 @@ 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( !isset($_POST['ids']) or !isset($_POST['tag']) ) return;
if($event->get_arg(0) == "tag") $this->_apply_mass_tags( $config, $page, $user, $event ); $tag = $_POST['tag'];
}
$tag_array = explode(" ",$tag);
private function _apply_mass_tags( $config, Page $page, $user, $event ) { $pos_tag_array = array();
if( !isset($_POST['ids']) or !isset($_POST['tag']) ) return; $neg_tag_array = array();
foreach($tag_array as $new_tag) {
$tag = $_POST['tag']; if (strpos($new_tag, '-') === 0)
$neg_tag_array[] = substr($new_tag,1);
$tag_array = explode(" ",$tag);
$pos_tag_array = array();
$neg_tag_array = array();
foreach($tag_array as $new_tag) {
if (strpos($new_tag, '-') === 0)
$neg_tag_array[] = substr($new_tag,1);
else
$pos_tag_array[] = $new_tag;
}
$ids = explode( ':', $_POST['ids'] );
$ids = array_filter ( $ids , 'is_numeric' );
$images = array_map( "Image::by_id", $ids );
if(isset($_POST['setadd']) &&
$_POST['setadd'] == 'set')
{
foreach($images as $image) {
$image->set_tags(Tag::explode($tag));
}
}
else
{
foreach($images as $image) {
if (!empty($neg_tag_array)) {
$img_tags = array_merge($pos_tag_array, explode(" ",$image->get_tag_list()));
$img_tags = array_diff($img_tags, $neg_tag_array);
$image->set_tags(Tag::explode($img_tags));
}
else else
$image->set_tags(Tag::explode($tag . " " . $image->get_tag_list())); $pos_tag_array[] = $new_tag;
} }
$ids = explode( ':', $_POST['ids'] );
$ids = array_filter ( $ids , 'is_numeric' );
$images = array_map( "Image::by_id", $ids );
if(isset($_POST['setadd']) && $_POST['setadd'] == 'set') {
foreach($images as $image) {
$image->set_tags(Tag::explode($tag));
}
}
else {
foreach($images as $image) {
if (!empty($neg_tag_array)) {
$img_tags = array_merge($pos_tag_array, explode(" ",$image->get_tag_list()));
$img_tags = array_diff($img_tags, $neg_tag_array);
$image->set_tags(Tag::explode($img_tags));
}
else
$image->set_tags(Tag::explode($tag . " " . $image->get_tag_list()));
}
}
$page->set_mode("redirect");
if(!isset($_SERVER['HTTP_REFERER'])) $_SERVER['HTTP_REFERER'] = make_link();
$page->set_redirect($_SERVER['HTTP_REFERER']);
} }
$page->set_mode("redirect");
if(!isset($_SERVER['HTTP_REFERER'])) $_SERVER['HTTP_REFERER'] = make_link();
$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)){
@ -397,15 +393,14 @@ class Pools extends Extension {
} }
$pools = $database->get_all(" $pools = $database->get_all("
SELECT p.id, p.user_id, p.public, p.title, p.description, SELECT p.id, p.user_id, p.public, p.title, p.description,
p.posts, u.name as user_name p.posts, u.name as user_name
FROM pools AS p FROM pools AS p
INNER JOIN users AS u INNER JOIN users AS u
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,12 +75,12 @@ 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){
@ -180,51 +180,15 @@ class ResizeImage extends Extension {
if (($image_obj->width != $info[0] ) || ($image_obj->height != $info[1])) { if (($image_obj->width != $info[0] ) || ($image_obj->height != $info[1])) {
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 */
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 ); list($new_height, $new_width) = $this->calc_new_size($image_obj, $width, $height);
$new_height = round( $image_obj->height * $factor );
}
/* Attempt to load the image */ /* Attempt to load the image */
switch ( $info[2] ) { switch ( $info[2] ) {
case IMAGETYPE_GIF: $image = imagecreatefromgif($image_filename); break; case IMAGETYPE_GIF: $image = imagecreatefromgif($image_filename); break;
@ -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 "filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash,
", "width"=>$new_width, "height"=>$new_height, "id"=>$image_obj->id
array( ));
"filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash,
"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

@ -25,16 +25,16 @@ class RSS_Comments extends Extension {
$page->set_type("application/rss+xml"); $page->set_type("application/rss+xml");
$comments = $database->get_all(" $comments = $database->get_all("
SELECT SELECT
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
LIMIT 10 LIMIT 10
"); ");
$data = ""; $data = "";
foreach($comments as $comment) { foreach($comments as $comment) {
@ -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,41 +97,77 @@ class ShimmieApi extends Extension {
$type = "name"; $type = "name";
} }
$all = $database->get_row( $all = $this->api_get_user($type, $query);
"SELECT id,name,joindate,class FROM users WHERE $type=?",
array($query));
if(!empty($all)){
//FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice..
// - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...);
for($i=0; $i<4; $i++) unset($all[$i]);
$all['uploadcount'] = Image::count_images(array("user_id=".$all['id']));
$all['commentcount'] = $database->get_one(
"SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id",
array("owner_id"=>$all['id']));
if(isset($_GET['recent'])){
$recent = $database->get_all(
"SELECT * FROM images WHERE owner_id=? ORDER BY id DESC LIMIT 0, 5",
array($all['id']));
$i = 0;
foreach($recent as $all['recentposts'][$i]){
unset($all['recentposts'][$i]['owner_id']); //We already know the owners id..
unset($all['recentposts'][$i]['owner_ip']);
for($x=0; $x<14; $x++) unset($all['recentposts'][$i][$x]);
if(empty($all['recentposts'][$i]['author'])) unset($all['recentposts'][$i]['author']);
if($all['recentposts'][$i]['notes'] > 0) $all['recentposts'][$i]['has_notes'] = "Y";
else $all['recentposts'][$i]['has_notes'] = "N";
unset($all['recentposts'][$i]['notes']);
$i += 1;
}
}
}
$page->set_data(json_encode($all)); $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(
"SELECT id, name, joindate, class FROM users WHERE $type=?",
array($query)
);
if (!empty($all)) {
//FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice..
// - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...);
for ($i = 0; $i < 4; $i++) unset($all[$i]);
$all['uploadcount'] = Image::count_images(array("user_id=" . $all['id']));
$all['commentcount'] = $database->get_one(
"SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id",
array("owner_id" => $all['id']));
if (isset($_GET['recent'])) {
$recent = $database->get_all(
"SELECT * FROM images WHERE owner_id=? ORDER BY id DESC LIMIT 0, 5",
array($all['id']));
$i = 0;
foreach ($recent as $all['recentposts'][$i]) {
unset($all['recentposts'][$i]['owner_id']); //We already know the owners id..
unset($all['recentposts'][$i]['owner_ip']);
for ($x = 0; $x < 14; $x++) unset($all['recentposts'][$i][$x]);
if (empty($all['recentposts'][$i]['author'])) unset($all['recentposts'][$i]['author']);
if ($all['recentposts'][$i]['notes'] > 0) $all['recentposts'][$i]['has_notes'] = "Y";
else $all['recentposts'][$i]['has_notes'] = "N";
unset($all['recentposts'][$i]['notes']);
$i += 1;
}
}
}
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");
@ -48,10 +48,12 @@ class TagEditTest extends ShimmiePHPUnitTestCase {
$this->log_out(); $this->log_out();
} }
/* /*
* 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

@ -22,7 +22,7 @@ class TagEditCloud extends Extension {
} }
} }
} }
public function onInitExt(InitExtEvent $event) { public function onInitExt(InitExtEvent $event) {
global $config; global $config;
$config->set_default_bool("tageditcloud_disable", false); $config->set_default_bool("tageditcloud_disable", false);
@ -74,16 +74,14 @@ 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) {
$cat_color[$row['category']] = $row['color']; $cat_color[$row['category']] = $row['color'];
} }
} }
$tag_data = null;
switch($sort_method) { switch($sort_method) {
case 'a': case 'a':
case 'p': case 'p':
@ -99,31 +97,29 @@ 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) {
$relevant_tags = implode(",",array_map(array($database,"escape"),$relevant_tags)); return null;
$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
FROM image_tags it1
JOIN image_tags it2 USING(image_id)
JOIN tags t1 ON it1.tag_id = t1.id
JOIN tags t2 ON it2.tag_id = t2.id
WHERE t1.count >= :tag_min2 AND t1.tag IN($relevant_tags)
GROUP BY t2.tag
ORDER BY count DESC
LIMIT :limit",
array("tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count));
} }
$relevant_tags = implode(",",array_map(array($database,"escape"),$relevant_tags));
$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
FROM image_tags it1
JOIN image_tags it2 USING(image_id)
JOIN tags t1 ON it1.tag_id = t1.id
JOIN tags t2 ON it2.tag_id = t2.id
WHERE t1.count >= :tag_min2 AND t1.tag IN($relevant_tags)
GROUP BY t2.tag
ORDER BY count DESC
LIMIT :limit",
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