PSR-2. I'm not a huge fan, but ugly consistency beats no consistency...

This commit is contained in:
Shish 2019-05-28 17:59:38 +01:00
parent 5ec3e89884
commit 34b05cca7c
295 changed files with 27094 additions and 24632 deletions

View File

@ -16,6 +16,6 @@ insert_final_newline = true
[*.{js,css,php}]
charset = utf-8
indent_style = tab
indent_style = space
indent_size = 4

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ images
thumbs
*.phar
*.sqlite
.php_cs.cache
#Composer
composer.phar

18
.php_cs.dist Normal file
View File

@ -0,0 +1,18 @@
<?php
$finder = PhpCsFixer\Finder::create()
->exclude('ext/amazon_s3/lib')
->exclude('vendor')
->in(__DIR__)
;
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
//'strict_param' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder)
;
?>

View File

@ -30,7 +30,7 @@ date_default_timezone_set('UTC');
<script type="text/javascript" src="vendor/bower-asset/jquery/dist/jquery.min.js"></script>
</head>
<body>
<?php if(FALSE) { ?>
<?php if (false) { ?>
<div id="installer">
<h1>Install Error</h1>
<div class="container">
@ -65,14 +65,17 @@ require_once "core/cacheengine.php";
require_once "core/dbengine.php";
require_once "core/database.php";
if(is_readable("data/config/shimmie.conf.php")) die("Shimmie is already installed.");
if (is_readable("data/config/shimmie.conf.php")) {
die("Shimmie is already installed.");
}
do_install();
// utilities {{{
// TODO: Can some of these be pushed into "core/???.inc.php" ?
function check_gd_version(): int {
function check_gd_version(): int
{
$gdversion = 0;
if (function_exists('gd_info')) {
@ -87,35 +90,34 @@ function check_gd_version(): int {
return $gdversion;
}
function check_im_version(): int {
function check_im_version(): int
{
$convert_check = exec("convert");
return (empty($convert_check) ? 0 : 1);
}
function eok($name, $value) {
function eok($name, $value)
{
echo "<br>$name ... ";
if ($value) {
echo "<span style='color: green'>ok</span>\n";
}
else {
} else {
echo "<span style='color: green'>failed</span>\n";
}
}
// }}}
function do_install() { // {{{
function do_install()
{ // {{{
if (file_exists("data/config/auto_install.conf.php")) {
require_once "data/config/auto_install.conf.php";
}
else if(@$_POST["database_type"] == "sqlite") {
} elseif (@$_POST["database_type"] == "sqlite") {
$id = bin2hex(random_bytes(5));
define('DATABASE_DSN', "sqlite:data/shimmie.{$id}.sqlite");
}
else if(isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) {
} elseif (isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) {
define('DATABASE_DSN', "{$_POST['database_type']}:user={$_POST['database_user']};password={$_POST['database_password']};host={$_POST['database_host']};dbname={$_POST['database_name']}");
}
else {
} else {
ask_questions();
return;
}
@ -126,17 +128,17 @@ function do_install() { // {{{
install_process();
} // }}}
function ask_questions() { // {{{
$warnings = array();
$errors = array();
function ask_questions()
{ // {{{
$warnings = [];
$errors = [];
if (check_gd_version() == 0 && check_im_version() == 0) {
$errors[] = "
No thumbnailers could be found - install the imagemagick
tools (or the PHP-GD library, if imagemagick is unavailable).
";
}
else if(check_im_version() == 0) {
} elseif (check_im_version() == 0) {
$warnings[] = "
The 'convert' command (from the imagemagick package)
could not be found - PHP-GD can be used instead, but
@ -243,14 +245,16 @@ EOD;
/**
* This is where the install really takes place.
*/
function install_process() { // {{{
function install_process()
{ // {{{
build_dirs();
create_tables();
insert_defaults();
write_config();
} // }}}
function create_tables() { // {{{
function create_tables()
{ // {{{
try {
$db = new Database();
@ -274,7 +278,7 @@ EOD;
newtag VARCHAR(128) NOT NULL,
PRIMARY KEY (oldtag)
");
$db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", array());
$db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", []);
$db->create_table("config", "
name VARCHAR(128) NOT NULL,
@ -289,7 +293,7 @@ EOD;
class VARCHAR(32) NOT NULL DEFAULT 'user',
email VARCHAR(128)
");
$db->execute("CREATE INDEX users_name_idx ON users(name)", array());
$db->execute("CREATE INDEX users_name_idx ON users(name)", []);
$db->create_table("images", "
id SCORE_AIPK,
@ -306,17 +310,17 @@ EOD;
locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N,
FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT
");
$db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", array());
$db->execute("CREATE INDEX images_width_idx ON images(width)", array());
$db->execute("CREATE INDEX images_height_idx ON images(height)", array());
$db->execute("CREATE INDEX images_hash_idx ON images(hash)", array());
$db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", []);
$db->execute("CREATE INDEX images_width_idx ON images(width)", []);
$db->execute("CREATE INDEX images_height_idx ON images(height)", []);
$db->execute("CREATE INDEX images_hash_idx ON images(hash)", []);
$db->create_table("tags", "
id SCORE_AIPK,
tag VARCHAR(64) UNIQUE NOT NULL,
count INTEGER NOT NULL DEFAULT 0
");
$db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", array());
$db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", []);
$db->create_table("image_tags", "
image_id INTEGER NOT NULL,
@ -325,44 +329,47 @@ EOD;
FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
");
$db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", array());
$db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", array());
$db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", []);
$db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", []);
$db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)");
$db->commit();
}
catch(PDOException $e) {
handle_db_errors(TRUE, "An error occurred while trying to create the database tables necessary for Shimmie.", $e->getMessage(), 3);
} catch (PDOException $e) {
handle_db_errors(true, "An error occurred while trying to create the database tables necessary for Shimmie.", $e->getMessage(), 3);
} catch (Exception $e) {
handle_db_errors(FALSE, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 4);
handle_db_errors(false, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 4);
}
} // }}}
function insert_defaults() { // {{{
function insert_defaults()
{ // {{{
try {
$db = new Database();
$db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", Array("name" => 'Anonymous', "pass" => null, "class" => 'anonymous'));
$db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", Array("name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')));
$db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", ["name" => 'Anonymous', "pass" => null, "class" => 'anonymous']);
$db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')]);
if (check_im_version() > 0) {
$db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", Array("name" => 'thumb_engine', "value" => 'convert'));
$db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'thumb_engine', "value" => 'convert']);
}
$db->commit();
}
catch(PDOException $e) {
handle_db_errors(TRUE, "An error occurred while trying to insert data into the database.", $e->getMessage(), 5);
}
catch (Exception $e) {
handle_db_errors(FALSE, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 6);
} catch (PDOException $e) {
handle_db_errors(true, "An error occurred while trying to insert data into the database.", $e->getMessage(), 5);
} catch (Exception $e) {
handle_db_errors(false, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 6);
}
} // }}}
function build_dirs() { // {{{
function build_dirs()
{ // {{{
// *try* and make default dirs. Ignore any errors --
// if something is amiss, we'll tell the user later
if(!file_exists("data")) @mkdir("data");
if(!is_writable("data")) @chmod("data", 0755);
if (!file_exists("data")) {
@mkdir("data");
}
if (!is_writable("data")) {
@chmod("data", 0755);
}
// Clear file status cache before checking again.
clearstatcache();
@ -385,7 +392,8 @@ function build_dirs() { // {{{
}
} // }}}
function write_config() { // {{{
function write_config()
{ // {{{
$file_content = '<' . '?php' . "\n" .
"define('DATABASE_DSN', '".DATABASE_DSN."');\n" .
'?' . '>';
@ -405,8 +413,7 @@ function write_config() { // {{{
</div>
</div>
EOD;
}
else {
} else {
$h_file_content = htmlentities($file_content);
print <<<EOD
<div id="installer">
@ -429,7 +436,8 @@ EOD;
echo "\n";
} // }}}
function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessage2, int $exitCode) {
function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessage2, int $exitCode)
{
$errorMessage1Extra = ($isPDO ? "Please check and ensure that the database configuration options are all correct." : "Please check the server log files for more information.");
print <<<EOD
<div id="installer">

View File

@ -5,12 +5,14 @@
*
* A collection of common functions for theme parts
*/
class BaseThemelet {
class BaseThemelet
{
/**
* Generic error message display
*/
public function display_error(int $code, string $title, string $message): void {
public function display_error(int $code, string $title, string $message): void
{
global $page;
$page->set_code($code);
$page->set_title($title);
@ -31,7 +33,8 @@ class BaseThemelet {
/**
* A specific, common error message
*/
public function display_permission_denied(): void {
public function display_permission_denied(): void
{
$this->display_error(403, "Permission Denied", "You do not have permission to access this page");
}
@ -40,7 +43,8 @@ class BaseThemelet {
* Generic thumbnail code; returns HTML rather than adding
* a block since thumbs tend to go inside blocks...
*/
public function build_thumb_html(Image $image): string {
public function build_thumb_html(Image $image): string
{
global $config;
$i_id = (int) $image->id;
@ -49,7 +53,7 @@ class BaseThemelet {
$h_tip = html_escape($image->get_tooltip());
$h_tags = html_escape(strtolower($image->get_tag_list()));
$extArr = array_flip(array('swf', 'svg', 'mp3')); //List of thumbless filetypes
$extArr = array_flip(['swf', 'svg', 'mp3']); //List of thumbless filetypes
if (!isset($extArr[$image->ext])) {
$tsize = get_thumbnail_size($image->width, $image->height);
} else {
@ -59,8 +63,12 @@ class BaseThemelet {
$custom_classes = "";
if (class_exists("Relationships")) {
if(property_exists($image, 'parent_id') && $image->parent_id !== NULL){ $custom_classes .= "shm-thumb-has_parent "; }
if(property_exists($image, 'has_children') && bool_escape($image->has_children)){ $custom_classes .= "shm-thumb-has_child "; }
if (property_exists($image, 'parent_id') && $image->parent_id !== null) {
$custom_classes .= "shm-thumb-has_parent ";
}
if (property_exists($image, 'has_children') && bool_escape($image->has_children)) {
$custom_classes .= "shm-thumb-has_child ";
}
}
return "<a href='$h_view_link' class='thumb shm-thumb shm-thumb-link {$custom_classes}' data-tags='$h_tags' data-post-id='$i_id'>".
@ -68,26 +76,36 @@ class BaseThemelet {
"</a>\n";
}
public function display_paginator(Page $page, string $base, string $query=null, int $page_number, int $total_pages, bool $show_random = FALSE) {
if($total_pages == 0) $total_pages = 1;
public function display_paginator(Page $page, string $base, string $query=null, int $page_number, int $total_pages, bool $show_random = false)
{
if ($total_pages == 0) {
$total_pages = 1;
}
$body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random);
$page->add_block(new Block(null, $body, "main", 90, "paginator"));
}
private function gen_page_link(string $base_url, string $query=null, string $page, string $name): string {
private function gen_page_link(string $base_url, string $query=null, string $page, string $name): string
{
$link = make_link($base_url.'/'.$page, $query);
return '<a href="'.$link.'">'.$name.'</a>';
}
private function gen_page_link_block(string $base_url, string $query=null, string $page, int $current_page, string $name): string {
private function gen_page_link_block(string $base_url, string $query=null, string $page, int $current_page, string $name): string
{
$paginator = "";
if($page == $current_page) $paginator .= "<b>";
if ($page == $current_page) {
$paginator .= "<b>";
}
$paginator .= $this->gen_page_link($base_url, $query, $page, $name);
if($page == $current_page) $paginator .= "</b>";
if ($page == $current_page) {
$paginator .= "</b>";
}
return $paginator;
}
private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query=null, bool $show_random): string {
private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query=null, bool $show_random): string
{
$next = $current_page + 1;
$prev = $current_page - 1;
@ -109,7 +127,7 @@ class BaseThemelet {
$start = $current_page-5 > 1 ? $current_page-5 : 1;
$end = $start+10 < $total_pages ? $start+10 : $total_pages;
$pages = array();
$pages = [];
foreach (range($start, $end) as $i) {
$pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i);
}

View File

@ -5,7 +5,8 @@
*
* A basic chunk of a page.
*/
class Block {
class Block
{
/**
* The block's title.
*
@ -52,7 +53,8 @@ class Block {
*/
public $is_content = true;
public function __construct(string $header=null, string $body=null, string $section="main", int $position=50, string $id=null) {
public function __construct(string $header=null, string $body=null, string $section="main", int $position=50, string $id=null)
{
$this->header = $header;
$this->body = $body;
$this->section = $section;
@ -67,14 +69,19 @@ class Block {
/**
* Get the HTML for this block.
*/
public function get_html(bool $hidable=false): string {
public function get_html(bool $hidable=false): string
{
$h = $this->header;
$b = $this->body;
$i = $this->id;
$html = "<section id='$i'>";
$h_toggler = $hidable ? " shm-toggler" : "";
if(!empty($h)) $html .= "<h3 data-toggle-sel='#$i' class='$h_toggler'>$h</h3>";
if(!empty($b)) $html .= "<div class='blockbody'>$b</div>";
if (!empty($h)) {
$html .= "<h3 data-toggle-sel='#$i' class='$h_toggler'>$h</h3>";
}
if (!empty($b)) {
$html .= "<div class='blockbody'>$b</div>";
}
$html .= "</section>\n";
return $html;
}
@ -89,8 +96,10 @@ class Block {
* Used because "new NavBlock()" is easier than "new Block('Navigation', ..."
*
*/
class NavBlock extends Block {
public function __construct() {
class NavBlock extends Block
{
public function __construct()
{
parent::__construct("Navigation", "<a href='".make_link()."'>Index</a>", "left", 0);
}
}

View File

@ -1,44 +1,60 @@
<?php
interface CacheEngine {
interface CacheEngine
{
public function get(string $key);
public function set(string $key, $val, int $time=0);
public function delete(string $key);
}
class NoCache implements CacheEngine {
public function get(string $key) {return false;}
public function set(string $key, $val, int $time=0) {}
public function delete(string $key) {}
class NoCache implements CacheEngine
{
public function get(string $key)
{
return false;
}
public function set(string $key, $val, int $time=0)
{
}
public function delete(string $key)
{
}
}
class MemcacheCache implements CacheEngine {
class MemcacheCache implements CacheEngine
{
/** @var \Memcache|null */
public $memcache=null;
public function __construct(string $args) {
public function __construct(string $args)
{
$hp = explode(":", $args);
$this->memcache = new Memcache;
@$this->memcache->pconnect($hp[0], $hp[1]);
}
public function get(string $key) {
public function get(string $key)
{
return $this->memcache->get($key);
}
public function set(string $key, $val, int $time=0) {
public function set(string $key, $val, int $time=0)
{
$this->memcache->set($key, $val, false, $time);
}
public function delete(string $key) {
public function delete(string $key)
{
$this->memcache->delete($key);
}
}
class MemcachedCache implements CacheEngine {
class MemcachedCache implements CacheEngine
{
/** @var \Memcached|null */
public $memcache=null;
public function __construct(string $args) {
public function __construct(string $args)
{
$hp = explode(":", $args);
$this->memcache = new Memcached;
#$this->memcache->setOption(Memcached::OPT_COMPRESSION, False);
@ -47,7 +63,8 @@ class MemcachedCache implements CacheEngine {
$this->memcache->addServer($hp[0], $hp[1]);
}
public function get(string $key) {
public function get(string $key)
{
$key = urlencode($key);
$val = $this->memcache->get($key);
@ -55,17 +72,16 @@ class MemcachedCache implements CacheEngine {
if ($res == Memcached::RES_SUCCESS) {
return $val;
}
else if($res == Memcached::RES_NOTFOUND) {
} elseif ($res == Memcached::RES_NOTFOUND) {
return false;
}
else {
} else {
error_log("Memcached error during get($key): $res");
return false;
}
}
public function set(string $key, $val, int $time=0) {
public function set(string $key, $val, int $time=0)
{
$key = urlencode($key);
$this->memcache->set($key, $val, $time);
@ -75,7 +91,8 @@ class MemcachedCache implements CacheEngine {
}
}
public function delete(string $key) {
public function delete(string $key)
{
$key = urlencode($key);
$this->memcache->delete($key);
@ -86,28 +103,35 @@ class MemcachedCache implements CacheEngine {
}
}
class APCCache implements CacheEngine {
public function __construct(string $args) {
class APCCache implements CacheEngine
{
public function __construct(string $args)
{
// $args is not used, but is passed in when APC cache is created.
}
public function get(string $key) {
public function get(string $key)
{
return apc_fetch($key);
}
public function set(string $key, $val, int $time=0) {
public function set(string $key, $val, int $time=0)
{
apc_store($key, $val, $time);
}
public function delete(string $key) {
public function delete(string $key)
{
apc_delete($key);
}
}
class RedisCache implements CacheEngine {
class RedisCache implements CacheEngine
{
private $redis=null;
public function __construct(string $args) {
public function __construct(string $args)
{
$this->redis = new Redis();
$hp = explode(":", $args);
$this->redis->pconnect($hp[0], $hp[1]);
@ -115,52 +139,54 @@ class RedisCache implements CacheEngine {
$this->redis->setOption(Redis::OPT_PREFIX, 'shm:');
}
public function get(string $key) {
public function get(string $key)
{
return $this->redis->get($key);
}
public function set(string $key, $val, int $time=0) {
public function set(string $key, $val, int $time=0)
{
if ($time > 0) {
$this->redis->setEx($key, $time, $val);
}
else {
} else {
$this->redis->set($key, $val);
}
}
public function delete(string $key) {
public function delete(string $key)
{
$this->redis->delete($key);
}
}
class Cache {
class Cache
{
public $engine;
public $hits=0, $misses=0;
public $hits=0;
public $misses=0;
public $time=0;
public function __construct(?string $dsn) {
$matches = array();
public function __construct(?string $dsn)
{
$matches = [];
if ($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches)) {
if ($matches[1] == "memcache") {
$c = new MemcacheCache($matches[2]);
}
else if($matches[1] == "memcached") {
} elseif ($matches[1] == "memcached") {
$c = new MemcachedCache($matches[2]);
}
else if($matches[1] == "apc") {
} elseif ($matches[1] == "apc") {
$c = new APCCache($matches[2]);
}
else if($matches[1] == "redis") {
} elseif ($matches[1] == "redis") {
$c = new RedisCache($matches[2]);
}
}
else {
} else {
$c = new NoCache();
}
$this->engine = $c;
}
public function get(string $key) {
public function get(string $key)
{
$val = $this->engine->get($key);
if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) {
$hit = $val === false ? "hit" : "miss";
@ -169,28 +195,34 @@ class Cache {
if ($val !== false) {
$this->hits++;
return $val;
}
else {
} else {
$this->misses++;
return false;
}
}
public function set(string $key, $val, int $time=0) {
public function set(string $key, $val, int $time=0)
{
$this->engine->set($key, $val, $time);
if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) {
file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND);
}
}
public function delete(string $key) {
public function delete(string $key)
{
$this->engine->delete($key);
if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) {
file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND);
}
}
public function get_hits(): int {return $this->hits;}
public function get_misses(): int {return $this->misses;}
public function get_hits(): int
{
return $this->hits;
}
public function get_misses(): int
{
return $this->misses;
}
}

View File

@ -3,10 +3,13 @@
* CAPTCHA abstraction *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function captcha_get_html(): string {
function captcha_get_html(): string
{
global $config, $user;
if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return "";
if (DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) {
return "";
}
$captcha = "";
if ($user->is_anonymous() && $config->get_bool("comment_captcha")) {
@ -23,10 +26,13 @@ function captcha_get_html(): string {
return $captcha;
}
function captcha_check(): bool {
function captcha_check(): bool
{
global $config, $user;
if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return true;
if (DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) {
return true;
}
if ($user->is_anonymous() && $config->get_bool("comment_captcha")) {
$r_privatekey = $config->get_string('api_recaptcha_privkey');
@ -38,8 +44,7 @@ function captcha_check(): bool {
log_info("core", "Captcha failed (ReCaptcha): " . implode("", $resp->getErrorCodes()));
return false;
}
}
else {
} else {
session_start();
$securimg = new Securimage();
if ($securimg->check($_POST['captcha_code']) === false) {
@ -51,5 +56,3 @@ function captcha_check(): bool {
return true;
}

View File

@ -5,7 +5,8 @@
*
* An abstract interface for altering a name:value pair list.
*/
interface Config {
interface Config
{
/**
* Save the list of name:value pairs to wherever they came from,
* so that the next time a page is loaded it will use the new
@ -97,7 +98,7 @@ interface Config {
/**
* Pick a value out of the table by name, cast to the appropriate data type.
*/
public function get_array(string $name, ?array $default=array()): ?array;
public function get_array(string $name, ?array $default=[]): ?array;
//@} /*--------------------------------------------------------------------------------------------*/
}
@ -108,74 +109,87 @@ interface Config {
* Common methods for manipulating the list, loading and saving is
* left to the concrete implementation
*/
abstract class BaseConfig implements Config {
public $values = array();
abstract class BaseConfig implements Config
{
public $values = [];
public function set_int(string $name, $value) {
public function set_int(string $name, $value)
{
$this->values[$name] = parse_shorthand_int($value);
$this->save($name);
}
public function set_string(string $name, $value) {
public function set_string(string $name, $value)
{
$this->values[$name] = $value;
$this->save($name);
}
public function set_bool(string $name, $value) {
public function set_bool(string $name, $value)
{
$this->values[$name] = bool_escape($value) ? 'Y' : 'N';
$this->save($name);
}
public function set_array(string $name, array $value) {
public function set_array(string $name, array $value)
{
$this->values[$name] = implode(",", $value);
$this->save($name);
}
public function set_default_int(string $name, int $value) {
public function set_default_int(string $name, int $value)
{
if (is_null($this->get($name))) {
$this->values[$name] = $value;
}
}
public function set_default_string(string $name, string $value) {
public function set_default_string(string $name, string $value)
{
if (is_null($this->get($name))) {
$this->values[$name] = $value;
}
}
public function set_default_bool(string $name, bool $value) {
public function set_default_bool(string $name, bool $value)
{
if (is_null($this->get($name))) {
$this->values[$name] = $value ? 'Y' : 'N';
}
}
public function set_default_array(string $name, array $value) {
public function set_default_array(string $name, array $value)
{
if (is_null($this->get($name))) {
$this->values[$name] = implode(",", $value);
}
}
public function get_int(string $name, $default=null) {
public function get_int(string $name, $default=null)
{
return (int)($this->get($name, $default));
}
public function get_string(string $name, $default=null) {
public function get_string(string $name, $default=null)
{
return $this->get($name, $default);
}
public function get_bool(string $name, $default=null) {
public function get_bool(string $name, $default=null)
{
return bool_escape($this->get($name, $default));
}
public function get_array(string $name, array $default=array()): array {
public function get_array(string $name, array $default=[]): array
{
return explode(",", $this->get($name, ""));
}
private function get(string $name, $default=null) {
private function get(string $name, $default=null)
{
if (isset($this->values[$name])) {
return $this->values[$name];
}
else {
} else {
return $default;
}
}
@ -187,12 +201,15 @@ abstract class BaseConfig implements Config {
*
* For testing, mostly.
*/
class HardcodeConfig extends BaseConfig {
public function __construct(array $dict) {
class HardcodeConfig extends BaseConfig
{
public function __construct(array $dict)
{
$this->values = $dict;
}
public function save(string $name=null) {
public function save(string $name=null)
{
// static config is static
}
}
@ -208,24 +225,25 @@ class HardcodeConfig extends BaseConfig {
* $config['baz'] = "qux";
* ?>
*/
class StaticConfig extends BaseConfig {
public function __construct(string $filename) {
class StaticConfig extends BaseConfig
{
public function __construct(string $filename)
{
if (file_exists($filename)) {
$config = array();
$config = [];
require_once $filename;
if (!empty($config)) {
$this->values = $config;
}
else {
} else {
throw new Exception("Config file '$filename' doesn't contain any config");
}
}
else {
} else {
throw new Exception("Config file '$filename' missing");
}
}
public function save(string $name=null) {
public function save(string $name=null)
{
// static config is static
}
}
@ -244,19 +262,20 @@ class StaticConfig extends BaseConfig {
* );
* \endcode
*/
class DatabaseConfig extends BaseConfig {
class DatabaseConfig extends BaseConfig
{
/** @var Database */
private $database = null;
public function __construct(Database $database) {
public function __construct(Database $database)
{
$this->database = $database;
$cached = $this->database->cache->get("config");
if ($cached) {
$this->values = $cached;
}
else {
$this->values = array();
} else {
$this->values = [];
foreach ($this->database->get_all("SELECT name, value FROM config") as $row) {
$this->values[$row["name"]] = $row["value"];
}
@ -264,16 +283,16 @@ class DatabaseConfig extends BaseConfig {
}
}
public function save(string $name=null) {
public function save(string $name=null)
{
if (is_null($name)) {
reset($this->values); // rewind the array to the first element
foreach ($this->values as $name => $value) {
$this->save($name);
}
}
else {
$this->database->Execute("DELETE FROM config WHERE name = :name", array("name"=>$name));
$this->database->Execute("INSERT INTO config VALUES (:name, :value)", array("name"=>$name, "value"=>$this->values[$name]));
} else {
$this->database->Execute("DELETE FROM config WHERE name = :name", ["name"=>$name]);
$this->database->Execute("INSERT INTO config VALUES (:name, :value)", ["name"=>$name, "value"=>$this->values[$name]]);
}
// rather than deleting and having some other request(s) do a thundering
// herd of race-conditioned updates, just save the updated version once here
@ -284,11 +303,12 @@ class DatabaseConfig extends BaseConfig {
/**
* Class MockConfig
*/
class MockConfig extends HardcodeConfig {
public function __construct(array $config=array()) {
class MockConfig extends HardcodeConfig
{
public function __construct(array $config=[])
{
$config["db_version"] = "999";
$config["anon_id"] = "0";
parent::__construct($config);
}
}

View File

@ -2,7 +2,8 @@
/**
* A class for controlled database access
*/
class Database {
class Database
{
/**
* The PDO database connection object, for anyone who wants direct access.
* @var null|PDO
@ -44,11 +45,13 @@ class Database {
* need it. There are some pages where all the data is in cache, so the
* DB connection is on-demand.
*/
public function __construct() {
public function __construct()
{
$this->cache = new Cache(CACHE_DSN);
}
private function connect_db() {
private function connect_db()
{
# FIXME: detect ADODB URI, automatically translate PDO DSN
/*
@ -57,9 +60,15 @@ class Database {
*
* http://stackoverflow.com/questions/237367
*/
$matches = array(); $db_user=null; $db_pass=null;
if(preg_match("/user=([^;]*)/", DATABASE_DSN, $matches)) $db_user=$matches[1];
if(preg_match("/password=([^;]*)/", DATABASE_DSN, $matches)) $db_pass=$matches[1];
$matches = [];
$db_user=null;
$db_pass=null;
if (preg_match("/user=([^;]*)/", DATABASE_DSN, $matches)) {
$db_user=$matches[1];
}
if (preg_match("/password=([^;]*)/", DATABASE_DSN, $matches)) {
$db_pass=$matches[1];
}
// https://bugs.php.net/bug.php?id=70221
$ka = DATABASE_KA;
@ -67,10 +76,10 @@ class Database {
$ka = false;
}
$db_params = array(
$db_params = [
PDO::ATTR_PERSISTENT => $ka,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
];
$this->db = new PDO(DATABASE_DSN, $db_user, $db_pass, $db_params);
$this->connect_engine();
@ -79,94 +88,109 @@ class Database {
$this->beginTransaction();
}
private function connect_engine() {
if(preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) $db_proto=$matches[1];
else throw new SCoreException("Can't figure out database engine");
private function connect_engine()
{
if (preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) {
$db_proto=$matches[1];
} else {
throw new SCoreException("Can't figure out database engine");
}
if ($db_proto === "mysql") {
$this->engine = new MySQL();
}
else if($db_proto === "pgsql") {
} elseif ($db_proto === "pgsql") {
$this->engine = new PostgreSQL();
}
else if($db_proto === "sqlite") {
} elseif ($db_proto === "sqlite") {
$this->engine = new SQLite();
}
else {
} else {
die('Unknown PDO driver: '.$db_proto);
}
}
public function beginTransaction() {
public function beginTransaction()
{
if ($this->transaction === false) {
$this->db->beginTransaction();
$this->transaction = true;
}
}
public function commit(): bool {
public function commit(): bool
{
if (!is_null($this->db)) {
if ($this->transaction === true) {
$this->transaction = false;
return $this->db->commit();
}
else {
} else {
throw new SCoreException("<p><b>Database Transaction Error:</b> Unable to call commit() as there is no transaction currently open.");
}
}
else {
} else {
throw new SCoreException("<p><b>Database Transaction Error:</b> Unable to call commit() as there is no connection currently open.");
}
}
public function rollback(): bool {
public function rollback(): bool
{
if (!is_null($this->db)) {
if ($this->transaction === true) {
$this->transaction = false;
return $this->db->rollback();
}
else {
} else {
throw new SCoreException("<p><b>Database Transaction Error:</b> Unable to call rollback() as there is no transaction currently open.");
}
}
else {
} else {
throw new SCoreException("<p><b>Database Transaction Error:</b> Unable to call rollback() as there is no connection currently open.");
}
}
public function escape(string $input): string {
if(is_null($this->db)) $this->connect_db();
public function escape(string $input): string
{
if (is_null($this->db)) {
$this->connect_db();
}
return $this->db->Quote($input);
}
public function scoreql_to_sql(string $input): string {
if(is_null($this->engine)) $this->connect_engine();
public function scoreql_to_sql(string $input): string
{
if (is_null($this->engine)) {
$this->connect_engine();
}
return $this->engine->scoreql_to_sql($input);
}
public function get_driver_name(): string {
if(is_null($this->engine)) $this->connect_engine();
public function get_driver_name(): string
{
if (is_null($this->engine)) {
$this->connect_engine();
}
return $this->engine->name;
}
private function count_execs(string $sql, array $inputarray) {
private function count_execs(string $sql, array $inputarray)
{
if ((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) {
$sql = trim(preg_replace('/\s+/msi', ' ', $sql));
if (isset($inputarray) && is_array($inputarray) && !empty($inputarray)) {
$text = $sql." -- ".join(", ", $inputarray)."\n";
}
else {
} else {
$text = $sql."\n";
}
file_put_contents("data/sql.log", $text, FILE_APPEND);
}
if(!is_array($inputarray)) $this->query_count++;
if (!is_array($inputarray)) {
$this->query_count++;
}
# handle 2-dimensional input arrays
else if(is_array(reset($inputarray))) $this->query_count += sizeof($inputarray);
else $this->query_count++;
elseif (is_array(reset($inputarray))) {
$this->query_count += sizeof($inputarray);
} else {
$this->query_count++;
}
}
private function count_time(string $method, float $start) {
private function count_time(string $method, float $start)
{
if ((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) {
$text = $method.":".(microtime(true) - $start)."\n";
file_put_contents("data/sql.log", $text, FILE_APPEND);
@ -174,9 +198,12 @@ class Database {
$this->dbtime += microtime(true) - $start;
}
public function execute(string $query, array $args=array()): PDOStatement {
public function execute(string $query, array $args=[]): PDOStatement
{
try {
if(is_null($this->db)) $this->connect_db();
if (is_null($this->db)) {
$this->connect_db();
}
$this->count_execs($query, $args);
$stmt = $this->db->prepare(
"-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" .
@ -187,19 +214,16 @@ class Database {
foreach ($args as $name=>$value) {
if (is_numeric($value)) {
$stmt->bindValue(':'.$name, $value, PDO::PARAM_INT);
}
else {
} else {
$stmt->bindValue(':'.$name, $value, PDO::PARAM_STR);
}
}
$stmt->execute();
}
else {
} else {
$stmt->execute($args);
}
return $stmt;
}
catch(PDOException $pdoe) {
} catch (PDOException $pdoe) {
throw new SCoreException($pdoe->getMessage()."<p><b>Query:</b> ".$query);
}
}
@ -207,7 +231,8 @@ class Database {
/**
* Execute an SQL query and return a 2D array.
*/
public function get_all(string $query, array $args=array()): array {
public function get_all(string $query, array $args=[]): array
{
$_start = microtime(true);
$data = $this->execute($query, $args)->fetchAll();
$this->count_time("get_all", $_start);
@ -217,7 +242,8 @@ class Database {
/**
* Execute an SQL query and return a single row.
*/
public function get_row(string $query, array $args=array()) {
public function get_row(string $query, array $args=[])
{
$_start = microtime(true);
$row = $this->execute($query, $args)->fetch();
$this->count_time("get_row", $_start);
@ -227,10 +253,11 @@ class Database {
/**
* Execute an SQL query and return the first column of each row.
*/
public function get_col(string $query, array $args=array()): array {
public function get_col(string $query, array $args=[]): array
{
$_start = microtime(true);
$stmt = $this->execute($query, $args);
$res = array();
$res = [];
foreach ($stmt as $row) {
$res[] = $row[0];
}
@ -241,10 +268,11 @@ class Database {
/**
* Execute an SQL query and return the the first row => the second row.
*/
public function get_pairs(string $query, array $args=array()): array {
public function get_pairs(string $query, array $args=[]): array
{
$_start = microtime(true);
$stmt = $this->execute($query, $args);
$res = array();
$res = [];
foreach ($stmt as $row) {
$res[$row[0]] = $row[1];
}
@ -255,7 +283,8 @@ class Database {
/**
* Execute an SQL query and return a single value.
*/
public function get_one(string $query, array $args=array()) {
public function get_one(string $query, array $args=[])
{
$_start = microtime(true);
$row = $this->execute($query, $args)->fetch();
$this->count_time("get_one", $_start);
@ -265,11 +294,11 @@ class Database {
/**
* Get the ID of the last inserted row.
*/
public function get_last_insert_id(string $seq): int {
public function get_last_insert_id(string $seq): int
{
if ($this->engine->name == "pgsql") {
return $this->db->lastInsertId($seq);
}
else {
} else {
return $this->db->lastInsertId();
}
}
@ -277,8 +306,11 @@ class Database {
/**
* Create a table from pseudo-SQL.
*/
public function create_table(string $name, string $data): void {
if(is_null($this->engine)) { $this->connect_engine(); }
public function create_table(string $name, string $data): void
{
if (is_null($this->engine)) {
$this->connect_engine();
}
$data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas
$this->execute($this->engine->create_table_sql($name, $data));
}
@ -288,8 +320,11 @@ class Database {
*
* @throws SCoreException
*/
public function count_tables(): int {
if(is_null($this->db) || is_null($this->engine)) $this->connect_db();
public function count_tables(): int
{
if (is_null($this->db) || is_null($this->engine)) {
$this->connect_db();
}
if ($this->engine->name === "mysql") {
return count(
@ -309,29 +344,35 @@ class Database {
}
}
class MockDatabase extends Database {
class MockDatabase extends Database
{
/** @var int */
private $query_id = 0;
/** @var array */
private $responses = array();
private $responses = [];
/** @var \NoCache|null */
public $cache = null;
public function __construct(array $responses = array()) {
public function __construct(array $responses = [])
{
$this->cache = new NoCache();
$this->responses = $responses;
}
public function execute(string $query, array $params=array()): PDOStatement {
log_debug("mock-database",
public function execute(string $query, array $params=[]): PDOStatement
{
log_debug(
"mock-database",
"QUERY: " . $query .
"\nARGS: " . var_export($params, true) .
"\nRETURN: " . var_export($this->responses[$this->query_id], true)
);
return $this->responses[$this->query_id++];
}
public function _execute(string $query, array $params=array()) {
log_debug("mock-database",
public function _execute(string $query, array $params=[])
{
log_debug(
"mock-database",
"QUERY: " . $query .
"\nARGS: " . var_export($params, true) .
"\nRETURN: " . var_export($this->responses[$this->query_id], true)
@ -339,16 +380,40 @@ class MockDatabase extends Database {
return $this->responses[$this->query_id++];
}
public function get_all(string $query, array $args=array()): array {return $this->_execute($query, $args);}
public function get_row(string $query, array $args=array()) {return $this->_execute($query, $args);}
public function get_col(string $query, array $args=array()): array {return $this->_execute($query, $args);}
public function get_pairs(string $query, array $args=array()): array {return $this->_execute($query, $args);}
public function get_one(string $query, array $args=array()) {return $this->_execute($query, $args);}
public function get_last_insert_id(string $seq): int {return $this->query_id;}
public function scoreql_to_sql(string $sql): string {return $sql;}
public function create_table(string $name, string $def) {}
public function connect_engine() {}
public function get_all(string $query, array $args=[]): array
{
return $this->_execute($query, $args);
}
public function get_row(string $query, array $args=[])
{
return $this->_execute($query, $args);
}
public function get_col(string $query, array $args=[]): array
{
return $this->_execute($query, $args);
}
public function get_pairs(string $query, array $args=[]): array
{
return $this->_execute($query, $args);
}
public function get_one(string $query, array $args=[])
{
return $this->_execute($query, $args);
}
public function get_last_insert_id(string $seq): int
{
return $this->query_id;
}
public function scoreql_to_sql(string $sql): string
{
return $sql;
}
public function create_table(string $name, string $def)
{
}
public function connect_engine()
{
}
}

View File

@ -1,28 +1,36 @@
<?php
class DBEngine {
class DBEngine
{
/** @var null|string */
public $name = null;
public function init(PDO $db) {}
public function init(PDO $db)
{
}
public function scoreql_to_sql(string $scoreql): string {
public function scoreql_to_sql(string $scoreql): string
{
return $scoreql;
}
public function create_table_sql(string $name, string $data): string {
public function create_table_sql(string $name, string $data): string
{
return 'CREATE TABLE '.$name.' ('.$data.')';
}
}
class MySQL extends DBEngine {
class MySQL extends DBEngine
{
/** @var string */
public $name = "mysql";
public function init(PDO $db) {
public function init(PDO $db)
{
$db->exec("SET NAMES utf8;");
}
public function scoreql_to_sql(string $data): string {
public function scoreql_to_sql(string $data): string
{
$data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY auto_increment", $data);
$data = str_replace("SCORE_INET", "VARCHAR(45)", $data);
$data = str_replace("SCORE_BOOL_Y", "'Y'", $data);
@ -35,28 +43,31 @@ class MySQL extends DBEngine {
return $data;
}
public function create_table_sql(string $name, string $data): string {
public function create_table_sql(string $name, string $data): string
{
$data = $this->scoreql_to_sql($data);
$ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'";
return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes;
}
}
class PostgreSQL extends DBEngine {
class PostgreSQL extends DBEngine
{
/** @var string */
public $name = "pgsql";
public function init(PDO $db) {
public function init(PDO $db)
{
if (array_key_exists('REMOTE_ADDR', $_SERVER)) {
$db->exec("SET application_name TO 'shimmie [{$_SERVER['REMOTE_ADDR']}]';");
}
else {
} else {
$db->exec("SET application_name TO 'shimmie [local]';");
}
$db->exec("SET statement_timeout TO 10000;");
}
public function scoreql_to_sql(string $data): string {
public function scoreql_to_sql(string $data): string
{
$data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data);
$data = str_replace("SCORE_INET", "INET", $data);
$data = str_replace("SCORE_BOOL_Y", "'t'", $data);
@ -69,32 +80,66 @@ class PostgreSQL extends DBEngine {
return $data;
}
public function create_table_sql(string $name, string $data): string {
public function create_table_sql(string $name, string $data): string
{
$data = $this->scoreql_to_sql($data);
return "CREATE TABLE $name ($data)";
}
}
// shimmie functions for export to sqlite
function _unix_timestamp($date) { return strtotime($date); }
function _now() { return date("Y-m-d h:i:s"); }
function _floor($a) { return floor($a); }
function _log($a, $b=null) {
if(is_null($b)) return log($a);
else return log($a, $b);
function _unix_timestamp($date)
{
return strtotime($date);
}
function _now()
{
return date("Y-m-d h:i:s");
}
function _floor($a)
{
return floor($a);
}
function _log($a, $b=null)
{
if (is_null($b)) {
return log($a);
} else {
return log($a, $b);
}
}
function _isnull($a)
{
return is_null($a);
}
function _md5($a)
{
return md5($a);
}
function _concat($a, $b)
{
return $a . $b;
}
function _lower($a)
{
return strtolower($a);
}
function _rand()
{
return rand();
}
function _ln($n)
{
return log($n);
}
function _isnull($a) { return is_null($a); }
function _md5($a) { return md5($a); }
function _concat($a, $b) { return $a . $b; }
function _lower($a) { return strtolower($a); }
function _rand() { return rand(); }
function _ln($n) { return log($n); }
class SQLite extends DBEngine {
class SQLite extends DBEngine
{
/** @var string */
public $name = "sqlite";
public function init(PDO $db) {
public function init(PDO $db)
{
ini_set('sqlite.assoc_case', 0);
$db->exec("PRAGMA foreign_keys = ON;");
$db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1);
@ -109,7 +154,8 @@ class SQLite extends DBEngine {
$db->sqliteCreateFunction('ln', '_ln', 1);
}
public function scoreql_to_sql(string $data): string {
public function scoreql_to_sql(string $data): string
{
$data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data);
$data = str_replace("SCORE_INET", "VARCHAR(45)", $data);
$data = str_replace("SCORE_BOOL_Y", "'Y'", $data);
@ -121,18 +167,18 @@ class SQLite extends DBEngine {
return $data;
}
public function create_table_sql(string $name, string $data): string {
public function create_table_sql(string $name, string $data): string
{
$data = $this->scoreql_to_sql($data);
$cols = array();
$cols = [];
$extras = "";
foreach (explode(",", $data) as $bit) {
$matches = array();
$matches = [];
if (preg_match("/(UNIQUE)? ?INDEX\s*\((.*)\)/", $bit, $matches)) {
$uni = $matches[1];
$col = $matches[2];
$extras .= "CREATE $uni INDEX {$name}_{$col} ON {$name}({$col});";
}
else {
} else {
$cols[] = $bit;
}
}

View File

@ -5,7 +5,8 @@
*
* A generic email.
*/
class Email {
class Email
{
/** @var string */
public $to;
/** @var string */
@ -29,7 +30,8 @@ class Email {
/** @var null|string */
public $footer;
public function __construct(string $to, string $subject, string $header, string $body) {
public function __construct(string $to, string $subject, string $header, string $body)
{
global $config;
$this->to = $to;
@ -37,8 +39,7 @@ class Email {
if (!isset($sub_prefix)) {
$this->subject = $subject;
}
else{
} else {
$this->subject = $sub_prefix." ".$subject;
}
@ -54,7 +55,8 @@ class Email {
$this->footer = $config->get_string("mail_fot");
}
public function send(): bool {
public function send(): bool
{
$headers = "From: ".$this->sitename." <".$this->siteemail.">\r\n";
$headers .= "Reply-To: ".$this->siteemail."\r\n";
$headers .= "X-Mailer: PHP/" . phpversion(). "\r\n";
@ -121,12 +123,10 @@ Copyright (C) <a href="'.$this->sitedomain.'">'.$this->sitename.'</a><br />
$sent = mail($this->to, $this->subject, $message, $headers);
if ($sent) {
log_info("mail", "Sent message '$this->subject' to '$this->to'");
}
else{
} else {
log_info("mail", "Error sending message '$this->subject' to '$this->to'");
}
return $sent;
}
}

View File

@ -4,8 +4,11 @@
*
* An event is anything that can be passed around via send_event($blah)
*/
abstract class Event {
public function __construct() {}
abstract class Event
{
public function __construct()
{
}
}
@ -16,7 +19,9 @@ abstract class Event {
*
* This event is sent before $user is set to anything
*/
class InitExtEvent extends Event {}
class InitExtEvent extends Event
{
}
/**
@ -27,7 +32,8 @@ class InitExtEvent extends Event {}
* true and ignores the matched part, such that $event->count_args() = 1 and
* $event->get_arg(0) = "42"
*/
class PageRequestEvent extends Event {
class PageRequestEvent extends Event
{
/**
* @var array
*/
@ -43,7 +49,8 @@ class PageRequestEvent extends Event {
*/
public $part_count;
public function __construct(string $path) {
public function __construct(string $path)
{
global $config;
// trim starting slashes
@ -59,8 +66,8 @@ class PageRequestEvent extends Event {
// voodoo so that an arg can contain a slash; is
// this still needed?
if(strpos($path, "^") !== FALSE) {
$unescaped = array();
if (strpos($path, "^") !== false) {
$unescaped = [];
foreach ($args as $part) {
$unescaped[] = _decaret($part);
}
@ -76,7 +83,8 @@ class PageRequestEvent extends Event {
*
* If it matches, store the remaining path elements in $args
*/
public function page_matches(string $name): bool {
public function page_matches(string $name): bool
{
$parts = explode("/", $name);
$this->part_count = count($parts);
@ -96,12 +104,12 @@ class PageRequestEvent extends Event {
/**
* Get the n th argument of the page request (if it exists.)
*/
public function get_arg(int $n): ?string {
public function get_arg(int $n): ?string
{
$offset = $this->part_count + $n;
if ($offset >= 0 && $offset < $this->arg_count) {
return $this->args[$offset];
}
else {
} else {
return null;
}
}
@ -109,7 +117,8 @@ class PageRequestEvent extends Event {
/**
* Returns the number of arguments the page request has.
*/
public function count_args(): int {
public function count_args(): int
{
return int_escape($this->arg_count - $this->part_count);
}
@ -117,27 +126,31 @@ class PageRequestEvent extends Event {
* Many things use these functions
*/
public function get_search_terms(): array {
$search_terms = array();
public function get_search_terms(): array
{
$search_terms = [];
if ($this->count_args() === 2) {
$search_terms = Tag::explode($this->get_arg(0));
}
return $search_terms;
}
public function get_page_number(): int {
public function get_page_number(): int
{
$page_number = 1;
if ($this->count_args() === 1) {
$page_number = int_escape($this->get_arg(0));
}
else if($this->count_args() === 2) {
} elseif ($this->count_args() === 2) {
$page_number = int_escape($this->get_arg(1));
}
if($page_number === 0) $page_number = 1; // invalid -> 0
if ($page_number === 0) {
$page_number = 1;
} // invalid -> 0
return $page_number;
}
public function get_page_size(): int {
public function get_page_size(): int
{
global $config;
return $config->get_int('index_images');
}
@ -147,7 +160,8 @@ class PageRequestEvent extends Event {
/**
* Sent when index.php is called from the command line
*/
class CommandEvent extends Event {
class CommandEvent extends Event
{
/**
* @var string
*/
@ -156,15 +170,16 @@ class CommandEvent extends Event {
/**
* @var array
*/
public $args = array();
public $args = [];
/**
* #param string[] $args
*/
public function __construct(array $args) {
public function __construct(array $args)
{
global $user;
$opts = array();
$opts = [];
$log_level = SCORE_LOG_WARNING;
$arg_count = count($args);
@ -193,8 +208,7 @@ class CommandEvent extends Event {
if (count($opts) > 0) {
$this->cmd = $opts[0];
$this->args = array_slice($opts, 1);
}
else {
} else {
print "\n";
print "Usage: php {$args[0]} [flags] [command]\n";
print "\n";
@ -216,7 +230,8 @@ class CommandEvent extends Event {
* A signal that some text needs formatting, the event carries
* both the text and the result
*/
class TextFormattingEvent extends Event {
class TextFormattingEvent extends Event
{
/**
* For reference
*
@ -238,7 +253,8 @@ class TextFormattingEvent extends Event {
*/
public $stripped;
public function __construct(string $text) {
public function __construct(string $text)
{
$h_text = html_escape(trim($text));
$this->original = $h_text;
$this->formatted = $h_text;
@ -250,7 +266,8 @@ class TextFormattingEvent extends Event {
/**
* A signal that something needs logging
*/
class LogEvent extends Event {
class LogEvent extends Event
{
/**
* a category, normally the extension name
*
@ -286,7 +303,8 @@ class LogEvent extends Event {
*/
public $args;
public function __construct(string $section, int $priority, string $message, array $args) {
public function __construct(string $section, int $priority, string $message, array $args)
{
$this->section = $section;
$this->priority = $priority;
$this->message = $message;
@ -294,4 +312,3 @@ class LogEvent extends Event {
$this->time = time();
}
}

View File

@ -5,14 +5,18 @@
*
* A base exception to be caught by the upper levels.
*/
class SCoreException extends Exception {}
class SCoreException extends Exception
{
}
/**
* Class PermissionDeniedException
*
* A fairly common, generic exception.
*/
class PermissionDeniedException extends SCoreException {}
class PermissionDeniedException extends SCoreException
{
}
/**
* Class ImageDoesNotExist
@ -21,9 +25,13 @@ class PermissionDeniedException extends SCoreException {}
*
* Example: Image::by_id(-1) returns null
*/
class ImageDoesNotExist extends SCoreException {}
class ImageDoesNotExist extends SCoreException
{
}
/*
* For validate_input()
*/
class InvalidInput extends SCoreException {}
class InvalidInput extends SCoreException
{
}

View File

@ -81,18 +81,21 @@
* Then re-implemented by Shish after he broke the forum and couldn't
* find the thread where the original was posted >_<
*/
abstract class Extension {
abstract class Extension
{
/** @var array which DBs this ext supports (blank for 'all') */
protected $db_support = [];
/** @var Themelet this theme's Themelet object */
public $theme;
public function __construct() {
public function __construct()
{
$this->theme = $this->get_theme_object(get_called_class());
}
public function is_live(): bool {
public function is_live(): bool
{
global $database;
return (
empty($this->db_support) ||
@ -103,17 +106,16 @@ abstract class Extension {
/**
* Find the theme object for a given extension.
*/
private function get_theme_object(string $base): ?Themelet {
private function get_theme_object(string $base): ?Themelet
{
$custom = 'Custom'.$base.'Theme';
$normal = $base.'Theme';
if (class_exists($custom)) {
return new $custom();
}
elseif(class_exists($normal)) {
} elseif (class_exists($normal)) {
return new $normal();
}
else {
} else {
return null;
}
}
@ -122,7 +124,8 @@ abstract class Extension {
* Override this to change the priority of the extension,
* lower numbered ones will receive events first.
*/
public function get_priority(): int {
public function get_priority(): int
{
return 50;
}
}
@ -132,8 +135,10 @@ abstract class Extension {
*
* Several extensions have this in common, make a common API.
*/
abstract class FormatterExtension extends Extension {
public function onTextFormatting(TextFormattingEvent $event) {
abstract class FormatterExtension extends Extension
{
public function onTextFormatting(TextFormattingEvent $event)
{
$event->formatted = $this->format($event->formatted);
$event->stripped = $this->strip($event->stripped);
}
@ -148,8 +153,10 @@ abstract class FormatterExtension extends Extension {
* This too is a common class of extension with many methods in common,
* so we have a base class to extend from.
*/
abstract class DataHandlerExtension extends Extension {
public function onDataUpload(DataUploadEvent $event) {
abstract class DataHandlerExtension extends Extension
{
public function onDataUpload(DataUploadEvent $event)
{
$supported_ext = $this->supported_ext($event->type);
$check_contents = $this->check_contents($event->tmpname);
if ($supported_ext && $check_contents) {
@ -184,8 +191,7 @@ abstract class DataHandlerExtension extends Extension {
$ire = new ImageReplaceEvent($image_id, $image);
send_event($ire);
$event->image_id = $image_id;
}
else {
} else {
$image = $this->create_image_from_data(warehouse_path("images", $event->hash), $event->metadata);
if (is_null($image)) {
throw new UploadException("Data handler failed to create image object from data");
@ -206,24 +212,24 @@ abstract class DataHandlerExtension extends Extension {
send_event(new LockSetEvent($image, !empty($locked)));
}
}
}
elseif($supported_ext && !$check_contents){
} elseif ($supported_ext && !$check_contents) {
throw new UploadException("Invalid or corrupted file");
}
}
public function onThumbnailGeneration(ThumbnailGenerationEvent $event) {
public function onThumbnailGeneration(ThumbnailGenerationEvent $event)
{
if ($this->supported_ext($event->type)) {
if (method_exists($this, 'create_thumb_force') && $event->force == true) {
$this->create_thumb_force($event->hash);
}
else {
} else {
$this->create_thumb($event->hash);
}
}
}
public function onDisplayingImage(DisplayingImageEvent $event) {
public function onDisplayingImage(DisplayingImageEvent $event)
{
global $page;
if ($this->supported_ext($event->image->ext)) {
$this->theme->display_image($page, $event->image);
@ -244,4 +250,3 @@ abstract class DataHandlerExtension extends Extension {
abstract protected function create_image_from_data(string $filename, array $metadata);
abstract protected function create_thumb(string $hash): bool;
}

View File

@ -3,7 +3,8 @@
/**
* An image is being added to the database.
*/
class ImageAdditionEvent extends Event {
class ImageAdditionEvent extends Event
{
/** @var User */
public $user;
@ -15,15 +16,18 @@ class ImageAdditionEvent extends Event {
* information. Also calls TagSetEvent to set the tags for
* this new image.
*/
public function __construct(Image $image) {
public function __construct(Image $image)
{
$this->image = $image;
}
}
class ImageAdditionException extends SCoreException {
class ImageAdditionException extends SCoreException
{
public $error;
public function __construct(string $error) {
public function __construct(string $error)
{
$this->error = $error;
}
}
@ -31,7 +35,8 @@ class ImageAdditionException extends SCoreException {
/**
* An image is being deleted.
*/
class ImageDeletionEvent extends Event {
class ImageDeletionEvent extends Event
{
/** @var \Image */
public $image;
@ -41,7 +46,8 @@ class ImageDeletionEvent extends Event {
* Used by things like tags and comments handlers to
* clean out related rows in their tables.
*/
public function __construct(Image $image) {
public function __construct(Image $image)
{
$this->image = $image;
}
}
@ -49,7 +55,8 @@ class ImageDeletionEvent extends Event {
/**
* An image is being replaced.
*/
class ImageReplaceEvent extends Event {
class ImageReplaceEvent extends Event
{
/** @var int */
public $id;
/** @var \Image */
@ -62,17 +69,20 @@ class ImageReplaceEvent extends Event {
* file, leaving the tags and such unchanged. Also removes
* the old image file and thumbnail from the disk.
*/
public function __construct(int $id, Image $image) {
public function __construct(int $id, Image $image)
{
$this->id = $id;
$this->image = $image;
}
}
class ImageReplaceException extends SCoreException {
class ImageReplaceException extends SCoreException
{
/** @var string */
public $error;
public function __construct(string $error) {
public function __construct(string $error)
{
$this->error = $error;
}
}
@ -80,7 +90,8 @@ class ImageReplaceException extends SCoreException {
/**
* Request a thumbnail be made for an image object.
*/
class ThumbnailGenerationEvent extends Event {
class ThumbnailGenerationEvent extends Event
{
/** @var string */
public $hash;
/** @var string */
@ -91,7 +102,8 @@ class ThumbnailGenerationEvent extends Event {
/**
* Request a thumbnail be made for an image object
*/
public function __construct(string $hash, string $type, bool $force=false) {
public function __construct(string $hash, string $type, bool $force=false)
{
$this->hash = $hash;
$this->type = $type;
$this->force = $force;
@ -105,7 +117,8 @@ class ThumbnailGenerationEvent extends Event {
* $original -- the formatting string, for reference
* $image -- the image who's link is being parsed
*/
class ParseLinkTemplateEvent extends Event {
class ParseLinkTemplateEvent extends Event
{
/** @var string */
public $link;
/** @var string */
@ -113,13 +126,15 @@ class ParseLinkTemplateEvent extends Event {
/** @var \Image */
public $image;
public function __construct(string $link, Image $image) {
public function __construct(string $link, Image $image)
{
$this->link = $link;
$this->original = $link;
$this->image = $image;
}
public function replace(string $needle, string $replace): void {
public function replace(string $needle, string $replace): void
{
$this->link = str_replace($needle, $replace, $this->link);
}
}

View File

@ -8,7 +8,8 @@
* image per se, but could be a video, sound file, or any
* other supported upload type.
*/
class Image {
class Image
{
private static $tag_n = 0; // temp hack
public static $order_sql = null; // this feels ugly
@ -54,7 +55,8 @@ class Image {
* One will very rarely construct an image directly, more common
* would be to use Image::by_id, Image::by_hash, etc.
*/
public function __construct(?array $row=null) {
public function __construct(?array $row=null)
{
if (!is_null($row)) {
foreach ($row as $name => $value) {
// some databases use table.name rather than name
@ -69,25 +71,33 @@ class Image {
}
}
public static function by_id(int $id) {
public static function by_id(int $id)
{
global $database;
$row = $database->get_row("SELECT * FROM images WHERE images.id=:id", array("id"=>$id));
$row = $database->get_row("SELECT * FROM images WHERE images.id=:id", ["id"=>$id]);
return ($row ? new Image($row) : null);
}
public static function by_hash(string $hash) {
public static function by_hash(string $hash)
{
global $database;
$row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", array("hash"=>$hash));
$row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", ["hash"=>$hash]);
return ($row ? new Image($row) : null);
}
public static function by_random(array $tags=array()) {
public static function by_random(array $tags=[])
{
$max = Image::count_images($tags);
if ($max < 1) return null; // From Issue #22 - opened by HungryFeline on May 30, 2011.
if ($max < 1) {
return null;
} // From Issue #22 - opened by HungryFeline on May 30, 2011.
$rand = mt_rand(0, $max-1);
$set = Image::find_images($rand, 1, $tags);
if(count($set) > 0) return $set[0];
else return null;
if (count($set) > 0) {
return $set[0];
} else {
return null;
}
}
/**
@ -96,13 +106,18 @@ class Image {
* #param string[] $tags
* #return Image[]
*/
public static function find_images(int $start, int $limit, array $tags=array()): array {
public static function find_images(int $start, int $limit, array $tags=[]): array
{
global $database, $user, $config;
$images = array();
$images = [];
if($start < 0) $start = 0;
if($limit < 1) $limit = 1;
if ($start < 0) {
$start = 0;
}
if ($limit < 1) {
$limit = 1;
}
if (SPEED_HAX) {
if (!$user->can("big_search") and count($tags) > 3) {
@ -118,7 +133,7 @@ class Image {
if (!$result) {
$querylet = Image::build_search_querylet($tags);
$querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order"))));
$querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", array("limit"=>$limit, "offset"=>$start)));
$querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start]));
#var_dump($querylet->sql); var_dump($querylet->variables);
$result = $database->execute($querylet->sql, $querylet->variables);
}
@ -133,19 +148,25 @@ class Image {
/*
* Accelerator stuff
*/
public static function get_acceleratable(array $tags) {
$ret = array(
"yays" => array(),
"nays" => array(),
);
public static function get_acceleratable(array $tags)
{
$ret = [
"yays" => [],
"nays" => [],
];
$yays = 0;
$nays = 0;
foreach ($tags as $tag) {
if (!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) {
return false;
}
if($tag[0] == "-") {$nays++; $ret["nays"][] = substr($tag, 1);}
else {$yays++; $ret["yays"][] = $tag;}
if ($tag[0] == "-") {
$nays++;
$ret["nays"][] = substr($tag, 1);
} else {
$yays++;
$ret["yays"][] = $tag;
}
}
if ($yays > 1 || $nays > 0) {
return $ret;
@ -153,11 +174,14 @@ class Image {
return false;
}
public static function get_accelerated_result(array $tags, int $offset, int $limit) {
public static function get_accelerated_result(array $tags, int $offset, int $limit)
{
global $database;
$req = Image::get_acceleratable($tags);
if(!$req) {return null;}
if (!$req) {
return null;
}
$req["offset"] = $offset;
$req["limit"] = $limit;
@ -165,22 +189,25 @@ class Image {
$list = implode(",", $response);
if ($list) {
$result = $database->execute("SELECT * FROM images WHERE id IN ($list) ORDER BY images.id DESC");
}
else {
} else {
$result = $database->execute("SELECT * FROM images WHERE 1=0 ORDER BY images.id DESC");
}
return $result;
}
public static function get_accelerated_count(array $tags) {
public static function get_accelerated_count(array $tags)
{
$req = Image::get_acceleratable($tags);
if(!$req) {return null;}
if (!$req) {
return null;
}
$req["count"] = true;
return Image::query_accelerator($req);
}
public static function query_accelerator($req) {
public static function query_accelerator($req)
{
$fp = @fsockopen("127.0.0.1", 21212);
if (!$fp) {
return null;
@ -206,7 +233,8 @@ class Image {
*
* #param string[] $tags
*/
public static function count_images(array $tags=array()): int {
public static function count_images(array $tags=[]): int
{
global $database;
$tag_count = count($tags);
@ -216,20 +244,21 @@ class Image {
$total = $database->get_one("SELECT COUNT(*) FROM images");
$database->cache->set("image-count", $total, 600);
}
}
else if($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) {
} elseif ($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) {
$total = $database->get_one(
$database->scoreql_to_sql("SELECT count FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)"),
array("tag"=>$tags[0]));
}
else {
["tag"=>$tags[0]]
);
} else {
$total = Image::get_accelerated_count($tags);
if (is_null($total)) {
$querylet = Image::build_search_querylet($tags);
$total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables);
}
}
if(is_null($total)) return 0;
if (is_null($total)) {
return 0;
}
return $total;
}
@ -238,7 +267,8 @@ class Image {
*
* #param string[] $tags
*/
public static function count_pages(array $tags=array()): float {
public static function count_pages(array $tags=[]): float
{
global $config;
return ceil(Image::count_images($tags) / $config->get_int('index_images'));
}
@ -255,14 +285,14 @@ class Image {
*
* #param string[] $tags
*/
public function get_next(array $tags=array(), bool $next=true): ?Image {
public function get_next(array $tags=[], bool $next=true): ?Image
{
global $database;
if ($next) {
$gtlt = "<";
$dir = "DESC";
}
else {
} else {
$gtlt = ">";
$dir = "ASC";
}
@ -275,8 +305,7 @@ class Image {
ORDER BY images.id '.$dir.'
LIMIT 1
');
}
else {
} else {
$tags[] = 'id'. $gtlt . $this->id;
$querylet = Image::build_search_querylet($tags);
$querylet->append_sql(' ORDER BY images.id '.$dir.' LIMIT 1');
@ -291,29 +320,32 @@ class Image {
*
* #param string[] $tags
*/
public function get_prev(array $tags=array()): ?Image {
public function get_prev(array $tags=[]): ?Image
{
return $this->get_next($tags, false);
}
/**
* Find the User who owns this Image
*/
public function get_owner(): User {
public function get_owner(): User
{
return User::by_id($this->owner_id);
}
/**
* Set the image's owner.
*/
public function set_owner(User $owner) {
public function set_owner(User $owner)
{
global $database;
if ($owner->id != $this->owner_id) {
$database->execute("
UPDATE images
SET owner_id=:owner_id
WHERE id=:id
", array("owner_id"=>$owner->id, "id"=>$this->id));
log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", null, array("image_id" => $this->id));
", ["owner_id"=>$owner->id, "id"=>$this->id]);
log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", null, ["image_id" => $this->id]);
}
}
@ -322,7 +354,8 @@ class Image {
*
* #return string[]
*/
public function get_tag_array(): array {
public function get_tag_array(): array
{
global $database;
if (!isset($this->tag_array)) {
$this->tag_array = $database->get_col("
@ -331,7 +364,7 @@ class Image {
JOIN tags ON image_tags.tag_id = tags.id
WHERE image_id=:id
ORDER BY tag
", array("id"=>$this->id));
", ["id"=>$this->id]);
}
return $this->tag_array;
}
@ -339,28 +372,32 @@ class Image {
/**
* Get this image's tags as a string.
*/
public function get_tag_list(): string {
public function get_tag_list(): string
{
return Tag::implode($this->get_tag_array());
}
/**
* Get the URL for the full size image
*/
public function get_image_link(): string {
public function get_image_link(): string
{
return $this->get_link('image_ilink', '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext');
}
/**
* Get the URL for the thumbnail
*/
public function get_thumb_link(): string {
public function get_thumb_link(): string
{
return $this->get_link('image_tlink', '_thumbs/$hash/thumb.jpg', 'thumb/$id.jpg');
}
/**
* Check configured template for a link, then try nice URL, then plain URL
*/
private function get_link(string $template, string $nice, string $plain): string {
private function get_link(string $template, string $nice, string $plain): string
{
global $config;
$image_link = $config->get_string($template);
@ -370,11 +407,9 @@ class Image {
$image_link = make_link($image_link);
}
return $this->parse_link_template($image_link);
}
else if($config->get_bool('nice_urls', false)) {
} elseif ($config->get_bool('nice_urls', false)) {
return $this->parse_link_template(make_link($nice));
}
else {
} else {
return $this->parse_link_template(make_link($plain));
}
}
@ -383,26 +418,26 @@ class Image {
* Get the tooltip for this image, formatted according to the
* configured template.
*/
public function get_tooltip(): string {
public function get_tooltip(): string
{
global $config;
$tt = $this->parse_link_template($config->get_string('image_tip'), "no_escape");
// Removes the size tag if the file is an mp3
if ($this->ext === 'mp3') {
$iitip = $tt;
$mp3tip = array("0x0");
$mp3tip = ["0x0"];
$h_tip = str_replace($mp3tip, " ", $iitip);
// Makes it work with a variation of the default tooltips (I.E $tags // $filesize // $size)
$justincase = array(" //", "// ", " //", "// ", " ");
$justincase = [" //", "// ", " //", "// ", " "];
if (strstr($h_tip, " ")) {
$h_tip = html_escape(str_replace($justincase, "", $h_tip));
} else {
$h_tip = html_escape($h_tip);
}
return $h_tip;
}
else {
} else {
return $tt;
}
}
@ -410,74 +445,85 @@ class Image {
/**
* Figure out where the full size image is on disk.
*/
public function get_image_filename(): string {
public function get_image_filename(): string
{
return warehouse_path("images", $this->hash);
}
/**
* Figure out where the thumbnail is on disk.
*/
public function get_thumb_filename(): string {
public function get_thumb_filename(): string
{
return warehouse_path("thumbs", $this->hash);
}
/**
* Get the original filename.
*/
public function get_filename(): string {
public function get_filename(): string
{
return $this->filename;
}
/**
* Get the image's mime type.
*/
public function get_mime_type(): string {
public function get_mime_type(): string
{
return getMimeType($this->get_image_filename(), $this->get_ext());
}
/**
* Get the image's filename extension
*/
public function get_ext(): string {
public function get_ext(): string
{
return $this->ext;
}
/**
* Get the image's source URL
*/
public function get_source(): string {
public function get_source(): string
{
return $this->source;
}
/**
* Set the image's source URL
*/
public function set_source(string $new_source): void {
public function set_source(string $new_source): void
{
global $database;
$old_source = $this->source;
if(empty($new_source)) $new_source = null;
if (empty($new_source)) {
$new_source = null;
}
if ($new_source != $old_source) {
$database->execute("UPDATE images SET source=:source WHERE id=:id", array("source"=>$new_source, "id"=>$this->id));
log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", null, array("image_id" => $this->id));
$database->execute("UPDATE images SET source=:source WHERE id=:id", ["source"=>$new_source, "id"=>$this->id]);
log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", null, ["image_id" => $this->id]);
}
}
/**
* Check if the image is locked.
*/
public function is_locked(): bool {
public function is_locked(): bool
{
return $this->locked;
}
public function set_locked(bool $tf) {
public function set_locked(bool $tf)
{
global $database;
$ln = $tf ? "Y" : "N";
$sln = $database->scoreql_to_sql('SCORE_BOOL_'.$ln);
$sln = str_replace("'", "", $sln);
$sln = str_replace('"', "", $sln);
if (bool_escape($sln) !== $this->locked) {
$database->execute("UPDATE images SET locked=:yn WHERE id=:id", array("yn"=>$sln, "id"=>$this->id));
log_info("core_image", "Setting Image #{$this->id} lock to: $ln", null, array("image_id" => $this->id));
$database->execute("UPDATE images SET locked=:yn WHERE id=:id", ["yn"=>$sln, "id"=>$this->id]);
log_info("core_image", "Setting Image #{$this->id} lock to: $ln", null, ["image_id" => $this->id]);
}
}
@ -486,16 +532,18 @@ class Image {
*
* Normally in preparation to set them to a new set.
*/
public function delete_tags_from_image(): void {
public function delete_tags_from_image(): void
{
global $database;
if ($database->get_driver_name() == "mysql") {
//mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this
$database->execute("
$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)
["id"=>$this->id]
);
} else {
$database->execute("
@ -506,19 +554,20 @@ class Image {
FROM image_tags
WHERE image_id = :id
)
", array("id"=>$this->id));
", ["id"=>$this->id]);
}
$database->execute("
DELETE
FROM image_tags
WHERE image_id=:id
", array("id"=>$this->id));
", ["id"=>$this->id]);
}
/**
* Set the tags for this image.
*/
public function set_tags(array $unfiltered_tags) {
public function set_tags(array $unfiltered_tags)
{
global $database;
$tags = [];
@ -550,24 +599,25 @@ class Image {
FROM tags
WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)
"),
array("tag"=>$tag)
["tag"=>$tag]
);
if (empty($id)) {
// a new tag
$database->execute(
"INSERT INTO tags(tag) VALUES (:tag)",
array("tag"=>$tag));
["tag"=>$tag]
);
$database->execute(
"INSERT INTO image_tags(image_id, tag_id)
VALUES(:id, (SELECT id FROM tags WHERE tag = :tag))",
array("id"=>$this->id, "tag"=>$tag));
}
else {
["id"=>$this->id, "tag"=>$tag]
);
} else {
// user of an existing tag
$database->execute("
INSERT INTO image_tags(image_id, tag_id)
VALUES(:iid, :tid)
", array("iid"=>$this->id, "tid"=>$id));
", ["iid"=>$this->id, "tid"=>$id]);
}
$database->execute(
$database->scoreql_to_sql("
@ -575,11 +625,11 @@ class Image {
SET count = count + 1
WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)
"),
array("tag"=>$tag)
["tag"=>$tag]
);
}
log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags), null, array("image_id" => $this->id));
log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags), null, ["image_id" => $this->id]);
$database->cache->delete("image-{$this->id}-tags");
}
}
@ -589,9 +639,10 @@ class Image {
*
* #param string[] $metatags
*/
public function parse_metatags(array $metatags, int $image_id): void {
public function parse_metatags(array $metatags, int $image_id): void
{
foreach ($metatags as $tag) {
$ttpe = new TagTermParseEvent($tag, $image_id, TRUE);
$ttpe = new TagTermParseEvent($tag, $image_id, true);
send_event($ttpe);
}
}
@ -599,11 +650,12 @@ class Image {
/**
* Delete this image from the database and disk
*/
public function delete(): void {
public function delete(): void
{
global $database;
$this->delete_tags_from_image();
$database->execute("DELETE FROM images WHERE id=:id", array("id"=>$this->id));
log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', null, array("image_id" => $this->id));
$database->execute("DELETE FROM images WHERE id=:id", ["id"=>$this->id]);
log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', null, ["image_id" => $this->id]);
unlink($this->get_image_filename());
unlink($this->get_thumb_filename());
@ -613,13 +665,15 @@ class Image {
* This function removes an image (and thumbnail) from the DISK ONLY.
* It DOES NOT remove anything from the database.
*/
public function remove_image_only(): void {
log_info("core_image", 'Removed Image File ('.$this->hash.')', null, array("image_id" => $this->id));
public function remove_image_only(): void
{
log_info("core_image", 'Removed Image File ('.$this->hash.')', null, ["image_id" => $this->id]);
@unlink($this->get_image_filename());
@unlink($this->get_thumb_filename());
}
public function parse_link_template(string $tmpl, string $_escape="url_escape", int $n=0): string {
public function parse_link_template(string $tmpl, string $_escape="url_escape", int $n=0): string
{
global $config;
// don't bother hitting the database if it won't be used...
@ -656,7 +710,7 @@ class Image {
static $flexihash = null;
static $fh_last_opts = null;
$matches = array();
$matches = [];
if (preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) {
$pre = $matches[1];
$opts = $matches[2];
@ -673,8 +727,7 @@ class Image {
if ($parts_count === 2) {
$opt_val = $parts[0];
$opt_weight = $parts[1];
}
elseif($parts_count === 1) {
} elseif ($parts_count === 1) {
$opt_val = $parts[0];
$opt_weight = 1;
}
@ -694,11 +747,12 @@ class Image {
/**
* #param string[] $terms
*/
private static function build_search_querylet(array $terms): Querylet {
private static function build_search_querylet(array $terms): Querylet
{
global $database;
$tag_querylets = array();
$img_querylets = array();
$tag_querylets = [];
$img_querylets = [];
$positive_tag_count = 0;
$negative_tag_count = 0;
@ -730,8 +784,7 @@ class Image {
foreach ($stpe->get_querylets() as $querylet) {
$img_querylets[] = new ImgQuerylet($querylet, $positive);
}
}
else {
} else {
// if the whole match is wild, skip this;
// if not, translate into SQL
if (str_replace("*", "", $term) != "") {
@ -739,8 +792,11 @@ class Image {
$term = str_replace('%', '\%', $term);
$term = str_replace('*', '%', $term);
$tag_querylets[] = new TagQuerylet($term, $positive);
if ($positive) $positive_tag_count++;
else $negative_tag_count++;
if ($positive) {
$positive_tag_count++;
} else {
$negative_tag_count++;
}
}
}
}
@ -780,16 +836,17 @@ class Image {
GROUP BY images.id
) AS images
WHERE 1=1
"), array("tag"=>$tag_querylets[0]->tag));
"), ["tag"=>$tag_querylets[0]->tag]);
}
// more than one positive tag, or more than zero negative tags
else {
if($database->get_driver_name() === "mysql")
if ($database->get_driver_name() === "mysql") {
$query = Image::build_ugly_search_querylet($tag_querylets);
else
} else {
$query = Image::build_accurate_search_querylet($tag_querylets);
}
}
/*
* Merge all the image metadata searches into one generic querylet
@ -798,10 +855,14 @@ class Image {
if (!empty($img_querylets)) {
$n = 0;
$img_sql = "";
$img_vars = array();
$img_vars = [];
foreach ($img_querylets as $iq) {
if ($n++ > 0) $img_sql .= " AND";
if (!$iq->positive) $img_sql .= " NOT";
if ($n++ > 0) {
$img_sql .= " AND";
}
if (!$iq->positive) {
$img_sql .= " NOT";
}
$img_sql .= " (" . $iq->qlet->sql . ")";
$img_vars = array_merge($img_vars, $iq->qlet->variables);
}
@ -835,11 +896,12 @@ class Image {
*
* #param TagQuerylet[] $tag_querylets
*/
private static function build_accurate_search_querylet(array $tag_querylets): Querylet {
private static function build_accurate_search_querylet(array $tag_querylets): Querylet
{
global $database;
$positive_tag_id_array = array();
$negative_tag_id_array = array();
$positive_tag_id_array = [];
$negative_tag_id_array = [];
foreach ($tag_querylets as $tq) {
$tag_ids = $database->get_col(
@ -848,7 +910,7 @@ class Image {
FROM tags
WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:tag)
"),
array("tag" => $tq->tag)
["tag" => $tq->tag]
);
if ($tq->positive) {
$positive_tag_id_array = array_merge($positive_tag_id_array, $tag_ids);
@ -867,7 +929,7 @@ class Image {
}
assert($positive_tag_id_array || $negative_tag_id_array, @$_GET['q']);
$wheres = array();
$wheres = [];
if (!empty($positive_tag_id_array)) {
$positive_tag_id_list = join(', ', $positive_tag_id_array);
$wheres[] = "tag_id IN ($positive_tag_id_list)";
@ -887,7 +949,7 @@ class Image {
GROUP BY image_id
HAVING COUNT(image_id) >= :search_score
)
", array("search_score"=>count($positive_tag_id_array)));
", ["search_score"=>count($positive_tag_id_array)]);
}
/**
@ -896,12 +958,15 @@ class Image {
*
* #param TagQuerylet[] $tag_querylets
*/
private static function build_ugly_search_querylet(array $tag_querylets): Querylet {
private static function build_ugly_search_querylet(array $tag_querylets): Querylet
{
global $database;
$positive_tag_count = 0;
foreach ($tag_querylets as $tq) {
if($tq->positive) $positive_tag_count++;
if ($tq->positive) {
$positive_tag_count++;
}
}
// only negative tags - shortcut to fail
@ -917,7 +982,7 @@ class Image {
// merge all the tag querylets into one generic one
$sql = "0";
$terms = array();
$terms = [];
foreach ($tag_querylets as $tq) {
$sign = $tq->positive ? "+" : "-";
$sql .= ' '.$sign.' IF(SUM(tag LIKE :tag'.Image::$tag_n.'), 1, 0)';
@ -926,7 +991,7 @@ class Image {
}
$tag_search = new Querylet($sql, $terms);
$tag_id_array = array();
$tag_id_array = [];
foreach ($tag_querylets as $tq) {
$tag_ids = $database->get_col(
@ -935,7 +1000,7 @@ class Image {
FROM tags
WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:tag)
"),
array("tag" => $tq->tag)
["tag" => $tq->tag]
);
$tag_id_array = array_merge($tag_id_array, $tag_ids);
@ -965,8 +1030,7 @@ class Image {
WHERE 1=1
', array_merge(
$tag_search->variables,
array("score"=>$positive_tag_count)
["score"=>$positive_tag_count]
));
}
}

View File

@ -9,7 +9,8 @@
*
* @throws UploadException
*/
function move_upload_to_archive(DataUploadEvent $event) {
function move_upload_to_archive(DataUploadEvent $event)
{
$target = warehouse_path("images", $event->hash);
if (!@copy($event->tmpname, $target)) {
$errors = error_get_last();
@ -25,8 +26,9 @@ function move_upload_to_archive(DataUploadEvent $event) {
*
* #return string[]
*/
function add_dir(string $base): array {
$results = array();
function add_dir(string $base): array
{
$results = [];
foreach (list_files($base) as $full_path) {
$short_path = str_replace($base, "", $full_path);
@ -37,8 +39,7 @@ function add_dir(string $base): array {
try {
add_image($full_path, $filename, $tags);
$result .= "ok";
}
catch(UploadException $ex) {
} catch (UploadException $ex) {
$result .= "failed: ".$ex->getMessage();
}
$results[] = $result;
@ -47,14 +48,15 @@ function add_dir(string $base): array {
return $results;
}
function add_image(string $tmpname, string $filename, string $tags): void {
function add_image(string $tmpname, string $filename, string $tags): void
{
assert(file_exists($tmpname));
$pathinfo = pathinfo($filename);
if (!array_key_exists('extension', $pathinfo)) {
throw new UploadException("File has no extension");
}
$metadata = array();
$metadata = [];
$metadata['filename'] = $pathinfo['basename'];
$metadata['extension'] = $pathinfo['extension'];
$metadata['tags'] = Tag::explode($tags);
@ -72,14 +74,23 @@ function add_image(string $tmpname, string $filename, string $tags): void {
*
* #return int[]
*/
function get_thumbnail_size(int $orig_width, int $orig_height): array {
function get_thumbnail_size(int $orig_width, int $orig_height): array
{
global $config;
if($orig_width === 0) $orig_width = 192;
if($orig_height === 0) $orig_height = 192;
if ($orig_width === 0) {
$orig_width = 192;
}
if ($orig_height === 0) {
$orig_height = 192;
}
if($orig_width > $orig_height * 5) $orig_width = $orig_height * 5;
if($orig_height > $orig_width * 5) $orig_height = $orig_width * 5;
if ($orig_width > $orig_height * 5) {
$orig_width = $orig_height * 5;
}
if ($orig_height > $orig_width * 5) {
$orig_height = $orig_width * 5;
}
$max_width = $config->get_int('thumb_width');
$max_height = $config->get_int('thumb_height');
@ -89,9 +100,8 @@ function get_thumbnail_size(int $orig_width, int $orig_height): array {
$scale = ($xscale < $yscale) ? $xscale : $yscale;
if ($scale > 1 && $config->get_bool('thumb_upscale')) {
return array((int)$orig_width, (int)$orig_height);
}
else {
return array((int)($orig_width*$scale), (int)($orig_height*$scale));
return [(int)$orig_width, (int)$orig_height];
} else {
return [(int)($orig_width*$scale), (int)($orig_height*$scale)];
}
}

View File

@ -1,48 +1,57 @@
<?php
class Querylet {
class Querylet
{
/** @var string */
public $sql;
/** @var array */
public $variables;
public function __construct(string $sql, array $variables=array()) {
public function __construct(string $sql, array $variables=[])
{
$this->sql = $sql;
$this->variables = $variables;
}
public function append(Querylet $querylet) {
public function append(Querylet $querylet)
{
$this->sql .= $querylet->sql;
$this->variables = array_merge($this->variables, $querylet->variables);
}
public function append_sql(string $sql) {
public function append_sql(string $sql)
{
$this->sql .= $sql;
}
public function add_variable($var) {
public function add_variable($var)
{
$this->variables[] = $var;
}
}
class TagQuerylet {
class TagQuerylet
{
/** @var string */
public $tag;
/** @var bool */
public $positive;
public function __construct(string $tag, bool $positive) {
public function __construct(string $tag, bool $positive)
{
$this->tag = $tag;
$this->positive = $positive;
}
}
class ImgQuerylet {
class ImgQuerylet
{
/** @var \Querylet */
public $qlet;
/** @var bool */
public $positive;
public function __construct(Querylet $qlet, bool $positive) {
public function __construct(Querylet $qlet, bool $positive)
{
$this->qlet = $qlet;
$this->positive = $positive;
}

View File

@ -7,8 +7,10 @@
* All the methods are static, one should never actually use a tag object.
*
*/
class Tag {
public static function implode(array $tags): string {
class Tag
{
public static function implode(array $tags): string
{
sort($tags);
$tags = implode(' ', $tags);
@ -20,13 +22,14 @@ class Tag {
*
* #return string[]
*/
public static function explode(string $tags, bool $tagme=true): array {
public static function explode(string $tags, bool $tagme=true): array
{
global $database;
$tags = explode(' ', trim($tags));
/* sanitise by removing invisible / dodgy characters */
$tag_array = array();
$tag_array = [];
foreach ($tags as $tag) {
$tag = preg_replace("/\s/", "", $tag); # whitespace
$tag = preg_replace('/\x20(\x0e|\x0f)/', '', $tag); # unicode RTL
@ -46,11 +49,11 @@ class Tag {
/* if user supplied a blank string, add "tagme" */
if (count($tag_array) === 0 && $tagme) {
$tag_array = array("tagme");
$tag_array = ["tagme"];
}
/* resolve aliases */
$new = array();
$new = [];
$i = 0;
$tag_count = count($tag_array);
while ($i<$tag_count) {
@ -67,13 +70,12 @@ class Tag {
FROM aliases
WHERE SCORE_STRNORM(oldtag)=SCORE_STRNORM(:tag)
"),
array("tag"=>$tag)
["tag"=>$tag]
);
if (empty($newtags)) {
//tag has no alias, use old tag
$aliases = array($tag);
}
else {
$aliases = [$tag];
} else {
$aliases = explode(" ", $newtags); // Tag::explode($newtags); - recursion can be infinite
}
@ -81,8 +83,7 @@ class Tag {
if (!in_array($alias, $new)) {
if ($tag == $alias) {
$new[] = $negative.$alias;
}
elseif(!in_array($alias, $tag_array)) {
} elseif (!in_array($alias, $tag_array)) {
$tag_array[] = $negative.$alias;
$tag_count++;
}

View File

@ -17,7 +17,8 @@ define("SCORE_LOG_NOTSET", 0);
* When taking action, a log event should be stored by the server
* Quite often, both of these happen at once, hence log_*() having $flash
*/
function log_msg(string $section, int $priority, string $message, ?string $flash=null, $args=array()) {
function log_msg(string $section, int $priority, string $message, ?string $flash=null, $args=[])
{
send_event(new LogEvent($section, $priority, $message, $args));
$threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0;
@ -30,24 +31,39 @@ function log_msg(string $section, int $priority, string $message, ?string $flash
}
// More shorthand ways of logging
function log_debug( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);}
function log_info( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);}
function log_warning( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);}
function log_error( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);}
function log_critical(string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);}
function log_debug(string $section, string $message, ?string $flash=null, $args=[])
{
log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);
}
function log_info(string $section, string $message, ?string $flash=null, $args=[])
{
log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);
}
function log_warning(string $section, string $message, ?string $flash=null, $args=[])
{
log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);
}
function log_error(string $section, string $message, ?string $flash=null, $args=[])
{
log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);
}
function log_critical(string $section, string $message, ?string $flash=null, $args=[])
{
log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);
}
/**
* Get a unique ID for this request, useful for grouping log messages.
*/
function get_request_id(): string {
function get_request_id(): string
{
static $request_id = null;
if (!$request_id) {
// not completely trustworthy, as a user can spoof this
if (@$_SERVER['HTTP_X_VARNISH']) {
$request_id = $_SERVER['HTTP_X_VARNISH'];
}
else {
} else {
$request_id = "P" . uniqid();
}
}

View File

@ -35,7 +35,8 @@
* The various extensions all add whatever they want to this structure,
* then Layout turns it into HTML.
*/
class Page {
class Page
{
/** @name Overall */
//@{
/** @var string */
@ -46,14 +47,16 @@ class Page {
/**
* Set what this page should do; "page", "data", or "redirect".
*/
public function set_mode(string $mode) {
public function set_mode(string $mode)
{
$this->mode = $mode;
}
/**
* Set the page's MIME type.
*/
public function set_type(string $type) {
public function set_type(string $type)
{
$this->type = $type;
}
@ -72,14 +75,16 @@ class Page {
/**
* Set the raw data to be sent.
*/
public function set_data(string $data) {
public function set_data(string $data)
{
$this->data = $data;
}
/**
* Set the recommended download filename.
*/
public function set_filename(string $filename) {
public function set_filename(string $filename)
{
$this->filename = $filename;
}
@ -96,7 +101,8 @@ class Page {
* Set the URL to redirect to (remember to use make_link() if linking
* to a page in the same site).
*/
public function set_redirect(string $redirect) {
public function set_redirect(string $redirect)
{
$this->redirect = $redirect;
}
@ -122,49 +128,59 @@ class Page {
public $quicknav = "";
/** @var string[] */
public $html_headers = array();
public $html_headers = [];
/** @var string[] */
public $http_headers = array();
public $http_headers = [];
/** @var string[][] */
public $cookies = array();
public $cookies = [];
/** @var Block[] */
public $blocks = array();
public $blocks = [];
/**
* Set the HTTP status code
*/
public function set_code(int $code): void {
public function set_code(int $code): void
{
$this->code = $code;
}
public function set_title(string $title): void {
public function set_title(string $title): void
{
$this->title = $title;
}
public function set_heading(string $heading): void {
public function set_heading(string $heading): void
{
$this->heading = $heading;
}
public function set_subheading(string $subheading): void {
public function set_subheading(string $subheading): void
{
$this->subheading = $subheading;
}
/**
* Add a line to the HTML head section.
*/
public function add_html_header(string $line, int $position=50): void {
while(isset($this->html_headers[$position])) $position++;
public function add_html_header(string $line, int $position=50): void
{
while (isset($this->html_headers[$position])) {
$position++;
}
$this->html_headers[$position] = $line;
}
/**
* Add a http header to be sent to the client.
*/
public function add_http_header(string $line, int $position=50): void {
while(isset($this->http_headers[$position])) $position++;
public function add_http_header(string $line, int $position=50): void
{
while (isset($this->http_headers[$position])) {
$position++;
}
$this->http_headers[$position] = $line;
}
@ -173,17 +189,18 @@ class Page {
* setcookie method, but prepends the site-wide cookie prefix to
* the $name argument before doing anything.
*/
public function add_cookie(string $name, string $value, int $time, string $path): void {
public function add_cookie(string $name, string $value, int $time, string $path): void
{
$full_name = COOKIE_PREFIX."_".$name;
$this->cookies[] = array($full_name, $value, $time, $path);
$this->cookies[] = [$full_name, $value, $time, $path];
}
public function get_cookie(string $name): ?string {
public function get_cookie(string $name): ?string
{
$full_name = COOKIE_PREFIX."_".$name;
if (isset($_COOKIE[$full_name])) {
return $_COOKIE[$full_name];
}
else {
} else {
return null;
}
}
@ -191,7 +208,8 @@ class Page {
/**
* Get all the HTML headers that are currently set and return as a string.
*/
public function get_all_html_headers(): string {
public function get_all_html_headers(): string
{
$data = '';
ksort($this->html_headers);
foreach ($this->html_headers as $line) {
@ -203,14 +221,16 @@ class Page {
/**
* Removes all currently set HTML headers (Be careful..).
*/
public function delete_all_html_headers(): void {
$this->html_headers = array();
public function delete_all_html_headers(): void
{
$this->html_headers = [];
}
/**
* Add a Block of data to the page.
*/
public function add_block(Block $block) {
public function add_block(Block $block)
{
$this->blocks[] = $block;
}
@ -221,7 +241,8 @@ class Page {
/**
* Display the page according to the mode and data given.
*/
public function display(): void {
public function display(): void
{
global $page, $user;
header("HTTP/1.0 {$this->code} Shimmie");
@ -246,8 +267,7 @@ class Page {
if ($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") {
header("Cache-control: public, max-age=600");
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT');
}
else {
} else {
#header("Cache-control: private, max-age=0");
header("Cache-control: no-cache");
header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT');
@ -293,7 +313,8 @@ class Page {
*
* TODO: This should really be configurable somehow...
*/
public function add_auto_html_headers(): void {
public function add_auto_html_headers(): void
{
global $config;
$data_href = get_base_href();

View File

@ -6,9 +6,10 @@
/**
* Remove an item from an array
*/
function array_remove(array $array, $to_remove): array {
function array_remove(array $array, $to_remove): array
{
$array = array_unique($array);
$a2 = array();
$a2 = [];
foreach ($array as $existing) {
if ($existing != $to_remove) {
$a2[] = $existing;
@ -22,7 +23,8 @@ function array_remove(array $array, $to_remove): array {
*
* Also removes duplicate values from the array.
*/
function array_add(array $array, $element): array {
function array_add(array $array, $element): array
{
// Could we just use array_push() ?
// http://www.php.net/manual/en/function.array-push.php
$array[] = $element;
@ -33,13 +35,15 @@ function array_add(array $array, $element): array {
/**
* Return the unique elements of an array, case insensitively
*/
function array_iunique(array $array): array {
$ok = array();
function array_iunique(array $array): array
{
$ok = [];
foreach ($array as $element) {
$found = false;
foreach ($ok as $existing) {
if (strtolower($element) == strtolower($existing)) {
$found = true; break;
$found = true;
break;
}
}
if (!$found) {
@ -54,7 +58,8 @@ function array_iunique(array $array): array {
*
* from http://uk.php.net/network
*/
function ip_in_range(string $IP, string $CIDR): bool {
function ip_in_range(string $IP, string $CIDR): bool
{
list($net, $mask) = explode("/", $CIDR);
$ip_net = ip2long($net);
@ -73,36 +78,32 @@ function ip_in_range(string $IP, string $CIDR): bool {
* from a patch by Christian Walde; only intended for use in the
* "extension manager" extension, but it seems to fit better here
*/
function deltree(string $f) {
function deltree(string $f)
{
//Because Windows (I know, bad excuse)
if (PHP_OS === 'WINNT') {
$real = realpath($f);
$path = realpath('./').'\\'.str_replace('/', '\\', $f);
if ($path != $real) {
rmdir($path);
}
else {
} else {
foreach (glob($f.'/*') as $sf) {
if (is_dir($sf) && !is_link($sf)) {
deltree($sf);
}
else {
} else {
unlink($sf);
}
}
rmdir($f);
}
}
else {
} else {
if (is_link($f)) {
unlink($f);
}
else if(is_dir($f)) {
} elseif (is_dir($f)) {
foreach (glob($f.'/*') as $sf) {
if (is_dir($sf) && !is_link($sf)) {
deltree($sf);
}
else {
} else {
unlink($sf);
}
}
@ -116,13 +117,14 @@ function deltree(string $f) {
*
* from a comment on http://uk.php.net/copy
*/
function full_copy(string $source, string $target) {
function full_copy(string $source, string $target)
{
if (is_dir($source)) {
@mkdir($target);
$d = dir($source);
while(FALSE !== ($entry = $d->read())) {
while (false !== ($entry = $d->read())) {
if ($entry == '.' || $entry == '..') {
continue;
}
@ -135,8 +137,7 @@ function full_copy(string $source, string $target) {
copy($Entry, $target . '/' . $entry);
}
$d->close();
}
else {
} else {
copy($source, $target);
}
}
@ -144,12 +145,13 @@ function full_copy(string $source, string $target) {
/**
* Return a list of all the regular files in a directory and subdirectories
*/
function list_files(string $base, string $_sub_dir=""): array {
function list_files(string $base, string $_sub_dir=""): array
{
assert(is_dir($base));
$file_list = array();
$file_list = [];
$files = array();
$files = [];
$dir = opendir("$base/$_sub_dir");
while ($f = readdir($dir)) {
$files[] = $f;
@ -162,8 +164,7 @@ function list_files(string $base, string $_sub_dir=""): array {
if (is_link($full_path)) {
// ignore
}
else if(is_dir($full_path)) {
} elseif (is_dir($full_path)) {
if (!($filename == "." || $filename == "..")) {
//subdirectory found
$file_list = array_merge(
@ -171,8 +172,7 @@ function list_files(string $base, string $_sub_dir=""): array {
list_files($base, "$_sub_dir/$filename")
);
}
}
else {
} else {
$full_path = str_replace("//", "/", $full_path);
$file_list[] = $full_path;
}
@ -186,8 +186,9 @@ if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/func
/**
* #return string[]
*/
function http_parse_headers (string $raw_headers): array {
$headers = array(); // $headers = [];
function http_parse_headers(string $raw_headers): array
{
$headers = []; // $headers = [];
foreach (explode("\n", $raw_headers) as $i => $h) {
$h = explode(':', $h, 2);
@ -196,10 +197,10 @@ if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/func
if (!isset($headers[$h[0]])) {
$headers[$h[0]] = trim($h[1]);
} elseif (is_array($headers[$h[0]])) {
$tmp = array_merge($headers[$h[0]],array(trim($h[1])));
$tmp = array_merge($headers[$h[0]], [trim($h[1])]);
$headers[$h[0]] = $tmp;
} else {
$tmp = array_merge(array($headers[$h[0]]),array(trim($h[1])));
$tmp = array_merge([$headers[$h[0]]], [trim($h[1])]);
$headers[$h[0]] = $tmp;
}
}
@ -212,7 +213,8 @@ if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/func
* HTTP Headers can sometimes be lowercase which will cause issues.
* In cases like these, we need to make sure to check for them if the camelcase version does not exist.
*/
function findHeader(array $headers, string $name): ?string {
function findHeader(array $headers, string $name): ?string
{
if (!is_array($headers)) {
return null;
}
@ -235,11 +237,15 @@ function findHeader(array $headers, string $name): ?string {
if (!function_exists('mb_strlen')) {
// TODO: we should warn the admin that they are missing multibyte support
function mb_strlen($str, $encoding) {
function mb_strlen($str, $encoding)
{
return strlen($str);
}
function mb_internal_encoding($encoding) {}
function mb_strtolower($str) {
function mb_internal_encoding($encoding)
{
}
function mb_strtolower($str)
{
return strtolower($str);
}
}
@ -268,20 +274,21 @@ const MIME_TYPE_MAP = [
* from the "Amazon S3 PHP class" which is Copyright (c) 2008, Donovan Schönknecht
* and released under the 'Simplified BSD License'.
*/
function getMimeType(string $file, string $ext=""): string {
function getMimeType(string $file, string $ext=""): string
{
// Static extension lookup
$ext = strtolower($ext);
if (array_key_exists($ext, MIME_TYPE_MAP)) { return MIME_TYPE_MAP[$ext]; }
if (array_key_exists($ext, MIME_TYPE_MAP)) {
return MIME_TYPE_MAP[$ext];
}
$type = false;
// Fileinfo documentation says fileinfo_open() will use the
// MAGIC env var for the magic file
if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false)
{
if (($type = finfo_file($finfo, $file)) !== false)
{
($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) {
if (($type = finfo_file($finfo, $file)) !== false) {
// Remove the charset and grab the last content-type
$type = explode(' ', str_replace('; charset=', ';charset=', $type));
$type = array_pop($type);
@ -291,15 +298,19 @@ function getMimeType(string $file, string $ext=""): string {
finfo_close($finfo);
// If anyone is still using mime_content_type()
} elseif (function_exists('mime_content_type'))
} elseif (function_exists('mime_content_type')) {
$type = trim(mime_content_type($file));
}
if ($type !== false && strlen($type) > 0) return $type;
if ($type !== false && strlen($type) > 0) {
return $type;
}
return 'application/octet-stream';
}
function getExtension(?string $mime_type): ?string {
function getExtension(?string $mime_type): ?string
{
if (empty($mime_type)) {
return null;
}
@ -311,8 +322,9 @@ function getExtension(?string $mime_type): ?string {
/**
* Like glob, with support for matching very long patterns with braces.
*/
function zglob(string $pattern): array {
$results = array();
function zglob(string $pattern): array
{
$results = [];
if (preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) {
$braced = explode(",", $matches[2]);
foreach ($braced as $b) {
@ -320,11 +332,13 @@ function zglob(string $pattern): array {
$results = array_merge($results, zglob($sub_pattern));
}
return $results;
}
else {
} else {
$r = glob($pattern);
if($r) return $r;
else return array();
if ($r) {
return $r;
} else {
return [];
}
}
}
@ -336,9 +350,12 @@ function zglob(string $pattern): array {
*
* PHP really, really sucks.
*/
function get_base_href(): string {
if(defined("BASE_HREF")) return BASE_HREF;
$possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO');
function get_base_href(): string
{
if (defined("BASE_HREF")) {
return BASE_HREF;
}
$possible_vars = ['SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO'];
$ok_var = null;
foreach ($possible_vars as $var) {
if (isset($_SERVER[$var]) && substr($_SERVER[$var], -4) === '.php') {
@ -354,12 +371,14 @@ function get_base_href(): string {
return $dir;
}
function startsWith(string $haystack, string $needle): bool {
function startsWith(string $haystack, string $needle): bool
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function endsWith(string $haystack, string $needle): bool {
function endsWith(string $haystack, string $needle): bool
{
$length = strlen($needle);
$start = $length * -1; //negative
return (substr($haystack, $start) === $needle);
@ -372,21 +391,24 @@ function endsWith(string $haystack, string $needle): bool {
/**
* Make some data safe for printing into HTML
*/
function html_escape(string $input): string {
function html_escape(string $input): string
{
return htmlentities($input, ENT_QUOTES, "UTF-8");
}
/**
* Unescape data that was made safe for printing into HTML
*/
function html_unescape(string $input): string {
function html_unescape(string $input): string
{
return html_entity_decode($input, ENT_QUOTES, "UTF-8");
}
/**
* Make sure some data is safe to be used in integer context
*/
function int_escape(string $input): int {
function int_escape(string $input): int
{
/*
Side note, Casting to an integer is FASTER than using intval.
http://hakre.wordpress.com/2010/05/13/php-casting-vs-intval/
@ -397,7 +419,8 @@ function int_escape(string $input): int {
/**
* Make sure some data is safe to be used in URL context
*/
function url_escape(string $input): string {
function url_escape(string $input): string
{
/*
Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night
green-ponies: indeed~
@ -429,7 +452,8 @@ function url_escape(string $input): string {
/**
* Make sure some data is safe to be used in SQL context
*/
function sql_escape(string $input): string {
function sql_escape(string $input): string
{
global $database;
return $database->escape($input);
}
@ -438,7 +462,8 @@ function sql_escape(string $input): string {
/**
* Turn all manner of HTML / INI / JS / DB booleans into a PHP one
*/
function bool_escape($input): bool {
function bool_escape($input): bool
{
/*
Sometimes, I don't like PHP -- this, is one of those times...
"a boolean FALSE is not considered a valid boolean value by this function."
@ -471,11 +496,13 @@ function bool_escape($input): bool {
* Some functions require a callback function for escaping,
* but we might not want to alter the data
*/
function no_escape(string $input): string {
function no_escape(string $input): string
{
return $input;
}
function clamp(int $val, int $min=null, int $max=null): int {
function clamp(int $val, int $min=null, int $max=null): int
{
if (!is_numeric($val) || (!is_null($min) && $val < $min)) {
$val = $min;
}
@ -488,7 +515,8 @@ function clamp(int $val, int $min=null, int $max=null): int {
return $val;
}
function xml_tag(string $name, array $attrs=array(), array $children=array()): string {
function xml_tag(string $name, array $attrs=[], array $children=[]): string
{
$xml = "<$name ";
foreach ($attrs as $k => $v) {
$xv = str_replace('&#039;', '&apos;', htmlspecialchars($v, ENT_QUOTES));
@ -500,8 +528,7 @@ function xml_tag(string $name, array $attrs=array(), array $children=array()): s
$xml .= xml_tag($child);
}
$xml .= "</$name>\n";
}
else {
} else {
$xml .= "/>\n";
}
return $xml;
@ -511,9 +538,12 @@ function xml_tag(string $name, array $attrs=array(), array $children=array()): s
* Original PHP code by Chirp Internet: www.chirp.com.au
* Please acknowledge use of this code by including this header.
*/
function truncate(string $string, int $limit, string $break=" ", string $pad="..."): string{
function truncate(string $string, int $limit, string $break=" ", string $pad="..."): string
{
// return with no change if string is shorter than $limit
if(strlen($string) <= $limit) return $string;
if (strlen($string) <= $limit) {
return $string;
}
// is $break present between $limit and the end of the string?
if (false !== ($breakpoint = strpos($string, $break, $limit))) {
@ -528,7 +558,8 @@ function truncate(string $string, int $limit, string $break=" ", string $pad="..
/**
* Turn a human readable filesize into an integer, eg 1KB -> 1024
*/
function parse_shorthand_int(string $limit): int {
function parse_shorthand_int(string $limit): int
{
if (preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) {
$value = $m[1];
if (isset($m[2])) {
@ -536,8 +567,10 @@ function parse_shorthand_int(string $limit): int {
/** @noinspection PhpMissingBreakStatementInspection */
case 'g': $value *= 1024; // fall through
/** @noinspection PhpMissingBreakStatementInspection */
// no break
case 'm': $value *= 1024; // fall through
/** @noinspection PhpMissingBreakStatementInspection */
// no break
case 'k': $value *= 1024; break;
default: $value = -1;
}
@ -551,19 +584,17 @@ function parse_shorthand_int(string $limit): int {
/**
* Turn an integer into a human readable filesize, eg 1024 -> 1KB
*/
function to_shorthand_int(int $int): string {
function to_shorthand_int(int $int): string
{
assert($int >= 0);
if ($int >= pow(1024, 3)) {
return sprintf("%.1fGB", $int / pow(1024, 3));
}
else if($int >= pow(1024, 2)) {
} elseif ($int >= pow(1024, 2)) {
return sprintf("%.1fMB", $int / pow(1024, 2));
}
else if($int >= 1024) {
} elseif ($int >= 1024) {
return sprintf("%.1fKB", $int / 1024);
}
else {
} else {
return (string)$int;
}
}
@ -572,7 +603,8 @@ function to_shorthand_int(int $int): string {
/**
* Turn a date into a time, a date, an "X minutes ago...", etc
*/
function autodate(string $date, bool $html=true): string {
function autodate(string $date, bool $html=true): string
{
$cpu = date('c', strtotime($date));
$hum = date('F j, Y; H:i', strtotime($date));
return ($html ? "<time datetime='$cpu'>$hum</time>" : $hum);
@ -581,7 +613,8 @@ function autodate(string $date, bool $html=true): string {
/**
* Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss )
*/
function isValidDateTime(string $dateTime): bool {
function isValidDateTime(string $dateTime): bool
{
if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) {
if (checkdate($matches[2], $matches[3], $matches[1])) {
return true;
@ -594,7 +627,8 @@ function isValidDateTime(string $dateTime): bool {
/**
* Check if a given string is a valid date. ( Format: yyyy-mm-dd )
*/
function isValidDate(string $date): bool {
function isValidDate(string $date): bool
{
if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) {
// checkdate wants (month, day, year)
if (checkdate($matches[2], $matches[3], $matches[1])) {
@ -605,8 +639,9 @@ function isValidDate(string $date): bool {
return false;
}
function validate_input(array $inputs): array {
$outputs = array();
function validate_input(array $inputs): array
{
$outputs = [];
foreach ($inputs as $key => $validations) {
$flags = explode(',', $validations);
@ -635,42 +670,35 @@ function validate_input(array $inputs): array {
}
}
$outputs[$key] = $id;
}
else if(in_array('user_name', $flags)) {
} elseif (in_array('user_name', $flags)) {
if (strlen($value) < 1) {
throw new InvalidInput("Username must be at least 1 character");
}
else if(!preg_match('/^[a-zA-Z0-9-_]+$/', $value)) {
} elseif (!preg_match('/^[a-zA-Z0-9-_]+$/', $value)) {
throw new InvalidInput(
"Username contains invalid characters. Allowed characters are ".
"letters, numbers, dash, and underscore");
"letters, numbers, dash, and underscore"
);
}
$outputs[$key] = $value;
}
else if(in_array('user_class', $flags)) {
} elseif (in_array('user_class', $flags)) {
global $_shm_user_classes;
if (!array_key_exists($value, $_shm_user_classes)) {
throw new InvalidInput("Invalid user class: ".html_escape($value));
}
$outputs[$key] = $value;
}
else if(in_array('email', $flags)) {
} elseif (in_array('email', $flags)) {
$outputs[$key] = trim($value);
}
else if(in_array('password', $flags)) {
} elseif (in_array('password', $flags)) {
$outputs[$key] = $value;
}
else if(in_array('int', $flags)) {
} elseif (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)) {
} elseif (in_array('bool', $flags)) {
$outputs[$key] = bool_escape($value);
}
else if(in_array('string', $flags)) {
} elseif (in_array('string', $flags)) {
if (in_array('trim', $flags)) {
$value = trim($value);
}
@ -681,11 +709,12 @@ function validate_input(array $inputs): array {
throw new InvalidInput("$key must not be blank");
}
if (in_array('nullify', $flags)) {
if(empty($value)) $value = null;
if (empty($value)) {
$value = null;
}
}
$outputs[$key] = $value;
}
else {
} else {
throw new InvalidInput("Unknown validation '$validations'");
}
}

View File

@ -5,9 +5,10 @@
/** @private */
global $_shm_event_listeners;
$_shm_event_listeners = array();
$_shm_event_listeners = [];
function _load_event_listeners() {
function _load_event_listeners()
{
global $_shm_event_listeners, $_shm_ctx;
$_shm_ctx->log_start("Loading extensions");
@ -15,8 +16,7 @@ function _load_event_listeners() {
$cache_path = data_path("cache/shm_event_listeners.php");
if (COMPILE_ELS && file_exists($cache_path)) {
require_once($cache_path);
}
else {
} else {
_set_event_listeners();
if (COMPILE_ELS) {
@ -27,21 +27,23 @@ function _load_event_listeners() {
$_shm_ctx->log_endok();
}
function _set_event_listeners() {
function _set_event_listeners()
{
global $_shm_event_listeners;
$_shm_event_listeners = array();
$_shm_event_listeners = [];
foreach (get_declared_classes() as $class) {
$rclass = new ReflectionClass($class);
if ($rclass->isAbstract()) {
// don't do anything
}
elseif(is_subclass_of($class, "Extension")) {
} elseif (is_subclass_of($class, "Extension")) {
/** @var Extension $extension */
$extension = new $class();
// skip extensions which don't support our current database
if(!$extension->is_live()) continue;
if (!$extension->is_live()) {
continue;
}
foreach (get_class_methods($extension) as $method) {
if (substr($method, 0, 2) == "on") {
@ -57,13 +59,14 @@ function _set_event_listeners() {
}
}
function _dump_event_listeners(array $event_listeners, string $path): void {
function _dump_event_listeners(array $event_listeners, string $path): void
{
$p = "<"."?php\n";
foreach (get_declared_classes() as $class) {
$rclass = new ReflectionClass($class);
if($rclass->isAbstract()) {}
elseif(is_subclass_of($class, "Extension")) {
if ($rclass->isAbstract()) {
} elseif (is_subclass_of($class, "Extension")) {
$p .= "\$$class = new $class(); ";
}
}
@ -82,7 +85,8 @@ function _dump_event_listeners(array $event_listeners, string $path): void {
file_put_contents($path, $p);
}
function ext_is_live(string $ext_name): bool {
function ext_is_live(string $ext_name): bool
{
if (class_exists($ext_name)) {
/** @var Extension $ext */
$ext = new $ext_name();
@ -99,26 +103,37 @@ $_shm_event_count = 0;
/**
* Send an event to all registered Extensions.
*/
function send_event(Event $event): void {
function send_event(Event $event): void
{
global $_shm_event_listeners, $_shm_event_count, $_shm_ctx;
if(!isset($_shm_event_listeners[get_class($event)])) return;
if (!isset($_shm_event_listeners[get_class($event)])) {
return;
}
$method_name = "on".str_replace("Event", "", get_class($event));
// send_event() is performance sensitive, and with the number
// of times context gets called the time starts to add up
$ctx_enabled = constant('CONTEXT');
if($ctx_enabled) $_shm_ctx->log_start(get_class($event));
if ($ctx_enabled) {
$_shm_ctx->log_start(get_class($event));
}
// SHIT: http://bugs.php.net/bug.php?id=35106
$my_event_listeners = $_shm_event_listeners[get_class($event)];
ksort($my_event_listeners);
foreach ($my_event_listeners as $listener) {
if($ctx_enabled) $_shm_ctx->log_start(get_class($listener));
if ($ctx_enabled) {
$_shm_ctx->log_start(get_class($listener));
}
if (method_exists($listener, $method_name)) {
$listener->$method_name($event);
}
if($ctx_enabled) $_shm_ctx->log_endok();
if ($ctx_enabled) {
$_shm_ctx->log_endok();
}
}
$_shm_event_count++;
if($ctx_enabled) $_shm_ctx->log_endok();
if ($ctx_enabled) {
$_shm_ctx->log_endok();
}
}

View File

@ -19,7 +19,12 @@
*
*/
function _d(string $name, $value) {if(!defined($name)) define($name, $value);}
function _d(string $name, $value)
{
if (!defined($name)) {
define($name, $value);
}
}
_d("DATABASE_DSN", null); // string PDO database connection details
_d("DATABASE_KA", true); // string Keep database connection alive
_d("CACHE_DSN", null); // string cache connection details
@ -50,5 +55,3 @@ _d("ENABLED_MODS", "imageboard");
*/
_d("SCORE_VERSION", 'develop/'.VERSION); // string SCore version
_d("ENABLED_EXTS", CORE_EXTS.",".EXTRA_EXTS);

View File

@ -1,8 +1,10 @@
<?php
require_once "core/polyfills.php";
class PolyfillsTest extends \PHPUnit\Framework\TestCase {
public function test_html_escape() {
class PolyfillsTest extends \PHPUnit\Framework\TestCase
{
public function test_html_escape()
{
$this->assertEquals(
html_escape("Foo & <waffles>"),
"Foo &amp; &lt;waffles&gt;"
@ -17,14 +19,16 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals(html_escape(html_unescape($x)), $x);
}
public function test_int_escape() {
public function test_int_escape()
{
$this->assertEquals(int_escape(""), 0);
$this->assertEquals(int_escape("1"), 1);
$this->assertEquals(int_escape("-1"), -1);
$this->assertEquals(int_escape("-1.5"), -1);
}
public function test_clamp() {
public function test_clamp()
{
$this->assertEquals(clamp(0, 5, 10), 5);
$this->assertEquals(clamp(5, 5, 10), 5);
$this->assertEquals(clamp(7, 5, 10), 7);
@ -32,7 +36,8 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals(clamp(15, 5, 10), 10);
}
public function test_shorthand_int() {
public function test_shorthand_int()
{
$this->assertEquals(to_shorthand_int(1231231231), "1.1GB");
$this->assertEquals(parse_shorthand_int("foo"), -1);

View File

@ -9,32 +9,30 @@
*
* eg make_link("post/list") becomes "/v2/index.php?q=post/list"
*/
function make_link(?string $page=null, ?string $query=null): string {
function make_link(?string $page=null, ?string $query=null): string
{
global $config;
if(is_null($page)) $page = $config->get_string('main_page');
if (is_null($page)) {
$page = $config->get_string('main_page');
}
if (!is_null(BASE_URL)) {
$base = BASE_URL;
}
elseif(NICE_URLS || $config->get_bool('nice_urls', false)) {
} elseif (NICE_URLS || $config->get_bool('nice_urls', false)) {
$base = str_replace('/'.basename($_SERVER["SCRIPT_FILENAME"]), "", $_SERVER["PHP_SELF"]);
}
else {
} else {
$base = "./".basename($_SERVER["SCRIPT_FILENAME"])."?q=";
}
if (is_null($query)) {
return str_replace("//", "/", $base.'/'.$page);
}
else {
} else {
if (strpos($base, "?")) {
return $base .'/'. $page .'&'. $query;
}
else if(strpos($query, "#") === 0) {
} elseif (strpos($query, "#") === 0) {
return $base .'/'. $page . $query;
}
else {
} else {
return $base .'/'. $page .'?'. $query;
}
}
@ -44,11 +42,13 @@ function make_link(?string $page=null, ?string $query=null): string {
/**
* Take the current URL and modify some parameters
*/
function modify_current_url(array $changes): string {
function modify_current_url(array $changes): string
{
return modify_url($_SERVER['QUERY_STRING'], $changes);
}
function modify_url(string $url, array $changes): string {
function modify_url(string $url, array $changes): string
{
// SHIT: PHP is officially the worst web API ever because it does not
// have a built-in function to do this.
@ -56,14 +56,13 @@ function modify_url(string $url, array $changes): string {
// didn't return the parsed array, preferring to overwrite global variables with
// whatever data the user supplied. Thankfully, 4.0.3 added an extra option to
// give it an array to use...
$params = array();
$params = [];
parse_str($url, $params);
if (isset($changes['q'])) {
$base = $changes['q'];
unset($changes['q']);
}
else {
} else {
$base = _get_query();
}
@ -72,7 +71,9 @@ function modify_url(string $url, array $changes): string {
}
foreach ($changes as $k => $v) {
if(is_null($v) and isset($params[$k])) unset($params[$k]);
if (is_null($v) and isset($params[$k])) {
unset($params[$k]);
}
$params[$k] = $v;
}
@ -83,7 +84,8 @@ function modify_url(string $url, array $changes): string {
/**
* Turn a relative link into an absolute one, including hostname
*/
function make_http(string $link): string {
function make_http(string $link): string
{
if (strpos($link, "://") > 0) {
return $link;
}

View File

@ -1,6 +1,7 @@
<?php
function _new_user(array $row): User {
function _new_user(array $row): User
{
return new User($row);
}
@ -12,7 +13,8 @@ function _new_user(array $row): User {
*
* The currently logged in user will always be accessible via the global variable $user.
*/
class User {
class User
{
/** @var int */
public $id;
@ -45,7 +47,8 @@ class User {
*
* @throws SCoreException
*/
public function __construct(array $row) {
public function __construct(array $row)
{
global $_shm_user_classes;
$this->id = int_escape($row['id']);
@ -56,46 +59,52 @@ class User {
if (array_key_exists($row["class"], $_shm_user_classes)) {
$this->class = $_shm_user_classes[$row["class"]];
}
else {
} else {
throw new SCoreException("User '{$this->name}' has invalid class '{$row["class"]}'");
}
}
public static function by_session(string $name, string $session) {
public static function by_session(string $name, string $session)
{
global $config, $database;
$row = $database->cache->get("user-session:$name-$session");
if (!$row) {
if ($database->get_driver_name() === "mysql") {
$query = "SELECT * FROM users WHERE name = :name AND md5(concat(pass, :ip)) = :sess";
}
else {
} else {
$query = "SELECT * FROM users WHERE name = :name AND md5(pass || :ip) = :sess";
}
$row = $database->get_row($query, array("name"=>$name, "ip"=>get_session_ip($config), "sess"=>$session));
$row = $database->get_row($query, ["name"=>$name, "ip"=>get_session_ip($config), "sess"=>$session]);
$database->cache->set("user-session:$name-$session", $row, 600);
}
return is_null($row) ? null : new User($row);
}
public static function by_id(int $id) {
public static function by_id(int $id)
{
global $database;
if ($id === 1) {
$cached = $database->cache->get('user-id:'.$id);
if($cached) return new User($cached);
if ($cached) {
return new User($cached);
}
}
$row = $database->get_row("SELECT * FROM users WHERE id = :id", ["id"=>$id]);
if ($id === 1) {
$database->cache->set('user-id:'.$id, $row, 600);
}
$row = $database->get_row("SELECT * FROM users WHERE id = :id", array("id"=>$id));
if($id === 1) $database->cache->set('user-id:'.$id, $row, 600);
return is_null($row) ? null : new User($row);
}
public static function by_name(string $name) {
public static function by_name(string $name)
{
global $database;
$row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE SCORE_STRNORM(name) = SCORE_STRNORM(:name)"), array("name"=>$name));
$row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE SCORE_STRNORM(name) = SCORE_STRNORM(:name)"), ["name"=>$name]);
return is_null($row) ? null : new User($row);
}
public static function by_name_and_pass(string $name, string $pass) {
public static function by_name_and_pass(string $name, string $pass)
{
$user = User::by_name($name);
if ($user) {
if ($user->passhash == md5(strtolower($name) . $pass)) {
@ -105,12 +114,10 @@ class User {
if (password_verify($pass, $user->passhash)) {
log_info("core-user", "Logged in as ".html_escape($name)." ({$user->class->name})");
return $user;
}
else {
} else {
log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid password)");
}
}
else {
} else {
log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid username)");
}
return null;
@ -119,58 +126,65 @@ class User {
/* useful user object functions start here */
public function can(string $ability): bool {
public function can(string $ability): bool
{
return $this->class->can($ability);
}
public function is_anonymous(): bool {
public function is_anonymous(): bool
{
global $config;
return ($this->id === $config->get_int('anon_id'));
}
public function is_logged_in(): bool {
public function is_logged_in(): bool
{
global $config;
return ($this->id !== $config->get_int('anon_id'));
}
public function is_admin(): bool {
public function is_admin(): bool
{
return ($this->class->name === "admin");
}
public function set_class(string $class) {
public function set_class(string $class)
{
global $database;
$database->Execute("UPDATE users SET class=:class WHERE id=:id", array("class"=>$class, "id"=>$this->id));
$database->Execute("UPDATE users SET class=:class WHERE id=:id", ["class"=>$class, "id"=>$this->id]);
log_info("core-user", 'Set class for '.$this->name.' to '.$class);
}
public function set_name(string $name) {
public function set_name(string $name)
{
global $database;
if (User::by_name($name)) {
throw new Exception("Desired username is already in use");
}
$old_name = $this->name;
$this->name = $name;
$database->Execute("UPDATE users SET name=:name WHERE id=:id", array("name"=>$this->name, "id"=>$this->id));
$database->Execute("UPDATE users SET name=:name WHERE id=:id", ["name"=>$this->name, "id"=>$this->id]);
log_info("core-user", "Changed username for {$old_name} to {$this->name}");
}
public function set_password(string $password) {
public function set_password(string $password)
{
global $database;
$hash = password_hash($password, PASSWORD_BCRYPT);
if (is_string($hash)) {
$this->passhash = $hash;
$database->Execute("UPDATE users SET pass=:hash WHERE id=:id", array("hash"=>$this->passhash, "id"=>$this->id));
$database->Execute("UPDATE users SET pass=:hash WHERE id=:id", ["hash"=>$this->passhash, "id"=>$this->id]);
log_info("core-user", 'Set password for '.$this->name);
}
else {
} else {
throw new SCoreException("Failed to hash password");
}
}
public function set_email(string $address) {
public function set_email(string $address)
{
global $database;
$database->Execute("UPDATE users SET email=:email WHERE id=:id", array("email"=>$address, "id"=>$this->id));
$database->Execute("UPDATE users SET email=:email WHERE id=:id", ["email"=>$address, "id"=>$this->id]);
log_info("core-user", 'Set email for '.$this->name);
}
@ -178,7 +192,8 @@ class User {
* Get a snippet of HTML which will render the user's avatar, be that
* a local file, a remote file, a gravatar, a something else, etc.
*/
public function get_avatar_html(): string {
public function get_avatar_html(): string
{
// FIXME: configurable
global $config;
if ($config->get_string("avatar_host") === "gravatar") {
@ -205,19 +220,22 @@ class User {
* the form was generated within the session. Salted and re-hashed so that
* reading a web page from the user's cache doesn't give access to the session key
*/
public function get_auth_token(): string {
public function get_auth_token(): string
{
global $config;
$salt = DATABASE_DSN;
$addr = get_session_ip($config);
return md5(md5($this->passhash . $addr) . "salty-csrf-" . $salt);
}
public function get_auth_html(): string {
public function get_auth_html(): string
{
$at = $this->get_auth_token();
return '<input type="hidden" name="auth_token" value="'.$at.'">';
}
public function check_auth_token(): bool {
public function check_auth_token(): bool
{
return (isset($_POST["auth_token"]) && $_POST["auth_token"] == $this->get_auth_token());
}
}

View File

@ -3,12 +3,13 @@
* @global UserClass[] $_shm_user_classes
*/
global $_shm_user_classes;
$_shm_user_classes = array();
$_shm_user_classes = [];
/**
* Class UserClass
*/
class UserClass {
class UserClass
{
/**
* @var null|string
@ -23,9 +24,10 @@ class UserClass {
/**
* @var array
*/
public $abilities = array();
public $abilities = [];
public function __construct(string $name, string $parent=null, array $abilities=array()) {
public function __construct(string $name, string $parent=null, array $abilities=[])
{
global $_shm_user_classes;
$this->name = $name;
@ -43,15 +45,14 @@ class UserClass {
*
* @throws SCoreException
*/
public function can(string $ability): bool {
public function can(string $ability): bool
{
if (array_key_exists($ability, $this->abilities)) {
$val = $this->abilities[$ability];
return $val;
}
else if(!is_null($this->parent)) {
} elseif (!is_null($this->parent)) {
return $this->parent->can($ability);
}
else {
} else {
global $_shm_user_classes;
$min_dist = 9999;
$min_ability = null;
@ -70,124 +71,123 @@ class UserClass {
// action_object_attribute
// action = create / view / edit / delete
// object = image / user / tag / setting
new UserClass("base", null, array(
"change_setting" => False, # modify web-level settings, eg the config table
"override_config" => False, # modify sys-level settings, eg shimmie.conf.php
"big_search" => False, # search for more than 3 tags at once (speed mode only)
new UserClass("base", null, [
"change_setting" => false, # modify web-level settings, eg the config table
"override_config" => false, # modify sys-level settings, eg shimmie.conf.php
"big_search" => false, # search for more than 3 tags at once (speed mode only)
"manage_extension_list" => False,
"manage_alias_list" => False,
"mass_tag_edit" => False,
"manage_extension_list" => false,
"manage_alias_list" => false,
"mass_tag_edit" => false,
"view_ip" => False, # view IP addresses associated with things
"ban_ip" => False,
"view_ip" => false, # view IP addresses associated with things
"ban_ip" => false,
"edit_user_name" => False,
"edit_user_password" => False,
"edit_user_info" => False, # email address, etc
"edit_user_class" => False,
"delete_user" => False,
"edit_user_name" => false,
"edit_user_password" => false,
"edit_user_info" => false, # email address, etc
"edit_user_class" => false,
"delete_user" => false,
"create_comment" => False,
"delete_comment" => False,
"bypass_comment_checks" => False, # spam etc
"create_comment" => false,
"delete_comment" => false,
"bypass_comment_checks" => false, # spam etc
"replace_image" => False,
"create_image" => False,
"edit_image_tag" => False,
"edit_image_source" => False,
"edit_image_owner" => False,
"edit_image_lock" => False,
"bulk_edit_image_tag" => False,
"bulk_edit_image_source" => False,
"delete_image" => False,
"replace_image" => false,
"create_image" => false,
"edit_image_tag" => false,
"edit_image_source" => false,
"edit_image_owner" => false,
"edit_image_lock" => false,
"bulk_edit_image_tag" => false,
"bulk_edit_image_source" => false,
"delete_image" => false,
"ban_image" => False,
"ban_image" => false,
"view_eventlog" => False,
"ignore_downtime" => False,
"view_eventlog" => false,
"ignore_downtime" => false,
"create_image_report" => False,
"view_image_report" => False, # deal with reported images
"create_image_report" => false,
"view_image_report" => false, # deal with reported images
"edit_wiki_page" => False,
"delete_wiki_page" => False,
"edit_wiki_page" => false,
"delete_wiki_page" => false,
"manage_blocks" => False,
"manage_blocks" => false,
"manage_admintools" => False,
"manage_admintools" => false,
"view_other_pms" => False,
"edit_feature" => False,
"bulk_edit_vote" => False,
"edit_other_vote" => False,
"view_sysinfo" => False,
"view_other_pms" => false,
"edit_feature" => false,
"bulk_edit_vote" => false,
"edit_other_vote" => false,
"view_sysinfo" => false,
"hellbanned" => False,
"view_hellbanned" => False,
"hellbanned" => false,
"view_hellbanned" => false,
"protected" => False, # only admins can modify protected users (stops a moderator changing an admin's password)
));
"protected" => false, # only admins can modify protected users (stops a moderator changing an admin's password)
]);
new UserClass("anonymous", "base", array(
));
new UserClass("anonymous", "base", [
]);
new UserClass("user", "base", array(
"big_search" => True,
"create_image" => True,
"create_comment" => True,
"edit_image_tag" => True,
"edit_image_source" => True,
"create_image_report" => True,
));
new UserClass("user", "base", [
"big_search" => true,
"create_image" => true,
"create_comment" => true,
"edit_image_tag" => true,
"edit_image_source" => true,
"create_image_report" => true,
]);
new UserClass("admin", "base", array(
"change_setting" => True,
"override_config" => True,
"big_search" => True,
"edit_image_lock" => True,
"view_ip" => True,
"ban_ip" => True,
"edit_user_name" => True,
"edit_user_password" => True,
"edit_user_info" => True,
"edit_user_class" => True,
"delete_user" => True,
"create_image" => True,
"delete_image" => True,
"ban_image" => True,
"create_comment" => True,
"delete_comment" => True,
"bypass_comment_checks" => True,
"replace_image" => True,
"manage_extension_list" => True,
"manage_alias_list" => True,
"edit_image_tag" => True,
"edit_image_source" => True,
"edit_image_owner" => True,
"bulk_edit_image_tag" => True,
"bulk_edit_image_source" => True,
"mass_tag_edit" => True,
"create_image_report" => True,
"view_image_report" => True,
"edit_wiki_page" => True,
"delete_wiki_page" => True,
"view_eventlog" => True,
"manage_blocks" => True,
"manage_admintools" => True,
"ignore_downtime" => True,
"view_other_pms" => True,
"edit_feature" => True,
"bulk_edit_vote" => True,
"edit_other_vote" => True,
"view_sysinfo" => True,
"view_hellbanned" => True,
"protected" => True,
));
new UserClass("admin", "base", [
"change_setting" => true,
"override_config" => true,
"big_search" => true,
"edit_image_lock" => true,
"view_ip" => true,
"ban_ip" => true,
"edit_user_name" => true,
"edit_user_password" => true,
"edit_user_info" => true,
"edit_user_class" => true,
"delete_user" => true,
"create_image" => true,
"delete_image" => true,
"ban_image" => true,
"create_comment" => true,
"delete_comment" => true,
"bypass_comment_checks" => true,
"replace_image" => true,
"manage_extension_list" => true,
"manage_alias_list" => true,
"edit_image_tag" => true,
"edit_image_source" => true,
"edit_image_owner" => true,
"bulk_edit_image_tag" => true,
"bulk_edit_image_source" => true,
"mass_tag_edit" => true,
"create_image_report" => true,
"view_image_report" => true,
"edit_wiki_page" => true,
"delete_wiki_page" => true,
"view_eventlog" => true,
"manage_blocks" => true,
"manage_admintools" => true,
"ignore_downtime" => true,
"view_other_pms" => true,
"edit_feature" => true,
"bulk_edit_vote" => true,
"edit_other_vote" => true,
"view_sysinfo" => true,
"view_hellbanned" => true,
"protected" => true,
]);
new UserClass("hellbanned", "user", array(
"hellbanned" => True,
));
new UserClass("hellbanned", "user", [
"hellbanned" => true,
]);
@include_once "data/config/user-classes.conf.php";

View File

@ -5,23 +5,30 @@ require_once "vendor/shish/libcontext-php/context.php";
* Misc *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function mtimefile(string $file): string {
function mtimefile(string $file): string
{
$data_href = get_base_href();
$mtime = filemtime($file);
return "$data_href/$file?$mtime";
}
function get_theme(): string {
function get_theme(): string
{
global $config;
$theme = $config->get_string("theme", "default");
if(!file_exists("themes/$theme")) $theme = "default";
if (!file_exists("themes/$theme")) {
$theme = "default";
}
return $theme;
}
function contact_link(): ?string {
function contact_link(): ?string
{
global $config;
$text = $config->get_string('contact_link');
if(is_null($text)) return null;
if (is_null($text)) {
return null;
}
if (
startsWith($text, "http:") ||
@ -45,18 +52,19 @@ function contact_link(): ?string {
/**
* Check if HTTPS is enabled for the server.
*/
function is_https_enabled(): bool {
function is_https_enabled(): bool
{
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
}
/**
* Compare two Block objects, used to sort them before being displayed
*/
function blockcmp(Block $a, Block $b): int {
function blockcmp(Block $a, Block $b): int
{
if ($a->position == $b->position) {
return 0;
}
else {
} else {
return ($a->position > $b->position);
}
}
@ -64,7 +72,8 @@ function blockcmp(Block $a, Block $b): int {
/**
* Figure out PHP's internal memory limit
*/
function get_memory_limit(): int {
function get_memory_limit(): int
{
global $config;
// thumbnail generation requires lots of memory
@ -88,8 +97,7 @@ function get_memory_limit(): int {
// No memory limit.
// Return the larger of the set limits.
return max($shimmie_limit, $default_limit);
}
else {
} else {
// PHP has a memory limit set.
if ($shimmie_limit > $memory) {
// Shimmie wants more memory than what PHP is currently set for.
@ -111,7 +119,8 @@ function get_memory_limit(): int {
* Get the currently active IP, masked to make it not change when the last
* octet or two change, for use in session cookies and such
*/
function get_session_ip(Config $config): string {
function get_session_ip(Config $config): string
{
$mask = $config->get_string("session_hash_mask", "255.255.0.0");
$addr = $_SERVER['REMOTE_ADDR'];
$addr = inet_ntop(inet_pton($addr) & inet_pton($mask));
@ -128,7 +137,8 @@ function get_session_ip(Config $config): string {
* the action actually takes place (eg onWhateverElse) - but much of the time, actions
* are taken from within onPageRequest...
*/
function flash_message(string $text, string $type="info") {
function flash_message(string $text, string $type="info")
{
global $page;
$current = $page->get_cookie("flash_message");
if ($current) {
@ -142,32 +152,39 @@ function flash_message(string $text, string $type="info") {
/**
* A shorthand way to send a TextFormattingEvent and get the results.
*/
function format_text(string $string): string {
function format_text(string $string): string
{
$tfe = new TextFormattingEvent($string);
send_event($tfe);
return $tfe->formatted;
}
function warehouse_path(string $base, string $hash, bool $create=true): string {
function warehouse_path(string $base, string $hash, bool $create=true): string
{
$ab = substr($hash, 0, 2);
$cd = substr($hash, 2, 2);
if (WH_SPLITS == 2) {
$pa = 'data/'.$base.'/'.$ab.'/'.$cd.'/'.$hash;
}
else {
} else {
$pa = 'data/'.$base.'/'.$ab.'/'.$hash;
}
if($create && !file_exists(dirname($pa))) mkdir(dirname($pa), 0755, true);
if ($create && !file_exists(dirname($pa))) {
mkdir(dirname($pa), 0755, true);
}
return $pa;
}
function data_path(string $filename): string {
function data_path(string $filename): string
{
$filename = "data/" . $filename;
if(!file_exists(dirname($filename))) mkdir(dirname($filename), 0755, true);
if (!file_exists(dirname($filename))) {
mkdir(dirname($filename), 0755, true);
}
return $filename;
}
function transload(string $url, string $mfile): ?array {
function transload(string $url, string $mfile): ?array
{
global $config;
if ($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) {
@ -228,12 +245,17 @@ function transload(string $url, string $mfile): ?array {
/**
* Get the active contents of a .php file
*/
function manual_include(string $fname): ?string {
static $included = array();
function manual_include(string $fname): ?string
{
static $included = [];
if(!file_exists($fname)) return null;
if (!file_exists($fname)) {
return null;
}
if(in_array($fname, $included)) return null;
if (in_array($fname, $included)) {
return null;
}
$included[] = $fname;
@ -256,12 +278,12 @@ function manual_include(string $fname): ?string {
}
function path_to_tags(string $path): string {
$matches = array();
function path_to_tags(string $path): string
{
$matches = [];
if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) {
$tags = $matches[1];
}
else {
} else {
$tags = dirname($path);
$tags = str_replace("/", " ", $tags);
$tags = str_replace("__", " ", $tags);
@ -284,15 +306,15 @@ $_shm_load_start = microtime(true);
* Collects some debug information (execution time, memory usage, queries, etc)
* and formats it to stick in the footer of the page.
*/
function get_debug_info(): string {
function get_debug_info(): string
{
global $config, $_shm_event_count, $database, $_shm_load_start;
$i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024);
if ($config->get_string("commit_hash", "unknown") == "unknown") {
$commit = "";
}
else {
} else {
$commit = " (".$config->get_string("commit_hash").")";
}
$time = sprintf("%.2f", microtime(true) - $_shm_load_start);
@ -310,7 +332,8 @@ function get_debug_info(): string {
return $debug;
}
function log_slow() {
function log_slow()
{
global $_shm_load_start;
if (!is_null(SLOW_PAGES)) {
$_time = microtime(true) - $_shm_load_start;
@ -322,7 +345,8 @@ function log_slow() {
}
}
function score_assert_handler($file, $line, $code, $desc = null) {
function score_assert_handler($file, $line, $code, $desc = null)
{
$file = basename($file);
print("Assertion failed at $file:$line: $code ($desc)");
/*
@ -339,9 +363,10 @@ function score_assert_handler($file, $line, $code, $desc = null) {
/** @privatesection */
function _version_check() {
function _version_check()
{
if (MIN_PHP_VERSION) {
if(version_compare(phpversion(), MIN_PHP_VERSION, ">=") === FALSE) {
if (version_compare(phpversion(), MIN_PHP_VERSION, ">=") === false) {
print "
Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION."
(PHP reports that it is version ".phpversion().")
@ -353,7 +378,8 @@ date and you should plan on moving elsewhere.
}
}
function _sanitise_environment() {
function _sanitise_environment()
{
global $_shm_ctx;
if (TIMEZONE) {
@ -393,9 +419,12 @@ function _sanitise_environment() {
}
function _get_themelet_files(string $_theme): array {
$base_themelets = array();
if(file_exists('themes/'.$_theme.'/custompage.class.php')) $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php';
function _get_themelet_files(string $_theme): array
{
$base_themelets = [];
if (file_exists('themes/'.$_theme.'/custompage.class.php')) {
$base_themelets[] = 'themes/'.$_theme.'/custompage.class.php';
}
$base_themelets[] = 'themes/'.$_theme.'/layout.class.php';
$base_themelets[] = 'themes/'.$_theme.'/themelet.class.php';
@ -409,7 +438,8 @@ function _get_themelet_files(string $_theme): array {
/**
* Used to display fatal errors to the web user.
*/
function _fatal_error(Exception $e) {
function _fatal_error(Exception $e)
{
$version = VERSION;
$message = $e->getMessage();
@ -440,24 +470,31 @@ function _fatal_error(Exception $e) {
* Necessary because various servers and various clients
* think that / is special...
*/
function _decaret(string $str): string {
function _decaret(string $str): string
{
$out = "";
$length = strlen($str);
for ($i=0; $i<$length; $i++) {
if ($str[$i] == "^") {
$i++;
if($str[$i] == "^") $out .= "^";
if($str[$i] == "s") $out .= "/";
if($str[$i] == "b") $out .= "\\";
if ($str[$i] == "^") {
$out .= "^";
}
else {
if ($str[$i] == "s") {
$out .= "/";
}
if ($str[$i] == "b") {
$out .= "\\";
}
} else {
$out .= $str[$i];
}
}
return $out;
}
function _get_user(): User {
function _get_user(): User
{
global $config, $page;
$user = null;
if ($page->get_cookie("user") && $page->get_cookie("session")) {
@ -474,7 +511,8 @@ function _get_user(): User {
return $user;
}
function _get_query(): string {
function _get_query(): string
{
return (@$_POST["q"]?:@$_GET["q"])?:"/";
}
@ -483,22 +521,28 @@ function _get_query(): string {
* Code coverage *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function _start_coverage(): void {
function _start_coverage(): void
{
if (function_exists("xdebug_start_code_coverage")) {
#xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE);
xdebug_start_code_coverage(XDEBUG_CC_UNUSED);
}
}
function _end_coverage(): void {
function _end_coverage(): void
{
if (function_exists("xdebug_get_code_coverage")) {
// Absolute path is necessary because working directory
// inside register_shutdown_function is unpredictable.
$absolute_path = dirname(dirname(__FILE__)) . "/data/coverage";
if(!file_exists($absolute_path)) mkdir($absolute_path);
if (!file_exists($absolute_path)) {
mkdir($absolute_path);
}
$n = 0;
$t = time();
while(file_exists("$absolute_path/$t.$n.log")) $n++;
while (file_exists("$absolute_path/$t.$n.log")) {
$n++;
}
file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage())));
}
}
@ -513,7 +557,8 @@ function _end_coverage(): void {
*
* FIXME: also check that IP ban ext is installed
*/
function show_ip(string $ip, string $ban_reason): string {
function show_ip(string $ip, string $ban_reason): string
{
global $user;
$u_reason = url_escape($ban_reason);
$u_end = url_escape("+1 week");
@ -525,14 +570,14 @@ function show_ip(string $ip, string $ban_reason): string {
/**
* Make a form tag with relevant auth token and stuff
*/
function make_form(string $target, string $method="POST", bool $multipart=False, string $form_id="", string $onsubmit=""): string {
function make_form(string $target, string $method="POST", bool $multipart=false, string $form_id="", string $onsubmit=""): string
{
global $user;
if ($method == "GET") {
$link = html_escape($target);
$target = make_link($target);
$extra_inputs = "<input type='hidden' name='q' value='$link'>";
}
else {
} else {
$extra_inputs = $user->get_auth_html();
}

View File

@ -23,39 +23,43 @@
/**
* Sent when the admin page is ready to be added to
*/
class AdminBuildingEvent extends Event {
class AdminBuildingEvent extends Event
{
/** @var \Page */
public $page;
public function __construct(Page $page) {
public function __construct(Page $page)
{
$this->page = $page;
}
}
class AdminActionEvent extends Event {
class AdminActionEvent extends Event
{
/** @var string */
public $action;
/** @var bool */
public $redirect = true;
public function __construct(string $action) {
public function __construct(string $action)
{
$this->action = $action;
}
}
class AdminPage extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class AdminPage extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("admin")) {
if (!$user->can("manage_admintools")) {
$this->theme->display_permission_denied();
}
else {
} else {
if ($event->count_args() == 0) {
send_event(new AdminBuildingEvent($page));
}
else {
} else {
$action = $event->get_arg(0);
$aae = new AdminActionEvent($action);
@ -74,7 +78,8 @@ class AdminPage extends Extension {
}
}
public function onCommand(CommandEvent $event) {
public function onCommand(CommandEvent $event)
{
if ($event->cmd == "help") {
print "\tget-page [query string]\n";
print "\t\teg 'get-page post/list'\n\n";
@ -91,40 +96,44 @@ class AdminPage extends Extension {
if ($image) {
print("Regenerating thumb for image {$image->id} ({$image->hash})\n");
send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true));
}
else {
} else {
print("Can't find image with hash {$event->args[0]}\n");
}
}
}
public function onAdminBuilding(AdminBuildingEvent $event) {
public function onAdminBuilding(AdminBuildingEvent $event)
{
$this->theme->display_page();
$this->theme->display_form();
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
if ($user->can("manage_admintools")) {
$event->add_link("Board Admin", make_link("admin"));
}
}
public function onAdminAction(AdminActionEvent $event) {
public function onAdminAction(AdminActionEvent $event)
{
$action = $event->action;
if (method_exists($this, $action)) {
$event->redirect = $this->$action();
}
}
public function onPostListBuilding(PostListBuildingEvent $event) {
public function onPostListBuilding(PostListBuildingEvent $event)
{
global $user;
if ($user->can("manage_admintools") && !empty($event->search_terms)) {
$event->add_control($this->theme->dbq_html(Tag::implode($event->search_terms)));
}
}
private function delete_by_query() {
private function delete_by_query()
{
global $page;
$query = $_POST['query'];
$reason = @$_POST['reason'];
@ -145,23 +154,26 @@ class AdminPage extends Extension {
return false;
}
private function set_tag_case() {
private function set_tag_case()
{
global $database;
$database->execute($database->scoreql_to_sql(
"UPDATE tags SET tag=:tag1 WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag2)"
), array("tag1" => $_POST['tag'], "tag2" => $_POST['tag']));
), ["tag1" => $_POST['tag'], "tag2" => $_POST['tag']]);
log_info("admin", "Fixed the case of ".html_escape($_POST['tag']), "Fixed case");
return true;
}
private function lowercase_all_tags() {
private function lowercase_all_tags()
{
global $database;
$database->execute("UPDATE tags SET tag=lower(tag)");
log_warning("admin", "Set all tags to lowercase", "Set all tags to lowercase");
return true;
}
private function recount_tag_use() {
private function recount_tag_use()
{
global $database;
$database->Execute("
UPDATE tags
@ -176,10 +188,11 @@ class AdminPage extends Extension {
}
private function database_dump() {
private function database_dump()
{
global $page;
$matches = array();
$matches = [];
preg_match("#^(?P<proto>\w+)\:(?:user=(?P<user>\w+)(?:;|$)|password=(?P<password>\w*)(?:;|$)|host=(?P<host>[\w\.\-]+)(?:;|$)|dbname=(?P<dbname>[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches);
$software = $matches['proto'];
$username = $matches['user'];
@ -214,16 +227,17 @@ class AdminPage extends Extension {
return false;
}
private function download_all_images() {
private function download_all_images()
{
global $database, $page;
$images = $database->get_all("SELECT hash, ext FROM images");
$filename = data_path('imgdump-'.date('Ymd').'.zip');
$zip = new ZipArchive;
if($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === TRUE){
if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === true) {
foreach ($images as $img) {
$img_loc = warehouse_path("images", $img["hash"], FALSE);
$img_loc = warehouse_path("images", $img["hash"], false);
$zip->addFile($img_loc, $img["hash"].".".$img["ext"]);
}
$zip->close();
@ -235,7 +249,8 @@ class AdminPage extends Extension {
return false; // we do want a redirect, but a manual one
}
private function reset_image_ids() {
private function reset_image_ids()
{
global $database;
//TODO: Make work with PostgreSQL + SQLite
@ -247,7 +262,7 @@ class AdminPage extends Extension {
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = :db
AND REFERENCED_COLUMN_NAME = 'id'
AND REFERENCED_TABLE_NAME = 'images'", array("db" => $matches['dbname']));
AND REFERENCED_TABLE_NAME = 'images'", ["db" => $matches['dbname']]);
$i = 1;
$ids = $database->get_col("SELECT id FROM images ORDER BY images.id ASC");
@ -273,4 +288,3 @@ class AdminPage extends Extension {
return true;
}
}

View File

@ -1,6 +1,8 @@
<?php
class AdminPageTest extends ShimmiePHPUnitTestCase {
public function testAuth() {
class AdminPageTest extends ShimmiePHPUnitTestCase
{
public function testAuth()
{
$this->get_page('admin');
$this->assert_response(403);
$this->assert_title("Permission Denied");
@ -16,7 +18,8 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
$this->assert_title("Admin Tools");
}
public function testLowercase() {
public function testLowercase()
{
$ts = time(); // we need a tag that hasn't been used before
$this->log_in_as_admin();
@ -37,7 +40,8 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
}
# FIXME: make sure the admin tools actually work
public function testRecount() {
public function testRecount()
{
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
@ -46,7 +50,8 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
send_event(new AdminActionEvent('recount_tag_use'));
}
public function testDump() {
public function testDump()
{
$this->log_in_as_admin();
$this->get_page('admin');
$this->assert_title("Admin Tools");
@ -57,7 +62,8 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
//$this->assert_response(200);
}
public function testDBQ() {
public function testDBQ()
{
$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");
@ -81,4 +87,3 @@ class AdminPageTest extends ShimmiePHPUnitTestCase {
$this->delete_image($image_id_3);
}
}

View File

@ -1,10 +1,12 @@
<?php
class AdminPageTheme extends Themelet {
class AdminPageTheme extends Themelet
{
/*
* Show the basics of a page, for other extensions to add to
*/
public function display_page() {
public function display_page()
{
global $page;
$page->set_title("Admin Tools");
@ -12,14 +14,14 @@ class AdminPageTheme extends Themelet {
$page->add_block(new NavBlock());
}
protected function button(string $name, string $action, bool $protected=false): string {
protected function button(string $name, string $action, bool $protected=false): string
{
$c_protected = $protected ? " protected" : "";
$html = make_form(make_link("admin/$action"), "POST", false, "admin$c_protected");
if ($protected) {
$html .= "<input type='submit' id='$action' value='$name' disabled='disabled'>";
$html .= "<input type='checkbox' onclick='$(\"#$action\").attr(\"disabled\", !$(this).is(\":checked\"))'>";
}
else {
} else {
$html .= "<input type='submit' id='$action' value='$name'>";
}
$html .= "</form>\n";
@ -32,17 +34,20 @@ class AdminPageTheme extends Themelet {
* 'recount tag use'
* etc
*/
public function display_form() {
public function display_form()
{
global $page, $database;
$html = "";
$html .= $this->button("All tags to lowercase", "lowercase_all_tags", true);
$html .= $this->button("Recount tag use", "recount_tag_use", false);
if(class_exists('ZipArchive'))
if (class_exists('ZipArchive')) {
$html .= $this->button("Download all images", "download_all_images", false);
}
$html .= $this->button("Download database contents", "database_dump", false);
if($database->get_driver_name() == "mysql")
if ($database->get_driver_name() == "mysql") {
$html .= $this->button("Reset image IDs", "reset_image_ids", true);
}
$page->add_block(new Block("Misc Admin Tools", $html));
$html = make_form(make_link("admin/set_tag_case"), "POST");
@ -52,7 +57,8 @@ class AdminPageTheme extends Themelet {
$page->add_block(new Block("Set Tag Case", $html));
}
public function dbq_html($terms) {
public function dbq_html($terms)
{
$h_terms = html_escape($terms);
$h_reason = "";
if (class_exists("ImageBan")) {
@ -68,4 +74,3 @@ class AdminPageTheme extends Themelet {
return $html;
}
}

View File

@ -10,22 +10,28 @@
* site admins can edit it, other people can view and download it
*/
class AddAliasEvent extends Event {
class AddAliasEvent extends Event
{
/** @var string */
public $oldtag;
/** @var string */
public $newtag;
public function __construct(string $oldtag, string $newtag) {
public function __construct(string $oldtag, string $newtag)
{
$this->oldtag = trim($oldtag);
$this->newtag = trim($newtag);
}
}
class AddAliasException extends SCoreException {}
class AddAliasException extends SCoreException
{
}
class AliasEditor extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class AliasEditor extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $config, $database, $page, $user;
if ($event->page_matches("alias")) {
@ -37,54 +43,48 @@ class AliasEditor extends Extension {
send_event($aae);
$page->set_mode("redirect");
$page->set_redirect(make_link("alias/list"));
}
catch(AddAliasException $ex) {
} catch (AddAliasException $ex) {
$this->theme->display_error(500, "Error adding alias", $ex->getMessage());
}
}
}
}
else if($event->get_arg(0) == "remove") {
} elseif ($event->get_arg(0) == "remove") {
if ($user->can("manage_alias_list")) {
if (isset($_POST['oldtag'])) {
$database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", array("oldtag" => $_POST['oldtag']));
$database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", ["oldtag" => $_POST['oldtag']]);
log_info("alias_editor", "Deleted alias for ".$_POST['oldtag'], "Deleted alias");
$page->set_mode("redirect");
$page->set_redirect(make_link("alias/list"));
}
}
}
else if($event->get_arg(0) == "list") {
} elseif ($event->get_arg(0) == "list") {
$page_number = $event->get_arg(1);
if (is_null($page_number) || !is_numeric($page_number)) {
$page_number = 0;
}
else if ($page_number <= 0) {
} elseif ($page_number <= 0) {
$page_number = 0;
}
else {
} else {
$page_number--;
}
$alias_per_page = $config->get_int('alias_items_per_page', 30);
$query = "SELECT oldtag, newtag FROM aliases ORDER BY newtag ASC LIMIT :limit OFFSET :offset";
$alias = $database->get_pairs($query,
array("limit"=>$alias_per_page, "offset"=>$page_number * $alias_per_page)
$alias = $database->get_pairs(
$query,
["limit"=>$alias_per_page, "offset"=>$page_number * $alias_per_page]
);
$total_pages = ceil($database->get_one("SELECT COUNT(*) FROM aliases") / $alias_per_page);
$this->theme->display_aliases($alias, $page_number + 1, $total_pages);
}
else if($event->get_arg(0) == "export") {
} elseif ($event->get_arg(0) == "export") {
$page->set_mode("data");
$page->set_type("text/csv");
$page->set_filename("aliases.csv");
$page->set_data($this->get_alias_csv($database));
}
else if($event->get_arg(0) == "import") {
} elseif ($event->get_arg(0) == "import") {
if ($user->can("manage_alias_list")) {
if (count($_FILES) > 0) {
$tmp = $_FILES['alias_file']['tmp_name'];
@ -93,41 +93,40 @@ class AliasEditor extends Extension {
log_info("alias_editor", "Imported aliases from file", "Imported aliases"); # FIXME: how many?
$page->set_mode("redirect");
$page->set_redirect(make_link("alias/list"));
}
else {
} else {
$this->theme->display_error(400, "No File Specified", "You have to upload a file");
}
}
else {
} else {
$this->theme->display_error(401, "Admins Only", "Only admins can edit the alias list");
}
}
}
}
public function onAddAlias(AddAliasEvent $event) {
public function onAddAlias(AddAliasEvent $event)
{
global $database;
$pair = array("oldtag" => $event->oldtag, "newtag" => $event->newtag);
$pair = ["oldtag" => $event->oldtag, "newtag" => $event->newtag];
if ($database->get_row("SELECT * FROM aliases WHERE oldtag=:oldtag AND lower(newtag)=lower(:newtag)", $pair)) {
throw new AddAliasException("That alias already exists");
}
else if($database->get_row("SELECT * FROM aliases WHERE oldtag=:newtag", array("newtag" => $event->newtag))) {
} elseif ($database->get_row("SELECT * FROM aliases WHERE oldtag=:newtag", ["newtag" => $event->newtag])) {
throw new AddAliasException("{$event->newtag} is itself an alias");
}
else {
} else {
$database->execute("INSERT INTO aliases(oldtag, newtag) VALUES(:oldtag, :newtag)", $pair);
log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", "Added alias");
}
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
if ($user->can("manage_alias_list")) {
$event->add_link("Alias Editor", make_link("alias/list"));
}
}
private function get_alias_csv(Database $database): string {
private function get_alias_csv(Database $database): string
{
$csv = "";
$aliases = $database->get_pairs("SELECT oldtag, newtag FROM aliases ORDER BY newtag");
foreach ($aliases as $old => $new) {
@ -136,7 +135,8 @@ class AliasEditor extends Extension {
return $csv;
}
private function add_alias_csv(Database $database, string $csv) {
private function add_alias_csv(Database $database, string $csv)
{
$csv = str_replace("\r", "\n", $csv);
foreach (explode("\n", $csv) as $line) {
$parts = str_getcsv($line);
@ -144,8 +144,7 @@ class AliasEditor extends Extension {
try {
$aae = new AddAliasEvent($parts[0], $parts[1]);
send_event($aae);
}
catch(AddAliasException $ex) {
} catch (AddAliasException $ex) {
$this->theme->display_error(500, "Error adding alias", $ex->getMessage());
}
}
@ -159,6 +158,8 @@ class AliasEditor extends Extension {
* search for the images and be redirected to the alias,
* missing out the images tagged with the old tag.
*/
public function get_priority(): int {return 60;}
public function get_priority(): int
{
return 60;
}
}

View File

@ -1,12 +1,15 @@
<?php
class AliasEditorTest extends ShimmiePHPUnitTestCase {
public function testAliasList() {
class AliasEditorTest extends ShimmiePHPUnitTestCase
{
public function testAliasList()
{
$this->get_page('alias/list');
$this->assert_response(200);
$this->assert_title("Alias List");
}
public function testAliasListReadOnly() {
public function testAliasListReadOnly()
{
// Check that normal users can't add aliases.
$this->log_in_as_user();
$this->get_page('alias/list');
@ -14,7 +17,8 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text("Add");
}
public function testAliasEditor() {
public function testAliasEditor()
{
/*
**********************************************************************
* FIXME: TODO:
@ -101,4 +105,3 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text("Add");
}
}

View File

@ -1,12 +1,14 @@
<?php
class AliasEditorTheme extends Themelet {
class AliasEditorTheme extends Themelet
{
/**
* Show a page of aliases.
*
* Note: $can_manage = whether things like "add new alias" should be shown
*/
public function display_aliases(array $aliases, int $pageNumber, int $totalPages): void {
public function display_aliases(array $aliases, int $pageNumber, int $totalPages): void
{
global $page, $user;
$can_manage = $user->can("manage_alias_list");
@ -21,8 +23,7 @@ class AliasEditorTheme extends Themelet {
</form>
</tr>
";
}
else {
} else {
$h_action = "";
$h_add = "";
}
@ -72,4 +73,3 @@ class AliasEditorTheme extends Themelet {
$this->display_paginator($page, "alias/list", null, $pageNumber, $totalPages);
}
}

View File

@ -9,15 +9,18 @@
require_once "ext/amazon_s3/lib/S3.php";
class UploadS3 extends Extension {
public function onInitExt(InitExtEvent $event) {
class UploadS3 extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config;
$config->set_default_string("amazon_s3_access", "");
$config->set_default_string("amazon_s3_secret", "");
$config->set_default_string("amazon_s3_bucket", "");
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Amazon S3");
$sb->add_text_option("amazon_s3_access", "Access key: ");
$sb->add_text_option("amazon_s3_secret", "<br>Secret key: ");
@ -25,7 +28,8 @@ class UploadS3 extends Extension {
$event->panel->add_block($sb);
}
public function onImageAddition(ImageAdditionEvent $event) {
public function onImageAddition(ImageAdditionEvent $event)
{
global $config;
$access = $config->get_string("amazon_s3_access");
$secret = $config->get_string("amazon_s3_secret");
@ -39,27 +43,28 @@ class UploadS3 extends Extension {
$bucket,
'thumbs/'.$event->image->hash,
S3::ACL_PUBLIC_READ,
array(),
array(
[],
[
"Content-Type" => "image/jpeg",
"Content-Disposition" => "inline; filename=image-" . $event->image->id . ".jpg",
)
]
);
$s3->putObjectFile(
warehouse_path("images", $event->image->hash),
$bucket,
'images/'.$event->image->hash,
S3::ACL_PUBLIC_READ,
array(),
array(
[],
[
"Content-Type" => $event->image->get_mime_type(),
"Content-Disposition" => "inline; filename=image-" . $event->image->id . "." . $event->image->ext,
)
]
);
}
}
public function onImageDeletion(ImageDeletionEvent $event) {
public function onImageDeletion(ImageDeletionEvent $event)
{
global $config;
$access = $config->get_string("amazon_s3_access");
$secret = $config->get_string("amazon_s3_secret");
@ -72,4 +77,3 @@ class UploadS3 extends Extension {
}
}
}

View File

@ -8,11 +8,13 @@
* Documentation:
* Simply enable this extention in the extention manager to enable arrow key navigation.
*/
class ArrowkeyNavigation extends Extension {
class ArrowkeyNavigation extends Extension
{
/**
* Adds functionality for post/view on images.
*/
public function onDisplayingImage(DisplayingImageEvent $event) {
public function onDisplayingImage(DisplayingImageEvent $event)
{
$prev_url = make_http(make_link("post/prev/".$event->image->id));
$next_url = make_http(make_link("post/next/".$event->image->id));
$this->add_arrowkeys_code($prev_url, $next_url);
@ -21,7 +23,8 @@ class ArrowkeyNavigation extends Extension {
/**
* Adds functionality for post/list.
*/
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
if ($event->page_matches("post/list")) {
$pageinfo = $this->get_list_pageinfo($event);
$prev_url = make_http(make_link("post/list/".$pageinfo["prev"]));
@ -33,7 +36,8 @@ class ArrowkeyNavigation extends Extension {
/**
* Adds the javascript to the page with the given urls.
*/
private function add_arrowkeys_code(string $prev_url, string $next_url) {
private function add_arrowkeys_code(string $prev_url, string $next_url)
{
global $page;
$page->add_html_header("<script type=\"text/javascript\">
@ -51,7 +55,8 @@ class ArrowkeyNavigation extends Extension {
/**
* Returns info about the current page number.
*/
private function get_list_pageinfo(PageRequestEvent $event): array {
private function get_list_pageinfo(PageRequestEvent $event): array
{
global $config, $database;
// get the amount of images per page
@ -62,30 +67,36 @@ class ArrowkeyNavigation extends Extension {
$prefix = "";
$page_number = int_escape($event->get_arg(0));
$total_pages = ceil($database->get_one(
"SELECT COUNT(*) FROM images") / $images_per_page);
}
else { // if there are tags, use pages with tags
"SELECT COUNT(*) FROM images"
) / $images_per_page);
} else { // if there are tags, use pages with tags
$prefix = url_escape($event->get_arg(0)) . "/";
$page_number = int_escape($event->get_arg(1));
$total_pages = ceil($database->get_one(
"SELECT count FROM tags WHERE tag=:tag",
array("tag"=>$event->get_arg(0))) / $images_per_page);
["tag"=>$event->get_arg(0)]
) / $images_per_page);
}
// creates previous & next values
// When previous first page, go to last page
if ($page_number <= 1) $prev = $total_pages;
else $prev = $page_number-1;
if ($page_number >= $total_pages) $next = 1;
else $next = $page_number+1;
if ($page_number <= 1) {
$prev = $total_pages;
} else {
$prev = $page_number-1;
}
if ($page_number >= $total_pages) {
$next = 1;
} else {
$next = $page_number+1;
}
// Create return array
$pageinfo = array(
$pageinfo = [
"prev" => $prefix.$prev,
"next" => $prefix.$next,
);
];
return $pageinfo;
}
}

View File

@ -8,7 +8,8 @@
* Documentation:
*
*/
class AuthorSetEvent extends Event {
class AuthorSetEvent extends Event
{
/** @var \Image */
public $image;
/** @var \User */
@ -16,22 +17,26 @@ class AuthorSetEvent extends Event {
/** @var string */
public $author;
public function __construct(Image $image, User $user, string $author) {
public function __construct(Image $image, User $user, string $author)
{
$this->image = $image;
$this->user = $user;
$this->author = $author;
}
}
class Artists extends Extension {
public function onImageInfoSet(ImageInfoSetEvent $event) {
class Artists extends Extension
{
public function onImageInfoSet(ImageInfoSetEvent $event)
{
global $user;
if (isset($_POST["tag_edit__author"])) {
send_event(new AuthorSetEvent($event->image, $user, $_POST["tag_edit__author"]));
}
}
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) {
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event)
{
global $user;
$artistName = $this->get_artistName_by_imageID($event->image->id);
if (!$user->is_anonymous()) {
@ -39,15 +44,17 @@ class Artists extends Extension {
}
}
public function onSearchTermParse(SearchTermParseEvent $event) {
$matches = array();
public function onSearchTermParse(SearchTermParseEvent $event)
{
$matches = [];
if (preg_match("/^author[=|:](.*)$/i", $event->term, $matches)) {
$char = $matches[1];
$event->add_querylet(new Querylet("Author = :author_char", array("author_char"=>$char)));
$event->add_querylet(new Querylet("Author = :author_char", ["author_char"=>$char]));
}
}
public function onInitExt(InitExtEvent $event) {
public function onInitExt(InitExtEvent $event)
{
global $config, $database;
if ($config->get_int("ext_artists_version") < 1) {
@ -100,43 +107,49 @@ class Artists extends Extension {
}
}
public function onAuthorSet(AuthorSetEvent $event) {
public function onAuthorSet(AuthorSetEvent $event)
{
global $database;
$author = strtolower($event->author);
if (strlen($author) === 0 || strpos($author, " "))
if (strlen($author) === 0 || strpos($author, " ")) {
return;
}
$paddedAuthor = str_replace(" ", "_", $author);
$artistID = NULL;
if ($this->artist_exists($author))
$artistID = null;
if ($this->artist_exists($author)) {
$artistID = $this->get_artist_id($author);
}
if (is_null($artistID) && $this->alias_exists_by_name($paddedAuthor))
if (is_null($artistID) && $this->alias_exists_by_name($paddedAuthor)) {
$artistID = $this->get_artistID_by_aliasName($paddedAuthor);
}
if (is_null($artistID) && $this->member_exists_by_name($paddedAuthor))
if (is_null($artistID) && $this->member_exists_by_name($paddedAuthor)) {
$artistID = $this->get_artistID_by_memberName($paddedAuthor);
}
if (is_null($artistID) && $this->url_exists_by_url($author))
if (is_null($artistID) && $this->url_exists_by_url($author)) {
$artistID = $this->get_artistID_by_url($author);
}
if (!is_null($artistID)) {
$artistName = $this->get_artistName_by_artistID($artistID);
}
else {
} else {
$this->save_new_artist($author, "");
$artistName = $author;
}
$database->execute(
"UPDATE images SET author = ? WHERE id = ?",
array($artistName, $event->image->id)
[$artistName, $event->image->id]
);
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("artist")) {
@ -152,8 +165,7 @@ class Artists extends Extension {
{
if (!$user->is_anonymous()) {
$this->theme->new_artist_composer();
}
else {
} else {
$this->theme->display_error(401, "Error", "You must be registered and logged in to create a new artist.");
}
break;
@ -170,13 +182,11 @@ class Artists extends Extension {
$newArtistID = $this->add_artist();
if ($newArtistID == -1) {
$this->theme->display_error(400, "Error", "Error when entering artist data.");
}
else {
} else {
$page->set_mode("redirect");
$page->set_redirect(make_link("artist/view/".$newArtistID));
}
}
else {
} else {
$this->theme->display_error(401, "Error", "You must be registered and logged in to create a new artist.");
}
break;
@ -220,8 +230,7 @@ class Artists extends Extension {
$userIsAdmin = $user->is_admin();
$this->theme->sidebar_options("editor", $artistID, $userIsAdmin);
}
else {
} else {
$this->theme->display_error(401, "Error", "You must be registered and logged in to edit an artist.");
}
break;
@ -277,8 +286,7 @@ class Artists extends Extension {
//***********ALIAS SECTION ***********************
case "alias":
{
switch ($event->get_arg(1))
{
switch ($event->get_arg(1)) {
case "add":
{
$artistID = $_POST['artistID'];
@ -319,8 +327,7 @@ class Artists extends Extension {
//**************** URLS SECTION **********************
case "url":
{
switch ($event->get_arg(1))
{
switch ($event->get_arg(1)) {
case "add":
{
$artistID = $_POST['artistID'];
@ -360,8 +367,7 @@ class Artists extends Extension {
//******************* MEMBERS SECTION *********************
case "member":
{
switch ($event->get_arg(1))
{
switch ($event->get_arg(1)) {
case "add":
{
$artistID = $_POST['artistID'];
@ -402,116 +408,134 @@ class Artists extends Extension {
}
}
private function get_artistName_by_imageID(int $imageID): string {
private function get_artistName_by_imageID(int $imageID): string
{
global $database;
$result = $database->get_row("SELECT author FROM images WHERE id = ?", array($imageID));
$result = $database->get_row("SELECT author FROM images WHERE id = ?", [$imageID]);
return stripslashes($result['author']);
}
private function url_exists_by_url(string $url): bool {
private function url_exists_by_url(string $url): bool
{
global $database;
$result = $database->get_one("SELECT COUNT(1) FROM artist_urls WHERE url = ?", array($url));
$result = $database->get_one("SELECT COUNT(1) FROM artist_urls WHERE url = ?", [$url]);
return ($result != 0);
}
private function member_exists_by_name(string $member): bool {
private function member_exists_by_name(string $member): bool
{
global $database;
$result = $database->get_one("SELECT COUNT(1) FROM artist_members WHERE name = ?", array($member));
$result = $database->get_one("SELECT COUNT(1) FROM artist_members WHERE name = ?", [$member]);
return ($result != 0);
}
private function alias_exists_by_name(string $alias): bool {
private function alias_exists_by_name(string $alias): bool
{
global $database;
$result = $database->get_one("SELECT COUNT(1) FROM artist_alias WHERE alias = ?", array($alias));
$result = $database->get_one("SELECT COUNT(1) FROM artist_alias WHERE alias = ?", [$alias]);
return ($result != 0);
}
private function alias_exists(int $artistID, string $alias): bool {
private function alias_exists(int $artistID, string $alias): bool
{
global $database;
$result = $database->get_one(
"SELECT COUNT(1) FROM artist_alias WHERE artist_id = ? AND alias = ?",
array($artistID, $alias)
[$artistID, $alias]
);
return ($result != 0);
}
private function get_artistID_by_url(string $url): int {
private function get_artistID_by_url(string $url): int
{
global $database;
return $database->get_one("SELECT artist_id FROM artist_urls WHERE url = ?", array($url));
return $database->get_one("SELECT artist_id FROM artist_urls WHERE url = ?", [$url]);
}
private function get_artistID_by_memberName(string $member): int {
private function get_artistID_by_memberName(string $member): int
{
global $database;
return $database->get_one("SELECT artist_id FROM artist_members WHERE name = ?", array($member));
return $database->get_one("SELECT artist_id FROM artist_members WHERE name = ?", [$member]);
}
private function get_artistName_by_artistID(int $artistID): string {
private function get_artistName_by_artistID(int $artistID): string
{
global $database;
return $database->get_one("SELECT name FROM artists WHERE id = ?", array($artistID));
return $database->get_one("SELECT name FROM artists WHERE id = ?", [$artistID]);
}
private function get_artistID_by_aliasID(int $aliasID): int {
private function get_artistID_by_aliasID(int $aliasID): int
{
global $database;
return $database->get_one("SELECT artist_id FROM artist_alias WHERE id = ?", array($aliasID));
return $database->get_one("SELECT artist_id FROM artist_alias WHERE id = ?", [$aliasID]);
}
private function get_artistID_by_memberID(int $memberID): int {
private function get_artistID_by_memberID(int $memberID): int
{
global $database;
return $database->get_one("SELECT artist_id FROM artist_members WHERE id = ?", array($memberID));
return $database->get_one("SELECT artist_id FROM artist_members WHERE id = ?", [$memberID]);
}
private function get_artistID_by_urlID(int $urlID): int {
private function get_artistID_by_urlID(int $urlID): int
{
global $database;
return $database->get_one("SELECT artist_id FROM artist_urls WHERE id = ?", array($urlID));
return $database->get_one("SELECT artist_id FROM artist_urls WHERE id = ?", [$urlID]);
}
private function delete_alias(int $aliasID) {
private function delete_alias(int $aliasID)
{
global $database;
$database->execute("DELETE FROM artist_alias WHERE id = ?", array($aliasID));
$database->execute("DELETE FROM artist_alias WHERE id = ?", [$aliasID]);
}
private function delete_url(int $urlID) {
private function delete_url(int $urlID)
{
global $database;
$database->execute("DELETE FROM artist_urls WHERE id = ?", array($urlID));
$database->execute("DELETE FROM artist_urls WHERE id = ?", [$urlID]);
}
private function delete_member(int $memberID) {
private function delete_member(int $memberID)
{
global $database;
$database->execute("DELETE FROM artist_members WHERE id = ?", array($memberID));
$database->execute("DELETE FROM artist_members WHERE id = ?", [$memberID]);
}
private function get_alias_by_id(int $aliasID): array {
private function get_alias_by_id(int $aliasID): array
{
global $database;
$result = $database->get_row("SELECT * FROM artist_alias WHERE id = ?", array($aliasID));
$result = $database->get_row("SELECT * FROM artist_alias WHERE id = ?", [$aliasID]);
$result["alias"] = stripslashes($result["alias"]);
return $result;
}
private function get_url_by_id(int $urlID): array {
private function get_url_by_id(int $urlID): array
{
global $database;
$result = $database->get_row("SELECT * FROM artist_urls WHERE id = ?", array($urlID));
$result = $database->get_row("SELECT * FROM artist_urls WHERE id = ?", [$urlID]);
$result["url"] = stripslashes($result["url"]);
return $result;
}
private function get_member_by_id(int $memberID): array {
private function get_member_by_id(int $memberID): array
{
global $database;
$result = $database->get_row("SELECT * FROM artist_members WHERE id = ?", array($memberID));
$result = $database->get_row("SELECT * FROM artist_members WHERE id = ?", [$memberID]);
$result["name"] = stripslashes($result["name"]);
return $result;
}
private function update_artist() {
private function update_artist()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
'id' => 'int',
'name' => 'string,lower',
'notes' => 'string,trim,nullify',
'aliases' => 'string,trim,nullify',
'aliasesIDs' => 'string,trim,nullify',
'members' => 'string,trim,nullify',
));
]);
$artistID = $inputs['id'];
$name = $inputs['name'];
$notes = $inputs['notes'];
@ -526,66 +550,67 @@ class Artists extends Extension {
$urlsAsString = $inputs["urls"];
$urlsIDsAsString = $inputs["urlsIDs"];
if(strpos($name, " "))
if (strpos($name, " ")) {
return;
}
global $database;
$database->execute(
"UPDATE artists SET name = ?, notes = ?, updated = now(), user_id = ? WHERE id = ? ",
array($name, $notes, $userID, $artistID)
[$name, $notes, $userID, $artistID]
);
// ALIAS MATCHING SECTION
$i = 0;
$aliasesAsArray = is_null($aliasesAsString) ? array() : explode(" ", $aliasesAsString);
$aliasesIDsAsArray = is_null($aliasesIDsAsString) ? array() : explode(" ", $aliasesIDsAsString);
while ($i < count($aliasesAsArray))
{
$aliasesAsArray = is_null($aliasesAsString) ? [] : explode(" ", $aliasesAsString);
$aliasesIDsAsArray = is_null($aliasesIDsAsString) ? [] : explode(" ", $aliasesIDsAsString);
while ($i < count($aliasesAsArray)) {
// if an alias was updated
if ($i < count($aliasesIDsAsArray))
if ($i < count($aliasesIDsAsArray)) {
$this->save_existing_alias($aliasesIDsAsArray[$i], $aliasesAsArray[$i], $userID);
else
} else {
// if we already updated all, save new ones
$this->save_new_alias($artistID, $aliasesAsArray[$i], $userID);
}
$i++;
}
// if we have more ids than alias, then some alias have been deleted -- delete them from db
while ($i < count($aliasesIDsAsArray))
while ($i < count($aliasesIDsAsArray)) {
$this->delete_alias($aliasesIDsAsArray[$i++]);
}
// MEMBERS MATCHING SECTION
$i = 0;
$membersAsArray = is_null($membersAsString) ? array() : explode(" ", $membersAsString);
$membersIDsAsArray = is_null($membersIDsAsString) ? array() : explode(" ", $membersIDsAsString);
while ($i < count($membersAsArray))
{
$membersAsArray = is_null($membersAsString) ? [] : explode(" ", $membersAsString);
$membersIDsAsArray = is_null($membersIDsAsString) ? [] : explode(" ", $membersIDsAsString);
while ($i < count($membersAsArray)) {
// if a member was updated
if ($i < count($membersIDsAsArray))
if ($i < count($membersIDsAsArray)) {
$this->save_existing_member($membersIDsAsArray[$i], $membersAsArray[$i], $userID);
else
} else {
// if we already updated all, save new ones
$this->save_new_member($artistID, $membersAsArray[$i], $userID);
}
$i++;
}
// if we have more ids than members, then some members have been deleted -- delete them from db
while ($i < count($membersIDsAsArray))
while ($i < count($membersIDsAsArray)) {
$this->delete_member($membersIDsAsArray[$i++]);
}
// URLS MATCHING SECTION
$i = 0;
$urlsAsString = str_replace("\r\n", "\n", $urlsAsString);
$urlsAsString = str_replace("\n\r", "\n", $urlsAsString);
$urlsAsArray = is_null($urlsAsString) ? array() : explode("\n", $urlsAsString);
$urlsIDsAsArray = is_null($urlsIDsAsString) ? array() : explode(" ", $urlsIDsAsString);
while ($i < count($urlsAsArray))
{
$urlsAsArray = is_null($urlsAsString) ? [] : explode("\n", $urlsAsString);
$urlsIDsAsArray = is_null($urlsIDsAsString) ? [] : explode(" ", $urlsIDsAsString);
while ($i < count($urlsAsArray)) {
// if an URL was updated
if ($i < count($urlsIDsAsArray)) {
$this->save_existing_url($urlsIDsAsArray[$i], $urlsAsArray[$i], $userID);
}
else {
} else {
$this->save_new_url($artistID, $urlsAsArray[$i], $userID);
}
@ -593,74 +618,83 @@ class Artists extends Extension {
}
// if we have more ids than urls, then some urls have been deleted -- delete them from db
while ($i < count($urlsIDsAsArray))
while ($i < count($urlsIDsAsArray)) {
$this->delete_url($urlsIDsAsArray[$i++]);
}
}
private function update_alias() {
private function update_alias()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"aliasID" => "int",
"alias" => "string,lower",
));
]);
$this->save_existing_alias($inputs['aliasID'], $inputs['alias'], $user->id);
}
private function save_existing_alias(int $aliasID, string $alias, int $userID) {
private function save_existing_alias(int $aliasID, string $alias, int $userID)
{
global $database;
$database->execute(
"UPDATE artist_alias SET alias = ?, updated = now(), user_id = ? WHERE id = ? ",
array($alias, $userID, $aliasID)
[$alias, $userID, $aliasID]
);
}
private function update_url() {
private function update_url()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"urlID" => "int",
"url" => "string",
));
]);
$this->save_existing_url($inputs['urlID'], $inputs['url'], $user->id);
}
private function save_existing_url(int $urlID, string $url, int $userID) {
private function save_existing_url(int $urlID, string $url, int $userID)
{
global $database;
$database->execute(
"UPDATE artist_urls SET url = ?, updated = now(), user_id = ? WHERE id = ?",
array($url, $userID, $urlID)
[$url, $userID, $urlID]
);
}
private function update_member() {
private function update_member()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"memberID" => "int",
"name" => "string,lower",
));
]);
$this->save_existing_member($inputs['memberID'], $inputs['name'], $user->id);
}
private function save_existing_member(int $memberID, string $memberName, int $userID) {
private function save_existing_member(int $memberID, string $memberName, int $userID)
{
global $database;
$database->execute(
"UPDATE artist_members SET name = ?, updated = now(), user_id = ? WHERE id = ?",
array($memberName, $userID, $memberID)
[$memberName, $userID, $memberID]
);
}
private function add_artist(){
private function add_artist()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"name" => "string,lower",
"notes" => "string,optional",
"aliases" => "string,lower,optional",
"members" => "string,lower,optional",
"urls" => "string,optional"
));
]);
$name = $inputs["name"];
if(strpos($name, " "))
if (strpos($name, " ")) {
return -1;
}
$notes = $inputs["notes"];
@ -675,24 +709,27 @@ class Artists extends Extension {
if (!$this->artist_exists($name)) {
$artistID = $this->save_new_artist($name, $notes);
log_info("artists", "Artist {$artistID} created by {$user->name}");
}
else {
} else {
$artistID = $this->get_artist_id($name);
}
if (!is_null($aliases)) {
$aliasArray = explode(" ", $aliases);
foreach($aliasArray as $alias)
if (!$this->alias_exists($artistID, $alias))
foreach ($aliasArray as $alias) {
if (!$this->alias_exists($artistID, $alias)) {
$this->save_new_alias($artistID, $alias, $userID);
}
}
}
if (!is_null($members)) {
$membersArray = explode(" ", $members);
foreach ($membersArray as $member)
if (!$this->member_exists($artistID, $member))
foreach ($membersArray as $member) {
if (!$this->member_exists($artistID, $member)) {
$this->save_new_member($artistID, $member, $userID);
}
}
}
if (!is_null($urls)) {
//delete double "separators"
@ -700,36 +737,41 @@ class Artists extends Extension {
$urls = str_replace("\n\r", "\n", $urls);
$urlsArray = explode("\n", $urls);
foreach ($urlsArray as $url)
if (!$this->url_exists($artistID, $url))
foreach ($urlsArray as $url) {
if (!$this->url_exists($artistID, $url)) {
$this->save_new_url($artistID, $url, $userID);
}
}
}
return $artistID;
}
private function save_new_artist(string $name, string $notes): int {
private function save_new_artist(string $name, string $notes): int
{
global $database, $user;
$database->execute("
INSERT INTO artists (user_id, name, notes, created, updated)
VALUES (?, ?, ?, now(), now())
", array($user->id, $name, $notes));
", [$user->id, $name, $notes]);
return $database->get_last_insert_id('artists_id_seq');
}
private function artist_exists(string $name): bool {
private function artist_exists(string $name): bool
{
global $database;
$result = $database->get_one(
"SELECT COUNT(1) FROM artists WHERE name = ?",
array($name)
[$name]
);
return ($result != 0);
}
private function get_artist(int $artistID): array {
private function get_artist(int $artistID): array
{
global $database;
$result = $database->get_row(
"SELECT * FROM artists WHERE id = ?",
array($artistID)
[$artistID]
);
$result["name"] = stripslashes($result["name"]);
@ -738,11 +780,12 @@ class Artists extends Extension {
return $result;
}
private function get_members(int $artistID): array {
private function get_members(int $artistID): array
{
global $database;
$result = $database->get_all(
"SELECT * FROM artist_members WHERE artist_id = ?",
array($artistID)
[$artistID]
);
$num = count($result);
@ -753,11 +796,12 @@ class Artists extends Extension {
return $result;
}
private function get_urls(int $artistID): array {
private function get_urls(int $artistID): array
{
global $database;
$result = $database->get_all(
"SELECT id, url FROM artist_urls WHERE artist_id = ?",
array($artistID)
[$artistID]
);
$num = count($result);
@ -768,28 +812,31 @@ class Artists extends Extension {
return $result;
}
private function get_artist_id(string $name): int {
private function get_artist_id(string $name): int
{
global $database;
return (int)$database->get_one(
"SELECT id FROM artists WHERE name = ?",
array($name)
[$name]
);
}
private function get_artistID_by_aliasName(string $alias): int {
private function get_artistID_by_aliasName(string $alias): int
{
global $database;
return (int)$database->get_one(
"SELECT artist_id FROM artist_alias WHERE alias = ?",
array($alias)
[$alias]
);
}
private function delete_artist(int $artistID) {
private function delete_artist(int $artistID)
{
global $database;
$database->execute(
"DELETE FROM artists WHERE id = ? ",
array($artistID)
[$artistID]
);
}
@ -850,15 +897,16 @@ class Artists extends Extension {
)
ORDER BY updated DESC
LIMIT ?, ?
", array(
",
[
$pageNumber * $artistsPerPage
, $artistsPerPage
));
]
);
$number_of_listings = count($listing);
for ($i = 0 ; $i < $number_of_listings ; $i++)
{
for ($i = 0 ; $i < $number_of_listings ; $i++) {
$listing[$i]["name"] = stripslashes($listing[$i]["name"]);
$listing[$i]["user_name"] = stripslashes($listing[$i]["user_name"]);
$listing[$i]["artist_name"] = stripslashes($listing[$i]["artist_name"]);
@ -881,91 +929,105 @@ class Artists extends Extension {
/*
* HERE WE ADD AN ALIAS
*/
private function add_urls() {
private function add_urls()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"artistID" => "int",
"urls" => "string",
));
]);
$artistID = $inputs["artistID"];
$urls = explode("\n", $inputs["urls"]);
foreach ($urls as $url)
if (!$this->url_exists($artistID, $url))
foreach ($urls as $url) {
if (!$this->url_exists($artistID, $url)) {
$this->save_new_url($artistID, $url, $user->id);
}
}
}
private function save_new_url(int $artistID, string $url, int $userID) {
private function save_new_url(int $artistID, string $url, int $userID)
{
global $database;
$database->execute(
"INSERT INTO artist_urls (artist_id, created, updated, url, user_id) VALUES (?, now(), now(), ?, ?)",
array($artistID, $url, $userID)
[$artistID, $url, $userID]
);
}
private function add_alias() {
private function add_alias()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"artistID" => "int",
"aliases" => "string,lower",
));
]);
$artistID = $inputs["artistID"];
$aliases = explode(" ", $inputs["aliases"]);
foreach ($aliases as $alias)
if (!$this->alias_exists($artistID, $alias))
foreach ($aliases as $alias) {
if (!$this->alias_exists($artistID, $alias)) {
$this->save_new_alias($artistID, $alias, $user->id);
}
}
}
private function save_new_alias(int $artistID, string $alias, int $userID) {
private function save_new_alias(int $artistID, string $alias, int $userID)
{
global $database;
$database->execute(
"INSERT INTO artist_alias (artist_id, created, updated, alias, user_id) VALUES (?, now(), now(), ?, ?)",
array($artistID, $alias, $userID)
[$artistID, $alias, $userID]
);
}
private function add_members() {
private function add_members()
{
global $user;
$inputs = validate_input(array(
$inputs = validate_input([
"artistID" => "int",
"members" => "string,lower",
));
]);
$artistID = $inputs["artistID"];
$members = explode(" ", $inputs["members"]);
foreach ($members as $member)
if (!$this->member_exists($artistID, $member))
foreach ($members as $member) {
if (!$this->member_exists($artistID, $member)) {
$this->save_new_member($artistID, $member, $user->id);
}
}
}
private function save_new_member(int $artistID, string $member, int $userID) {
private function save_new_member(int $artistID, string $member, int $userID)
{
global $database;
$database->execute(
"INSERT INTO artist_members (artist_id, name, created, updated, user_id) VALUES (?, ?, now(), now(), ?)",
array($artistID, $member, $userID)
[$artistID, $member, $userID]
);
}
private function member_exists(int $artistID, string $member): bool {
private function member_exists(int $artistID, string $member): bool
{
global $database;
$result = $database->get_one(
"SELECT COUNT(1) FROM artist_members WHERE artist_id = ? AND name = ?",
array($artistID, $member)
[$artistID, $member]
);
return ($result != 0);
}
private function url_exists(int $artistID, string $url): bool {
private function url_exists(int $artistID, string $url): bool
{
global $database;
$result = $database->get_one(
"SELECT COUNT(1) FROM artist_urls WHERE artist_id = ? AND url = ?",
array($artistID, $url)
[$artistID, $url]
);
return ($result != 0);
}
@ -973,7 +1035,8 @@ class Artists extends Extension {
/**
* HERE WE GET THE INFO OF THE ALIAS
*/
private function get_alias(int $artistID): array {
private function get_alias(int $artistID): array
{
global $database;
$result = $database->get_all("
@ -981,7 +1044,7 @@ class Artists extends Extension {
FROM artist_alias
WHERE artist_id = ?
ORDER BY alias ASC
", array($artistID));
", [$artistID]);
for ($i = 0 ; $i < count($result) ; $i++) {
$result[$i]["alias_name"] = stripslashes($result[$i]["alias_name"]);

View File

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

View File

@ -1,6 +1,8 @@
<?php
class ArtistsTheme extends Themelet {
public function get_author_editor_html(string $author): string {
class ArtistsTheme extends Themelet
{
public function get_author_editor_html(string $author): string
{
$h_author = html_escape($author);
return "
<tr>
@ -13,7 +15,8 @@ class ArtistsTheme extends Themelet {
";
}
public function sidebar_options(string $mode, ?int $artistID=NULL, $is_admin=FALSE): bool {
public function sidebar_options(string $mode, ?int $artistID=null, $is_admin=false): bool
{
global $page, $user;
$html = "";
@ -64,10 +67,13 @@ class ArtistsTheme extends Themelet {
</form>";
}
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;
$artistName = $artist['name'];
@ -126,7 +132,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Edit artist", $html, "main", 10));
}
public function new_artist_composer() {
public function new_artist_composer()
{
global $page, $user;
$html = "<form action=".make_link("artist/create")." method='POST'>
@ -146,7 +153,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artists", $html, "main", 10));
}
public function list_artists($artists, $pageNumber, $totalPages) {
public function list_artists($artists, $pageNumber, $totalPages)
{
global $user, $page;
$html = "<table id='poolsList' class='zebra'>".
@ -156,31 +164,34 @@ class ArtistsTheme extends Themelet {
"<th>Last updater</th>".
"<th>Posts</th>";
if(!$user->is_anonymous()) $html .= "<th colspan='2'>Action</th>"; // space for edit link
if (!$user->is_anonymous()) {
$html .= "<th colspan='2'>Action</th>";
} // space for edit link
$html .= "</tr></thead>";
$deletionLinkActionArray = array(
$deletionLinkActionArray = [
'artist' => 'artist/nuke/',
'alias' => 'artist/alias/delete/',
'member' => 'artist/member/delete/',
);
];
$editionLinkActionArray = array(
$editionLinkActionArray = [
'artist' => 'artist/edit/',
'alias' => 'artist/alias/edit/',
'member' => 'artist/member/edit/',
);
];
$typeTextArray = array(
$typeTextArray = [
'artist' => 'Artist',
'alias' => 'Alias',
'member' => 'Member',
);
];
foreach ($artists as $artist) {
if ($artist['type'] != 'artist')
if ($artist['type'] != 'artist') {
$artist['name'] = str_replace("_", " ", $artist['name']);
}
$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>";
@ -202,8 +213,12 @@ class ArtistsTheme extends Themelet {
"<td>".$user_link."</td>".
"<td>".$artist['posts']."</td>";
if(!$user->is_anonymous()) $html .= "<td>".$edit_link."</td>";
if($user->is_admin()) $html .= "<td>".$del_link."</td>";
if (!$user->is_anonymous()) {
$html .= "<td>".$edit_link."</td>";
}
if ($user->is_admin()) {
$html .= "<td>".$del_link."</td>";
}
$html .= "</tr>";
}
@ -217,7 +232,8 @@ class ArtistsTheme extends Themelet {
$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;
$html = '
@ -235,7 +251,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artist Aliases", $html, "main", 20));
}
public function show_new_member_composer($artistID) {
public function show_new_member_composer($artistID)
{
global $user;
$html = '
@ -253,7 +270,8 @@ class ArtistsTheme extends Themelet {
$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;
$html = '
@ -271,7 +289,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artist URLs", $html, "main", 40));
}
public function show_alias_editor($alias) {
public function show_alias_editor($alias)
{
global $user;
$html = '
@ -288,7 +307,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Edit Alias", $html, "main", 10));
}
public function show_url_editor($url) {
public function show_url_editor($url)
{
global $user;
$html = '
@ -305,7 +325,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Edit URL", $html, "main", 10));
}
public function show_member_editor($member) {
public function show_member_editor($member)
{
global $user;
$html = '
@ -322,7 +343,8 @@ class ArtistsTheme extends Themelet {
$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;
$artist_link = "<a href='".make_link("post/list/".$artist['name']."/1")."'>".str_replace("_", " ", $artist['name'])."</a>";
@ -333,8 +355,12 @@ class ArtistsTheme extends Themelet {
<th></th>
<th></th>";
if ($userIsLogged) $html .= "<th></th>";
if ($userIsAdmin) $html .= "<th></th>";
if ($userIsLogged) {
$html .= "<th></th>";
}
if ($userIsAdmin) {
$html .= "<th></th>";
}
$html .= " <tr>
</thead>
@ -342,8 +368,12 @@ class ArtistsTheme extends Themelet {
<tr>
<td class='left'>Name:</td>
<td class='left'>".$artist_link."</td>";
if ($userIsLogged) $html .= "<td></td>";
if ($userIsAdmin) $html .= "<td></td>";
if ($userIsLogged) {
$html .= "<td></td>";
}
if ($userIsAdmin) {
$html .= "<td></td>";
}
$html .= "</tr>";
$html .= $this->render_aliases($aliases, $userIsLogged, $userIsAdmin);
@ -353,8 +383,12 @@ class ArtistsTheme extends Themelet {
$html .= "<tr>
<td class='left'>Notes:</td>
<td class='left'>".$artist["notes"]."</td>";
if ($userIsLogged) $html .= "<td></td>";
if ($userIsAdmin) $html .= "<td></td>";
if ($userIsLogged) {
$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?
//same question for deletion
$html .= "</tr>
@ -377,7 +411,8 @@ class ArtistsTheme extends Themelet {
$page->add_block(new Block("Artist Images", $artist_images, "main", 20));
}
private function render_aliases(array $aliases, bool $userIsLogged, bool $userIsAdmin): string {
private function render_aliases(array $aliases, bool $userIsLogged, bool $userIsAdmin): string
{
$html = "";
if (count($aliases) > 0) {
$aliasViewLink = str_replace("_", " ", $aliases[0]['alias_name']); // no link anymore
@ -388,11 +423,13 @@ class ArtistsTheme extends Themelet {
<td class='left'>Aliases:</td>
<td class='left'>" . $aliasViewLink . "</td>";
if ($userIsLogged)
if ($userIsLogged) {
$html .= "<td class='left'>" . $aliasEditLink . "</td>";
}
if ($userIsAdmin)
if ($userIsAdmin) {
$html .= "<td class='left'>" . $aliasDeleteLink . "</td>";
}
$html .= "</tr>";
@ -405,10 +442,12 @@ class ArtistsTheme extends Themelet {
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>" . $aliasViewLink . "</td>";
if ($userIsLogged)
if ($userIsLogged) {
$html .= "<td class='left'>" . $aliasEditLink . "</td>";
if ($userIsAdmin)
}
if ($userIsAdmin) {
$html .= "<td class='left'>" . $aliasDeleteLink . "</td>";
}
$html .= "</tr>";
}
@ -417,7 +456,8 @@ class ArtistsTheme extends Themelet {
return $html;
}
private function render_members(array $members, bool $userIsLogged, bool $userIsAdmin): string {
private function render_members(array $members, bool $userIsLogged, bool $userIsAdmin): string
{
$html = "";
if (count($members) > 0) {
$memberViewLink = str_replace("_", " ", $members[0]['name']); // no link anymore
@ -427,10 +467,12 @@ class ArtistsTheme extends Themelet {
$html .= "<tr>
<td class='left'>Members:</td>
<td class='left'>" . $memberViewLink . "</td>";
if ($userIsLogged)
if ($userIsLogged) {
$html .= "<td class='left'>" . $memberEditLink . "</td>";
if ($userIsAdmin)
}
if ($userIsAdmin) {
$html .= "<td class='left'>" . $memberDeleteLink . "</td>";
}
$html .= "</tr>";
@ -443,10 +485,12 @@ class ArtistsTheme extends Themelet {
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>" . $memberViewLink . "</td>";
if ($userIsLogged)
if ($userIsLogged) {
$html .= "<td class='left'>" . $memberEditLink . "</td>";
if ($userIsAdmin)
}
if ($userIsAdmin) {
$html .= "<td class='left'>" . $memberDeleteLink . "</td>";
}
$html .= "</tr>";
}
@ -455,7 +499,8 @@ class ArtistsTheme extends Themelet {
return $html;
}
private function render_urls(array $urls, bool $userIsLogged, bool $userIsAdmin): string {
private function render_urls(array $urls, bool $userIsLogged, bool $userIsAdmin): string
{
$html = "";
if (count($urls) > 0) {
$urlViewLink = "<a href='" . str_replace("_", " ", $urls[0]['url']) . "' target='_blank'>" . str_replace("_", " ", $urls[0]['url']) . "</a>";
@ -466,11 +511,13 @@ class ArtistsTheme extends Themelet {
<td class='left'>URLs:</td>
<td class='left'>" . $urlViewLink . "</td>";
if ($userIsLogged)
if ($userIsLogged) {
$html .= "<td class='left'>" . $urlEditLink . "</td>";
}
if ($userIsAdmin)
if ($userIsAdmin) {
$html .= "<td class='left'>" . $urlDeleteLink . "</td>";
}
$html .= "</tr>";
@ -483,11 +530,13 @@ class ArtistsTheme extends Themelet {
$html .= "<tr>
<td class='left'>&nbsp;</td>
<td class='left'>" . $urlViewLink . "</td>";
if ($userIsLogged)
if ($userIsLogged) {
$html .= "<td class='left'>" . $urlEditLink . "</td>";
}
if ($userIsAdmin)
if ($userIsAdmin) {
$html .= "<td class='left'>" . $urlDeleteLink . "</td>";
}
$html .= "</tr>";
}
@ -496,6 +545,4 @@ class ArtistsTheme extends Themelet {
}
return $html;
}
}

View File

@ -5,14 +5,21 @@
* Description: Adds autocomplete to search & tagging.
*/
class AutoComplete extends Extension {
public function get_priority(): int {return 30;} // before Home
class AutoComplete extends Extension
{
public function get_priority(): int
{
return 30;
} // before Home
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $page, $database;
if ($event->page_matches("api/internal/autocomplete")) {
if(!isset($_GET["s"])) return;
if (!isset($_GET["s"])) {
return;
}
$page->set_mode("data");
$page->set_type("application/json");
@ -31,7 +38,7 @@ class AutoComplete extends Extension {
//$limit = 0;
$cache_key = "autocomplete-$s";
$limitSQL = "";
$SQLarr = array("search"=>"$s%");
$SQLarr = ["search"=>"$s%"];
if (isset($_GET["limit"]) && $_GET["limit"] !== 0) {
$limitSQL = "LIMIT :limit";
$SQLarr['limit'] = $_GET["limit"];
@ -40,13 +47,15 @@ class AutoComplete extends Extension {
$res = $database->cache->get($cache_key);
if (!$res) {
$res = $database->get_pairs($database->scoreql_to_sql("
$res = $database->get_pairs(
$database->scoreql_to_sql("
SELECT tag, count
FROM tags
WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:search)
AND count > 0
ORDER BY count DESC
$limitSQL"), $SQLarr
$limitSQL"),
$SQLarr
);
$database->cache->set($cache_key, $res, 600);
}

View File

@ -1,7 +1,9 @@
<?php
class AutoCompleteTheme extends Themelet {
public function build_autocomplete(Page $page) {
class AutoCompleteTheme extends Themelet
{
public function build_autocomplete(Page $page)
{
$base_href = get_base_href();
// TODO: AJAX test and fallback.

View File

@ -20,8 +20,10 @@
* from Essex"
*/
class BanWords extends Extension {
public function onInitExt(InitExtEvent $event) {
class BanWords extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config;
$config->set_default_string('banned_words', "
a href=
@ -53,26 +55,30 @@ xanax
");
}
public function onCommentPosting(CommentPostingEvent $event) {
public function onCommentPosting(CommentPostingEvent $event)
{
global $user;
if (!$user->can("bypass_comment_checks")) {
$this->test_text($event->comment, new CommentPostingException("Comment contains banned terms"));
}
}
public function onSourceSet(SourceSetEvent $event) {
public function onSourceSet(SourceSetEvent $event)
{
$this->test_text($event->source, new SCoreException("Source contains banned terms"));
}
public function onTagSet(TagSetEvent $event) {
public function onTagSet(TagSetEvent $event)
{
$this->test_text(Tag::implode($event->tags), new SCoreException("Tags contain banned terms"));
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Banned Phrases");
$sb->add_label("One per line, lines that start with slashes are treated as regex<br/>");
$sb->add_longtext_option("banned_words");
$failed = array();
$failed = [];
foreach ($this->get_words() as $word) {
if ($word[0] == '/') {
if (preg_match($word, "") === false) {
@ -89,7 +95,8 @@ xanax
/**
* Throws if the comment contains banned words.
*/
private function test_text(string $comment, Exception $ex): void {
private function test_text(string $comment, Exception $ex): void
{
$comment = strtolower($comment);
foreach ($this->get_words() as $word) {
@ -98,8 +105,7 @@ xanax
if (preg_match($word, $comment) === 1) {
throw $ex;
}
}
else {
} else {
// other words are literal
if (strpos($comment, $word) !== false) {
throw $ex;
@ -108,9 +114,10 @@ xanax
}
}
private function get_words(): array {
private function get_words(): array
{
global $config;
$words = array();
$words = [];
$banned = $config->get_string("banned_words");
foreach (explode("\n", $banned) as $word) {
@ -125,6 +132,8 @@ xanax
return $words;
}
public function get_priority(): int {return 30;}
public function get_priority(): int
{
return 30;
}
}

View File

@ -1,17 +1,19 @@
<?php
class BanWordsTest extends ShimmiePHPUnitTestCase {
public function check_blocked($image_id, $words) {
class BanWordsTest extends ShimmiePHPUnitTestCase
{
public function check_blocked($image_id, $words)
{
global $user;
try {
send_event(new CommentPostingEvent($image_id, $user, $words));
$this->fail("Exception not thrown");
}
catch(CommentPostingException $e) {
} catch (CommentPostingException $e) {
$this->assertEquals($e->getMessage(), "Comment contains banned terms");
}
}
public function testWordBan() {
public function testWordBan()
{
global $config;
$config->set_string("banned_words", "viagra\nporn\n\n/http:.*\.cn\//");
@ -30,4 +32,3 @@ class BanWordsTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text('http://something.cn/');
}
}

View File

@ -25,12 +25,14 @@
* </ul>
*/
class BBCode extends FormatterExtension {
public function format(string $text): string {
class BBCode extends FormatterExtension
{
public function format(string $text): string
{
$text = $this->extract_code($text);
foreach(array(
foreach ([
"b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4",
) as $el) {
] as $el) {
$text = preg_replace("!\[$el\](.*?)\[/$el\]!s", "<$el>$1</$el>", $text);
}
$text = preg_replace('!^&gt;&gt;([^\d].+)!', '<blockquote><small>$1</small></blockquote>', $text);
@ -48,12 +50,15 @@ class BBCode extends FormatterExtension {
$text = str_replace("\n", "\n<br>", $text);
$text = preg_replace("/\[quote\](.*?)\[\/quote\]/s", "<blockquote><small>\\1</small></blockquote>", $text);
$text = preg_replace("/\[quote=(.*?)\](.*?)\[\/quote\]/s", "<blockquote><em>\\1 said:</em><br><small>\\2</small></blockquote>", $text);
while(preg_match("/\[list\](.*?)\[\/list\]/s", $text))
while (preg_match("/\[list\](.*?)\[\/list\]/s", $text)) {
$text = preg_replace("/\[list\](.*?)\[\/list\]/s", "<ul>\\1</ul>", $text);
while(preg_match("/\[ul\](.*?)\[\/ul\]/s", $text))
}
while (preg_match("/\[ul\](.*?)\[\/ul\]/s", $text)) {
$text = preg_replace("/\[ul\](.*?)\[\/ul\]/s", "<ul>\\1</ul>", $text);
while(preg_match("/\[ol\](.*?)\[\/ol\]/s", $text))
}
while (preg_match("/\[ol\](.*?)\[\/ol\]/s", $text)) {
$text = preg_replace("/\[ol\](.*?)\[\/ol\]/s", "<ol>\\1</ol>", $text);
}
$text = preg_replace("/\[li\](.*?)\[\/li\]/s", "<li>\\1</li>", $text);
$text = preg_replace("#\[\*\]#s", "<li>", $text);
$text = preg_replace("#<br><(li|ul|ol|/ul|/ol)>#s", "<\\1>", $text);
@ -63,11 +68,12 @@ class BBCode extends FormatterExtension {
return $text;
}
public function strip(string $text): string {
foreach(array(
public function strip(string $text): string
{
foreach ([
"b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4",
"code", "url", "email", "li",
) as $el) {
] as $el) {
$text = preg_replace("!\[$el\](.*?)\[/$el\]!s", '$1', $text);
}
$text = preg_replace("!\[anchor=(.*?)\](.*?)\[/anchor\]!s", '$2', $text);
@ -83,24 +89,33 @@ class BBCode extends FormatterExtension {
return $text;
}
private function filter_spoiler(string $text): string {
private function filter_spoiler(string $text): string
{
return str_replace(
array("[spoiler]","[/spoiler]"),
array("<span style=\"background-color:#000; color:#000;\">","</span>"),
$text);
["[spoiler]","[/spoiler]"],
["<span style=\"background-color:#000; color:#000;\">","</span>"],
$text
);
}
private function strip_spoiler(string $text): string {
private function strip_spoiler(string $text): string
{
$l1 = strlen("[spoiler]");
$l2 = strlen("[/spoiler]");
while (true) {
$start = strpos($text, "[spoiler]");
if($start === false) break;
if ($start === false) {
break;
}
$end = strpos($text, "[/spoiler]");
if($end === false) break;
if ($end === false) {
break;
}
if($end < $start) break;
if ($end < $start) {
break;
}
$beginning = substr($text, 0, $start);
$middle = str_rot13(substr($text, $start+$l1, ($end-$start-$l1)));
@ -111,7 +126,8 @@ class BBCode extends FormatterExtension {
return $text;
}
private function extract_code(string $text): string {
private function extract_code(string $text): string
{
# at the end of this function, the only code! blocks should be
# the ones we've added -- others may contain malicious content,
# which would only appear after decoding
@ -122,12 +138,18 @@ class BBCode extends FormatterExtension {
$l2 = strlen("[/code]");
while (true) {
$start = strpos($text, "[code]");
if($start === false) break;
if ($start === false) {
break;
}
$end = strpos($text, "[/code]", $start);
if($end === false) break;
if ($end === false) {
break;
}
if($end < $start) break;
if ($end < $start) {
break;
}
$beginning = substr($text, 0, $start);
$middle = base64_encode(substr($text, $start+$l1, ($end-$start-$l1)));
@ -138,15 +160,20 @@ class BBCode extends FormatterExtension {
return $text;
}
private function insert_code(string $text): string {
private function insert_code(string $text): string
{
$l1 = strlen("[code!]");
$l2 = strlen("[/code!]");
while (true) {
$start = strpos($text, "[code!]");
if($start === false) break;
if ($start === false) {
break;
}
$end = strpos($text, "[/code!]");
if($end === false) break;
if ($end === false) {
break;
}
$beginning = substr($text, 0, $start);
$middle = base64_decode(substr($text, $start+$l1, ($end-$start-$l1)));

View File

@ -1,85 +1,110 @@
<?php
class BBCodeTest extends ShimmiePHPUnitTestCase {
public function testBasics() {
class BBCodeTest extends ShimmiePHPUnitTestCase
{
public function testBasics()
{
$this->assertEquals(
$this->filter("[b]bold[/b][i]italic[/i]"),
"<b>bold</b><i>italic</i>");
"<b>bold</b><i>italic</i>"
);
}
public function testStacking() {
public function testStacking()
{
$this->assertEquals(
$this->filter("[b]B[/b][i]I[/i][b]B[/b]"),
"<b>B</b><i>I</i><b>B</b>");
"<b>B</b><i>I</i><b>B</b>"
);
$this->assertEquals(
$this->filter("[b]bold[i]bolditalic[/i]bold[/b]"),
"<b>bold<i>bolditalic</i>bold</b>");
"<b>bold<i>bolditalic</i>bold</b>"
);
}
public function testFailure() {
public function testFailure()
{
$this->assertEquals(
$this->filter("[b]bold[i]italic"),
"[b]bold[i]italic");
"[b]bold[i]italic"
);
}
public function testCode() {
public function testCode()
{
$this->assertEquals(
$this->filter("[code][b]bold[/b][/code]"),
"<pre>[b]bold[/b]</pre>");
"<pre>[b]bold[/b]</pre>"
);
}
public function testNestedList() {
public function testNestedList()
{
$this->assertEquals(
$this->filter("[list][*]a[list][*]a[*]b[/list][*]b[/list]"),
"<ul><li>a<ul><li>a<li>b</ul><li>b</ul>");
"<ul><li>a<ul><li>a<li>b</ul><li>b</ul>"
);
$this->assertEquals(
$this->filter("[ul][*]a[ol][*]a[*]b[/ol][*]b[/ul]"),
"<ul><li>a<ol><li>a<li>b</ol><li>b</ul>");
"<ul><li>a<ol><li>a<li>b</ol><li>b</ul>"
);
}
public function testSpoiler() {
public function testSpoiler()
{
$this->assertEquals(
$this->filter("[spoiler]ShishNet[/spoiler]"),
"<span style=\"background-color:#000; color:#000;\">ShishNet</span>");
"<span style=\"background-color:#000; color:#000;\">ShishNet</span>"
);
$this->assertEquals(
$this->strip("[spoiler]ShishNet[/spoiler]"),
"FuvfuArg");
"FuvfuArg"
);
#$this->assertEquals(
# $this->filter("[spoiler]ShishNet"),
# "[spoiler]ShishNet");
}
public function testURL() {
public function testURL()
{
$this->assertEquals(
$this->filter("[url]http://shishnet.org[/url]"),
"<a href=\"http://shishnet.org\">http://shishnet.org</a>");
"<a href=\"http://shishnet.org\">http://shishnet.org</a>"
);
$this->assertEquals(
$this->filter("[url=http://shishnet.org]ShishNet[/url]"),
"<a href=\"http://shishnet.org\">ShishNet</a>");
"<a href=\"http://shishnet.org\">ShishNet</a>"
);
$this->assertEquals(
$this->filter("[url=javascript:alert(\"owned\")]click to fail[/url]"),
"[url=javascript:alert(\"owned\")]click to fail[/url]");
"[url=javascript:alert(\"owned\")]click to fail[/url]"
);
}
public function testEmailURL() {
public function testEmailURL()
{
$this->assertEquals(
$this->filter("[email]spam@shishnet.org[/email]"),
"<a href=\"mailto:spam@shishnet.org\">spam@shishnet.org</a>");
"<a href=\"mailto:spam@shishnet.org\">spam@shishnet.org</a>"
);
}
public function testAnchor() {
public function testAnchor()
{
$this->assertEquals(
$this->filter("[anchor=rules]Rules[/anchor]"),
'<span class="anchor">Rules <a class="alink" href="#bb-rules" name="bb-rules" title="link to this anchor"> ¶ </a></span>');
'<span class="anchor">Rules <a class="alink" href="#bb-rules" name="bb-rules" title="link to this anchor"> ¶ </a></span>'
);
}
private function filter($in) {
private function filter($in)
{
$bb = new BBCode();
return $bb->format($in);
}
private function strip($in) {
private function strip($in)
{
$bb = new BBCode();
return $bb->strip($in);
}
}

View File

@ -7,8 +7,10 @@
* Description: Add HTML to some space (News, Ads, etc)
*/
class Blocks extends Extension {
public function onInitExt(InitExtEvent $event) {
class Blocks extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config, $database;
if ($config->get_int("ext_blocks_version") < 1) {
$database->create_table("blocks", "
@ -19,19 +21,21 @@ class Blocks extends Extension {
priority INTEGER NOT NULL,
content TEXT NOT NULL
");
$database->execute("CREATE INDEX blocks_pages_idx ON blocks(pages)", array());
$database->execute("CREATE INDEX blocks_pages_idx ON blocks(pages)", []);
$config->set_int("ext_blocks_version", 1);
}
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
if ($user->can("manage_blocks")) {
$event->add_link("Blocks Editor", make_link("blocks/list"));
}
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $database, $page, $user;
$blocks = $database->cache->get("blocks");
@ -54,7 +58,7 @@ class Blocks extends Extension {
$database->execute("
INSERT INTO blocks (pages, title, area, priority, content)
VALUES (?, ?, ?, ?, ?)
", array($_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content']));
", [$_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content']]);
log_info("blocks", "Added Block #".($database->get_last_insert_id('blocks_id_seq'))." (".$_POST['title'].")");
$database->cache->delete("blocks");
$page->set_mode("redirect");
@ -67,25 +71,22 @@ class Blocks extends Extension {
$database->execute("
DELETE FROM blocks
WHERE id=?
", array($_POST['id']));
", [$_POST['id']]);
log_info("blocks", "Deleted Block #".$_POST['id']);
}
else {
} else {
$database->execute("
UPDATE blocks SET pages=?, title=?, area=?, priority=?, content=?
WHERE id=?
", array($_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content'], $_POST['id']));
", [$_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content'], $_POST['id']]);
log_info("blocks", "Updated Block #".$_POST['id']." (".$_POST['title'].")");
}
$database->cache->delete("blocks");
$page->set_mode("redirect");
$page->set_redirect(make_link("blocks/list"));
}
}
else if($event->get_arg(0) == "list") {
} elseif ($event->get_arg(0) == "list") {
$this->theme->display_blocks($database->get_all("SELECT * FROM blocks ORDER BY area, priority"));
}
}
}
}

View File

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

View File

@ -1,6 +1,8 @@
<?php
class BlocksTheme extends Themelet {
public function display_blocks($blocks) {
class BlocksTheme extends Themelet
{
public function display_blocks($blocks)
{
global $page;
$html = "<table class='form' style='width: 100%;'>";
@ -43,4 +45,3 @@ class BlocksTheme extends Themelet {
$page->add_block(new Block("Block Editor", $html));
}
}

View File

@ -8,8 +8,10 @@
*
* Development TODO at http://github.com/zshall/shimmie2/issues
*/
class Blotter extends Extension {
public function onInitExt(InitExtEvent $event) {
class Blotter extends Extension
{
public function onInitExt(InitExtEvent $event)
{
/**
* I love re-using this installer don't I...
*/
@ -32,8 +34,10 @@ class Blotter extends Extension {
important SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N
");
// Insert sample data:
$database->execute("INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)",
array("Installed the blotter extension!", "Y"));
$database->execute(
"INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)",
["Installed the blotter extension!", "Y"]
);
log_info("blotter", "Installed tables for blotter extension.");
$config->set_int("blotter_version", 1);
}
@ -43,22 +47,25 @@ class Blotter extends Extension {
$config->set_default_string("blotter_position", "subheading");
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Blotter");
$sb->add_int_option("blotter_recent", "<br />Number of recent entries to display: ");
$sb->add_text_option("blotter_color", "<br />Color of important updates: (ABCDEF format) ");
$sb->add_choice_option("blotter_position", array("Top of page" => "subheading", "In navigation bar" => "left"), "<br>Position: ");
$sb->add_choice_option("blotter_position", ["Top of page" => "subheading", "In navigation bar" => "left"], "<br>Position: ");
$event->panel->add_block($sb);
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
if ($user->is_admin()) {
$event->add_link("Blotter Editor", make_link("blotter/editor"));
}
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $page, $database, $user;
if ($event->page_matches("blotter")) {
switch ($event->get_arg(0)) {
@ -81,11 +88,19 @@ class Blotter extends Extension {
$this->theme->display_permission_denied();
} else {
$entry_text = $_POST['entry_text'];
if($entry_text == "") { die("No entry message!"); }
if(isset($_POST['important'])) { $important = 'Y'; } else { $important = 'N'; }
if ($entry_text == "") {
die("No entry message!");
}
if (isset($_POST['important'])) {
$important = 'Y';
} else {
$important = 'N';
}
// Now insert into db:
$database->execute("INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)",
array($entry_text, $important));
$database->execute(
"INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)",
[$entry_text, $important]
);
log_info("blotter", "Added Message: $entry_text");
$page->set_mode("redirect");
$page->set_redirect(make_link("blotter/editor"));
@ -99,8 +114,10 @@ class Blotter extends Extension {
$this->theme->display_permission_denied();
} else {
$id = int_escape($_POST['id']);
if(!isset($id)) { die("No ID!"); }
$database->Execute("DELETE FROM blotter WHERE id=:id", array("id"=>$id));
if (!isset($id)) {
die("No ID!");
}
$database->Execute("DELETE FROM blotter WHERE id=:id", ["id"=>$id]);
log_info("blotter", "Removed Entry #$id");
$page->set_mode("redirect");
$page->set_redirect(make_link("blotter/editor"));
@ -121,7 +138,8 @@ class Blotter extends Extension {
$this->display_blotter();
}
private function display_blotter() {
private function display_blotter()
{
global $database, $config;
$limit = $config->get_int("blotter_recent", 5);
$sql = 'SELECT * FROM blotter ORDER BY id DESC LIMIT '.intval($limit);
@ -129,4 +147,3 @@ class Blotter extends Extension {
$this->theme->display_blotter($entries);
}
}

View File

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

View File

@ -1,6 +1,8 @@
<?php
class BlotterTheme extends Themelet {
public function display_editor($entries) {
class BlotterTheme extends Themelet
{
public function display_editor($entries)
{
global $page;
$html = $this->get_html_for_blotter_editor($entries);
$page->set_title("Blotter Editor");
@ -9,7 +11,8 @@ class BlotterTheme extends Themelet {
$page->add_block(new Block("Navigation", "<a href='".make_link()."'>Index</a>", "left", 0));
}
public function display_blotter_page($entries) {
public function display_blotter_page($entries)
{
global $page;
$html = $this->get_html_for_blotter_page($entries);
$page->set_title("Blotter");
@ -17,14 +20,16 @@ class BlotterTheme extends Themelet {
$page->add_block(new Block("Blotter Entries", $html, "main", 10));
}
public function display_blotter($entries) {
public function display_blotter($entries)
{
global $page, $config;
$html = $this->get_html_for_blotter($entries);
$position = $config->get_string("blotter_position", "subheading");
$page->add_block(new Block(null, $html, $position, 20));
}
private function get_html_for_blotter_editor($entries) {
private function get_html_for_blotter_editor($entries)
{
global $user;
/**
@ -59,7 +64,11 @@ class BlotterTheme extends Themelet {
$id = $entries[$i]['id'];
$entry_date = $entries[$i]['entry_date'];
$entry_text = $entries[$i]['entry_text'];
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)
$table_rows .=
@ -90,7 +99,8 @@ class BlotterTheme extends Themelet {
return $html;
}
private function get_html_for_blotter_page($entries) {
private function get_html_for_blotter_page($entries)
{
/**
* This one displays a list of all blotter entries.
*/
@ -120,7 +130,8 @@ class BlotterTheme extends Themelet {
return $html;
}
private function get_html_for_blotter($entries) {
private function get_html_for_blotter($entries)
{
global $config;
$i_color = $config->get_string("blotter_color", "#FF0000");
$position = $config->get_string("blotter_position", "subheading");
@ -155,8 +166,7 @@ class BlotterTheme extends Themelet {
if (count($entries) === 0) {
$out_text = "No blotter entries yet.";
$in_text = "Empty.";
}
else {
} else {
$clean_date = date("m/d/y", strtotime($entries[0]['entry_date']));
$out_text = "Blotter updated: {$clean_date}";
$in_text = "<ul>$entries_list</ul>";

View File

@ -13,13 +13,16 @@
* engine" notification they have
*/
class BrowserSearch extends Extension {
public function onInitExt(InitExtEvent $event) {
class BrowserSearch extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config;
$config->set_default_string("search_suggestions_results_order", 'a');
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $config, $database, $page;
// Add in header code to let the browser know that the search plugin exists
@ -54,9 +57,7 @@ class BrowserSearch extends Extension {
$page->set_mode("data");
$page->set_type("text/xml");
$page->set_data($xml);
}
else if(
} elseif (
$event->page_matches("browser_search") &&
!$config->get_bool("disable_search_suggestions")
) {
@ -65,9 +66,9 @@ class BrowserSearch extends Extension {
// Now to get DB results
if ($config->get_string("search_suggestions_results_order") == "a") {
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30",array($tag_search."%"));
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30", [$tag_search."%"]);
} else {
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30",array($tag_search."%"));
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30", [$tag_search."%"]);
}
@ -75,7 +76,7 @@ class BrowserSearch extends Extension {
// ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]]
$json_tag_list = "";
$tags_array = array();
$tags_array = [];
foreach ($tags as $tag) {
array_push($tags_array, $tag['tag']);
}
@ -89,8 +90,9 @@ class BrowserSearch extends Extension {
}
}
public function onSetupBuilding(SetupBuildingEvent $event) {
$sort_by = array();
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sort_by = [];
$sort_by['Alphabetical'] = 'a';
$sort_by['Tag Count'] = 't';
@ -101,4 +103,3 @@ class BrowserSearch extends Extension {
$event->panel->add_block($sb);
}
}

View File

@ -1,8 +1,9 @@
<?php
class BrowserSearchTest extends ShimmiePHPUnitTestCase {
public function testBasic() {
class BrowserSearchTest extends ShimmiePHPUnitTestCase
{
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/test");
}
}

View File

@ -15,17 +15,22 @@
* <p><b>Note:</b> requires the "admin" extension to be enabled
*/
class BulkAddEvent extends Event {
public $dir, $results;
class BulkAddEvent extends Event
{
public $dir;
public $results;
public function __construct(string $dir) {
public function __construct(string $dir)
{
$this->dir = $dir;
$this->results = array();
$this->results = [];
}
}
class BulkAdd extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class BulkAdd extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("bulk_add")) {
if ($user->is_admin() && $user->check_auth_token() && isset($_POST['dir'])) {
@ -44,7 +49,8 @@ class BulkAdd extends Extension {
}
}
public function onCommand(CommandEvent $event) {
public function onCommand(CommandEvent $event)
{
if ($event->cmd == "help") {
print "\tbulk-add [directory]\n";
print "\t\tImport this directory\n\n";
@ -58,15 +64,16 @@ class BulkAdd extends Extension {
}
}
public function onAdminBuilding(AdminBuildingEvent $event) {
public function onAdminBuilding(AdminBuildingEvent $event)
{
$this->theme->display_admin_block();
}
public function onBulkAdd(BulkAddEvent $event) {
public function onBulkAdd(BulkAddEvent $event)
{
if (is_dir($event->dir) && is_readable($event->dir)) {
$event->results = add_dir($event->dir);
}
else {
} else {
$h_dir = html_escape($event->dir);
$event->results[] = "Error, $h_dir is not a readable directory";
}

View File

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

View File

@ -1,12 +1,14 @@
<?php
class BulkAddTheme extends Themelet {
private $messages = array();
class BulkAddTheme extends Themelet
{
private $messages = [];
/*
* Show a standard page for results to be put into
*/
public function display_upload_results(Page $page) {
public function display_upload_results(Page $page)
{
$page->set_title("Adding folder");
$page->set_heading("Adding folder");
$page->add_block(new NavBlock());
@ -22,7 +24,8 @@ class BulkAddTheme extends Themelet {
* links to bulk_add with POST[dir] set to the name of a server-side
* directory full of images
*/
public function display_admin_block() {
public function display_admin_block()
{
global $page;
$html = "
Add a folder full of images; any subfolders will have their names
@ -40,7 +43,8 @@ class BulkAddTheme extends Themelet {
$page->add_block(new Block("Bulk Add", $html));
}
public function add_status($title, $body) {
public function add_status($title, $body)
{
$this->messages[] = new Block($title, $body);
}
}

View File

@ -18,8 +18,10 @@
*
*/
class BulkAddCSV extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class BulkAddCSV extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("bulk_add_csv")) {
if ($user->is_admin() && $user->check_auth_token() && isset($_POST['csv'])) {
@ -30,7 +32,8 @@ class BulkAddCSV extends Extension {
}
}
public function onCommand(CommandEvent $event) {
public function onCommand(CommandEvent $event)
{
if ($event->cmd == "help") {
print " bulk-add-csv [/path/to.csv]\n";
print " Import this .csv file (refer to documentation)\n\n";
@ -48,21 +51,23 @@ class BulkAddCSV extends Extension {
}
}
public function onAdminBuilding(AdminBuildingEvent $event) {
public function onAdminBuilding(AdminBuildingEvent $event)
{
$this->theme->display_admin_block();
}
/**
* Generate the necessary DataUploadEvent for a given image and tags.
*/
private function add_image(string $tmpname, string $filename, string $tags, string $source, string $rating, string $thumbfile) {
private function add_image(string $tmpname, string $filename, string $tags, string $source, string $rating, string $thumbfile)
{
assert(file_exists($tmpname));
$pathinfo = pathinfo($filename);
if (!array_key_exists('extension', $pathinfo)) {
throw new UploadException("File has no extension");
}
$metadata = array();
$metadata = [];
$metadata['filename'] = $pathinfo['basename'];
$metadata['extension'] = $pathinfo['extension'];
$metadata['tags'] = Tag::explode($tags);
@ -72,7 +77,7 @@ class BulkAddCSV extends Extension {
if ($event->image_id == -1) {
throw new UploadException("File type not recognised");
} else {
if(class_exists("RatingSetEvent") && in_array($rating, array("s", "q", "e"))) {
if (class_exists("RatingSetEvent") && in_array($rating, ["s", "q", "e"])) {
$ratingevent = new RatingSetEvent(Image::by_id($event->image_id), $rating);
send_event($ratingevent);
}
@ -82,7 +87,8 @@ class BulkAddCSV extends Extension {
}
}
private function add_csv(string $csvfile) {
private function add_csv(string $csvfile)
{
if (!file_exists($csvfile)) {
$this->theme->add_status("Error", "$csvfile not found");
return;
@ -96,7 +102,7 @@ class BulkAddCSV extends Extension {
$list = "";
$csvhandle = fopen($csvfile, "r");
while (($csvdata = fgetcsv($csvhandle, 0, ",")) !== FALSE) {
while (($csvdata = fgetcsv($csvhandle, 0, ",")) !== false) {
if (count($csvdata) != 5) {
if (strlen($list) > 0) {
$this->theme->add_status("Error", "<b>Encountered malformed data. Line $linenum $csvfile</b><br>".$list);
@ -120,8 +126,7 @@ class BulkAddCSV extends Extension {
try {
$this->add_image($fullpath, $pathinfo["basename"], $tags, $source, $rating, $thumbfile);
$list .= "ok\n";
}
catch(Exception $ex) {
} catch (Exception $ex) {
$list .= "failed:<br>". $ex->getMessage();
}
} else {
@ -136,4 +141,3 @@ class BulkAddCSV extends Extension {
fclose($csvhandle);
}
}

View File

@ -1,12 +1,14 @@
<?php
class BulkAddCSVTheme extends Themelet {
private $messages = array();
class BulkAddCSVTheme extends Themelet
{
private $messages = [];
/*
* Show a standard page for results to be put into
*/
public function display_upload_results(Page $page) {
public function display_upload_results(Page $page)
{
$page->set_title("Adding images from csv");
$page->set_heading("Adding images from csv");
$page->add_block(new NavBlock());
@ -20,7 +22,8 @@ class BulkAddCSVTheme extends Themelet {
* links to bulk_add_csv with POST[csv] set to the name of a server-side
* csv file
*/
public function display_admin_block() {
public function display_admin_block()
{
global $page;
$html = "
Add images from a csv. Images will be tagged and have their
@ -37,8 +40,8 @@ class BulkAddCSVTheme extends Themelet {
$page->add_block(new Block("Bulk Add CSV", $html));
}
public function add_status($title, $body) {
public function add_status($title, $body)
{
$this->messages[] = new Block($title, $body);
}
}

View File

@ -10,16 +10,22 @@
*/
//todo: removal by tag returns 1 less image in test for some reason, actually a combined search doesn't seem to work for shit either
class BulkRemove extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class BulkRemove extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $user;
if ($event->page_matches("bulk_remove") && $user->is_admin() && $user->check_auth_token()) {
if ($event->get_arg(0) == "confirm") $this->do_bulk_remove();
else $this->show_confirm();
if ($event->get_arg(0) == "confirm") {
$this->do_bulk_remove();
} else {
$this->show_confirm();
}
}
}
public function onAdminBuilding(AdminBuildingEvent $event) {
public function onAdminBuilding(AdminBuildingEvent $event)
{
global $page;
$html = "<b>Be extremely careful when using this!</b><br>
Once an image is removed there is no way to recover it so it is recommended that
@ -47,7 +53,7 @@ class BulkRemove extends Extension {
private function determine_images()
{
// set vars
$images_for_removal = array();
$images_for_removal = [];
$error = "";
$min_id = $_POST['remove_id_min'];
@ -56,40 +62,40 @@ class BulkRemove extends Extension {
// if using id range to remove (comined removal with tags)
if ($min_id != "" && $max_id != "")
{
if ($min_id != "" && $max_id != "") {
// error if values are not correctly entered
if (!is_numeric($min_id) || !is_numeric($max_id) ||
intval($max_id) < intval($min_id))
intval($max_id) < intval($min_id)) {
$error = "Values not correctly entered for removal between id.";
else { // if min & max id are valid
} else { // if min & max id are valid
// Grab the list of images & place it in the removing array
foreach (Image::find_images(intval($min_id), intval($max_id)) as $image)
foreach (Image::find_images(intval($min_id), intval($max_id)) as $image) {
array_push($images_for_removal, $image);
}
}
}
// refine previous results or create results from tags
if ($tags != "")
{
if ($tags != "") {
$tags_arr = explode(" ", $_POST['remove_tags']);
// Search all images with the specified tags & add to list
foreach (Image::find_images(1, 2147483647, $tags_arr) as $image)
foreach (Image::find_images(1, 2147483647, $tags_arr) as $image) {
array_push($images_for_removal, $image);
}
}
// if no images were found with the given info
if (count($images_for_removal) == 0)
if (count($images_for_removal) == 0) {
$error = "No images selected for removal";
}
//var_dump($tags_arr);
return array(
return [
"error" => $error,
"images_for_removal" => $images_for_removal);
"images_for_removal" => $images_for_removal];
}
// displays confirmation to admin before removal
@ -122,12 +128,13 @@ class BulkRemove extends Extension {
global $page;
// display error if user didn't go through admin board
if (!isset($_POST["bulk_remove_images"])) {
$page->add_block(new Block("Bulk Remove Error",
"Please use Board Admin to use bulk remove."));
$page->add_block(new Block(
"Bulk Remove Error",
"Please use Board Admin to use bulk remove."
));
}
//
$image_arr = $_POST["bulk_remove_images"];
}
}

View File

@ -8,7 +8,7 @@ include '../php/functions.php';
include '../php/yshout.class.php';
include '../php/ajaxcall.class.php';
if (isset($_POST['mode']))
if (isset($_POST['mode'])) {
switch ($_POST['mode']) {
case 'login':
doLogin();
@ -29,162 +29,188 @@ if (isset($_POST['mode']))
doResetPreferences();
break;
}
}
function doLogin() {
function doLogin()
{
global $kioskMode;
if ($kioskMode) {
logout();
$result = array(
$result = [
'error' => false,
'html' => cp()
);
];
echo json_encode($result);
return;
}
login(md5($_POST['password']));
$result = array();
$result = [];
if (loggedIn()) {
$result['error'] = false;
$result['html'] = cp();
} else
} else {
$result['error'] = 'invalid';
}
echo json_encode($result);
}
function doLogout() {
function doLogout()
{
logout();
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
}
function doUnban() {
function doUnban()
{
global $kioskMode;
if ($kioskMode) {
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
return;
}
if (!loggedIn()) return;
if (!loggedIn()) {
return;
}
$ys = ys();
$result = array();
$result = [];
$ip = $_POST['ip'];
if ($ys->banned($ip)) {
$ys->unban($ip);
$result['error'] = false;
} else
} else {
$result['error'] = 'notbanned';
}
echo json_encode($result);
}
function doUnbanAll() {
function doUnbanAll()
{
global $kioskMode;
if ($kioskMode) {
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
return;
}
if (!loggedIn()) return;
if (!loggedIn()) {
return;
}
$ys = ys();
$ys->unbanAll();
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
}
function doSetPreference() {
function doSetPreference()
{
global $prefs, $kioskMode;
if ($kioskMode) {
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
return;
}
if (!loggedIn()) return;
if (!loggedIn()) {
return;
}
$pref = $_POST['preference'];
$value = magic($_POST['value']);
if ($value === 'true') $value = true;
if ($value === 'false') $value = false;
if ($value === 'true') {
$value = true;
}
if ($value === 'false') {
$value = false;
}
$prefs[$pref] = $value;
savePrefs($prefs);
if ($pref == 'password') login(md5($value));
if ($pref == 'password') {
login(md5($value));
}
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
}
function doResetPreferences() {
function doResetPreferences()
{
global $prefs, $kioskMode;
if ($kioskMode) {
$result = array(
$result = [
'error' => false
);
];
echo json_encode($result);
return;
}
if (!loggedIn()) return;
if (!loggedIn()) {
return;
}
resetPrefs();
login(md5($prefs['password']));
// $prefs['password'] = 'lol no';
$result = array(
$result = [
'error' => false,
'prefs' => $prefs
);
];
echo json_encode($result);
}
/* CP Display */
function cp() {
function cp()
{
global $kioskMode;
if (!loggedIn() && !$kioskMode) return 'You\'re not logged in!';
if (!loggedIn() && !$kioskMode) {
return 'You\'re not logged in!';
}
return '
@ -236,7 +262,8 @@ function cp() {
</div>';
}
function bansList() {
function bansList()
{
global $kioskMode;
$ys = ys();
@ -256,15 +283,17 @@ function bansList() {
';
}
if (!$hasBans)
if (!$hasBans) {
$html = '<p id="no-bans">No one is banned.</p>';
else
} else {
$html .= '</ul>';
}
return $html;
}
function preferencesForm() {
function preferencesForm()
{
global $prefs, $kioskMode;
return '
@ -434,7 +463,8 @@ function preferencesForm() {
';
}
function about() {
function about()
{
global $prefs;
$html = '
@ -454,4 +484,3 @@ function about() {
return $html;
}

View File

@ -35,7 +35,9 @@
</form>
</div>
<?php
if (loggedIn()) echo cp();
if (loggedIn()) {
echo cp();
}
?>
</div>
</div>

View File

@ -12,25 +12,22 @@
$log = 1;
if (isset($_GET['log']))
{
if (isset($_GET['log'])) {
$log = $_GET['log'];
}
if (isset($_POST['log']))
{
if (isset($_POST['log'])) {
$log = $_POST['log'];
}
if (filter_var($log, FILTER_VALIDATE_INT) === false)
{
if (filter_var($log, FILTER_VALIDATE_INT) === false) {
$log = 1;
}
$ys = ys($log);
$posts = $ys->posts();
if (sizeof($posts) === 0)
if (sizeof($posts) === 0) {
$html .= '
<div id="ys-post-1" class="ys-post ys-first ys-admin-post">
<span class="ys-post-timestamp">13:37</span>
@ -38,6 +35,7 @@
<span class="ys-post-message">Hey, there aren\'t any posts in this log.</span>
</div>
';
}
$id = 0;
@ -122,8 +120,9 @@ if (isset($_POST['p'])) {
<select id="log">
<?php
for ($i = 1; $i <= $prefs['logs']; $i++)
for ($i = 1; $i <= $prefs['logs']; $i++) {
echo '<option' . ($log == $i ? ' selected' : '') . ' rel="' . $i . '">Log ' . $i . '</option>' . "\n";
}
?>
</select>
</div>

View File

@ -5,4 +5,3 @@
include 'php/functions.php';
include 'php/yshout.class.php';
include 'php/ajaxcall.class.php';

View File

@ -8,8 +8,10 @@
* Documentation:
* This chatbox uses YShout 5 as core.
*/
class Chatbox extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class Chatbox extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
// Adds header to enable chatbox

View File

@ -1,19 +1,23 @@
<?php
class AjaxCall {
class AjaxCall
{
public $reqType;
public $updates;
function AjaxCall($log = null) {
public function AjaxCall($log = null)
{
header('Content-type: application/json');
session_start();
if (isset($log)) $_SESSION['yLog'] = $log;
if (isset($log)) {
$_SESSION['yLog'] = $log;
}
$this->reqType = $_POST['reqType'];
}
function process() {
public function process()
{
switch ($this->reqType) {
case 'init':
@ -27,7 +31,10 @@
cookie('yNickname', $nickname);
$ys = ys($_SESSION['yLog']);
if ($ys->banned(ip())) { $this->sendBanned(); break; }
if ($ys->banned(ip())) {
$this->sendBanned();
break;
}
if ($post = $ys->post($nickname, $message)) {
// To use $post somewheres later
$this->sendUpdates();
@ -36,7 +43,10 @@
case 'refresh':
$ys = ys($_SESSION['yLog']);
if ($ys->banned(ip())) { $this->sendBanned(); break; }
if ($ys->banned(ip())) {
$this->sendBanned();
break;
}
$this->sendUpdates();
break;
@ -75,10 +85,11 @@
}
}
function doBan() {
public function doBan()
{
$ip = $_POST['ip'];
$nickname = $_POST['nickname'];
$send = array();
$send = [];
$ys = ys($_SESSION['yLog']);
switch (true) {
@ -90,17 +101,19 @@
break;
default:
$ys->ban($ip, $nickname);
if ($ip == ip())
if ($ip == ip()) {
$send['bannedSelf'] = true;
}
$send['error'] = false;
}
echo json_encode($send);
}
function doUnban() {
public function doUnban()
{
$ip = $_POST['ip'];
$send = array();
$send = [];
$ys = ys($_SESSION['yLog']);
switch (true) {
@ -118,9 +131,10 @@
echo json_encode($send);
}
function doDelete() {
public function doDelete()
{
$uid = $_POST['uid'];
$send = array();
$send = [];
$ys = ys($_SESSION['yLog']);
switch (true) {
@ -135,33 +149,36 @@
echo json_encode($send);
}
function banSelf() {
public function banSelf()
{
$ys = ys($_SESSION['yLog']);
$nickname = $_POST['nickname'];
$ys->ban(ip(), $nickname);
$send = array();
$send = [];
$send['error'] = false;
echo json_encode($send);
}
function unbanSelf() {
public function unbanSelf()
{
if (loggedIn()) {
$ys = ys($_SESSION['yLog']);
$ys->unban(ip());
$send = array();
$send = [];
$send['error'] = false;
} else {
$send = array();
$send = [];
$send['error'] = 'admin';
}
echo json_encode($send);
}
function reload() {
public function reload()
{
global $prefs;
$ys = ys($_SESSION['yLog']);
@ -171,7 +188,8 @@
echo json_encode($this->updates);
}
function initSession() {
public function initSession()
{
$_SESSION['yLatestTimestamp'] = 0;
$_SESSION['yYPath'] = $_POST['yPath'];
$_SESSION['yLog'] = $_POST['log'];
@ -181,18 +199,22 @@
}
}
function sendBanned() {
$this->updates = array(
public function sendBanned()
{
$this->updates = [
'banned' => true
);
];
echo json_encode($this->updates);
}
function sendUpdates() {
public function sendUpdates()
{
global $prefs;
$ys = ys($_SESSION['yLog']);
if (!$ys->hasPostsAfter($_SESSION['yLatestTimestamp'])) return;
if (!$ys->hasPostsAfter($_SESSION['yLatestTimestamp'])) {
return;
}
$posts = $ys->postsAfter($_SESSION['yLatestTimestamp']);
$this->setSessTimestamp($posts);
@ -202,17 +224,21 @@
echo json_encode($this->updates);
}
function setSessTimestamp(&$posts) {
if (!$posts) return;
public function setSessTimestamp(&$posts)
{
if (!$posts) {
return;
}
$latest = array_slice($posts, -1, 1);
$_SESSION['yLatestTimestamp'] = $latest[0]['timestamp'];
}
function sendFirstUpdates() {
public function sendFirstUpdates()
{
global $prefs, $overrideNickname;
$this->updates = array();
$this->updates = [];
$ys = ys($_SESSION['yLog']);
@ -222,26 +248,31 @@
$this->updates['posts'] = $posts;
$this->updates['prefs'] = $this->cleanPrefs($prefs);
if ($nickname = cookieGet('yNickname'))
if ($nickname = cookieGet('yNickname')) {
$this->updates['nickname'] = $nickname;
}
if ($overrideNickname)
if ($overrideNickname) {
$this->updates['nickname'] = $overrideNickname;
}
if ($ys->banned(ip()))
if ($ys->banned(ip())) {
$this->updates['banned'] = true;
}
echo json_encode($this->updates);
}
function cleanPrefs($prefs) {
public function cleanPrefs($prefs)
{
unset($prefs['password']);
return $prefs;
}
function clearLog() {
public function clearLog()
{
//$log = $_POST['log'];
$send = array();
$send = [];
$ys = ys($_SESSION['yLog']);
switch (true) {
@ -256,11 +287,12 @@
echo json_encode($send);
}
function clearLogs() {
public function clearLogs()
{
global $prefs;
//$log = $_POST['log'];
$send = array();
$send = [];
//$ys = ys($_SESSION['yLog']);
@ -280,5 +312,3 @@
echo json_encode($send);
}
}

View File

@ -1,19 +1,27 @@
<?php
class FileStorage {
class FileStorage
{
public $shoutLog;
public $path;
public $handle;
public $shoutLog, $path, $handle;
function FileStorage($path, $shoutLog = false) {
public function FileStorage($path, $shoutLog = false)
{
$this->shoutLog = $shoutLog;
$folder = 'logs';
if (!is_dir($folder)) $folder = '../' . $folder;
if (!is_dir($folder)) $folder = '../' . $folder;
if (!is_dir($folder)) {
$folder = '../' . $folder;
}
if (!is_dir($folder)) {
$folder = '../' . $folder;
}
$this->path = $folder . '/' . $path . '.txt';
}
function open($lock = false) {
public function open($lock = false)
{
$this->handle = fopen($this->path, 'a+');
if ($lock) {
@ -22,63 +30,77 @@ class FileStorage {
}
}
function close(&$array) {
if (isset($array))
public function close(&$array)
{
if (isset($array)) {
$this->save($array);
}
$this->unlock();
fclose($this->handle);
unset($this->handle);
}
function load() {
if (($contents = $this->read($this->path)) == null)
public function load()
{
if (($contents = $this->read($this->path)) == null) {
return $this->resetArray();
}
return unserialize($contents);
}
function save(&$array, $unlock = true) {
public function save(&$array, $unlock = true)
{
$contents = serialize($array);
$this->write($contents);
if ($unlock) $this->unlock();
if ($unlock) {
$this->unlock();
}
}
function unlock() {
if (isset($this->handle))
public function unlock()
{
if (isset($this->handle)) {
flock($this->handle, LOCK_UN);
}
function lock() {
if (isset($this->handle))
flock($this->handle, LOCK_EX);
}
function read() {
public function lock()
{
if (isset($this->handle)) {
flock($this->handle, LOCK_EX);
}
}
public function read()
{
fseek($this->handle, 0);
//return stream_get_contents($this->handle);
return file_get_contents($this->path);
}
function write($contents) {
public function write($contents)
{
ftruncate($this->handle, 0);
fwrite($this->handle, $contents);
}
function resetArray() {
if ($this->shoutLog)
$default = array(
'info' => array(
public function resetArray()
{
if ($this->shoutLog) {
$default = [
'info' => [
'latestTimestamp' => -1
),
],
'posts' => array()
);
else
$default = array();
'posts' => []
];
} else {
$default = [];
}
$this->save($default, false);
return $default;
}
}

View File

@ -1,65 +1,86 @@
<?php
function cookie($name, $data) {
function cookie($name, $data)
{
return setcookie($name, $data, time() + 60 * 60 * 24 * 30, '/');
}
function cookieGet($name, $default = null) {
if (isset($_COOKIE[$name]))
function cookieGet($name, $default = null)
{
if (isset($_COOKIE[$name])) {
return $_COOKIE[$name];
else
} else {
return $default;
}
}
function cookieClear($name) {
function cookieClear($name)
{
setcookie($name, false, time() - 42);
}
function getVar($name) {
if (isset($_POST[$name])) { return $_POST[$name]; }
if (isset($_GET[$name])) { return $_GET[$name]; }
function getVar($name)
{
if (isset($_POST[$name])) {
return $_POST[$name];
}
if (isset($_GET[$name])) {
return $_GET[$name];
}
return null;
}
function clean($s) {
function clean($s)
{
$s = magic($s);
$s = htmlspecialchars($s);
return $s;
}
function magic($s) {
if (get_magic_quotes_gpc()) { $s = stripslashes($s); }
function magic($s)
{
if (get_magic_quotes_gpc()) {
$s = stripslashes($s);
}
return $s;
}
function ip() {
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
function ip()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
else
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
function ipValid($ip) {
if ($ip == long2ip(ip2long($ip)))
function ipValid($ip)
{
if ($ip == long2ip(ip2long($ip))) {
return true;
}
return false;
}
function validIP($ip) {
if ($ip == long2ip(ip2long($ip)))
function validIP($ip)
{
if ($ip == long2ip(ip2long($ip))) {
return true;
}
return false;
}
function ts() {
function ts()
{
// return microtime(true);
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function len($string) {
$i = 0; $count = 0;
function len($string)
{
$i = 0;
$count = 0;
$len = strlen($string);
while ($i < $len) {
@ -67,7 +88,9 @@
$count++;
$i++;
if ($i >= $len) break;
if ($i >= $len) {
break;
}
if ($chr & 0x80) {
$chr <<= 1;
while ($chr & 0x80) {
@ -80,17 +103,20 @@
return $count;
}
function error($err) {
function error($err)
{
echo 'Error: ' . $err;
exit;
}
function ys($log = 1) {
global $yShout, $prefs;
if ($yShout) return $yShout;
if (filter_var($log, FILTER_VALIDATE_INT, array("options" => array("min_range" => 0, "max_range" => $prefs['logs']))) === false)
function ys($log = 1)
{
global $yShout, $prefs;
if ($yShout) {
return $yShout;
}
if (filter_var($log, FILTER_VALIDATE_INT, ["options" => ["min_range" => 0, "max_range" => $prefs['logs']]]) === false) {
$log = 1;
}
@ -98,19 +124,22 @@
return new YShout($log, loggedIn());
}
function dstart() {
function dstart()
{
global $ts;
$ts = ts();
}
function dstop() {
function dstop()
{
global $ts;
echo 'Time elapsed: ' . ((ts() - $ts) * 100000);
exit;
}
function login($hash) {
function login($hash)
{
// echo 'login: ' . $hash . "\n";
$_SESSION['yLoginHash'] = $hash;
@ -118,24 +147,28 @@
// return loggedIn();
}
function logout() {
function logout()
{
$_SESSION['yLoginHash'] = '';
cookie('yLoginHash', '');
// cookieClear('yLoginHash');
}
function loggedIn() {
function loggedIn()
{
global $prefs;
$loginHash = cookieGet('yLoginHash', false);
// echo 'loggedin: ' . $loginHash . "\n";
// echo 'pw: ' . $prefs['password'] . "\n";
if (isset($loginHash)) return $loginHash == md5($prefs['password']);
if (isset($loginHash)) {
return $loginHash == md5($prefs['password']);
}
if (isset($_SESSION['yLoginHash']))
if (isset($_SESSION['yLoginHash'])) {
return $_SESSION['yLoginHash'] == md5($prefs['password']);
}
return false;
}

View File

@ -1,8 +1,9 @@
<?php
class YShout {
function YShout($path, $admin = false) {
class YShout
{
public function YShout($path, $admin = false)
{
global $storage;
// Redo to check for folders or just not break, because nonexistent files should be allowed.
// if (!file_exists($path)) error('That file does not exist.');
@ -11,30 +12,35 @@ class YShout {
$this->admin = $admin;
}
function posts() {
public function posts()
{
global $null;
$this->storage->open();
$s = $this->storage->load();
$this->storage->close($null);
if ($s)
if ($s) {
return $s['posts'];
}
}
function info() {
public function info()
{
global $null;
$s = $this->storage->open(true);
$this->storage->close($null);
if ($s)
if ($s) {
return $s['info'];
}
}
function postsAfter($ts) {
public function postsAfter($ts)
{
$allPosts = $this->posts();
$posts = array();
$posts = [];
/* for ($i = sizeof($allPosts) - 1; $i > -1; $i--) {
$post = $allPosts[$i];
@ -44,15 +50,17 @@ class YShout {
} */
foreach ($allPosts as $post) {
if ($post['timestamp'] > $ts)
if ($post['timestamp'] > $ts) {
$posts[] = $post;
}
}
$this->postProcess($posts);
return $posts;
}
function latestPosts($num) {
public function latestPosts($num)
{
$allPosts = $this->posts();
$posts = array_slice($allPosts, -$num, $num);
@ -60,48 +68,61 @@ class YShout {
return array_values($posts);
}
function hasPostsAfter($ts) {
public function hasPostsAfter($ts)
{
$info = $this->info();
$timestamp = $info['latestTimestamp'];
return $timestamp > $ts;
}
function post($nickname, $message) {
public function post($nickname, $message)
{
global $prefs;
if ($this->banned(ip()) /* && !$this->admin*/) return false;
if ($this->banned(ip()) /* && !$this->admin*/) {
return false;
}
if (!$this->validate($message, $prefs['messageLength'])) return false;
if (!$this->validate($nickname, $prefs['nicknameLength'])) return false;
if (!$this->validate($message, $prefs['messageLength'])) {
return false;
}
if (!$this->validate($nickname, $prefs['nicknameLength'])) {
return false;
}
$message = trim(clean($message));
$nickname = trim(clean($nickname));
if ($message == '') return false;
if ($nickname == '') return false;
if ($message == '') {
return false;
}
if ($nickname == '') {
return false;
}
$timestamp = ts();
$message = $this->censor($message);
$nickname = $this->censor($nickname);
$post = array(
$post = [
'nickname' => $nickname,
'message' => $message,
'timestamp' => $timestamp,
'admin' => $this->admin,
'uid' => md5($timestamp . ' ' . $nickname),
'adminInfo' => array(
'adminInfo' => [
'ip' => ip()
)
);
]
];
$s = $this->storage->open(true);
$s['posts'][] = $post;
if (sizeof($s['posts']) > $prefs['history'])
if (sizeof($s['posts']) > $prefs['history']) {
$this->truncate($s['posts']);
}
$s['info']['latestTimestamp'] = $post['timestamp'];
@ -110,14 +131,16 @@ class YShout {
return $post;
}
function truncate(&$array) {
public function truncate(&$array)
{
global $prefs;
$array = array_slice($array, -$prefs['history']);
$array = array_values($array);
}
function clear() {
public function clear()
{
global $null;
$this->storage->open(true);
@ -126,7 +149,8 @@ class YShout {
$this->storage->close($null);
}
function bans() {
public function bans()
{
global $storage, $null;
$s = new $storage('yshout.bans');
@ -137,23 +161,25 @@ class YShout {
return $bans;
}
function ban($ip, $nickname = '', $info = '') {
public function ban($ip, $nickname = '', $info = '')
{
global $storage;
$s = new $storage('yshout.bans');
$bans = $s->open(true);
$bans[] = array(
$bans[] = [
'ip' => $ip,
'nickname' => $nickname,
'info' => $info,
'timestamp' => ts()
);
];
$s->close($bans);
}
function banned($ip) {
public function banned($ip)
{
global $storage, $null;
$s = new $storage('yshout.bans');
@ -161,30 +187,33 @@ class YShout {
$s->close($null);
foreach ($bans as $ban) {
if ($ban['ip'] == $ip)
if ($ban['ip'] == $ip) {
return true;
}
}
return false;
}
function unban($ip) {
public function unban($ip)
{
global $storage;
$s = new $storage('yshout.bans');
$bans = $s->open(true);
foreach($bans as $key=>$value)
foreach ($bans as $key=>$value) {
if ($value['ip'] == $ip) {
unset($bans[$key]);
}
}
$bans = array_values($bans);
$s->close($bans);
}
function unbanAll() {
public function unbanAll()
{
global $storage, $null;
$s = new $storage('yshout.bans');
@ -193,7 +222,8 @@ class YShout {
$s->close($null);
}
function delete($uid) {
public function delete($uid)
{
global $prefs, $storage;
@ -202,12 +232,12 @@ class YShout {
$posts = $s['posts'];
foreach ($posts as $key=>$value) {
if (!isset($value['uid']))
if (!isset($value['uid'])) {
unset($posts['key']);
else
if($value['uid'] == $uid)
} elseif ($value['uid'] == $uid) {
unset($posts[$key]);
}
}
$s['posts'] = array_values($posts);
$this->storage->close($s);
@ -215,11 +245,13 @@ class YShout {
return true;
}
function validate($str, $maxLen) {
public function validate($str, $maxLen)
{
return len($str) <= $maxLen;
}
function censor($str) {
public function censor($str)
{
global $prefs;
$cWords = explode(' ', $prefs['censorWords']);
@ -227,25 +259,34 @@ class YShout {
$endings = '|ed|es|ing|s|er|ers';
$arrEndings = explode('|', $endings);
foreach ($cWords as $cWord) foreach ($words as $i=>$word) {
foreach ($cWords as $cWord) {
foreach ($words as $i=>$word) {
$pattern = '/^(' . $cWord . ')+(' . $endings . ')\W*$/i';
$words[$i] = preg_replace($pattern, str_repeat('*', strlen($word)), $word);
}
}
return implode(' ', $words);
}
function postProcess(&$post) {
public function postProcess(&$post)
{
if (isset($post['message'])) {
if ($this->banned($post['adminInfo']['ip'])) $post['banned'] = true;
if (!$this->admin) unset($post['adminInfo']);
if ($this->banned($post['adminInfo']['ip'])) {
$post['banned'] = true;
}
if (!$this->admin) {
unset($post['adminInfo']);
}
} else {
foreach ($post as $key=>$value) {
if ($this->banned($value['adminInfo']['ip'])) $post[$key]['banned'] = true;
if (!$this->admin) unset($post[$key]['adminInfo']);
if ($this->banned($value['adminInfo']['ip'])) {
$post[$key]['banned'] = true;
}
if (!$this->admin) {
unset($post[$key]['adminInfo']);
}
}
}
}
}

View File

@ -7,7 +7,8 @@
$storage = 'FileStorage';
function loadPrefs() {
function loadPrefs()
{
global $prefs, $storage, $null;
$s = new $storage('yshout.prefs');
$s->open();
@ -15,7 +16,8 @@
$s->close($null);
}
function savePrefs($newPrefs) {
function savePrefs($newPrefs)
{
global $prefs, $storage;
$s = new $storage('yshout.prefs');
@ -24,8 +26,9 @@
$prefs = $newPrefs;
}
function resetPrefs() {
$defaultPrefs = array(
function resetPrefs()
{
$defaultPrefs = [
'password' => 'fortytwo', // The password for the CP
'refresh' => 6000, // Refresh rate
@ -63,12 +66,10 @@
'postFormLink' => 'history',
'info' => 'inline'
);
];
savePrefs($defaultPrefs);
}
resetPrefs();
//loadPrefs();

View File

@ -5,7 +5,7 @@ ob_start();
set_error_handler('errorOccurred');
include 'include.php';
if (isset($_POST['reqFor']))
if (isset($_POST['reqFor'])) {
switch ($_POST['reqFor']) {
case 'shout':
@ -22,17 +22,18 @@ if (isset($_POST['reqFor']))
default:
exit;
}
} else {
include 'example.html';
}
function errorOccurred($num, $str, $file, $line) {
$err = array (
function errorOccurred($num, $str, $file, $line)
{
$err = [
'yError' => "$str. \n File: $file \n Line: $line"
);
];
echo json_encode($err);
exit;
}

View File

@ -11,7 +11,8 @@
require_once "vendor/ifixit/php-akismet/akismet.class.php";
class CommentPostingEvent extends Event {
class CommentPostingEvent extends Event
{
/** @var int */
public $image_id;
/** @var \User */
@ -19,7 +20,8 @@ class CommentPostingEvent extends Event {
/** @var string */
public $comment;
public function __construct(int $image_id, User $user, string $comment) {
public function __construct(int $image_id, User $user, string $comment)
{
$this->image_id = $image_id;
$this->user = $user;
$this->comment = $comment;
@ -31,23 +33,36 @@ class CommentPostingEvent extends Event {
* detectors to get a feel for what should be deleted
* and what should be kept?
*/
class CommentDeletionEvent extends Event {
class CommentDeletionEvent extends Event
{
/** @var int */
public $comment_id;
public function __construct(int $comment_id) {
public function __construct(int $comment_id)
{
$this->comment_id = $comment_id;
}
}
class CommentPostingException extends SCoreException {}
class CommentPostingException extends SCoreException
{
}
class Comment {
public $owner, $owner_id, $owner_name, $owner_email, $owner_class;
public $comment, $comment_id;
public $image_id, $poster_ip, $posted;
class Comment
{
public $owner;
public $owner_id;
public $owner_name;
public $owner_email;
public $owner_class;
public $comment;
public $comment_id;
public $image_id;
public $poster_ip;
public $posted;
public function __construct($row) {
public function __construct($row)
{
$this->owner = null;
$this->owner_id = $row['user_id'];
$this->owner_name = $row['user_name'];
@ -60,26 +75,32 @@ class Comment {
$this->posted = $row['posted'];
}
public static function count_comments_by_user(User $user): int {
public static function count_comments_by_user(User $user): int
{
global $database;
return $database->get_one("
SELECT COUNT(*) AS count
FROM comments
WHERE owner_id=:owner_id
", array("owner_id"=>$user->id));
", ["owner_id"=>$user->id]);
}
public function get_owner(): User {
if(empty($this->owner)) $this->owner = User::by_id($this->owner_id);
public function get_owner(): User
{
if (empty($this->owner)) {
$this->owner = User::by_id($this->owner_id);
}
return $this->owner;
}
}
class CommentList extends Extension {
class CommentList extends Extension
{
/** @var CommentListTheme $theme */
public $theme;
public function onInitExt(InitExtEvent $event) {
public function onInitExt(InitExtEvent $event)
{
global $config, $database;
$config->set_default_int('comment_window', 5);
$config->set_default_int('comment_limit', 10);
@ -100,9 +121,9 @@ class CommentList extends Extension {
FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE,
FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT
");
$database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", array());
$database->execute("CREATE INDEX comments_owner_id_idx ON comments(owner_id)", array());
$database->execute("CREATE INDEX comments_posted_idx ON comments(posted)", array());
$database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", []);
$database->execute("CREATE INDEX comments_owner_id_idx ON comments(owner_id)", []);
$database->execute("CREATE INDEX comments_posted_idx ON comments(posted)", []);
$config->set_int("ext_comments_version", 3);
}
@ -116,7 +137,7 @@ class CommentList extends Extension {
posted SCORE_DATETIME DEFAULT NULL,
comment TEXT NOT NULL
");
$database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", array());
$database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", []);
$config->set_int("ext_comments_version", 1);
}
@ -136,7 +157,8 @@ class CommentList extends Extension {
}
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
if ($event->page_matches("comment")) {
switch ($event->get_arg(0)) {
case "add": $this->onPageRequest_add(); break;
@ -148,7 +170,8 @@ class CommentList extends Extension {
}
}
private function onPageRequest_add() {
private function onPageRequest_add()
{
global $user, $page;
if (isset($_POST['image_id']) && isset($_POST['comment'])) {
try {
@ -163,7 +186,8 @@ class CommentList extends Extension {
}
}
private function onPageRequest_delete(PageRequestEvent $event) {
private function onPageRequest_delete(PageRequestEvent $event)
{
global $user, $page;
if ($user->can("delete_comment")) {
// FIXME: post, not args
@ -182,7 +206,8 @@ class CommentList extends Extension {
}
}
private function onPageRequest_bulk_delete() {
private function onPageRequest_bulk_delete()
{
global $user, $database, $page;
if ($user->can("delete_comment") && !empty($_POST["ip"])) {
$ip = $_POST['ip'];
@ -191,7 +216,7 @@ class CommentList extends Extension {
SELECT id
FROM comments
WHERE owner_ip=:ip
", array("ip" => $ip));
", ["ip" => $ip]);
$num = count($comment_ids);
log_warning("comment", "Deleting $num comments from $ip");
foreach ($comment_ids as $cid) {
@ -206,12 +231,14 @@ class CommentList extends Extension {
}
}
private function onPageRequest_list(PageRequestEvent $event) {
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) {
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);
@ -223,11 +250,13 @@ class CommentList extends Extension {
$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();
}
public function onPostListBuilding(PostListBuildingEvent $event) {
public function onPostListBuilding(PostListBuildingEvent $event)
{
global $config, $database;
$cc = $config->get_int("comment_count");
if ($cc > 0) {
@ -242,7 +271,8 @@ class CommentList extends Extension {
}
}
public function onUserPageBuilding(UserPageBuildingEvent $event) {
public function onUserPageBuilding(UserPageBuildingEvent $event)
{
$i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1;
$i_comment_count = Comment::count_comments_by_user($event->display_user);
$h_comment_rate = sprintf("%.1f", ($i_comment_count / $i_days_old));
@ -252,7 +282,8 @@ class CommentList extends Extension {
$this->theme->display_recent_user_comments($recent, $event->display_user);
}
public function onDisplayingImage(DisplayingImageEvent $event) {
public function onDisplayingImage(DisplayingImageEvent $event)
{
global $user;
$this->theme->display_image_comments(
$event->image,
@ -262,20 +293,23 @@ class CommentList extends Extension {
}
// TODO: split akismet into a separate class, which can veto the event
public function onCommentPosting(CommentPostingEvent $event) {
public function onCommentPosting(CommentPostingEvent $event)
{
$this->add_comment_wrapper($event->image_id, $event->user, $event->comment);
}
public function onCommentDeletion(CommentDeletionEvent $event) {
public function onCommentDeletion(CommentDeletionEvent $event)
{
global $database;
$database->Execute("
DELETE FROM comments
WHERE id=:comment_id
", array("comment_id"=>$event->comment_id));
", ["comment_id"=>$event->comment_id]);
log_info("comment", "Deleting Comment #{$event->comment_id}");
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Comment Options");
$sb->add_bool_option("comment_captcha", "Require CAPTCHA for anonymous comments: ");
$sb->add_label("<br>Limit to ");
@ -294,15 +328,15 @@ class CommentList extends Extension {
$event->panel->add_block($sb);
}
public function onSearchTermParse(SearchTermParseEvent $event) {
$matches = array();
public function onSearchTermParse(SearchTermParseEvent $event)
{
$matches = [];
if (preg_match("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
$cmp = ltrim($matches[1], ":") ?: "=";
$comments = $matches[2];
$event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM comments GROUP BY image_id HAVING count(image_id) $cmp $comments)"));
}
else if(preg_match("/^commented_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif (preg_match("/^commented_by[=|:](.*)$/i", $event->term, $matches)) {
$user = User::by_name($matches[1]);
if (!is_null($user)) {
$user_id = $user->id;
@ -311,15 +345,15 @@ class CommentList extends Extension {
}
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)"));
}
else if(preg_match("/^commented_by_userno[=|:]([0-9]+)$/i", $event->term, $matches)) {
} elseif (preg_match("/^commented_by_userno[=|:]([0-9]+)$/i", $event->term, $matches)) {
$user_id = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)"));
}
}
// page building {{{
private function build_page(int $current_page) {
private function build_page(int $current_page)
{
global $database, $user;
$where = SPEED_HAX ? "WHERE posted > now() - interval '24 hours'" : "";
@ -346,22 +380,22 @@ class CommentList extends Extension {
GROUP BY image_id
ORDER BY latest DESC
LIMIT :limit OFFSET :offset
", array("limit"=>$threads_per_page, "offset"=>$start));
", ["limit"=>$threads_per_page, "offset"=>$start]);
$user_ratings = ext_is_live("Ratings") ? Ratings::get_user_privs($user) : "";
$images = array();
$images = [];
while ($row = $result->fetch()) {
$image = Image::by_id($row["image_id"]);
if (
ext_is_live("Ratings") && !is_null($image) &&
strpos($user_ratings, $image->rating) === FALSE
strpos($user_ratings, $image->rating) === false
) {
$image = null; // this is "clever", I may live to regret it
}
if (!is_null($image)) {
$comments = $this->get_comments($image->id);
$images[] = array($image, $comments);
$images[] = [$image, $comments];
}
}
@ -373,10 +407,11 @@ class CommentList extends Extension {
/**
* #return Comment[]
*/
private function get_generic_comments(string $query, array $args): array {
private function get_generic_comments(string $query, array $args): array
{
global $database;
$rows = $database->get_all($query, $args);
$comments = array();
$comments = [];
foreach ($rows as $row) {
$comments[] = new Comment($row);
}
@ -386,7 +421,8 @@ class CommentList extends Extension {
/**
* #return Comment[]
*/
private function get_recent_comments(int $count): array {
private function get_recent_comments(int $count): array
{
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,
@ -397,13 +433,14 @@ class CommentList extends Extension {
LEFT JOIN users ON comments.owner_id=users.id
ORDER BY comments.id DESC
LIMIT :limit
", array("limit"=>$count));
", ["limit"=>$count]);
}
/**
* #return Comment[]
*/
private function get_user_comments(int $user_id, int $count, int $offset=0): array {
private function get_user_comments(int $user_id, int $count, int $offset=0): array
{
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,
@ -415,13 +452,14 @@ class CommentList extends Extension {
WHERE users.id = :user_id
ORDER BY comments.id DESC
LIMIT :limit OFFSET :offset
", array("user_id"=>$user_id, "offset"=>$offset, "limit"=>$count));
", ["user_id"=>$user_id, "offset"=>$offset, "limit"=>$count]);
}
/**
* #return Comment[]
*/
private function get_comments(int $image_id): array {
private function get_comments(int $image_id): array
{
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,
@ -432,34 +470,41 @@ class CommentList extends Extension {
LEFT JOIN users ON comments.owner_id=users.id
WHERE comments.image_id=:image_id
ORDER BY comments.id ASC
", array("image_id"=>$image_id));
", ["image_id"=>$image_id]);
}
// }}}
// add / remove / edit comments {{{
private function is_comment_limit_hit(): bool {
private function is_comment_limit_hit(): bool
{
global $config, $database;
// sqlite fails at intervals
if($database->get_driver_name() === "sqlite") return false;
if ($database->get_driver_name() === "sqlite") {
return false;
}
$window = int_escape($config->get_int('comment_window'));
$max = int_escape($config->get_int('comment_limit'));
if($database->get_driver_name() == "mysql") $window_sql = "interval $window minute";
else $window_sql = "interval '$window minute'";
if ($database->get_driver_name() == "mysql") {
$window_sql = "interval $window minute";
} else {
$window_sql = "interval '$window minute'";
}
// 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 AND posted > now() - $window_sql
", array("remote_ip"=>$_SERVER['REMOTE_ADDR']));
", ["remote_ip"=>$_SERVER['REMOTE_ADDR']]);
return (count($result) >= $max);
}
private function hash_match(): bool {
private function hash_match(): bool
{
return ($_POST['hash'] == $this->get_hash());
}
@ -470,20 +515,22 @@ class CommentList extends Extension {
*
* FIXME: assumes comments are posted via HTTP...
*/
public static function get_hash(): string {
public static function get_hash(): string
{
return md5($_SERVER['REMOTE_ADDR'] . date("%Y%m%d"));
}
private function is_spam_akismet(string $text): bool {
private function is_spam_akismet(string $text): bool
{
global $config, $user;
if (strlen($config->get_string('comment_wordpress_key')) > 0) {
$comment = array(
$comment = [
'author' => $user->name,
'email' => $user->email,
'website' => '',
'body' => $text,
'permalink' => '',
);
];
# akismet breaks if there's no referrer in the environment; so if there
# isn't, supply one manually
@ -499,12 +546,12 @@ class CommentList extends Extension {
$akismet = new Akismet(
$_SERVER['SERVER_NAME'],
$config->get_string('comment_wordpress_key'),
$comment);
$comment
);
if ($akismet->errorsExist()) {
return false;
}
else {
} else {
return $akismet->isSpam();
}
}
@ -512,17 +559,19 @@ class CommentList extends Extension {
return false;
}
private function is_dupe(int $image_id, string $comment): bool {
private function is_dupe(int $image_id, string $comment): bool
{
global $database;
return (bool)$database->get_row("
SELECT *
FROM comments
WHERE image_id=:image_id AND comment=:comment
", array("image_id"=>$image_id, "comment"=>$comment));
", ["image_id"=>$image_id, "comment"=>$comment]);
}
// do some checks
private function add_comment_wrapper(int $image_id, User $user, string $comment) {
private function add_comment_wrapper(int $image_id, User $user, string $comment)
{
global $database, $page;
if (!$user->can("bypass_comment_checks")) {
@ -537,58 +586,54 @@ class CommentList extends Extension {
$database->Execute(
"INSERT INTO comments(image_id, owner_id, owner_ip, posted, comment) ".
"VALUES(:image_id, :user_id, :remote_addr, now(), :comment)",
array("image_id"=>$image_id, "user_id"=>$user->id, "remote_addr"=>$_SERVER['REMOTE_ADDR'], "comment"=>$comment));
["image_id"=>$image_id, "user_id"=>$user->id, "remote_addr"=>$_SERVER['REMOTE_ADDR'], "comment"=>$comment]
);
$cid = $database->get_last_insert_id('comments_id_seq');
$snippet = substr($comment, 0, 100);
$snippet = str_replace("\n", " ", $snippet);
$snippet = str_replace("\r", " ", $snippet);
log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", null, array("image_id"=>$image_id, "comment_id"=>$cid));
log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", null, ["image_id"=>$image_id, "comment_id"=>$cid]);
}
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;
// basic sanity checks
if (!$user->can("create_comment")) {
throw new CommentPostingException("Anonymous posting has been disabled");
}
else if(is_null(Image::by_id($image_id))) {
} elseif (is_null(Image::by_id($image_id))) {
throw new CommentPostingException("The image does not exist");
}
else if(trim($comment) == "") {
} elseif (trim($comment) == "") {
throw new CommentPostingException("Comments need text...");
}
else if(strlen($comment) > 9000) {
} elseif (strlen($comment) > 9000) {
throw new CommentPostingException("Comment too long~");
}
// advanced sanity checks
elseif (strlen($comment)/strlen(gzcompress($comment)) > 10) {
throw new CommentPostingException("Comment too repetitive~");
}
else if($user->is_anonymous() && !$this->hash_match()) {
} elseif ($user->is_anonymous() && !$this->hash_match()) {
$page->add_cookie("nocache", "Anonymous Commenter", time()+60*60*24, "/");
throw new CommentPostingException(
"Comment submission form is out of date; refresh the ".
"comment form to show you aren't a spammer~");
"comment form to show you aren't a spammer~"
);
}
// database-querying checks
elseif ($this->is_comment_limit_hit()) {
throw new CommentPostingException("You've posted several comments recently; wait a minute and try again...");
}
else if($this->is_dupe($image_id, $comment)) {
} elseif ($this->is_dupe($image_id, $comment)) {
throw new CommentPostingException("Someone already made that comment on that image -- try and be more original?");
}
// rate-limited external service checks last
elseif ($config->get_bool('comment_captcha') && !captcha_check()) {
throw new CommentPostingException("Error in captcha");
}
else if($user->is_anonymous() && $this->is_spam_akismet($comment)) {
} elseif ($user->is_anonymous() && $this->is_spam_akismet($comment)) {
throw new CommentPostingException("Akismet thinks that your comment is spam. Try rewriting the comment, or logging in.");
}
}
// }}}
}

View File

@ -1,19 +1,23 @@
<?php
class CommentListTest extends ShimmiePHPUnitTestCase {
public function setUp() {
class CommentListTest extends ShimmiePHPUnitTestCase
{
public function setUp()
{
global $config;
parent::setUp();
$config->set_int("comment_limit", 100);
$this->log_out();
}
public function tearDown() {
public function tearDown()
{
global $config;
$config->set_int("comment_limit", 10);
parent::tearDown();
}
public function testCommentsPage() {
public function testCommentsPage()
{
global $user;
$this->log_in_as_user();
@ -27,32 +31,28 @@ class CommentListTest extends ShimmiePHPUnitTestCase {
# dupe
try {
send_event(new CommentPostingEvent($image_id, $user, "Test Comment ASDFASDF"));
}
catch(CommentPostingException $e) {
} catch (CommentPostingException $e) {
$this->assertContains("try and be more original", $e->getMessage());
}
# empty comment
try {
send_event(new CommentPostingEvent($image_id, $user, ""));
}
catch(CommentPostingException $e) {
} catch (CommentPostingException $e) {
$this->assertContains("Comments need text", $e->getMessage());
}
# whitespace is still empty...
try {
send_event(new CommentPostingEvent($image_id, $user, " \t\r\n"));
}
catch(CommentPostingException $e) {
} catch (CommentPostingException $e) {
$this->assertContains("Comments need text", $e->getMessage());
}
# repetitive (aka. gzip gives >= 10x improvement)
try {
send_event(new CommentPostingEvent($image_id, $user, str_repeat("U", 5000)));
}
catch(CommentPostingException $e) {
} catch (CommentPostingException $e) {
$this->assertContains("Comment too repetitive", $e->getMessage());
}
@ -85,7 +85,8 @@ class CommentListTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text('ASDFASDF');
}
public function testSingleDel() {
public function testSingleDel()
{
$this->markTestIncomplete();
$this->log_in_as_admin();

View File

@ -1,13 +1,15 @@
<?php
class CommentListTheme extends Themelet {
class CommentListTheme extends Themelet
{
private $comments_shown = 0;
private $show_anon_id = false;
private $anon_id = 1;
private $anon_cid = 0;
private $anon_map = array();
private $anon_map = [];
private $ct = null;
private function get_anon_colour($ip) {
private function get_anon_colour($ip)
{
if (is_null($this->ct)) {
$this->ct = hsl_rainbow();
}
@ -20,7 +22,8 @@ class CommentListTheme extends Themelet {
/**
* Display a page with a list of images, and for each image, the image's comments.
*/
public function display_comment_list(array $images, int $page_number, int $total_pages, bool $can_post) {
public function display_comment_list(array $images, int $page_number, int $total_pages, bool $can_post)
{
global $config, $page, $user;
// aaaaaaargh php
@ -64,8 +67,7 @@ class CommentListTheme extends Themelet {
$comment_html .= "<p>showing $comment_limit of $comment_count comments</p>";
$comments = array_slice($comments, -$comment_limit);
$this->show_anon_id = false;
}
else {
} else {
$this->show_anon_id = true;
}
$this->anon_id = 1;
@ -80,8 +82,7 @@ class CommentListTheme extends Themelet {
if ($can_post) {
if (!$comment_captcha) {
$comment_html .= $this->build_postbox($image->id);
}
else {
} else {
$link = make_link("post/view/".$image->id);
$comment_html .= "<a href='$link'>Add Comment</a>";
}
@ -100,7 +101,8 @@ class CommentListTheme extends Themelet {
}
public function display_admin_block() {
public function display_admin_block()
{
global $page;
$html = '
@ -122,7 +124,8 @@ class CommentListTheme extends Themelet {
*
* #param Comment[] $comments An array of Comment objects to be shown
*/
public function display_recent_comments(array $comments) {
public function display_recent_comments(array $comments)
{
global $page;
$this->show_anon_id = false;
$html = "";
@ -139,7 +142,8 @@ class CommentListTheme extends Themelet {
*
* #param Comment[] $comments
*/
public function display_image_comments(Image $image, array $comments, bool $postbox) {
public function display_image_comments(Image $image, array $comments, bool $postbox)
{
global $page;
$this->show_anon_id = true;
$html = "";
@ -158,7 +162,8 @@ class CommentListTheme extends Themelet {
*
* #param Comment[] $comments
*/
public function display_recent_user_comments(array $comments, User $user) {
public function display_recent_user_comments(array $comments, User $user)
{
global $page;
$html = "";
foreach ($comments as $comment) {
@ -166,14 +171,14 @@ class CommentListTheme extends Themelet {
}
if (empty($html)) {
$html = '<p>No comments by this user.</p>';
}
else {
} else {
$html .= "<p><a href='".make_link("comment/beta-search/{$user->name}/1")."'>More</a></p>";
}
$page->add_block(new Block("Comments", $html, "left", 70, "comment-list-user"));
}
public function display_all_user_comments(array $comments, int $page_number, int $total_pages, User $user) {
public function display_all_user_comments(array $comments, int $page_number, int $total_pages, User $user)
{
global $page;
assert(is_numeric($page_number));
@ -205,7 +210,8 @@ class CommentListTheme extends Themelet {
$this->display_paginator($page, "comment/beta-search/{$user->name}", null, $page_number, $total_pages);
}
protected function comment_to_html(Comment $comment, bool $trim=false): string {
protected function comment_to_html(Comment $comment, bool $trim=false): string
{
global $config, $user;
$tfe = new TextFormattingEvent($comment->comment);
@ -236,8 +242,7 @@ class CommentListTheme extends Themelet {
}
$h_userlink = "<span class='username'>" . $h_name . $anoncode . $anoncode2 . "</span>";
$this->anon_id++;
}
else {
} else {
$h_userlink = '<a class="username" href="'.make_link('user/'.$h_name).'">'.$h_name.'</a>';
}
@ -249,8 +254,7 @@ class CommentListTheme extends Themelet {
<a href=\"".make_link("post/view/$i_image_id#c$i_comment_id")."\">&gt;&gt;&gt;</a>
</div>
";
}
else {
} else {
$h_avatar = "";
if (!empty($comment->owner_email)) {
$hash = md5(strtolower($comment->owner_email));
@ -280,7 +284,8 @@ class CommentListTheme extends Themelet {
return $html;
}
protected function build_postbox(int $image_id): string {
protected function build_postbox(int $image_id): string
{
global $config;
$i_image_id = int_escape($image_id);
@ -300,4 +305,3 @@ class CommentListTheme extends Themelet {
';
}
}

View File

@ -7,7 +7,8 @@
* Description: Uploads images automatically using Cron Jobs
* Documentation: Installation guide: activate this extension and navigate to www.yoursite.com/cron_upload
*/
class CronUploader extends Extension {
class CronUploader extends Extension
{
// TODO: Checkbox option to only allow localhost + a list of additional IP adresses that can be set in /cron_upload
// TODO: Change logging to MySQL + display log at /cron_upload
// TODO: Move stuff to theme.php
@ -22,7 +23,7 @@ class CronUploader extends Extension {
* Lists all files & info required to upload.
* @var array
*/
private $image_queue = array();
private $image_queue = [];
/**
* Cron Uploader root directory
@ -40,7 +41,8 @@ class CronUploader extends Extension {
* Checks if the cron upload page has been accessed
* and initializes the upload.
*/
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $config, $user;
if ($event->page_matches("cron_upload")) {
@ -50,16 +52,15 @@ class CronUploader extends Extension {
if ($this->upload_key != "" && $event->get_arg(0) == $this->upload_key) {
// log in as admin
$this->process_upload(); // Start upload
}
else if ($user->is_admin()) {
} elseif ($user->is_admin()) {
$this->set_dir();
$this->display_documentation();
}
}
}
private function display_documentation() {
private function display_documentation()
{
global $page;
$this->set_dir(); // Determines path to cron_uploader_dir
@ -137,7 +138,8 @@ class CronUploader extends Extension {
$page->add_block($block_install);
}
public function onInitExt(InitExtEvent $event) {
public function onInitExt(InitExtEvent $event)
{
global $config;
// Set default values
if ($config->get_string("cron_uploader_key", "")) {
@ -149,7 +151,8 @@ class CronUploader extends Extension {
}
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$this->set_dir();
$cron_url = make_http(make_link("/cron_upload/" . $this->upload_key));
@ -171,7 +174,8 @@ class CronUploader extends Extension {
/*
* Generates a unique key for the website to prevent unauthorized access.
*/
private function generate_key() {
private function generate_key()
{
$length = 20;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
@ -186,7 +190,8 @@ class CronUploader extends Extension {
/*
* Set the directory for the image queue. If no directory was given, set it to the default directory.
*/
private function set_dir() {
private function set_dir()
{
global $config;
// Determine directory (none = default)
@ -199,12 +204,15 @@ class CronUploader extends Extension {
}
// Make the directory if it doesn't exist yet
if (!is_dir($dir . "/queue/"))
if (!is_dir($dir . "/queue/")) {
mkdir($dir . "/queue/", 0775, true);
if (!is_dir($dir . "/uploaded/"))
}
if (!is_dir($dir . "/uploaded/")) {
mkdir($dir . "/uploaded/", 0775, true);
if (!is_dir($dir . "/failed_to_upload/"))
}
if (!is_dir($dir . "/failed_to_upload/")) {
mkdir($dir . "/failed_to_upload/", 0775, true);
}
$this->root_dir = $dir;
return $dir;
@ -213,7 +221,8 @@ class CronUploader extends Extension {
/**
* Returns amount of files & total size of dir.
*/
function scan_dir(string $path): array{
public function scan_dir(string $path): array
{
$ite=new RecursiveDirectoryIterator($path);
$bytestotal=0;
@ -226,20 +235,23 @@ class CronUploader extends Extension {
$size_mb = $bytestotal / 1048576; // to mb
$size_mb = number_format($size_mb, 2, '.', '');
return array('total_files'=>$nbfiles,'total_mb'=>$size_mb);
return ['total_files'=>$nbfiles,'total_mb'=>$size_mb];
}
/**
* Uploads the image & handles everything
*/
public function process_upload(int $upload_count = 0): bool {
public function process_upload(int $upload_count = 0): bool
{
global $config;
set_time_limit(0);
$this->set_dir();
$this->generate_image_queue();
// Gets amount of imgs to upload
if ($upload_count == 0) $upload_count = $config->get_int ("cron_uploader_count", 1);
if ($upload_count == 0) {
$upload_count = $config->get_int("cron_uploader_count", 1);
}
// Throw exception if there's nothing in the queue
if (count($this->image_queue) == 0) {
@ -258,9 +270,7 @@ class CronUploader extends Extension {
try {
$this->add_image($img[0], $img[1], $img[2]);
$this->move_uploaded($img[0], $img[1], false);
}
catch (Exception $e) {
} catch (Exception $e) {
$this->move_uploaded($img[0], $img[1], true);
}
@ -274,7 +284,8 @@ class CronUploader extends Extension {
return true;
}
private function move_uploaded($path, $filename, $corrupt = false) {
private function move_uploaded($path, $filename, $corrupt = false)
{
global $config;
// Create
@ -285,8 +296,7 @@ class CronUploader extends Extension {
// Move to corrupt dir
$newDir .= "/failed_to_upload/";
$info = "ERROR: Image was not uploaded.";
}
else {
} else {
$newDir .= "/uploaded/";
$info = "Image successfully uploaded. ";
}
@ -300,26 +310,29 @@ class CronUploader extends Extension {
/**
* Generate the necessary DataUploadEvent for a given image and tags.
*/
private function add_image(string $tmpname, string $filename, string $tags) {
private function add_image(string $tmpname, string $filename, string $tags)
{
assert(file_exists($tmpname));
$pathinfo = pathinfo($filename);
if (! array_key_exists('extension', $pathinfo)) {
throw new UploadException("File has no extension");
}
$metadata = array();
$metadata = [];
$metadata ['filename'] = $pathinfo ['basename'];
$metadata ['extension'] = $pathinfo ['extension'];
$metadata ['tags'] = array(); // = $tags; doesn't work when not logged in here
$metadata ['tags'] = []; // = $tags; doesn't work when not logged in here
$metadata ['source'] = null;
$event = new DataUploadEvent($tmpname, $metadata);
send_event($event);
// Generate info message
$infomsg = ""; // Will contain info message
if ($event->image_id == -1)
if ($event->image_id == -1) {
$infomsg = "File type not recognised. Filename: {$filename}";
else $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}";
} else {
$infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}";
}
$msgNumber = $this->add_upload_info($infomsg);
// Set tags
@ -327,13 +340,15 @@ class CronUploader extends Extension {
$img->set_tags(Tag::explode($tags));
}
private function generate_image_queue($base = "", $subdir = "") {
if ($base == "")
private function generate_image_queue($base = "", $subdir = "")
{
if ($base == "") {
$base = $this->root_dir . "/queue";
}
if (! is_dir($base)) {
$this->add_upload_info("Image Queue Directory could not be found at \"$base\".");
return array();
return [];
}
foreach (glob("$base/$subdir/*") as $fullpath) {
@ -346,7 +361,7 @@ class CronUploader extends Extension {
$this->generate_image_queue($base, str_replace($base, "", $fullpath));
} else {
$pathinfo = pathinfo($fullpath);
$matches = array ();
$matches = [];
if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches)) {
$tags = $matches [1];
@ -354,15 +369,17 @@ class CronUploader extends Extension {
$tags = $subdir;
$tags = str_replace("/", " ", $tags);
$tags = str_replace("__", " ", $tags);
if ($tags == "") $tags = " ";
if ($tags == "") {
$tags = " ";
}
$tags = trim($tags);
}
$img = array (
$img = [
0 => $fullpath,
1 => $pathinfo ["basename"],
2 => $tags
);
];
array_push($this->image_queue, $img);
}
}
@ -371,7 +388,8 @@ class CronUploader extends Extension {
/**
* Adds a message to the info being published at the end
*/
private function add_upload_info(string $text, int $addon = 0): int {
private function add_upload_info(string $text, int $addon = 0): int
{
$info = $this->upload_info;
$time = "[" .date('Y-m-d H:i:s'). "]";
@ -395,7 +413,8 @@ class CronUploader extends Extension {
/**
* This is run at the end to display & save the log.
*/
private function handle_log() {
private function handle_log()
{
global $page;
// Display message
@ -406,12 +425,13 @@ class CronUploader extends Extension {
// Save log
$log_path = $this->root_dir . "/uploads.log";
if (file_exists($log_path))
if (file_exists($log_path)) {
$prev_content = file_get_contents($log_path);
else $prev_content = "";
} else {
$prev_content = "";
}
$content = $prev_content ."\r\n".$this->upload_info;
file_put_contents($log_path, $content);
}
}

View File

@ -14,44 +14,54 @@
*
* You can also add your website name as prefix or suffix to the title of each page on your website.
*/
class custom_html_headers extends Extension {
class custom_html_headers extends Extension
{
# Adds setup block for custom <head> content
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Custom HTML Headers");
// custom headers
$sb->add_longtext_option("custom_html_headers",
"HTML Code to place within &lt;head&gt;&lt;/head&gt; on all pages<br>");
$sb->add_longtext_option(
"custom_html_headers",
"HTML Code to place within &lt;head&gt;&lt;/head&gt; on all pages<br>"
);
// modified title
$sb->add_choice_option("sitename_in_title", array(
$sb->add_choice_option("sitename_in_title", [
"none" => 0,
"as prefix" => 1,
"as suffix" => 2
), "<br>Add website name in title");
], "<br>Add website name in title");
$event->panel->add_block($sb);
}
public function onInitExt(InitExtEvent $event) {
public function onInitExt(InitExtEvent $event)
{
global $config;
$config->set_default_int("sitename_in_title", 0);
}
# Load Analytics tracking code on page request
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
$this->handle_custom_html_headers();
$this->handle_modified_page_title();
}
private function handle_custom_html_headers() {
private function handle_custom_html_headers()
{
global $config, $page;
$header = $config->get_string('custom_html_headers', '');
if ($header!='') $page->add_html_header($header);
if ($header!='') {
$page->add_html_header($header);
}
}
private function handle_modified_page_title() {
private function handle_modified_page_title()
{
global $config, $page;
// get config values
@ -60,13 +70,13 @@ class custom_html_headers extends Extension {
// if feature is enabled & sitename isn't already in title
// (can occur on index & other pages)
if ($sitename_in_title != 0 && !strstr($page->title, $site_title))
{
if ($sitename_in_title == 1)
$page->title = "$site_title - $page->title"; // as prefix
else if ($sitename_in_title == 2)
$page->title = "$page->title - $site_title"; // as suffix
if ($sitename_in_title != 0 && !strstr($page->title, $site_title)) {
if ($sitename_in_title == 1) {
$page->title = "$site_title - $page->title";
} // as prefix
elseif ($sitename_in_title == 2) {
$page->title = "$page->title - $site_title";
} // as suffix
}
}
}

View File

@ -47,15 +47,18 @@ Completely compatibility will probably involve a rewrite with a different URL
*/
class DanbooruApi extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class DanbooruApi extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
if ($event->page_matches("api") && ($event->get_arg(0) == 'danbooru')) {
$this->api_danbooru($event);
}
}
// Danbooru API
private function api_danbooru(PageRequestEvent $event) {
private function api_danbooru(PageRequestEvent $event)
{
global $page;
$page->set_mode("data");
@ -63,14 +66,10 @@ class DanbooruApi extends Extension {
// No XML data is returned from this function
$page->set_type("text/plain");
$this->api_add_post();
}
elseif(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) {
} elseif (($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) {
$page->set_type("application/xml");
$page->set_data($this->api_find_posts());
}
elseif($event->get_arg(1) == 'find_tags') {
} elseif ($event->get_arg(1) == 'find_tags') {
$page->set_type("application/xml");
$page->set_data($this->api_find_tags());
}
@ -91,7 +90,8 @@ class DanbooruApi extends Extension {
* Authenticates a user based on the contents of the login and password parameters
* or makes them anonymous. Does not set any cookies or anything permanent.
*/
private function authenticate_user() {
private function authenticate_user()
{
global $config, $user;
if (isset($_REQUEST['login']) && isset($_REQUEST['password'])) {
@ -102,8 +102,7 @@ class DanbooruApi extends Extension {
$duser = User::by_name_and_pass($name, $pass);
if (!is_null($duser)) {
$user = $duser;
}
else {
} else {
$user = User::by_id($config->get_int("anon_id", 0));
}
}
@ -118,31 +117,31 @@ class DanbooruApi extends Extension {
* - 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() {
private function api_find_tags(): string
{
global $database;
$results = array();
$results = [];
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));
[$id]
);
foreach ($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
$results[] = [$row['count'], $row['tag'], $row['id']];
}
}
}
elseif(isset($_GET['name'])) {
} 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));
[$name]
);
foreach ($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
$results[] = [$row['count'], $row['tag'], $row['id']];
}
}
}
@ -150,26 +149,26 @@ class DanbooruApi extends Extension {
elseif (false && isset($_GET['tags'])) {
$start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0;
$tags = Tag::explode($_GET['tags']);
}
else {
} 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));
[$start]
);
foreach ($sqlresult as $row) {
$results[] = array($row['count'], $row['tag'], $row['id']);
$results[] = [$row['count'], $row['tag'], $row['id']];
}
}
// Tag results collected, build XML output
$xml = "<tags>\n";
foreach ($results as $tag) {
$xml .= xml_tag("tag", array(
$xml .= xml_tag("tag", [
"type" => "0",
"counts" => $tag[0],
"name" => $tag[1],
"id" => $tag[2],
));
]);
}
$xml .= "</tags>";
return $xml;
@ -189,8 +188,9 @@ class DanbooruApi extends Extension {
*
* #return string
*/
private function api_find_posts() {
$results = array();
private function api_find_posts()
{
$results = [];
$this->authenticate_user();
$start = 0;
@ -201,26 +201,25 @@ class DanbooruApi extends Extension {
$results[] = Image::by_hash($md5);
}
$count = count($results);
}
elseif(isset($_GET['id'])) {
} elseif (isset($_GET['id'])) {
$idlist = explode(",", $_GET['id']);
foreach ($idlist as $id) {
$results[] = Image::by_id($id);
}
$count = count($results);
}
else {
} else {
$limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100;
// Calculate start offset.
if (isset($_GET['page'])) // Danbooru API uses 'page' >= 1
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
} elseif (isset($_GET['pid'])) { // Gelbooru API uses 'pid' >= 0
$start = int_escape($_GET['pid']) * $limit;
else
} else {
$start = 0;
}
$tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : array();
$tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : [];
$count = Image::count_images($tags);
$results = Image::find_images(max($start, 0), min($limit, 100), $tags);
}
@ -231,12 +230,13 @@ class DanbooruApi extends Extension {
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))
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(
$xml .= xml_tag("post", [
"id" => $img->id,
"md5" => $img->hash,
"file_name" => $img->filename,
@ -253,7 +253,7 @@ class DanbooruApi extends Extension {
"source" => $img->source,
"score" => 0,
"author" => $owner->name
));
]);
}
$xml .= "</posts>";
return $xml;
@ -286,7 +286,8 @@ class DanbooruApi extends Extension {
* Get:
* - Redirected to the newly uploaded post.
*/
private function api_add_post() {
private function api_add_post()
{
global $user, $config, $page;
$danboorup_kludge = 1; // danboorup for firefox makes broken links out of location: /path
@ -354,14 +355,16 @@ class DanbooruApi extends Extension {
$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);
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 = [];
$metadata['filename'] = $fileinfo['basename'];
$metadata['extension'] = $fileinfo['extension'];
$metadata['tags'] = $posttags;
@ -376,7 +379,9 @@ class DanbooruApi extends Extension {
// 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);
if ($danboorup_kludge) {
$newid = make_http($newid);
}
// Did we POST or GET this call?
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@ -391,5 +396,3 @@ class DanbooruApi extends Extension {
}
}
}

View File

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

View File

@ -12,17 +12,23 @@
* message specified in the box.
*/
class Downtime extends Extension {
public function get_priority(): int {return 10;}
class Downtime extends Extension
{
public function get_priority(): int
{
return 10;
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Downtime");
$sb->add_bool_option("downtime", "Disable non-admin access: ");
$sb->add_longtext_option("downtime_message", "<br>");
$event->panel->add_block($sb);
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $config, $page, $user;
if ($config->get_bool("downtime")) {
@ -39,8 +45,12 @@ class Downtime extends Extension {
}
}
private function is_safe_page(PageRequestEvent $event) {
if($event->page_matches("user_admin/login")) return true;
else return false;
private function is_safe_page(PageRequestEvent $event)
{
if ($event->page_matches("user_admin/login")) {
return true;
} else {
return false;
}
}
}

View File

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

View File

@ -1,18 +1,25 @@
<?php
class DowntimeTheme extends Themelet {
class DowntimeTheme extends Themelet
{
/**
* Show the admin that downtime mode is enabled
*/
public function display_notification(Page $page) {
$page->add_block(new Block("Downtime",
"<span style='font-size: 1.5em'><b><center>DOWNTIME MODE IS ON!</center></b></span>", "left", 0));
public function display_notification(Page $page)
{
$page->add_block(new Block(
"Downtime",
"<span style='font-size: 1.5em'><b><center>DOWNTIME MODE IS ON!</center></b></span>",
"left",
0
));
}
/**
* Display $message and exit
*/
public function display_message(string $message) {
public function display_message(string $message)
{
global $config, $user, $page;
$theme_name = $config->get_string('theme');
$data_href = get_base_href();
@ -21,7 +28,8 @@ class DowntimeTheme extends Themelet {
$page->set_mode('data');
$page->set_code(503);
$page->set_data(<<<EOD
$page->set_data(
<<<EOD
<html>
<head>
<title>Downtime</title>

View File

@ -16,14 +16,17 @@
/**
* Class Emoticons
*/
class Emoticons extends FormatterExtension {
public function format(string $text): string {
class Emoticons extends FormatterExtension
{
public function format(string $text): string
{
$data_href = get_base_href();
$text = preg_replace("/:([a-z]*?):/s", "<img src='$data_href/ext/emoticons/default/\\1.gif'>", $text);
return $text;
}
public function strip(string $text): string {
public function strip(string $text): string
{
return $text;
}
}
@ -31,11 +34,12 @@ class Emoticons extends FormatterExtension {
/**
* Class EmoticonList
*/
class EmoticonList extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class EmoticonList extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
if ($event->page_matches("emote/list")) {
$this->theme->display_emotes(glob("ext/emoticons/default/*"));
}
}
}

View File

@ -1,6 +1,8 @@
<?php
class EmoticonTest extends ShimmiePHPUnitTestCase {
public function testEmoticons() {
class EmoticonTest extends ShimmiePHPUnitTestCase
{
public function testEmoticons()
{
global $user;
$this->log_in_as_user();
@ -16,4 +18,3 @@ class EmoticonTest extends ShimmiePHPUnitTestCase {
//$this->assert_text(":arrow:");
}
}

View File

@ -1,6 +1,8 @@
<?php
class EmoticonListTheme extends Themelet {
public function display_emotes(array $list) {
class EmoticonListTheme extends Themelet
{
public function display_emotes(array $list)
{
global $page;
$data_href = get_base_href();
$html = "<html><head><title>Emoticon list</title></head><body>";
@ -10,7 +12,9 @@ class EmoticonListTheme extends Themelet {
$pathinfo = pathinfo($item);
$name = $pathinfo["filename"];
$html .= "<td><img src='$data_href/$item'> :$name:</td>";
if($n++ % 3 == 0) $html .= "</tr><tr>";
if ($n++ % 3 == 0) {
$html .= "</tr><tr>";
}
}
$html .= "</tr></table>";
$html .= "</body></html>";
@ -18,4 +22,3 @@ class EmoticonListTheme extends Themelet {
$page->set_data($html);
}
}

View File

@ -12,8 +12,10 @@
* versions of PHP I should test with, etc.
*/
class ET extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class ET extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $user;
if ($event->page_matches("system_info")) {
if ($user->can("view_sysinfo")) {
@ -22,7 +24,8 @@ class ET extends Extension {
}
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
if ($user->can("view_sysinfo")) {
$event->add_link("System Info", make_link("system_info"));
@ -32,10 +35,11 @@ class ET extends Extension {
/**
* Collect the information and return it in a keyed array.
*/
private function get_info() {
private function get_info()
{
global $config, $database;
$info = array();
$info = [];
$info['site_title'] = $config->get_string("title");
$info['site_theme'] = $config->get_string("theme");
$info['site_url'] = "http://" . $_SERVER["HTTP_HOST"] . get_base_href();
@ -61,13 +65,12 @@ class ET extends Extension {
$info['stat_tags'] = $database->get_one("SELECT COUNT(*) FROM tags");
$info['stat_image_tags'] = $database->get_one("SELECT COUNT(*) FROM image_tags");
$els = array();
$els = [];
foreach (get_declared_classes() as $class) {
$rclass = new ReflectionClass($class);
if ($rclass->isAbstract()) {
// don't do anything
}
elseif(is_subclass_of($class, "Extension")) {
} elseif (is_subclass_of($class, "Extension")) {
$els[] = $class;
}
}
@ -82,4 +85,3 @@ class ET extends Extension {
return $info;
}
}

View File

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

View File

@ -1,12 +1,14 @@
<?php
class ETTheme extends Themelet {
class ETTheme extends Themelet
{
/*
* Create a page showing info
*
* $info = an array of ($name => $value)
*/
public function display_info_page($info) {
public function display_info_page($info)
{
global $page;
$page->set_title("System Info");
@ -15,7 +17,8 @@ class ETTheme extends Themelet {
$page->add_block(new Block("Information:", $this->build_data_form($info)));
}
protected function build_data_form($info) {
protected function build_data_form($info)
{
$data = <<<EOD
Optional:
Site title: {$info['site_title']}
@ -59,4 +62,3 @@ EOD;
return $html;
}
}

View File

@ -12,17 +12,27 @@
* extensions and read their documentation
*/
function __extman_extcmp(ExtensionInfo $a, ExtensionInfo $b): int {
function __extman_extcmp(ExtensionInfo $a, ExtensionInfo $b): int
{
return strcmp($a->name, $b->name);
}
class ExtensionInfo {
public $ext_name, $name, $link, $author, $email;
public $description, $documentation, $version, $visibility;
class ExtensionInfo
{
public $ext_name;
public $name;
public $link;
public $author;
public $email;
public $description;
public $documentation;
public $version;
public $visibility;
public $enabled;
public function __construct($main) {
$matches = array();
public function __construct($main)
{
$matches = [];
$lines = file($main);
$number_of_lines = count($lines);
preg_match("#ext/(.*)/main.php#", $main, $matches);
@ -34,27 +44,21 @@ class ExtensionInfo {
$line = $lines[$i];
if (preg_match("/Name: (.*)/", $line, $matches)) {
$this->name = $matches[1];
}
else if(preg_match("/Visibility: (.*)/", $line, $matches)) {
} elseif (preg_match("/Visibility: (.*)/", $line, $matches)) {
$this->visibility = $matches[1];
}
else if(preg_match("/Link: (.*)/", $line, $matches)) {
} elseif (preg_match("/Link: (.*)/", $line, $matches)) {
$this->link = $matches[1];
if ($this->link[0] == "/") {
$this->link = make_link(substr($this->link, 1));
}
}
else if(preg_match("/Version: (.*)/", $line, $matches)) {
} elseif (preg_match("/Version: (.*)/", $line, $matches)) {
$this->version = $matches[1];
}
else if(preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) {
} elseif (preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) {
$this->author = $matches[1];
$this->email = $matches[2];
}
else if(preg_match("/Author: (.*)/", $line, $matches)) {
} elseif (preg_match("/Author: (.*)/", $line, $matches)) {
$this->author = $matches[1];
}
else if(preg_match("/(.*)Description: ?(.*)/", $line, $matches)) {
} elseif (preg_match("/(.*)Description: ?(.*)/", $line, $matches)) {
$this->description = $matches[2];
$start = $matches[1]." ";
$start_len = strlen($start);
@ -62,8 +66,7 @@ class ExtensionInfo {
$this->description .= " ".substr($lines[$i+1], $start_len);
$i++;
}
}
else if(preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) {
} elseif (preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) {
$this->documentation = $matches[2];
$start = $matches[1]." ";
$start_len = strlen($start);
@ -72,25 +75,31 @@ class ExtensionInfo {
$i++;
}
$this->documentation = str_replace('$site', make_http(get_base_href()), $this->documentation);
}
else if(preg_match("/\*\//", $line, $matches)) {
} elseif (preg_match("/\*\//", $line, $matches)) {
break;
}
}
}
private function is_enabled(string $fname): ?bool {
private function is_enabled(string $fname): ?bool
{
$core = explode(",", CORE_EXTS);
$extra = explode(",", EXTRA_EXTS);
if(in_array($fname, $extra)) return true; // enabled
if(in_array($fname, $core)) return null; // core
if (in_array($fname, $extra)) {
return true;
} // enabled
if (in_array($fname, $core)) {
return null;
} // core
return false; // not enabled
}
}
class ExtManager extends Extension {
public function onPageRequest(PageRequestEvent $event) {
class ExtManager extends Extension
{
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("ext_manager")) {
if ($user->can("manage_extension_list")) {
@ -100,17 +109,17 @@ class ExtManager extends Extension {
log_warning("ext_manager", "Active extensions changed", "Active extensions changed");
$page->set_mode("redirect");
$page->set_redirect(make_link("ext_manager"));
} else {
$this->theme->display_error(
500,
"File Operation Failed",
"The config file (data/config/extensions.conf.php) isn't writable by the web server :("
);
}
else {
$this->theme->display_error(500, "File Operation Failed",
"The config file (data/config/extensions.conf.php) isn't writable by the web server :(");
}
}
else {
} else {
$this->theme->display_table($page, $this->get_extensions(true), true);
}
}
else {
} else {
$this->theme->display_table($page, $this->get_extensions(false), false);
}
}
@ -120,30 +129,30 @@ class ExtManager extends Extension {
if (file_exists("ext/$ext/main.php")) {
$info = new ExtensionInfo("ext/$ext/main.php");
$this->theme->display_doc($page, $info);
}
else {
} else {
$this->theme->display_table($page, $this->get_extensions(false), false);
}
}
}
public function onCommand(CommandEvent $event) {
public function onCommand(CommandEvent $event)
{
if ($event->cmd == "help") {
print "\tdisable-all-ext\n";
print "\t\tdisable all extensions\n\n";
}
if ($event->cmd == "disable-all-ext") {
$this->write_config(array());
$this->write_config([]);
}
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
if ($user->can("manage_extension_list")) {
$event->add_link("Extension Manager", make_link("ext_manager"));
}
else {
} else {
$event->add_link("Help", make_link("ext_doc"));
}
}
@ -151,12 +160,12 @@ class ExtManager extends Extension {
/**
* #return ExtensionInfo[]
*/
private function get_extensions(bool $all): array {
$extensions = array();
private function get_extensions(bool $all): array
{
$extensions = [];
if ($all) {
$exts = zglob("ext/*/main.php");
}
else {
} else {
$exts = zglob("ext/{".ENABLED_EXTS."}/main.php");
}
foreach ($exts as $main) {
@ -166,12 +175,13 @@ class ExtManager extends Extension {
return $extensions;
}
private function set_things($settings) {
private function set_things($settings)
{
$core = explode(",", CORE_EXTS);
$extras = array();
$extras = [];
foreach (glob("ext/*/main.php") as $main) {
$matches = array();
$matches = [];
preg_match("#ext/(.*)/main.php#", $main, $matches);
$fname = $matches[1];
@ -186,7 +196,8 @@ class ExtManager extends Extension {
/**
* #param string[] $extras
*/
private function write_config(array $extras) {
private function write_config(array $extras)
{
file_put_contents(
"data/config/extensions.conf.php",
'<'.'?php'."\n".

View File

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

View File

@ -1,10 +1,12 @@
<?php
class ExtManagerTheme extends Themelet {
class ExtManagerTheme extends Themelet
{
/**
* #param ExtensionInfo[] $extensions
*/
public function display_table(Page $page, array $extensions, bool $editable) {
public function display_table(Page $page, array $extensions, bool $editable)
{
$h_en = $editable ? "<th>Enabled</th>" : "";
$html = "
".make_form(make_link("ext_manager/set"))."
@ -20,12 +22,14 @@ class ExtManagerTheme extends Themelet {
<tbody>
";
foreach ($extensions as $extension) {
if(!$editable && $extension->visibility == "admin") continue;
if (!$editable && $extension->visibility == "admin") {
continue;
}
$h_name = html_escape(empty($extension->name) ? $extension->ext_name : $extension->name);
$h_description = html_escape($extension->description);
$h_link = make_link("ext_doc/".url_escape($extension->ext_name));
$h_enabled = ($extension->enabled === TRUE ? " checked='checked'" : ($extension->enabled === FALSE ? "" : " disabled checked='checked'"));
$h_enabled = ($extension->enabled === true ? " checked='checked'" : ($extension->enabled === false ? "" : " disabled checked='checked'"));
$h_enabled_box = $editable ? "<td><input type='checkbox' name='ext_".html_escape($extension->ext_name)."'$h_enabled></td>" : "";
$h_docs = ($extension->documentation ? "<a href='$h_link'>■</a>" : ""); //TODO: A proper "docs" symbol would be preferred here.
@ -109,13 +113,13 @@ class ExtManagerTheme extends Themelet {
}
*/
public function display_doc(Page $page, ExtensionInfo $info) {
public function display_doc(Page $page, ExtensionInfo $info)
{
$author = "";
if ($info->author) {
if ($info->email) {
$author = "<br><b>Author:</b> <a href=\"mailto:".html_escape($info->email)."\">".html_escape($info->author)."</a>";
}
else {
} else {
$author = "<br><b>Author:</b> ".html_escape($info->author);
}
}
@ -138,4 +142,3 @@ class ExtManagerTheme extends Themelet {
$page->add_block(new Block("Documentation", $html));
}
}

View File

@ -13,7 +13,8 @@
* using the $favorites placeholder
*/
class FavoriteSetEvent extends Event {
class FavoriteSetEvent extends Event
{
/** @var int */
public $image_id;
/** @var \User */
@ -21,7 +22,8 @@ class FavoriteSetEvent extends Event {
/** @var bool */
public $do_set;
public function __construct(int $image_id, User $user, bool $do_set) {
public function __construct(int $image_id, User $user, bool $do_set)
{
assert(is_int($image_id));
assert(is_bool($do_set));
@ -31,15 +33,18 @@ class FavoriteSetEvent extends Event {
}
}
class Favorites extends Extension {
public function onInitExt(InitExtEvent $event) {
class Favorites extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config;
if ($config->get_int("ext_favorites_version", 0) < 1) {
$this->install();
}
}
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
{
global $database, $user;
if (!$user->is_anonymous()) {
$user_id = $user->id;
@ -47,20 +52,23 @@ class Favorites extends Extension {
$is_favorited = $database->get_one(
"SELECT COUNT(*) AS ct FROM user_favorites WHERE user_id = :user_id AND image_id = :image_id",
array("user_id"=>$user_id, "image_id"=>$image_id)) > 0;
["user_id"=>$user_id, "image_id"=>$image_id]
) > 0;
$event->add_part($this->theme->get_voter_html($event->image, $is_favorited));
}
}
public function onDisplayingImage(DisplayingImageEvent $event) {
public function onDisplayingImage(DisplayingImageEvent $event)
{
$people = $this->list_persons_who_have_favorited($event->image);
if (count($people) > 0) {
$this->theme->display_people($people);
}
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("change_favorite") && !$user->is_anonymous() && $user->check_auth_token()) {
$image_id = int_escape($_POST['image_id']);
@ -68,8 +76,7 @@ class Favorites extends Extension {
if ($_POST['favorite_action'] == "set") {
send_event(new FavoriteSetEvent($image_id, $user, true));
log_debug("favourite", "Favourite set for $image_id", "Favourite added");
}
else {
} else {
send_event(new FavoriteSetEvent($image_id, $user, false));
log_debug("favourite", "Favourite removed for $image_id", "Favourite removed");
}
@ -79,15 +86,17 @@ class Favorites extends Extension {
}
}
public function onUserPageBuilding(UserPageBuildingEvent $event) {
$i_favorites_count = Image::count_images(array("favorited_by={$event->display_user->name}"));
public function onUserPageBuilding(UserPageBuildingEvent $event)
{
$i_favorites_count = Image::count_images(["favorited_by={$event->display_user->name}"]);
$i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1;
$h_favorites_rate = sprintf("%.1f", ($i_favorites_count / $i_days_old));
$favorites_link = make_link("post/list/favorited_by={$event->display_user->name}/1");
$event->add_stats("<a href='$favorites_link'>Images favorited</a>: $i_favorites_count, $h_favorites_rate per day");
}
public function onImageInfoSet(ImageInfoSetEvent $event) {
public function onImageInfoSet(ImageInfoSetEvent $event)
{
global $user;
if (
in_array('favorite_action', $_POST) &&
@ -97,55 +106,58 @@ class Favorites extends Extension {
}
}
public function onFavoriteSet(FavoriteSetEvent $event) {
public function onFavoriteSet(FavoriteSetEvent $event)
{
global $user;
$this->add_vote($event->image_id, $user->id, $event->do_set);
}
// FIXME: this should be handled by the foreign key. Check that it
// is, and then remove this
public function onImageDeletion(ImageDeletionEvent $event) {
public function onImageDeletion(ImageDeletionEvent $event)
{
global $database;
$database->execute("DELETE FROM user_favorites WHERE image_id=:image_id", array("image_id"=>$event->image->id));
$database->execute("DELETE FROM user_favorites WHERE image_id=:image_id", ["image_id"=>$event->image->id]);
}
public function onParseLinkTemplate(ParseLinkTemplateEvent $event) {
public function onParseLinkTemplate(ParseLinkTemplateEvent $event)
{
$event->replace('$favorites', $event->image->favorites);
}
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
{
global $user;
$username = url_escape($user->name);
$event->add_link("My Favorites", make_link("post/list/favorited_by=$username/1"), 20);
}
public function onSearchTermParse(SearchTermParseEvent $event) {
$matches = array();
public function onSearchTermParse(SearchTermParseEvent $event)
{
$matches = [];
if (preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
$cmp = ltrim($matches[1], ":") ?: "=";
$favorites = $matches[2];
$event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE favorites $cmp $favorites)"));
}
else if(preg_match("/^favorited_by[=|:](.*)$/i", $event->term, $matches)) {
} elseif (preg_match("/^favorited_by[=|:](.*)$/i", $event->term, $matches)) {
$user = User::by_name($matches[1]);
if (!is_null($user)) {
$user_id = $user->id;
}
else {
} else {
$user_id = -1;
}
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)"));
}
else if(preg_match("/^favorited_by_userno[=|:](\d+)$/i", $event->term, $matches)) {
} elseif (preg_match("/^favorited_by_userno[=|:](\d+)$/i", $event->term, $matches)) {
$user_id = int_escape($matches[1]);
$event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)"));
}
}
private function install() {
private function install()
{
global $database;
global $config;
@ -160,7 +172,7 @@ class Favorites extends Extension {
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE
");
$database->execute("CREATE INDEX user_favorites_image_id_idx ON user_favorites(image_id)", array());
$database->execute("CREATE INDEX user_favorites_image_id_idx ON user_favorites(image_id)", []);
$config->set_int("ext_favorites_version", 2);
}
@ -176,31 +188,36 @@ class Favorites extends Extension {
}
}
private function add_vote(int $image_id, int $user_id, bool $do_set) {
private function add_vote(int $image_id, int $user_id, bool $do_set)
{
global $database;
if ($do_set) {
$database->Execute(
"INSERT INTO user_favorites(image_id, user_id, created_at) VALUES(:image_id, :user_id, NOW())",
array("image_id"=>$image_id, "user_id"=>$user_id));
["image_id"=>$image_id, "user_id"=>$user_id]
);
} else {
$database->Execute(
"DELETE FROM user_favorites WHERE image_id = :image_id AND user_id = :user_id",
array("image_id"=>$image_id, "user_id"=>$user_id));
["image_id"=>$image_id, "user_id"=>$user_id]
);
}
$database->Execute(
"UPDATE images SET favorites=(SELECT COUNT(*) FROM user_favorites WHERE image_id=:image_id) WHERE id=:user_id",
array("image_id"=>$image_id, "user_id"=>$user_id));
["image_id"=>$image_id, "user_id"=>$user_id]
);
}
/**
* #return string[]
*/
private function list_persons_who_have_favorited(Image $image): array {
private function list_persons_who_have_favorited(Image $image): array
{
global $database;
return $database->get_col(
"SELECT name FROM users WHERE id IN (SELECT user_id FROM user_favorites WHERE image_id = :image_id) ORDER BY name",
array("image_id"=>$image->id));
["image_id"=>$image->id]
);
}
}

View File

@ -1,6 +1,8 @@
<?php
class FavoritesTest extends ShimmiePHPUnitTestCase {
public function testFavorites() {
class FavoritesTest extends ShimmiePHPUnitTestCase
{
public function testFavorites()
{
$this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "test");
@ -26,4 +28,3 @@ class FavoritesTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text("Favorited By");
}
}

View File

@ -1,7 +1,9 @@
<?php
class FavoritesTheme extends Themelet {
public function get_voter_html(Image $image, $is_favorited) {
class FavoritesTheme extends Themelet
{
public function get_voter_html(Image $image, $is_favorited)
{
$i_image_id = int_escape($image->id);
$name = $is_favorited ? "unset" : "set";
$label = $is_favorited ? "Un-Favorite" : "Favorite";
@ -16,7 +18,8 @@ class FavoritesTheme extends Themelet {
return $html;
}
public function display_people($username_array) {
public function display_people($username_array)
{
global $page;
$i_favorites = count($username_array);
@ -32,5 +35,3 @@ class FavoritesTheme extends Themelet {
$page->add_block(new Block("Favorited By", $html, "left", 25));
}
}

View File

@ -19,13 +19,16 @@
* every couple of hours.
*/
class Featured extends Extension {
public function onInitExt(InitExtEvent $event) {
class Featured extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config;
$config->set_default_int('featured_id', 0);
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $config, $page, $user;
if ($event->page_matches("featured_image")) {
if ($event->get_arg(0) == "set" && $user->check_auth_token()) {
@ -56,7 +59,8 @@ class Featured extends Extension {
}
}
public function onPostListBuilding(PostListBuildingEvent $event) {
public function onPostListBuilding(PostListBuildingEvent $event)
{
global $config, $database, $page, $user;
$fid = $config->get_int("featured_id");
if ($fid > 0) {
@ -70,7 +74,7 @@ class Featured extends Extension {
}
if (!is_null($image)) {
if (ext_is_live("Ratings")) {
if(strpos(Ratings::get_user_privs($user), $image->rating) === FALSE) {
if (strpos(Ratings::get_user_privs($user), $image->rating) === false) {
return;
}
}
@ -79,11 +83,11 @@ class Featured extends Extension {
}
}
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
{
global $user;
if ($user->can("edit_feature")) {
$event->add_part($this->theme->get_buttons_html($event->image->id));
}
}
}

View File

@ -1,6 +1,8 @@
<?php
class FeaturedTest extends ShimmiePHPUnitTestCase {
public function testFeatured() {
class FeaturedTest extends ShimmiePHPUnitTestCase
{
public function testFeatured()
{
$this->log_in_as_user();
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
@ -32,4 +34,3 @@ class FeaturedTest extends ShimmiePHPUnitTestCase {
$this->assert_no_text("Featured Image");
}
}

View File

@ -1,11 +1,14 @@
<?php
class FeaturedTheme extends Themelet {
public function display_featured(Page $page, Image $image): void {
class FeaturedTheme extends Themelet
{
public function display_featured(Page $page, Image $image): void
{
$page->add_block(new Block("Featured Image", $this->build_featured_html($image), "left", 3));
}
public function get_buttons_html(int $image_id): string {
public function get_buttons_html(int $image_id): string
{
global $user;
return "
".make_form(make_link("featured_image/set"))."
@ -16,7 +19,8 @@ class FeaturedTheme extends Themelet {
";
}
public function build_featured_html(Image $image, ?string $query=null): string {
public function build_featured_html(Image $image, ?string $query=null): string
{
$i_id = int_escape($image->id);
$h_view_link = make_link("post/view/$i_id", $query);
$h_thumb_link = $image->get_thumb_link();
@ -30,4 +34,3 @@ class FeaturedTheme extends Themelet {
";
}
}

View File

@ -15,8 +15,10 @@ Todo:
*Smiley filter, word filter, etc should work with our extension
*/
class Forum extends Extension {
public function onInitExt(InitExtEvent $event) {
class Forum extends Extension
{
public function onInitExt(InitExtEvent $event)
{
global $config, $database;
// shortcut to latest
@ -31,7 +33,7 @@ class Forum extends Extension {
uptodate SCORE_DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT
");
$database->execute("CREATE INDEX forum_threads_date_idx ON forum_threads(date)", array());
$database->execute("CREATE INDEX forum_threads_date_idx ON forum_threads(date)", []);
$database->create_table("forum_posts", "
id SCORE_AIPK,
@ -42,7 +44,7 @@ class Forum extends Extension {
FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT,
FOREIGN KEY (thread_id) REFERENCES forum_threads (id) ON UPDATE CASCADE ON DELETE CASCADE
");
$database->execute("CREATE INDEX forum_posts_date_idx ON forum_posts(date)", array());
$database->execute("CREATE INDEX forum_posts_date_idx ON forum_posts(date)", []);
$config->set_int("forum_version", 2);
$config->set_int("forumTitleSubString", 25);
@ -60,7 +62,8 @@ class Forum extends Extension {
}
}
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Forum");
$sb->add_int_option("forumTitleSubString", "Title max long: ");
$sb->add_int_option("forumThreadsPerPage", "<br>Threads per page: ");
@ -70,11 +73,12 @@ class Forum extends Extension {
$event->panel->add_block($sb);
}
public function onUserPageBuilding(UserPageBuildingEvent $event) {
public function onUserPageBuilding(UserPageBuildingEvent $event)
{
global $database;
$threads_count = $database->get_one("SELECT COUNT(*) FROM forum_threads WHERE user_id=?", array($event->display_user->id));
$posts_count = $database->get_one("SELECT COUNT(*) FROM forum_posts WHERE user_id=?", array($event->display_user->id));
$threads_count = $database->get_one("SELECT COUNT(*) FROM forum_threads WHERE user_id=?", [$event->display_user->id]);
$posts_count = $database->get_one("SELECT COUNT(*) FROM forum_posts WHERE user_id=?", [$event->display_user->id]);
$days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1;
@ -86,29 +90,35 @@ class Forum extends Extension {
}
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $page, $user;
if ($event->page_matches("forum")) {
switch ($event->get_arg(0)) {
case "index":
$this->show_last_threads($page, $event, $user->is_admin());
if(!$user->is_anonymous()) $this->theme->display_new_thread_composer($page);
if (!$user->is_anonymous()) {
$this->theme->display_new_thread_composer($page);
}
break;
case "view":
$threadID = int_escape($event->get_arg(1));
$pageNumber = int_escape($event->get_arg(2));
list($errors) = $this->sanity_check_viewed_thread($threadID);
if($errors!=null)
{
if ($errors!=null) {
$this->theme->display_error(500, "Error", $errors);
break;
}
$this->show_posts($event, $user->is_admin());
if($user->is_admin()) $this->theme->add_actions_block($page, $threadID);
if(!$user->is_anonymous()) $this->theme->display_new_post_composer($page, $threadID);
if ($user->is_admin()) {
$this->theme->add_actions_block($page, $threadID);
}
if (!$user->is_anonymous()) {
$this->theme->display_new_post_composer($page, $threadID);
}
break;
case "new":
global $page;
@ -116,12 +126,10 @@ class Forum extends Extension {
break;
case "create":
$redirectTo = "forum/index";
if (!$user->is_anonymous())
{
if (!$user->is_anonymous()) {
list($errors) = $this->sanity_check_new_thread();
if($errors!=null)
{
if ($errors!=null) {
$this->theme->display_error(500, "Error", $errors);
break;
}
@ -139,7 +147,9 @@ class Forum extends Extension {
$threadID = int_escape($event->get_arg(1));
$postID = int_escape($event->get_arg(2));
if ($user->is_admin()) {$this->delete_post($postID);}
if ($user->is_admin()) {
$this->delete_post($postID);
}
$page->set_mode("redirect");
$page->set_redirect(make_link("forum/view/".$threadID));
@ -147,8 +157,9 @@ class Forum extends Extension {
case "nuke":
$threadID = int_escape($event->get_arg(1));
if ($user->is_admin())
if ($user->is_admin()) {
$this->delete_thread($threadID);
}
$page->set_mode("redirect");
$page->set_redirect(make_link("forum/index"));
@ -156,12 +167,10 @@ class Forum extends Extension {
case "answer":
$threadID = int_escape($_POST["threadID"]);
$total_pages = $this->get_total_pages_for_thread($threadID);
if (!$user->is_anonymous())
{
if (!$user->is_anonymous()) {
list($errors) = $this->sanity_check_new_post();
if ($errors!=null)
{
if ($errors!=null) {
$this->theme->display_error(500, "Error", $errors);
break;
}
@ -182,7 +191,7 @@ class Forum extends Extension {
private function get_total_pages_for_thread(int $threadID)
{
global $database, $config;
$result = $database->get_row("SELECT COUNT(1) AS count FROM forum_posts WHERE thread_id = ?", array($threadID));
$result = $database->get_row("SELECT COUNT(1) AS count FROM forum_posts WHERE thread_id = ?", [$threadID]);
return ceil($result["count"] / $config->get_int("forumPostsPerPage"));
}
@ -190,70 +199,54 @@ class Forum extends Extension {
private function sanity_check_new_thread()
{
$errors = null;
if (!array_key_exists("title", $_POST))
{
if (!array_key_exists("title", $_POST)) {
$errors .= "<div id='error'>No title supplied.</div>";
}
else if (strlen($_POST["title"]) == 0)
{
} elseif (strlen($_POST["title"]) == 0) {
$errors .= "<div id='error'>You cannot have an empty title.</div>";
}
else if (strlen(html_escape($_POST["title"])) > 255)
{
} elseif (strlen(html_escape($_POST["title"])) > 255) {
$errors .= "<div id='error'>Your title is too long.</div>";
}
if (!array_key_exists("message", $_POST))
{
if (!array_key_exists("message", $_POST)) {
$errors .= "<div id='error'>No message supplied.</div>";
}
else if (strlen($_POST["message"]) == 0)
{
} elseif (strlen($_POST["message"]) == 0) {
$errors .= "<div id='error'>You cannot have an empty message.</div>";
}
return array($errors);
return [$errors];
}
private function sanity_check_new_post()
{
$errors = null;
if (!array_key_exists("threadID", $_POST))
{
if (!array_key_exists("threadID", $_POST)) {
$errors = "<div id='error'>No thread ID supplied.</div>";
}
else if (strlen($_POST["threadID"]) == 0)
{
} elseif (strlen($_POST["threadID"]) == 0) {
$errors = "<div id='error'>No thread ID supplied.</div>";
}
else if (is_numeric($_POST["threadID"]))
if (!array_key_exists("message", $_POST))
{
} elseif (is_numeric($_POST["threadID"])) {
if (!array_key_exists("message", $_POST)) {
$errors .= "<div id='error'>No message supplied.</div>";
}
else if (strlen($_POST["message"]) == 0)
{
} elseif (strlen($_POST["message"]) == 0) {
$errors .= "<div id='error'>You cannot have an empty message.</div>";
}
}
return array($errors);
return [$errors];
}
private function sanity_check_viewed_thread(int $threadID)
{
$errors = null;
if (!$this->threadExists($threadID))
{
if (!$this->threadExists($threadID)) {
$errors = "<div id='error'>Inexistent thread.</div>";
}
return array($errors);
return [$errors];
}
private function get_thread_title(int $threadID)
{
global $database;
$result = $database->get_row("SELECT t.title FROM forum_threads AS t WHERE t.id = ? ", array($threadID));
$result = $database->get_row("SELECT t.title FROM forum_threads AS t WHERE t.id = ? ", [$threadID]);
return $result["title"];
}
@ -264,14 +257,15 @@ class Forum extends Extension {
$threadsPerPage = $config->get_int('forumThreadsPerPage', 15);
$totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_threads") / $threadsPerPage);
if(is_null($pageNumber) || !is_numeric($pageNumber))
if (is_null($pageNumber) || !is_numeric($pageNumber)) {
$pageNumber = 0;
else if ($pageNumber <= 0)
} elseif ($pageNumber <= 0) {
$pageNumber = 0;
else if ($pageNumber >= $totalPages)
} elseif ($pageNumber >= $totalPages) {
$pageNumber = $totalPages - 1;
else
} else {
$pageNumber--;
}
$threads = $database->get_all(
"SELECT f.id, f.sticky, f.title, f.date, f.uptodate, u.name AS user_name, u.email AS user_email, u.class AS user_class, sum(1) - 1 AS response_count ".
@ -281,8 +275,8 @@ class Forum extends Extension {
"INNER JOIN forum_posts AS p ".
"ON p.thread_id = f.id ".
"GROUP BY f.id, f.sticky, f.title, f.date, u.name, u.email, u.class ".
"ORDER BY f.sticky ASC, f.uptodate DESC LIMIT :limit OFFSET :offset"
, array("limit"=>$threadsPerPage, "offset"=>$pageNumber * $threadsPerPage)
"ORDER BY f.sticky ASC, f.uptodate DESC LIMIT :limit OFFSET :offset",
["limit"=>$threadsPerPage, "offset"=>$pageNumber * $threadsPerPage]
);
$this->theme->display_thread_list($page, $threads, $showAdminOptions, $pageNumber + 1, $totalPages);
@ -294,17 +288,18 @@ class Forum extends Extension {
$threadID = $event->get_arg(1);
$pageNumber = $event->get_arg(2);
$postsPerPage = $config->get_int('forumPostsPerPage', 15);
$totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_posts WHERE thread_id = ?", array($threadID)) / $postsPerPage);
$totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_posts WHERE thread_id = ?", [$threadID]) / $postsPerPage);
$threadTitle = $this->get_thread_title($threadID);
if(is_null($pageNumber) || !is_numeric($pageNumber))
if (is_null($pageNumber) || !is_numeric($pageNumber)) {
$pageNumber = 0;
else if ($pageNumber <= 0)
} elseif ($pageNumber <= 0) {
$pageNumber = 0;
else if ($pageNumber >= $totalPages)
} elseif ($pageNumber >= $totalPages) {
$pageNumber = $totalPages - 1;
else
} else {
$pageNumber--;
}
$posts = $database->get_all(
"SELECT p.id, p.date, p.message, u.name as user_name, u.email AS user_email, u.class AS user_class ".
@ -313,8 +308,8 @@ class Forum extends Extension {
"ON p.user_id = u.id ".
"WHERE thread_id = :thread_id ".
"ORDER BY p.date ASC ".
"LIMIT :limit OFFSET :offset"
, array("thread_id"=>$threadID, "offset"=>$pageNumber * $postsPerPage, "limit"=>$postsPerPage)
"LIMIT :limit OFFSET :offset",
["thread_id"=>$threadID, "offset"=>$pageNumber * $postsPerPage, "limit"=>$postsPerPage]
);
$this->theme->display_thread($posts, $showAdminOptions, $threadTitle, $threadID, $pageNumber + 1, $totalPages);
}
@ -329,12 +324,14 @@ class Forum extends Extension {
}
global $database;
$database->execute("
$database->execute(
"
INSERT INTO forum_threads
(title, sticky, user_id, date, uptodate)
VALUES
(?, ?, ?, now(), now())",
array($title, $sticky, $user->id));
[$title, $sticky, $user->id]
);
$threadID = $database->get_last_insert_id("forum_threads_id_seq");
@ -356,14 +353,13 @@ class Forum extends Extension {
$database->execute("INSERT INTO forum_posts
(thread_id, user_id, date, message)
VALUES
(?, ?, now(), ?)"
, array($threadID, $userID, $message));
(?, ?, now(), ?)", [$threadID, $userID, $message]);
$postID = $database->get_last_insert_id("forum_posts_id_seq");
log_info("forum", "Post {$postID} created by {$user->name}");
$database->execute("UPDATE forum_threads SET uptodate=now() WHERE id=?", array ($threadID));
$database->execute("UPDATE forum_threads SET uptodate=now() WHERE id=?", [$threadID]);
}
private function retrieve_posts(int $threadID, int $pageNumber)
@ -378,27 +374,28 @@ class Forum extends Extension {
"ON p.user_id = u.id ".
"WHERE thread_id = :thread_id ".
"ORDER BY p.date ASC ".
"LIMIT :limit OFFSET :offset "
, array("thread_id"=>$threadID, "offset"=>($pageNumber - 1) * $postsPerPage, "limit"=>$postsPerPage));
"LIMIT :limit OFFSET :offset ",
["thread_id"=>$threadID, "offset"=>($pageNumber - 1) * $postsPerPage, "limit"=>$postsPerPage]
);
}
private function delete_thread(int $threadID)
{
global $database;
$database->execute("DELETE FROM forum_threads WHERE id = ?", array($threadID));
$database->execute("DELETE FROM forum_posts WHERE thread_id = ?", array($threadID));
$database->execute("DELETE FROM forum_threads WHERE id = ?", [$threadID]);
$database->execute("DELETE FROM forum_posts WHERE thread_id = ?", [$threadID]);
}
private function delete_post(int $postID)
{
global $database;
$database->execute("DELETE FROM forum_posts WHERE id = ?", array($postID));
$database->execute("DELETE FROM forum_posts WHERE id = ?", [$postID]);
}
private function threadExists(int $threadID)
{
global $database;
$result=$database->get_one("SELECT EXISTS (SELECT * FROM forum_threads WHERE id= ?)", array($threadID));
$result=$database->get_one("SELECT EXISTS (SELECT * FROM forum_threads WHERE id= ?)", [$threadID]);
if ($result==1) {
return true;
} else {

View File

@ -1,12 +1,13 @@
<?php
class ForumTheme extends Themelet {
class ForumTheme extends Themelet
{
public function display_thread_list(Page $page, $threads, $showAdminOptions, $pageNumber, $totalPages)
{
if (count($threads) == 0)
if (count($threads) == 0) {
$html = "There are no threads to show.";
else
} else {
$html = $this->make_thread_list($threads, $showAdminOptions);
}
$page->set_title(html_escape("Forum"));
$page->set_heading(html_escape("Forum"));
@ -24,11 +25,13 @@ class ForumTheme extends Themelet {
$html = make_form(make_link("forum/create"));
if (!is_null($threadTitle))
if (!is_null($threadTitle)) {
$threadTitle = html_escape($threadTitle);
}
if(!is_null($threadText))
if (!is_null($threadText)) {
$threadText = html_escape($threadText);
}
$html .= "
<table style='width: 500px;'>
@ -94,8 +97,7 @@ class ForumTheme extends Themelet {
"<th>Message</th>".
"</tr></thead>";
foreach ($posts as $post)
{
foreach ($posts as $post) {
$current_post++;
$message = $post["message"];
@ -149,7 +151,6 @@ class ForumTheme extends Themelet {
<td class='forumSubuser'></td>
<td class='forumSubmessage'></td>
</tr>";
}
$html .= "</tbody></table>";
@ -159,7 +160,6 @@ class ForumTheme extends Themelet {
$page->set_title(html_escape($threadTitle));
$page->set_heading(html_escape($threadTitle));
$page->add_block(new Block($threadTitle, $html, "main", 20));
}
@ -182,8 +182,7 @@ class ForumTheme extends Themelet {
"<th>Updated</th>".
"<th>Responses</th>";
if($showAdminOptions)
{
if ($showAdminOptions) {
$html .= "<th>Actions</th>";
}
@ -191,15 +190,13 @@ class ForumTheme extends Themelet {
$current_post = 0;
foreach($threads as $thread)
{
foreach ($threads as $thread) {
$oe = ($current_post++ % 2 == 0) ? "even" : "odd";
global $config;
$titleSubString = $config->get_int('forumTitleSubString');
if ($titleSubString < strlen($thread["title"]))
{
if ($titleSubString < strlen($thread["title"])) {
$title = substr($thread["title"], 0, $titleSubString);
$title = $title."...";
} else {
@ -218,8 +215,9 @@ class ForumTheme extends Themelet {
"<td>".autodate($thread["uptodate"])."</td>".
"<td>".$thread["response_count"]."</td>";
if ($showAdminOptions)
if ($showAdminOptions) {
$html .= '<td><a href="'.make_link("forum/nuke/".$thread["id"]).'" title="Delete '.$title.'">Delete</a></td>';
}
$html .= "</tr>";
}
@ -229,4 +227,3 @@ class ForumTheme extends Themelet {
return $html;
}
}

View File

@ -8,9 +8,11 @@
* Documentation:
* User has to enter their Google Analytics ID in the Board Config to use this extention.
*/
class google_analytics extends Extension {
class google_analytics extends Extension
{
# Add analytics to config
public function onSetupBuilding(SetupBuildingEvent $event) {
public function onSetupBuilding(SetupBuildingEvent $event)
{
$sb = new SetupBlock("Google Analytics");
$sb->add_text_option("google_analytics_id", "Analytics ID: ");
$sb->add_label("<br>(eg. UA-xxxxxxxx-x)");
@ -18,7 +20,8 @@ class google_analytics extends Extension {
}
# Load Analytics tracking code on page request
public function onPageRequest(PageRequestEvent $event) {
public function onPageRequest(PageRequestEvent $event)
{
global $config, $page;
$google_analytics_id = $config->get_string('google_analytics_id', '');
@ -35,4 +38,3 @@ class google_analytics extends Extension {
}
}
}

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