PSR-2. I'm not a huge fan, but ugly consistency beats no consistency...
This commit is contained in:
parent
5ec3e89884
commit
34b05cca7c
295 changed files with 27094 additions and 24632 deletions
|
@ -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
1
.gitignore
vendored
|
@ -4,6 +4,7 @@ images
|
|||
thumbs
|
||||
*.phar
|
||||
*.sqlite
|
||||
.php_cs.cache
|
||||
|
||||
#Composer
|
||||
composer.phar
|
||||
|
|
18
.php_cs.dist
Normal file
18
.php_cs.dist
Normal 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)
|
||||
;
|
||||
|
||||
?>
|
|
@ -23,8 +23,8 @@ $_shm_files = array_merge(
|
|||
zglob("core/{".ENABLED_MODS."}/*.php"),
|
||||
zglob("ext/{".ENABLED_EXTS."}/main.php")
|
||||
);
|
||||
foreach($_shm_files as $_shm_filename) {
|
||||
if(basename($_shm_filename)[0] != "_") {
|
||||
foreach ($_shm_files as $_shm_filename) {
|
||||
if (basename($_shm_filename)[0] != "_") {
|
||||
require_once $_shm_filename;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ $_shm_ctx->log_endok();
|
|||
|
||||
// load the theme parts
|
||||
$_shm_ctx->log_start("Loading themelets");
|
||||
foreach(_get_themelet_files(get_theme()) as $themelet) {
|
||||
foreach (_get_themelet_files(get_theme()) as $themelet) {
|
||||
require_once $themelet;
|
||||
}
|
||||
unset($themelet);
|
||||
|
|
|
@ -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">
|
||||
|
@ -43,7 +43,7 @@ date_default_timezone_set('UTC');
|
|||
</div>
|
||||
</div>
|
||||
<pre style="display:none">
|
||||
<?php } elseif(!file_exists("vendor/")) { ?>
|
||||
<?php } elseif (!file_exists("vendor/")) { ?>
|
||||
<div id="installer">
|
||||
<h1>Install Error</h1>
|
||||
<h3>Warning: Composer vendor folder does not exist!</h3>
|
||||
|
@ -65,21 +65,24 @@ 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')){
|
||||
if (function_exists('gd_info')) {
|
||||
$gd_info = gd_info();
|
||||
if (substr_count($gd_info['GD Version'], '2.')) {
|
||||
$gdversion = 2;
|
||||
} else if (substr_count($gd_info['GD Version'], '1.')) {
|
||||
} elseif (substr_count($gd_info['GD Version'], '1.')) {
|
||||
$gdversion = 1;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
if ($value) {
|
||||
echo "<span style='color: green'>ok</span>\n";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
echo "<span style='color: green'>failed</span>\n";
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
function do_install() { // {{{
|
||||
if(file_exists("data/config/auto_install.conf.php")) {
|
||||
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) {
|
||||
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
|
||||
|
@ -144,7 +146,7 @@ function ask_questions() { // {{{
|
|||
";
|
||||
}
|
||||
|
||||
if(!function_exists('mb_strlen')) {
|
||||
if (!function_exists('mb_strlen')) {
|
||||
$errors[] = "
|
||||
The mbstring PHP extension is missing - multibyte languages
|
||||
(eg non-english languages) may not work right.
|
||||
|
@ -152,7 +154,7 @@ function ask_questions() { // {{{
|
|||
}
|
||||
|
||||
$drivers = PDO::getAvailableDrivers();
|
||||
if(
|
||||
if (
|
||||
!in_array("mysql", $drivers) &&
|
||||
!in_array("pgsql", $drivers) &&
|
||||
!in_array("sqlite", $drivers)
|
||||
|
@ -243,18 +245,20 @@ 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();
|
||||
|
||||
if ( $db->count_tables() > 0 ) {
|
||||
if ($db->count_tables() > 0) {
|
||||
print <<<EOD
|
||||
<div id="installer">
|
||||
<h1>Shimmie Installer</h1>
|
||||
|
@ -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,49 +329,52 @@ 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'));
|
||||
if (check_im_version() > 0) {
|
||||
$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();
|
||||
|
||||
if(!file_exists("data") || !is_writable("data")) {
|
||||
if (!file_exists("data") || !is_writable("data")) {
|
||||
print "
|
||||
<div id='installer'>
|
||||
<h1>Shimmie Installer</h1>
|
||||
|
@ -385,16 +392,17 @@ function build_dirs() { // {{{
|
|||
}
|
||||
} // }}}
|
||||
|
||||
function write_config() { // {{{
|
||||
function write_config()
|
||||
{ // {{{
|
||||
$file_content = '<' . '?php' . "\n" .
|
||||
"define('DATABASE_DSN', '".DATABASE_DSN."');\n" .
|
||||
'?' . '>';
|
||||
|
||||
if(!file_exists("data/config")) {
|
||||
if (!file_exists("data/config")) {
|
||||
mkdir("data/config", 0755, true);
|
||||
}
|
||||
|
||||
if(file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) {
|
||||
if (file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) {
|
||||
header("Location: index.php");
|
||||
print <<<EOD
|
||||
<div id="installer">
|
||||
|
@ -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">
|
||||
|
|
|
@ -5,24 +5,26 @@
|
|||
*
|
||||
* 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);
|
||||
$page->set_heading($title);
|
||||
$has_nav = false;
|
||||
foreach($page->blocks as $block) {
|
||||
if($block->header == "Navigation") {
|
||||
foreach ($page->blocks as $block) {
|
||||
if ($block->header == "Navigation") {
|
||||
$has_nav = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$has_nav) {
|
||||
if (!$has_nav) {
|
||||
$page->add_block(new NavBlock());
|
||||
}
|
||||
$page->add_block(new Block("Error", $message));
|
||||
|
@ -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,18 +53,22 @@ 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
|
||||
if(!isset($extArr[$image->ext])){
|
||||
$extArr = array_flip(['swf', 'svg', 'mp3']); //List of thumbless filetypes
|
||||
if (!isset($extArr[$image->ext])) {
|
||||
$tsize = get_thumbnail_size($image->width, $image->height);
|
||||
}else{
|
||||
} else {
|
||||
//Use max thumbnail size if using thumbless filetype
|
||||
$tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height'));
|
||||
}
|
||||
|
||||
$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 (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 ";
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -98,7 +116,7 @@ class BaseThemelet {
|
|||
$prev_html = $at_start ? "Prev" : $this->gen_page_link($base_url, $query, $prev, "Prev");
|
||||
|
||||
$random_html = "-";
|
||||
if($show_random) {
|
||||
if ($show_random) {
|
||||
$rand = mt_rand(1, $total_pages);
|
||||
$random_html = $this->gen_page_link($base_url, $query, $rand, "Random");
|
||||
}
|
||||
|
@ -109,8 +127,8 @@ class BaseThemelet {
|
|||
$start = $current_page-5 > 1 ? $current_page-5 : 1;
|
||||
$end = $start+10 < $total_pages ? $start+10 : $total_pages;
|
||||
|
||||
$pages = array();
|
||||
foreach(range($start, $end) as $i) {
|
||||
$pages = [];
|
||||
foreach (range($start, $end) as $i) {
|
||||
$pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i);
|
||||
}
|
||||
$pages_html = implode(" | ", $pages);
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*
|
||||
* A basic chunk of a page.
|
||||
*/
|
||||
class Block {
|
||||
class Block
|
||||
{
|
||||
/**
|
||||
* The block's title.
|
||||
*
|
||||
|
@ -52,29 +53,35 @@ 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;
|
||||
$this->position = $position;
|
||||
|
||||
if(is_null($id)) {
|
||||
if (is_null($id)) {
|
||||
$id = (empty($header) ? md5($body) : $header) . $section;
|
||||
}
|
||||
$this->id = preg_replace('/[^\w]/', '',str_replace(' ', '_', $id));
|
||||
$this->id = preg_replace('/[^\w]/', '', str_replace(' ', '_', $id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,67 +63,75 @@ 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);
|
||||
$res = $this->memcache->getResultCode();
|
||||
|
||||
if($res == Memcached::RES_SUCCESS) {
|
||||
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);
|
||||
$res = $this->memcache->getResultCode();
|
||||
if($res != Memcached::RES_SUCCESS) {
|
||||
if ($res != Memcached::RES_SUCCESS) {
|
||||
error_log("Memcached error during set($key): $res");
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(string $key) {
|
||||
public function delete(string $key)
|
||||
{
|
||||
$key = urlencode($key);
|
||||
|
||||
$this->memcache->delete($key);
|
||||
$res = $this->memcache->getResultCode();
|
||||
if($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) {
|
||||
if ($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) {
|
||||
error_log("Memcached error during delete($key): $res");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,82 +139,90 @@ 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) {
|
||||
if($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();
|
||||
if($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches)) {
|
||||
if($matches[1] == "memcache") {
|
||||
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'])) {
|
||||
if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) {
|
||||
$hit = $val === false ? "hit" : "miss";
|
||||
file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND);
|
||||
}
|
||||
if($val !== false) {
|
||||
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'])) {
|
||||
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'])) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
* 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")) {
|
||||
if ($user->is_anonymous() && $config->get_bool("comment_captcha")) {
|
||||
$r_publickey = $config->get_string("api_recaptcha_pubkey");
|
||||
if(!empty($r_publickey)) {
|
||||
if (!empty($r_publickey)) {
|
||||
$captcha = "
|
||||
<div class=\"g-recaptcha\" data-sitekey=\"{$r_publickey}\"></div>
|
||||
<script type=\"text/javascript\" src=\"https://www.google.com/recaptcha/api.js\"></script>";
|
||||
|
@ -23,26 +26,28 @@ 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")) {
|
||||
if ($user->is_anonymous() && $config->get_bool("comment_captcha")) {
|
||||
$r_privatekey = $config->get_string('api_recaptcha_privkey');
|
||||
if(!empty($r_privatekey)) {
|
||||
if (!empty($r_privatekey)) {
|
||||
$recaptcha = new \ReCaptcha\ReCaptcha($r_privatekey);
|
||||
$resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if(!$resp->isSuccess()) {
|
||||
if (!$resp->isSuccess()) {
|
||||
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) {
|
||||
if ($securimg->check($_POST['captcha_code']) === false) {
|
||||
log_info("core", "Captcha failed (Securimage)");
|
||||
return false;
|
||||
}
|
||||
|
@ -51,5 +56,3 @@ function captcha_check(): bool {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
128
core/config.php
128
core/config.php
|
@ -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) {
|
||||
if(is_null($this->get($name))) {
|
||||
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) {
|
||||
if(is_null($this->get($name))) {
|
||||
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) {
|
||||
if(is_null($this->get($name))) {
|
||||
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) {
|
||||
if(is_null($this->get($name))) {
|
||||
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) {
|
||||
if(isset($this->values[$name])) {
|
||||
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) {
|
||||
if(file_exists($filename)) {
|
||||
$config = array();
|
||||
class StaticConfig extends BaseConfig
|
||||
{
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
if (file_exists($filename)) {
|
||||
$config = [];
|
||||
require_once $filename;
|
||||
if(!empty($config)) {
|
||||
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,36 +262,37 @@ 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) {
|
||||
if ($cached) {
|
||||
$this->values = $cached;
|
||||
}
|
||||
else {
|
||||
$this->values = array();
|
||||
foreach($this->database->get_all("SELECT name, value FROM config") as $row) {
|
||||
} else {
|
||||
$this->values = [];
|
||||
foreach ($this->database->get_all("SELECT name, value FROM config") as $row) {
|
||||
$this->values[$row["name"]] = $row["value"];
|
||||
}
|
||||
$this->database->cache->set("config", $this->values);
|
||||
}
|
||||
}
|
||||
|
||||
public function save(string $name=null) {
|
||||
if(is_null($name)) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,20 +60,26 @@ 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;
|
||||
if(version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") {
|
||||
if (version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") {
|
||||
$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,104 +88,122 @@ 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") {
|
||||
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 {
|
||||
if(!is_null($this->db)) {
|
||||
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 {
|
||||
if(!is_null($this->db)) {
|
||||
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) {
|
||||
if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) {
|
||||
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)) {
|
||||
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) {
|
||||
if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) {
|
||||
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);
|
||||
}
|
||||
$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" .
|
||||
|
@ -184,22 +211,19 @@ class Database {
|
|||
);
|
||||
// $stmt = $this->db->prepare($query);
|
||||
if (!array_key_exists(0, $args)) {
|
||||
foreach($args as $name=>$value) {
|
||||
if(is_numeric($value)) {
|
||||
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,11 +253,12 @@ 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();
|
||||
foreach($stmt as $row) {
|
||||
$res = [];
|
||||
foreach ($stmt as $row) {
|
||||
$res[] = $row[0];
|
||||
}
|
||||
$this->count_time("get_col", $_start);
|
||||
|
@ -241,11 +268,12 @@ 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();
|
||||
foreach($stmt as $row) {
|
||||
$res = [];
|
||||
foreach ($stmt as $row) {
|
||||
$res[$row[0]] = $row[1];
|
||||
}
|
||||
$this->count_time("get_pairs", $_start);
|
||||
|
@ -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 {
|
||||
if($this->engine->name == "pgsql") {
|
||||
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,18 +320,21 @@ 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") {
|
||||
if ($this->engine->name === "mysql") {
|
||||
return count(
|
||||
$this->get_all("SHOW TABLES")
|
||||
);
|
||||
} else if ($this->engine->name === "pgsql") {
|
||||
} elseif ($this->engine->name === "pgsql") {
|
||||
return count(
|
||||
$this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'")
|
||||
);
|
||||
} else if ($this->engine->name === "sqlite") {
|
||||
} elseif ($this->engine->name === "sqlite") {
|
||||
return count(
|
||||
$this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'")
|
||||
);
|
||||
|
@ -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_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 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 scoreql_to_sql(string $sql): string
|
||||
{
|
||||
return $sql;
|
||||
}
|
||||
public function create_table(string $name, string $def)
|
||||
{
|
||||
}
|
||||
public function connect_engine()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
if(array_key_exists('REMOTE_ADDR', $_SERVER)) {
|
||||
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();
|
||||
if(preg_match("/(UNIQUE)? ?INDEX\s*\((.*)\)/", $bit, $matches)) {
|
||||
foreach (explode(",", $data) as $bit) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*
|
||||
* A generic email.
|
||||
*/
|
||||
class Email {
|
||||
class Email
|
||||
{
|
||||
/** @var string */
|
||||
public $to;
|
||||
/** @var string */
|
||||
|
@ -29,16 +30,16 @@ 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;
|
||||
|
||||
$sub_prefix = $config->get_string("mail_sub");
|
||||
|
||||
if(!isset($sub_prefix)){
|
||||
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";
|
||||
|
@ -119,14 +121,12 @@ Copyright (C) <a href="'.$this->sitedomain.'">'.$this->sitename.'</a><br />
|
|||
</html>
|
||||
';
|
||||
$sent = mail($this->to, $this->subject, $message, $headers);
|
||||
if($sent){
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
101
core/event.php
101
core/event.php
|
@ -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,14 +49,15 @@ class PageRequestEvent extends Event {
|
|||
*/
|
||||
public $part_count;
|
||||
|
||||
public function __construct(string $path) {
|
||||
public function __construct(string $path)
|
||||
{
|
||||
global $config;
|
||||
|
||||
// trim starting slashes
|
||||
$path = ltrim($path, "/");
|
||||
|
||||
// if path is not specified, use the default front page
|
||||
if(empty($path)) { /* empty is faster than strlen */
|
||||
if (empty($path)) { /* empty is faster than strlen */
|
||||
$path = $config->get_string('front_page');
|
||||
}
|
||||
|
||||
|
@ -59,9 +66,9 @@ class PageRequestEvent extends Event {
|
|||
|
||||
// voodoo so that an arg can contain a slash; is
|
||||
// this still needed?
|
||||
if(strpos($path, "^") !== FALSE) {
|
||||
$unescaped = array();
|
||||
foreach($args as $part) {
|
||||
if (strpos($path, "^") !== false) {
|
||||
$unescaped = [];
|
||||
foreach ($args as $part) {
|
||||
$unescaped[] = _decaret($part);
|
||||
}
|
||||
$args = $unescaped;
|
||||
|
@ -76,16 +83,17 @@ 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);
|
||||
|
||||
if($this->part_count > $this->arg_count) {
|
||||
if ($this->part_count > $this->arg_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for($i=0; $i<$this->part_count; $i++) {
|
||||
if($parts[$i] != $this->args[$i]) {
|
||||
for ($i=0; $i<$this->part_count; $i++) {
|
||||
if ($parts[$i] != $this->args[$i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
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();
|
||||
if($this->count_args() === 2) {
|
||||
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) {
|
||||
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,23 +170,24 @@ 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);
|
||||
|
||||
for($i=1; $i<$arg_count; $i++) {
|
||||
switch($args[$i]) {
|
||||
for ($i=1; $i<$arg_count; $i++) {
|
||||
switch ($args[$i]) {
|
||||
case '-u':
|
||||
$user = User::by_name($args[++$i]);
|
||||
if(is_null($user)) {
|
||||
if (is_null($user)) {
|
||||
die("Unknown user");
|
||||
}
|
||||
break;
|
||||
|
@ -190,11 +205,10 @@ class CommandEvent extends Event {
|
|||
|
||||
define("CLI_LOG_LEVEL", $log_level);
|
||||
|
||||
if(count($opts) > 0) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
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,16 +153,18 @@ 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) {
|
||||
if ($supported_ext && $check_contents) {
|
||||
move_upload_to_archive($event);
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
|
||||
/* Check if we are replacing an image */
|
||||
if(array_key_exists('replace', $event->metadata) && isset($event->metadata['replace'])) {
|
||||
if (array_key_exists('replace', $event->metadata) && isset($event->metadata['replace'])) {
|
||||
/* hax: This seems like such a dirty way to do this.. */
|
||||
|
||||
/* Validate things */
|
||||
|
@ -166,7 +173,7 @@ abstract class DataHandlerExtension extends Extension {
|
|||
/* Check to make sure the image exists. */
|
||||
$existing = Image::by_id($image_id);
|
||||
|
||||
if(is_null($existing)) {
|
||||
if (is_null($existing)) {
|
||||
throw new UploadException("Image to replace does not exist!");
|
||||
}
|
||||
if ($existing->hash === $event->metadata['hash']) {
|
||||
|
@ -177,17 +184,16 @@ abstract class DataHandlerExtension extends Extension {
|
|||
$event->metadata['tags'] = $existing->get_tag_list();
|
||||
$image = $this->create_image_from_data(warehouse_path("images", $event->metadata['hash']), $event->metadata);
|
||||
|
||||
if(is_null($image)) {
|
||||
if (is_null($image)) {
|
||||
throw new UploadException("Data handler failed to create image object from data");
|
||||
}
|
||||
|
||||
$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)) {
|
||||
if (is_null($image)) {
|
||||
throw new UploadException("Data handler failed to create image object from data");
|
||||
}
|
||||
$iae = new ImageAdditionEvent($image);
|
||||
|
@ -195,37 +201,37 @@ abstract class DataHandlerExtension extends Extension {
|
|||
$event->image_id = $iae->image->id;
|
||||
|
||||
// Rating Stuff.
|
||||
if(!empty($event->metadata['rating'])){
|
||||
if (!empty($event->metadata['rating'])) {
|
||||
$rating = $event->metadata['rating'];
|
||||
send_event(new RatingSetEvent($image, $rating));
|
||||
}
|
||||
|
||||
// Locked Stuff.
|
||||
if(!empty($event->metadata['locked'])){
|
||||
if (!empty($event->metadata['locked'])) {
|
||||
$locked = $event->metadata['locked'];
|
||||
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) {
|
||||
if($this->supported_ext($event->type)) {
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,9 +55,10 @@ 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) {
|
||||
if(!is_null($row)) {
|
||||
foreach($row as $name => $value) {
|
||||
public function __construct(?array $row=null)
|
||||
{
|
||||
if (!is_null($row)) {
|
||||
foreach ($row as $name => $value) {
|
||||
// some databases use table.name rather than name
|
||||
$name = str_replace("images.", "", $name);
|
||||
$this->$name = $value; // hax, this is likely the cause of much scrutinizer-ci complaints.
|
||||
|
@ -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,34 +106,39 @@ 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) {
|
||||
if (SPEED_HAX) {
|
||||
if (!$user->can("big_search") and count($tags) > 3) {
|
||||
throw new SCoreException("Anonymous users may only search for up to 3 tags at a time");
|
||||
}
|
||||
}
|
||||
|
||||
$result = null;
|
||||
if(SEARCH_ACCEL) {
|
||||
if (SEARCH_ACCEL) {
|
||||
$result = Image::get_accelerated_result($tags, $start, $limit);
|
||||
}
|
||||
|
||||
if(!$result) {
|
||||
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);
|
||||
}
|
||||
|
||||
while($row = $result->fetch()) {
|
||||
while ($row = $result->fetch()) {
|
||||
$images[] = new Image($row);
|
||||
}
|
||||
Image::$order_sql = null;
|
||||
|
@ -133,54 +148,66 @@ 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)) {
|
||||
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) {
|
||||
}
|
||||
if ($yays > 1 || $nays > 0) {
|
||||
return $ret;
|
||||
}
|
||||
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;
|
||||
|
||||
$response = Image::query_accelerator($req);
|
||||
$list = implode(",", $response);
|
||||
if($list) {
|
||||
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,30 +233,32 @@ 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);
|
||||
|
||||
if($tag_count === 0) {
|
||||
if ($tag_count === 0) {
|
||||
$total = $database->cache->get("image-count");
|
||||
if(!$total) {
|
||||
if (!$total) {
|
||||
$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)) {
|
||||
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,19 +285,19 @@ 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) {
|
||||
if ($next) {
|
||||
$gtlt = "<";
|
||||
$dir = "DESC";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$gtlt = ">";
|
||||
$dir = "ASC";
|
||||
}
|
||||
|
||||
if(count($tags) === 0) {
|
||||
if (count($tags) === 0) {
|
||||
$row = $database->get_row('
|
||||
SELECT images.*
|
||||
FROM images
|
||||
|
@ -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) {
|
||||
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,16 +354,17 @@ class Image {
|
|||
*
|
||||
* #return string[]
|
||||
*/
|
||||
public function get_tag_array(): array {
|
||||
public function get_tag_array(): array
|
||||
{
|
||||
global $database;
|
||||
if(!isset($this->tag_array)) {
|
||||
if (!isset($this->tag_array)) {
|
||||
$this->tag_array = $database->get_col("
|
||||
SELECT tag
|
||||
FROM image_tags
|
||||
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,42 +372,44 @@ 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);
|
||||
|
||||
if(!empty($image_link)) {
|
||||
if(!(strpos($image_link, "://") > 0) && !startsWith($image_link, "/")) {
|
||||
if (!empty($image_link)) {
|
||||
if (!(strpos($image_link, "://") > 0) && !startsWith($image_link, "/")) {
|
||||
$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'){
|
||||
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(" //", "// ", " //", "// ", " ");
|
||||
if(strstr($h_tip, " ")) {
|
||||
$justincase = [" //", "// ", " //", "// ", " "];
|
||||
if (strstr($h_tip, " ")) {
|
||||
$h_tip = html_escape(str_replace($justincase, "", $h_tip));
|
||||
}else{
|
||||
} 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($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));
|
||||
if (empty($new_source)) {
|
||||
$new_source = null;
|
||||
}
|
||||
if ($new_source != $old_source) {
|
||||
$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));
|
||||
if (bool_escape($sln) !== $this->locked) {
|
||||
$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") {
|
||||
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,28 +554,29 @@ 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 = [];
|
||||
foreach ($unfiltered_tags as $tag) {
|
||||
if(mb_strlen($tag, 'UTF-8') > 255){
|
||||
if (mb_strlen($tag, 'UTF-8') > 255) {
|
||||
flash_message("Can't set a tag longer than 255 characters");
|
||||
continue;
|
||||
}
|
||||
if(startsWith($tag, "-")) {
|
||||
if (startsWith($tag, "-")) {
|
||||
flash_message("Can't set a tag which starts with a minus");
|
||||
continue;
|
||||
}
|
||||
|
@ -535,39 +584,40 @@ class Image {
|
|||
$tags[] = $tag;
|
||||
}
|
||||
|
||||
if(count($tags) <= 0) {
|
||||
if (count($tags) <= 0) {
|
||||
throw new SCoreException('Tried to set zero tags');
|
||||
}
|
||||
|
||||
if(Tag::implode($tags) != $this->get_tag_list()) {
|
||||
if (Tag::implode($tags) != $this->get_tag_list()) {
|
||||
// delete old
|
||||
$this->delete_tags_from_image();
|
||||
// insert each new tags
|
||||
foreach($tags as $tag) {
|
||||
foreach ($tags as $tag) {
|
||||
$id = $database->get_one(
|
||||
$database->scoreql_to_sql("
|
||||
SELECT id
|
||||
FROM tags
|
||||
WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)
|
||||
"),
|
||||
array("tag"=>$tag)
|
||||
["tag"=>$tag]
|
||||
);
|
||||
if(empty($id)) {
|
||||
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 {
|
||||
foreach($metatags as $tag) {
|
||||
$ttpe = new TagTermParseEvent($tag, $image_id, TRUE);
|
||||
public function parse_metatags(array $metatags, int $image_id): void
|
||||
{
|
||||
foreach ($metatags as $tag) {
|
||||
$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,18 +665,20 @@ 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...
|
||||
$tags = "";
|
||||
if(strpos($tmpl, '$tags') !== false) { // * stabs dynamically typed languages with a rusty spoon *
|
||||
if (strpos($tmpl, '$tags') !== false) { // * stabs dynamically typed languages with a rusty spoon *
|
||||
$tags = $this->get_tag_list();
|
||||
$tags = str_replace("/", "", $tags);
|
||||
$tags = preg_replace("/^\.+/", "", $tags);
|
||||
|
@ -648,7 +702,7 @@ class Image {
|
|||
$tmpl = str_replace('$date', $_escape(autodate($this->posted, false)), $tmpl);
|
||||
|
||||
// nothing seems to use this, sending the event out to 50 exts is a lot of overhead
|
||||
if(!SPEED_HAX) {
|
||||
if (!SPEED_HAX) {
|
||||
$plte = new ParseLinkTemplateEvent($tmpl, $this);
|
||||
send_event($plte);
|
||||
$tmpl = $plte->link;
|
||||
|
@ -656,25 +710,24 @@ class Image {
|
|||
|
||||
static $flexihash = null;
|
||||
static $fh_last_opts = null;
|
||||
$matches = array();
|
||||
if(preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) {
|
||||
$matches = [];
|
||||
if (preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) {
|
||||
$pre = $matches[1];
|
||||
$opts = $matches[2];
|
||||
$post = $matches[3];
|
||||
|
||||
if($opts != $fh_last_opts) {
|
||||
if ($opts != $fh_last_opts) {
|
||||
$fh_last_opts = $opts;
|
||||
$flexihash = new Flexihash\Flexihash();
|
||||
foreach(explode(",", $opts) as $opt) {
|
||||
foreach (explode(",", $opts) as $opt) {
|
||||
$parts = explode("=", $opt);
|
||||
$parts_count = count($parts);
|
||||
$opt_val = "";
|
||||
$opt_weight = 0;
|
||||
if($parts_count === 2) {
|
||||
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,17 +784,19 @@ 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) != "") {
|
||||
if (str_replace("*", "", $term) != "") {
|
||||
$term = str_replace('_', '\_', $term);
|
||||
$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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -758,7 +814,7 @@ class Image {
|
|||
*/
|
||||
|
||||
// no tags, do a simple search
|
||||
if($positive_tag_count === 0 && $negative_tag_count === 0) {
|
||||
if ($positive_tag_count === 0 && $negative_tag_count === 0) {
|
||||
$query = new Querylet("
|
||||
SELECT images.*
|
||||
FROM images
|
||||
|
@ -767,7 +823,7 @@ class Image {
|
|||
}
|
||||
|
||||
// one positive tag (a common case), do an optimised search
|
||||
else if($positive_tag_count === 1 && $negative_tag_count === 0) {
|
||||
elseif ($positive_tag_count === 1 && $negative_tag_count === 0) {
|
||||
# "LIKE" to account for wildcards
|
||||
$query = new Querylet($database->scoreql_to_sql("
|
||||
SELECT *
|
||||
|
@ -780,28 +836,33 @@ 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
|
||||
* and append to the base querylet with "AND blah"
|
||||
*/
|
||||
if(!empty($img_querylets)) {
|
||||
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,16 +958,19 @@ 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++;
|
||||
foreach ($tag_querylets as $tq) {
|
||||
if ($tq->positive) {
|
||||
$positive_tag_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// only negative tags - shortcut to fail
|
||||
if($positive_tag_count == 0) {
|
||||
if ($positive_tag_count == 0) {
|
||||
// TODO: This isn't currently implemented.
|
||||
// SEE: https://github.com/shish/shimmie2/issues/66
|
||||
return new Querylet("
|
||||
|
@ -917,8 +982,8 @@ class Image {
|
|||
|
||||
// merge all the tag querylets into one generic one
|
||||
$sql = "0";
|
||||
$terms = array();
|
||||
foreach($tag_querylets as $tq) {
|
||||
$terms = [];
|
||||
foreach ($tag_querylets as $tq) {
|
||||
$sign = $tq->positive ? "+" : "-";
|
||||
$sql .= ' '.$sign.' IF(SUM(tag LIKE :tag'.Image::$tag_n.'), 1, 0)';
|
||||
$terms['tag'.Image::$tag_n] = $tq->tag;
|
||||
|
@ -926,20 +991,20 @@ class Image {
|
|||
}
|
||||
$tag_search = new Querylet($sql, $terms);
|
||||
|
||||
$tag_id_array = array();
|
||||
$tag_id_array = [];
|
||||
|
||||
foreach($tag_querylets as $tq) {
|
||||
foreach ($tag_querylets as $tq) {
|
||||
$tag_ids = $database->get_col(
|
||||
$database->scoreql_to_sql("
|
||||
SELECT id
|
||||
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);
|
||||
|
||||
if($tq->positive && count($tag_ids) == 0) {
|
||||
if ($tq->positive && count($tag_ids) == 0) {
|
||||
# one of the positive tags had zero results, therefor there
|
||||
# can be no results; "where 1=0" should shortcut things
|
||||
return new Querylet("
|
||||
|
@ -965,8 +1030,7 @@ class Image {
|
|||
WHERE 1=1
|
||||
', array_merge(
|
||||
$tag_search->variables,
|
||||
array("score"=>$positive_tag_count)
|
||||
["score"=>$positive_tag_count]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
*
|
||||
* @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)) {
|
||||
if (!@copy($event->tmpname, $target)) {
|
||||
$errors = error_get_last();
|
||||
throw new UploadException(
|
||||
"Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ".
|
||||
|
@ -25,10 +26,11 @@ 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) {
|
||||
foreach (list_files($base) as $full_path) {
|
||||
$short_path = str_replace($base, "", $full_path);
|
||||
$filename = basename($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,21 +48,22 @@ 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)) {
|
||||
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);
|
||||
$metadata['source'] = null;
|
||||
$event = new DataUploadEvent($tmpname, $metadata);
|
||||
send_event($event);
|
||||
if($event->image_id == -1) {
|
||||
if ($event->image_id == -1) {
|
||||
throw new UploadException("File type not recognised");
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -88,10 +99,9 @@ function get_thumbnail_size(int $orig_width, int $orig_height): array {
|
|||
$yscale = ($max_width / $orig_width);
|
||||
$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));
|
||||
if ($scale > 1 && $config->get_bool('thumb_upscale')) {
|
||||
return [(int)$orig_width, (int)$orig_height];
|
||||
} else {
|
||||
return [(int)($orig_width*$scale), (int)($orig_height*$scale)];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,43 +22,44 @@ 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();
|
||||
foreach($tags as $tag) {
|
||||
$tag_array = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tag = preg_replace("/\s/", "", $tag); # whitespace
|
||||
$tag = preg_replace('/\x20(\x0e|\x0f)/', '', $tag); # unicode RTL
|
||||
$tag = preg_replace("/\.+/", ".", $tag); # strings of dots?
|
||||
$tag = preg_replace("/^(\.+[\/\\\\])+/", "", $tag); # trailing slashes?
|
||||
$tag = trim($tag, ", \t\n\r\0\x0B");
|
||||
|
||||
if(mb_strlen($tag, 'UTF-8') > 255){
|
||||
if (mb_strlen($tag, 'UTF-8') > 255) {
|
||||
flash_message("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!empty($tag)) {
|
||||
if (!empty($tag)) {
|
||||
$tag_array[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
/* if user supplied a blank string, add "tagme" */
|
||||
if(count($tag_array) === 0 && $tagme) {
|
||||
$tag_array = array("tagme");
|
||||
if (count($tag_array) === 0 && $tagme) {
|
||||
$tag_array = ["tagme"];
|
||||
}
|
||||
|
||||
/* resolve aliases */
|
||||
$new = array();
|
||||
$new = [];
|
||||
$i = 0;
|
||||
$tag_count = count($tag_array);
|
||||
while($i<$tag_count) {
|
||||
while ($i<$tag_count) {
|
||||
$tag = $tag_array[$i];
|
||||
$negative = '';
|
||||
if(!empty($tag) && ($tag[0] == '-')) {
|
||||
if (!empty($tag) && ($tag[0] == '-')) {
|
||||
$negative = '-';
|
||||
$tag = substr($tag, 1);
|
||||
}
|
||||
|
@ -67,22 +70,20 @@ class Tag {
|
|||
FROM aliases
|
||||
WHERE SCORE_STRNORM(oldtag)=SCORE_STRNORM(:tag)
|
||||
"),
|
||||
array("tag"=>$tag)
|
||||
["tag"=>$tag]
|
||||
);
|
||||
if(empty($newtags)) {
|
||||
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
|
||||
}
|
||||
|
||||
foreach($aliases as $alias) {
|
||||
if(!in_array($alias, $new)) {
|
||||
if($tag == $alias) {
|
||||
foreach ($aliases as $alias) {
|
||||
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++;
|
||||
}
|
||||
|
|
|
@ -17,37 +17,53 @@ 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;
|
||||
|
||||
if((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) {
|
||||
if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) {
|
||||
print date("c")." $section: $message\n";
|
||||
}
|
||||
if(!is_null($flash)) {
|
||||
if (!is_null($flash)) {
|
||||
flash_message($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) {
|
||||
if (!$request_id) {
|
||||
// not completely trustworthy, as a user can spoof this
|
||||
if(@$_SERVER['HTTP_X_VARNISH']) {
|
||||
if (@$_SERVER['HTTP_X_VARNISH']) {
|
||||
$request_id = $_SERVER['HTTP_X_VARNISH'];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$request_id = "P" . uniqid();
|
||||
}
|
||||
}
|
||||
|
|
113
core/page.php
113
core/page.php
|
@ -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])) {
|
||||
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");
|
||||
|
@ -229,25 +250,24 @@ class Page {
|
|||
header("X-Powered-By: SCore-".SCORE_VERSION);
|
||||
|
||||
if (!headers_sent()) {
|
||||
foreach($this->http_headers as $head) {
|
||||
foreach ($this->http_headers as $head) {
|
||||
header($head);
|
||||
}
|
||||
foreach($this->cookies as $c) {
|
||||
foreach ($this->cookies as $c) {
|
||||
setcookie($c[0], $c[1], $c[2], $c[3]);
|
||||
}
|
||||
} else {
|
||||
print "Error: Headers have already been sent to the client.";
|
||||
}
|
||||
|
||||
switch($this->mode) {
|
||||
switch ($this->mode) {
|
||||
case "page":
|
||||
if(CACHE_HTTP) {
|
||||
if (CACHE_HTTP) {
|
||||
header("Vary: Cookie, Accept-Encoding");
|
||||
if($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") {
|
||||
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');
|
||||
|
@ -257,7 +277,7 @@ class Page {
|
|||
# header("Cache-control: no-cache");
|
||||
# header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT');
|
||||
#}
|
||||
if($this->get_cookie("flash_message") !== null) {
|
||||
if ($this->get_cookie("flash_message") !== null) {
|
||||
$this->add_cookie("flash_message", "", -1, "/");
|
||||
}
|
||||
usort($this->blocks, "blockcmp");
|
||||
|
@ -267,7 +287,7 @@ class Page {
|
|||
break;
|
||||
case "data":
|
||||
header("Content-Length: ".strlen($this->data));
|
||||
if(!is_null($this->filename)) {
|
||||
if (!is_null($this->filename)) {
|
||||
header('Content-Disposition: attachment; filename='.$this->filename);
|
||||
}
|
||||
print $this->data;
|
||||
|
@ -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();
|
||||
|
@ -307,7 +328,7 @@ class Page {
|
|||
|
||||
//We use $config_latest to make sure cache is reset if config is ever updated.
|
||||
$config_latest = 0;
|
||||
foreach(zglob("data/config/*") as $conf) {
|
||||
foreach (zglob("data/config/*") as $conf) {
|
||||
$config_latest = max($config_latest, filemtime($conf));
|
||||
}
|
||||
|
||||
|
@ -317,14 +338,14 @@ class Page {
|
|||
zglob("ext/{".ENABLED_EXTS."}/style.css"),
|
||||
zglob("themes/$theme_name/style.css")
|
||||
);
|
||||
foreach($css_files as $css) {
|
||||
foreach ($css_files as $css) {
|
||||
$css_latest = max($css_latest, filemtime($css));
|
||||
}
|
||||
$css_md5 = md5(serialize($css_files));
|
||||
$css_cache_file = data_path("cache/style/{$theme_name}.{$css_latest}.{$css_md5}.css");
|
||||
if(!file_exists($css_cache_file)) {
|
||||
if (!file_exists($css_cache_file)) {
|
||||
$css_data = "";
|
||||
foreach($css_files as $file) {
|
||||
foreach ($css_files as $file) {
|
||||
$file_data = file_get_contents($file);
|
||||
$pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/';
|
||||
$replace = 'url("../../../'.dirname($file).'/$1")';
|
||||
|
@ -348,14 +369,14 @@ class Page {
|
|||
zglob("ext/{".ENABLED_EXTS."}/script.js"),
|
||||
zglob("themes/$theme_name/script.js")
|
||||
);
|
||||
foreach($js_files as $js) {
|
||||
foreach ($js_files as $js) {
|
||||
$js_latest = max($js_latest, filemtime($js));
|
||||
}
|
||||
$js_md5 = md5(serialize($js_files));
|
||||
$js_cache_file = data_path("cache/script/{$theme_name}.{$js_latest}.{$js_md5}.js");
|
||||
if(!file_exists($js_cache_file)) {
|
||||
if (!file_exists($js_cache_file)) {
|
||||
$js_data = "";
|
||||
foreach($js_files as $file) {
|
||||
foreach ($js_files as $file) {
|
||||
$js_data .= file_get_contents($file) . "\n";
|
||||
}
|
||||
file_put_contents($js_cache_file, $js_data);
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
/**
|
||||
* 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();
|
||||
foreach($array as $existing) {
|
||||
if($existing != $to_remove) {
|
||||
$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,16 +35,18 @@ function array_add(array $array, $element): array {
|
|||
/**
|
||||
* Return the unique elements of an array, case insensitively
|
||||
*/
|
||||
function array_iunique(array $array): array {
|
||||
$ok = array();
|
||||
foreach($array as $element) {
|
||||
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;
|
||||
foreach ($ok as $existing) {
|
||||
if (strtolower($element) == strtolower($existing)) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$found) {
|
||||
if (!$found) {
|
||||
$ok[] = $element;
|
||||
}
|
||||
}
|
||||
|
@ -54,13 +58,14 @@ function array_iunique(array $array): array {
|
|||
*
|
||||
* from http://uk.php.net/network
|
||||
*/
|
||||
function ip_in_range(string $IP, string $CIDR): bool {
|
||||
list ($net, $mask) = explode("/", $CIDR);
|
||||
function ip_in_range(string $IP, string $CIDR): bool
|
||||
{
|
||||
list($net, $mask) = explode("/", $CIDR);
|
||||
|
||||
$ip_net = ip2long ($net);
|
||||
$ip_net = ip2long($net);
|
||||
$ip_mask = ~((1 << (32 - $mask)) - 1);
|
||||
|
||||
$ip_ip = ip2long ($IP);
|
||||
$ip_ip = ip2long($IP);
|
||||
|
||||
$ip_ip_net = $ip_ip & $ip_mask;
|
||||
|
||||
|
@ -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') {
|
||||
if (PHP_OS === 'WINNT') {
|
||||
$real = realpath($f);
|
||||
$path = realpath('./').'\\'.str_replace('/', '\\', $f);
|
||||
if($path != $real) {
|
||||
if ($path != $real) {
|
||||
rmdir($path);
|
||||
}
|
||||
else {
|
||||
foreach(glob($f.'/*') as $sf) {
|
||||
} 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)) {
|
||||
foreach(glob($f.'/*') as $sf) {
|
||||
} elseif (is_dir($f)) {
|
||||
foreach (glob($f.'/*') as $sf) {
|
||||
if (is_dir($sf) && !is_link($sf)) {
|
||||
deltree($sf);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
unlink($sf);
|
||||
}
|
||||
}
|
||||
|
@ -116,27 +117,27 @@ function deltree(string $f) {
|
|||
*
|
||||
* from a comment on http://uk.php.net/copy
|
||||
*/
|
||||
function full_copy(string $source, string $target) {
|
||||
if(is_dir($source)) {
|
||||
function full_copy(string $source, string $target)
|
||||
{
|
||||
if (is_dir($source)) {
|
||||
@mkdir($target);
|
||||
|
||||
$d = dir($source);
|
||||
|
||||
while(FALSE !== ($entry = $d->read())) {
|
||||
if($entry == '.' || $entry == '..') {
|
||||
while (false !== ($entry = $d->read())) {
|
||||
if ($entry == '.' || $entry == '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$Entry = $source . '/' . $entry;
|
||||
if(is_dir($Entry)) {
|
||||
if (is_dir($Entry)) {
|
||||
full_copy($Entry, $target . '/' . $entry);
|
||||
continue;
|
||||
}
|
||||
copy($Entry, $target . '/' . $entry);
|
||||
}
|
||||
$d->close();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
copy($source, $target);
|
||||
}
|
||||
}
|
||||
|
@ -144,35 +145,34 @@ 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)) {
|
||||
while ($f = readdir($dir)) {
|
||||
$files[] = $f;
|
||||
}
|
||||
closedir($dir);
|
||||
sort($files);
|
||||
|
||||
foreach($files as $filename) {
|
||||
foreach ($files as $filename) {
|
||||
$full_path = "$base/$_sub_dir/$filename";
|
||||
|
||||
if(is_link($full_path)) {
|
||||
if (is_link($full_path)) {
|
||||
// ignore
|
||||
}
|
||||
else if(is_dir($full_path)) {
|
||||
if(!($filename == "." || $filename == "..")) {
|
||||
} elseif (is_dir($full_path)) {
|
||||
if (!($filename == "." || $filename == "..")) {
|
||||
//subdirectory found
|
||||
$file_list = array_merge(
|
||||
$file_list,
|
||||
list_files($base, "$_sub_dir/$filename")
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$full_path = str_replace("//", "/", $full_path);
|
||||
$file_list[] = $full_path;
|
||||
}
|
||||
|
@ -186,20 +186,21 @@ 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);
|
||||
|
||||
if (isset($h[1])){
|
||||
if(!isset($headers[$h[0]])){
|
||||
if (isset($h[1])) {
|
||||
if (!isset($headers[$h[0]])) {
|
||||
$headers[$h[0]] = trim($h[1]);
|
||||
}else if(is_array($headers[$h[0]])){
|
||||
$tmp = array_merge($headers[$h[0]],array(trim($h[1])));
|
||||
} elseif (is_array($headers[$h[0]])) {
|
||||
$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])));
|
||||
} else {
|
||||
$tmp = array_merge([$headers[$h[0]]], [trim($h[1])]);
|
||||
$headers[$h[0]] = $tmp;
|
||||
}
|
||||
}
|
||||
|
@ -212,20 +213,21 @@ 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;
|
||||
}
|
||||
|
||||
$header = null;
|
||||
|
||||
if(array_key_exists($name, $headers)) {
|
||||
if (array_key_exists($name, $headers)) {
|
||||
$header = $headers[$name];
|
||||
} else {
|
||||
$headers = array_change_key_case($headers); // convert all to lower case.
|
||||
$lc_name = strtolower($name);
|
||||
|
||||
if(array_key_exists($lc_name, $headers)) {
|
||||
if (array_key_exists($lc_name, $headers)) {
|
||||
$header = $headers[$lc_name];
|
||||
}
|
||||
}
|
||||
|
@ -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,16 +298,20 @@ 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 {
|
||||
if(empty($mime_type)){
|
||||
function getExtension(?string $mime_type): ?string
|
||||
{
|
||||
if (empty($mime_type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -311,20 +322,23 @@ function getExtension(?string $mime_type): ?string {
|
|||
/**
|
||||
* Like glob, with support for matching very long patterns with braces.
|
||||
*/
|
||||
function zglob(string $pattern): array {
|
||||
$results = array();
|
||||
if(preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) {
|
||||
function zglob(string $pattern): array
|
||||
{
|
||||
$results = [];
|
||||
if (preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) {
|
||||
$braced = explode(",", $matches[2]);
|
||||
foreach($braced as $b) {
|
||||
foreach ($braced as $b) {
|
||||
$sub_pattern = $matches[1].$b.$matches[3];
|
||||
$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,12 +350,15 @@ 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') {
|
||||
foreach ($possible_vars as $var) {
|
||||
if (isset($_SERVER[$var]) && substr($_SERVER[$var], -4) === '.php') {
|
||||
$ok_var = $_SERVER[$var];
|
||||
break;
|
||||
}
|
||||
|
@ -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~
|
||||
|
@ -416,7 +439,7 @@ function url_escape(string $input): string {
|
|||
return filter_var($input, FILTER_SANITIZE_URL);
|
||||
}
|
||||
*/
|
||||
if(is_null($input)) {
|
||||
if (is_null($input)) {
|
||||
return "";
|
||||
}
|
||||
$input = str_replace('^', '^^', $input);
|
||||
|
@ -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."
|
||||
|
@ -447,14 +472,14 @@ function bool_escape($input): bool {
|
|||
*/
|
||||
if (is_bool($input)) {
|
||||
return $input;
|
||||
} else if (is_numeric($input)) {
|
||||
} elseif (is_numeric($input)) {
|
||||
return ($input === 1);
|
||||
} else {
|
||||
$value = filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
if (!is_null($value)) {
|
||||
return $value;
|
||||
} else {
|
||||
$input = strtolower( trim($input) );
|
||||
$input = strtolower(trim($input));
|
||||
return (
|
||||
$input === "y" ||
|
||||
$input === "yes" ||
|
||||
|
@ -471,37 +496,39 @@ 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 {
|
||||
if(!is_numeric($val) || (!is_null($min) && $val < $min)) {
|
||||
function clamp(int $val, int $min=null, int $max=null): int
|
||||
{
|
||||
if (!is_numeric($val) || (!is_null($min) && $val < $min)) {
|
||||
$val = $min;
|
||||
}
|
||||
if(!is_null($max) && $val > $max) {
|
||||
if (!is_null($max) && $val > $max) {
|
||||
$val = $max;
|
||||
}
|
||||
if(!is_null($min) && !is_null($max)) {
|
||||
if (!is_null($min) && !is_null($max)) {
|
||||
assert($val >= $min && $val <= $max, "$min <= $val <= $max");
|
||||
}
|
||||
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) {
|
||||
foreach ($attrs as $k => $v) {
|
||||
$xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES));
|
||||
$xml .= "$k=\"$xv\" ";
|
||||
}
|
||||
if(count($children) > 0) {
|
||||
if (count($children) > 0) {
|
||||
$xml .= ">\n";
|
||||
foreach($children as $child) {
|
||||
foreach ($children as $child) {
|
||||
$xml .= xml_tag($child);
|
||||
}
|
||||
$xml .= "</$name>\n";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$xml .= "/>\n";
|
||||
}
|
||||
return $xml;
|
||||
|
@ -511,13 +538,16 @@ 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))) {
|
||||
if($breakpoint < strlen($string) - 1) {
|
||||
if (false !== ($breakpoint = strpos($string, $break, $limit))) {
|
||||
if ($breakpoint < strlen($string) - 1) {
|
||||
$string = substr($string, 0, $breakpoint) . $pad;
|
||||
}
|
||||
}
|
||||
|
@ -528,16 +558,19 @@ 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 {
|
||||
if(preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) {
|
||||
function parse_shorthand_int(string $limit): int
|
||||
{
|
||||
if (preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) {
|
||||
$value = $m[1];
|
||||
if (isset($m[2])) {
|
||||
switch(strtolower($m[2])) {
|
||||
switch (strtolower($m[2])) {
|
||||
/** @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)) {
|
||||
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,87 +639,82 @@ 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) {
|
||||
foreach ($inputs as $key => $validations) {
|
||||
$flags = explode(',', $validations);
|
||||
|
||||
if(in_array('bool', $flags) && !isset($_POST[$key])) {
|
||||
if (in_array('bool', $flags) && !isset($_POST[$key])) {
|
||||
$_POST[$key] = 'off';
|
||||
}
|
||||
|
||||
if(in_array('optional', $flags)) {
|
||||
if(!isset($_POST[$key]) || trim($_POST[$key]) == "") {
|
||||
if (in_array('optional', $flags)) {
|
||||
if (!isset($_POST[$key]) || trim($_POST[$key]) == "") {
|
||||
$outputs[$key] = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!isset($_POST[$key]) || trim($_POST[$key]) == "") {
|
||||
if (!isset($_POST[$key]) || trim($_POST[$key]) == "") {
|
||||
throw new InvalidInput("Input '$key' not set");
|
||||
}
|
||||
|
||||
$value = trim($_POST[$key]);
|
||||
|
||||
if(in_array('user_id', $flags)) {
|
||||
if (in_array('user_id', $flags)) {
|
||||
$id = int_escape($value);
|
||||
if(in_array('exists', $flags)) {
|
||||
if(is_null(User::by_id($id))) {
|
||||
if (in_array('exists', $flags)) {
|
||||
if (is_null(User::by_id($id))) {
|
||||
throw new InvalidInput("User #$id does not exist");
|
||||
}
|
||||
}
|
||||
$outputs[$key] = $id;
|
||||
}
|
||||
else if(in_array('user_name', $flags)) {
|
||||
if(strlen($value) < 1) {
|
||||
} 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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
if(in_array('trim', $flags)) {
|
||||
} elseif (in_array('string', $flags)) {
|
||||
if (in_array('trim', $flags)) {
|
||||
$value = trim($value);
|
||||
}
|
||||
if(in_array('lower', $flags)) {
|
||||
if (in_array('lower', $flags)) {
|
||||
$value = strtolower($value);
|
||||
}
|
||||
if(in_array('not-empty', $flags)) {
|
||||
if (in_array('not-empty', $flags)) {
|
||||
throw new InvalidInput("$key must not be blank");
|
||||
}
|
||||
if(in_array('nullify', $flags)) {
|
||||
if(empty($value)) $value = null;
|
||||
if (in_array('nullify', $flags)) {
|
||||
if (empty($value)) {
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
$outputs[$key] = $value;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw new InvalidInput("Unknown validation '$validations'");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
|
||||
/** @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");
|
||||
|
||||
$cache_path = data_path("cache/shm_event_listeners.php");
|
||||
if(COMPILE_ELS && file_exists($cache_path)) {
|
||||
if (COMPILE_ELS && file_exists($cache_path)) {
|
||||
require_once($cache_path);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_set_event_listeners();
|
||||
|
||||
if(COMPILE_ELS) {
|
||||
if (COMPILE_ELS) {
|
||||
_dump_event_listeners($_shm_event_listeners, $cache_path);
|
||||
}
|
||||
}
|
||||
|
@ -27,27 +27,29 @@ 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) {
|
||||
foreach (get_declared_classes() as $class) {
|
||||
$rclass = new ReflectionClass($class);
|
||||
if($rclass->isAbstract()) {
|
||||
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") {
|
||||
foreach (get_class_methods($extension) as $method) {
|
||||
if (substr($method, 0, 2) == "on") {
|
||||
$event = substr($method, 2) . "Event";
|
||||
$pos = $extension->get_priority() * 100;
|
||||
while(isset($_shm_event_listeners[$event][$pos])) {
|
||||
while (isset($_shm_event_listeners[$event][$pos])) {
|
||||
$pos += 1;
|
||||
}
|
||||
$_shm_event_listeners[$event][$pos] = $extension;
|
||||
|
@ -57,21 +59,22 @@ 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) {
|
||||
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(); ";
|
||||
}
|
||||
}
|
||||
|
||||
$p .= "\$_shm_event_listeners = array(\n";
|
||||
foreach($event_listeners as $event => $listeners) {
|
||||
foreach ($event_listeners as $event => $listeners) {
|
||||
$p .= "\t'$event' => array(\n";
|
||||
foreach($listeners as $id => $listener) {
|
||||
foreach ($listeners as $id => $listener) {
|
||||
$p .= "\t\t$id => \$".get_class($listener).",\n";
|
||||
}
|
||||
$p .= "\t),\n";
|
||||
|
@ -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(method_exists($listener, $method_name)) {
|
||||
foreach ($my_event_listeners as $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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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 & <waffles>"
|
||||
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
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 {
|
||||
if(strpos($base, "?")) {
|
||||
if (is_null($query)) {
|
||||
return str_replace("//", "/", $base.'/'.$page);
|
||||
} 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,23 +56,24 @@ 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'])) {
|
||||
if (isset($changes['q'])) {
|
||||
$base = $changes['q'];
|
||||
unset($changes['q']);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$base = _get_query();
|
||||
}
|
||||
|
||||
if(isset($params['q'])) {
|
||||
if (isset($params['q'])) {
|
||||
unset($params['q']);
|
||||
}
|
||||
|
||||
foreach($changes as $k => $v) {
|
||||
if(is_null($v) and isset($params[$k])) unset($params[$k]);
|
||||
foreach ($changes as $k => $v) {
|
||||
if (is_null($v) and isset($params[$k])) {
|
||||
unset($params[$k]);
|
||||
}
|
||||
$params[$k] = $v;
|
||||
}
|
||||
|
||||
|
@ -83,12 +84,13 @@ function modify_url(string $url, array $changes): string {
|
|||
/**
|
||||
* Turn a relative link into an absolute one, including hostname
|
||||
*/
|
||||
function make_http(string $link): string {
|
||||
if(strpos($link, "://") > 0) {
|
||||
function make_http(string $link): string
|
||||
{
|
||||
if (strpos($link, "://") > 0) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
if(strlen($link) > 0 && $link[0] != '/') {
|
||||
if (strlen($link) > 0 && $link[0] != '/') {
|
||||
$link = get_base_href() . '/' . $link;
|
||||
}
|
||||
|
||||
|
|
116
core/user.php
116
core/user.php
|
@ -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']);
|
||||
|
@ -54,63 +57,67 @@ class User {
|
|||
$this->join_date = $row['joindate'];
|
||||
$this->passhash = $row['pass'];
|
||||
|
||||
if(array_key_exists($row["class"], $_shm_user_classes)) {
|
||||
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") {
|
||||
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) {
|
||||
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)) {
|
||||
if ($user) {
|
||||
if ($user->passhash == md5(strtolower($name) . $pass)) {
|
||||
log_info("core-user", "Migrating from md5 to bcrypt for ".html_escape($name));
|
||||
$user->set_password($pass);
|
||||
}
|
||||
if(password_verify($pass, $user->passhash)) {
|
||||
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)) {
|
||||
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)) {
|
||||
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,11 +192,12 @@ 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") {
|
||||
if(!empty($this->email)) {
|
||||
if ($config->get_string("avatar_host") === "gravatar") {
|
||||
if (!empty($this->email)) {
|
||||
$hash = md5(strtolower($this->email));
|
||||
$s = $config->get_string("avatar_gravatar_size");
|
||||
$d = urlencode($config->get_string("avatar_gravatar_default"));
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,15 +24,16 @@ 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;
|
||||
$this->abilities = $abilities;
|
||||
|
||||
if(!is_null($parent)) {
|
||||
if (!is_null($parent)) {
|
||||
$this->parent = $_shm_user_classes[$parent];
|
||||
}
|
||||
|
||||
|
@ -43,21 +45,20 @@ class UserClass {
|
|||
*
|
||||
* @throws SCoreException
|
||||
*/
|
||||
public function can(string $ability): bool {
|
||||
if(array_key_exists($ability, $this->abilities)) {
|
||||
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;
|
||||
foreach($_shm_user_classes['base']->abilities as $a => $cando) {
|
||||
foreach ($_shm_user_classes['base']->abilities as $a => $cando) {
|
||||
$v = levenshtein($ability, $a);
|
||||
if($v < $min_dist) {
|
||||
if ($v < $min_dist) {
|
||||
$min_dist = $v;
|
||||
$min_ability = $a;
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
|
|
229
core/util.php
229
core/util.php
|
@ -5,25 +5,32 @@ 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(
|
||||
if (
|
||||
startsWith($text, "http:") ||
|
||||
startsWith($text, "https:") ||
|
||||
startsWith($text, "mailto:")
|
||||
|
@ -31,11 +38,11 @@ function contact_link(): ?string {
|
|||
return $text;
|
||||
}
|
||||
|
||||
if(strpos($text, "@")) {
|
||||
if (strpos($text, "@")) {
|
||||
return "mailto:$text";
|
||||
}
|
||||
|
||||
if(strpos($text, "/")) {
|
||||
if (strpos($text, "/")) {
|
||||
return "http://$text";
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
if($a->position == $b->position) {
|
||||
function blockcmp(Block $a, Block $b): int
|
||||
{
|
||||
if ($a->position == $b->position) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return ($a->position > $b->position);
|
||||
}
|
||||
}
|
||||
|
@ -64,14 +72,15 @@ 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
|
||||
$default_limit = 8*1024*1024; // 8 MB of memory is PHP's default.
|
||||
$shimmie_limit = parse_shorthand_int($config->get_int("thumb_mem_limit"));
|
||||
|
||||
if($shimmie_limit < 3*1024*1024) {
|
||||
if ($shimmie_limit < 3*1024*1024) {
|
||||
// we aren't going to fit, override
|
||||
$shimmie_limit = $default_limit;
|
||||
}
|
||||
|
@ -84,18 +93,17 @@ function get_memory_limit(): int {
|
|||
*/
|
||||
$memory = parse_shorthand_int(ini_get("memory_limit"));
|
||||
|
||||
if($memory == -1) {
|
||||
if ($memory == -1) {
|
||||
// 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.
|
||||
|
||||
// Attempt to set PHP's memory limit.
|
||||
if ( ini_set("memory_limit", $shimmie_limit) === false ) {
|
||||
if (ini_set("memory_limit", $shimmie_limit) === false) {
|
||||
/* We can't change PHP's limit, oh well, return whatever its currently set to */
|
||||
return $memory;
|
||||
}
|
||||
|
@ -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,10 +137,11 @@ 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) {
|
||||
if ($current) {
|
||||
$text = $current . "\n" . $text;
|
||||
}
|
||||
# the message should be viewed pretty much immediately,
|
||||
|
@ -142,35 +152,42 @@ 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) {
|
||||
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")) {
|
||||
if ($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) {
|
||||
$ch = curl_init($url);
|
||||
$fp = fopen($mfile, "w");
|
||||
|
||||
|
@ -194,7 +211,7 @@ function transload(string $url, string $mfile): ?array {
|
|||
return $headers;
|
||||
}
|
||||
|
||||
if($config->get_string("transload_engine") === "wget") {
|
||||
if ($config->get_string("transload_engine") === "wget") {
|
||||
$s_url = escapeshellarg($url);
|
||||
$s_mfile = escapeshellarg($mfile);
|
||||
system("wget --no-check-certificate $s_url --output-document=$s_mfile");
|
||||
|
@ -202,14 +219,14 @@ function transload(string $url, string $mfile): ?array {
|
|||
return file_exists($mfile);
|
||||
}
|
||||
|
||||
if($config->get_string("transload_engine") === "fopen") {
|
||||
if ($config->get_string("transload_engine") === "fopen") {
|
||||
$fp_in = @fopen($url, "r");
|
||||
$fp_out = fopen($mfile, "w");
|
||||
if(!$fp_in || !$fp_out) {
|
||||
if (!$fp_in || !$fp_out) {
|
||||
return null;
|
||||
}
|
||||
$length = 0;
|
||||
while(!feof($fp_in) && $length <= $config->get_int('upload_size')) {
|
||||
while (!feof($fp_in) && $length <= $config->get_int('upload_size')) {
|
||||
$data = fread($fp_in, 8192);
|
||||
$length += strlen($data);
|
||||
fwrite($fp_out, $data);
|
||||
|
@ -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();
|
||||
if(preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) {
|
||||
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"){
|
||||
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,11 +332,12 @@ function get_debug_info(): string {
|
|||
return $debug;
|
||||
}
|
||||
|
||||
function log_slow() {
|
||||
function log_slow()
|
||||
{
|
||||
global $_shm_load_start;
|
||||
if(!is_null(SLOW_PAGES)) {
|
||||
if (!is_null(SLOW_PAGES)) {
|
||||
$_time = microtime(true) - $_shm_load_start;
|
||||
if($_time > SLOW_PAGES) {
|
||||
if ($_time > SLOW_PAGES) {
|
||||
$_query = _get_query();
|
||||
$_dbg = get_debug_info();
|
||||
file_put_contents("data/slow-pages.log", "$_time $_query $_dbg\n", FILE_APPEND | LOCK_EX);
|
||||
|
@ -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() {
|
||||
if(MIN_PHP_VERSION) {
|
||||
if(version_compare(phpversion(), MIN_PHP_VERSION, ">=") === FALSE) {
|
||||
function _version_check()
|
||||
{
|
||||
if (MIN_PHP_VERSION) {
|
||||
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,16 +378,17 @@ date and you should plan on moving elsewhere.
|
|||
}
|
||||
}
|
||||
|
||||
function _sanitise_environment() {
|
||||
function _sanitise_environment()
|
||||
{
|
||||
global $_shm_ctx;
|
||||
|
||||
if(TIMEZONE) {
|
||||
if (TIMEZONE) {
|
||||
date_default_timezone_set(TIMEZONE);
|
||||
}
|
||||
|
||||
# ini_set('zend.assertions', 1); // generate assertions
|
||||
ini_set('assert.exception', 1); // throw exceptions when failed
|
||||
if(DEBUG) {
|
||||
if (DEBUG) {
|
||||
error_reporting(E_ALL);
|
||||
assert_options(ASSERT_ACTIVE, 1);
|
||||
assert_options(ASSERT_BAIL, 1);
|
||||
|
@ -372,19 +398,19 @@ function _sanitise_environment() {
|
|||
}
|
||||
|
||||
$_shm_ctx = new Context();
|
||||
if(CONTEXT) {
|
||||
if (CONTEXT) {
|
||||
$_shm_ctx->set_log(CONTEXT);
|
||||
}
|
||||
|
||||
if(COVERAGE) {
|
||||
if (COVERAGE) {
|
||||
_start_coverage();
|
||||
register_shutdown_function("_end_coverage");
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') {
|
||||
if(isset($_SERVER['REMOTE_ADDR'])) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') {
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
die("CLI with remote addr? Confused, not taking the risk.");
|
||||
}
|
||||
$_SERVER['REMOTE_ADDR'] = "0.0.0.0";
|
||||
|
@ -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,33 +470,40 @@ 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] == "^") {
|
||||
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")) {
|
||||
if ($page->get_cookie("user") && $page->get_cookie("session")) {
|
||||
$tmp_user = User::by_session($page->get_cookie("user"), $page->get_cookie("session"));
|
||||
if(!is_null($tmp_user)) {
|
||||
if (!is_null($tmp_user)) {
|
||||
$user = $tmp_user;
|
||||
}
|
||||
}
|
||||
if(is_null($user)) {
|
||||
if (is_null($user)) {
|
||||
$user = User::by_id($config->get_int("anon_id", 0));
|
||||
}
|
||||
assert(!is_null($user));
|
||||
|
@ -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 {
|
||||
if(function_exists("xdebug_start_code_coverage")) {
|
||||
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 {
|
||||
if(function_exists("xdebug_get_code_coverage")) {
|
||||
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,22 +570,22 @@ 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") {
|
||||
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();
|
||||
}
|
||||
|
||||
$extra = empty($form_id) ? '' : 'id="'. $form_id .'"';
|
||||
if($multipart) {
|
||||
if ($multipart) {
|
||||
$extra .= " enctype='multipart/form-data'";
|
||||
}
|
||||
if($onsubmit) {
|
||||
if ($onsubmit) {
|
||||
$extra .= ' onsubmit="'.$onsubmit.'"';
|
||||
}
|
||||
return '<form action="'.$target.'" method="'.$method.'" '.$extra.'>'.$extra_inputs;
|
||||
|
|
|
@ -23,49 +23,53 @@
|
|||
/**
|
||||
* 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")) {
|
||||
if ($event->page_matches("admin")) {
|
||||
if (!$user->can("manage_admintools")) {
|
||||
$this->theme->display_permission_denied();
|
||||
}
|
||||
else {
|
||||
if($event->count_args() == 0) {
|
||||
} else {
|
||||
if ($event->count_args() == 0) {
|
||||
send_event(new AdminBuildingEvent($page));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$action = $event->get_arg(0);
|
||||
$aae = new AdminActionEvent($action);
|
||||
|
||||
if($user->check_auth_token()) {
|
||||
if ($user->check_auth_token()) {
|
||||
log_info("admin", "Util: $action");
|
||||
set_time_limit(0);
|
||||
send_event($aae);
|
||||
}
|
||||
|
||||
if($aae->redirect) {
|
||||
if ($aae->redirect) {
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect(make_link("admin"));
|
||||
}
|
||||
|
@ -74,57 +78,62 @@ class AdminPage extends Extension {
|
|||
}
|
||||
}
|
||||
|
||||
public function onCommand(CommandEvent $event) {
|
||||
if($event->cmd == "help") {
|
||||
public function onCommand(CommandEvent $event)
|
||||
{
|
||||
if ($event->cmd == "help") {
|
||||
print "\tget-page [query string]\n";
|
||||
print "\t\teg 'get-page post/list'\n\n";
|
||||
print "\tregen-thumb [hash]\n";
|
||||
print "\t\tregenerate a thumbnail\n\n";
|
||||
}
|
||||
if($event->cmd == "get-page") {
|
||||
if ($event->cmd == "get-page") {
|
||||
global $page;
|
||||
send_event(new PageRequestEvent($event->args[0]));
|
||||
$page->display();
|
||||
}
|
||||
if($event->cmd == "regen-thumb") {
|
||||
if ($event->cmd == "regen-thumb") {
|
||||
$image = Image::by_hash($event->args[0]);
|
||||
if($image) {
|
||||
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")) {
|
||||
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)) {
|
||||
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)) {
|
||||
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'];
|
||||
|
@ -133,8 +142,8 @@ class AdminPage extends Extension {
|
|||
$images = Image::find_images(0, 1000000, Tag::explode($query));
|
||||
$count = count($images);
|
||||
log_warning("admin", "Mass-deleting $count images from $query", "Mass deleted $count images");
|
||||
foreach($images as $image) {
|
||||
if($reason && class_exists("ImageBan")) {
|
||||
foreach ($images as $image) {
|
||||
if ($reason && class_exists("ImageBan")) {
|
||||
send_event(new AddImageHashBanEvent($image->hash, $reason));
|
||||
}
|
||||
send_event(new ImageDeletionEvent($image));
|
||||
|
@ -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'];
|
||||
|
@ -187,7 +200,7 @@ class AdminPage extends Extension {
|
|||
$hostname = $matches['host'];
|
||||
$database = $matches['dbname'];
|
||||
|
||||
switch($software) {
|
||||
switch ($software) {
|
||||
case 'mysql':
|
||||
$cmd = "mysqldump -h$hostname -u$username -p$password $database";
|
||||
break;
|
||||
|
@ -204,7 +217,7 @@ class AdminPage extends Extension {
|
|||
|
||||
//FIXME: .SQL dump is empty if cmd doesn't exist
|
||||
|
||||
if($cmd) {
|
||||
if ($cmd) {
|
||||
$page->set_mode("data");
|
||||
$page->set_type("application/x-unknown");
|
||||
$page->set_filename('shimmie-'.date('Ymd').'.sql');
|
||||
|
@ -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){
|
||||
foreach($images as $img){
|
||||
$img_loc = warehouse_path("images", $img["hash"], FALSE);
|
||||
if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === true) {
|
||||
foreach ($images as $img) {
|
||||
$img_loc = warehouse_path("images", $img["hash"], false);
|
||||
$zip->addFile($img_loc, $img["hash"].".".$img["ext"]);
|
||||
}
|
||||
$zip->close();
|
||||
|
@ -235,27 +249,28 @@ 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
|
||||
//TODO: Update score_log (Having an optional ID column for score_log would be nice..)
|
||||
preg_match("#^(?P<proto>\w+)\:(?:user=(?P<user>\w+)(?:;|$)|password=(?P<password>\w*)(?:;|$)|host=(?P<host>[\w\.\-]+)(?:;|$)|dbname=(?P<dbname>[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches);
|
||||
|
||||
if($matches['proto'] == "mysql"){
|
||||
if ($matches['proto'] == "mysql") {
|
||||
$tables = $database->get_col("SELECT TABLE_NAME
|
||||
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");
|
||||
foreach($ids as $id){
|
||||
foreach ($ids as $id) {
|
||||
$sql = "SET FOREIGN_KEY_CHECKS=0;
|
||||
UPDATE images SET id={$i} WHERE image_id={$id};";
|
||||
|
||||
foreach($tables as $table){
|
||||
foreach ($tables as $table) {
|
||||
$sql .= "UPDATE {$table} SET image_id={$i} WHERE image_id={$id};";
|
||||
}
|
||||
|
||||
|
@ -265,12 +280,11 @@ class AdminPage extends Extension {
|
|||
$i++;
|
||||
}
|
||||
$database->execute("ALTER TABLE images AUTO_INCREMENT=".(count($ids) + 1));
|
||||
}elseif($matches['proto'] == "pgsql"){
|
||||
} elseif ($matches['proto'] == "pgsql") {
|
||||
//TODO: Make this work with PostgreSQL
|
||||
}elseif($matches['proto'] == "sqlite"){
|
||||
} elseif ($matches['proto'] == "sqlite") {
|
||||
//TODO: Make this work with SQLite
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
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,10 +57,11 @@ 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")) {
|
||||
if (class_exists("ImageBan")) {
|
||||
$h_reason = "<input type='text' name='reason' placeholder='Ban reason (leave blank to not ban)'>";
|
||||
}
|
||||
$html = make_form(make_link("admin/delete_by_query"), "POST") . "
|
||||
|
@ -68,4 +74,3 @@ class AdminPageTheme extends Themelet {
|
|||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,142 +10,141 @@
|
|||
* 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")) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if($user->can("manage_alias_list")) {
|
||||
if(isset($_POST['oldtag']) && isset($_POST['newtag'])) {
|
||||
if ($event->page_matches("alias")) {
|
||||
if ($event->get_arg(0) == "add") {
|
||||
if ($user->can("manage_alias_list")) {
|
||||
if (isset($_POST['oldtag']) && isset($_POST['newtag'])) {
|
||||
try {
|
||||
$aae = new AddAliasEvent($_POST['oldtag'], $_POST['newtag']);
|
||||
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") {
|
||||
if($user->can("manage_alias_list")) {
|
||||
if(isset($_POST['oldtag'])) {
|
||||
$database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", array("oldtag" => $_POST['oldtag']));
|
||||
} elseif ($event->get_arg(0) == "remove") {
|
||||
if ($user->can("manage_alias_list")) {
|
||||
if (isset($_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)) {
|
||||
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") {
|
||||
if($user->can("manage_alias_list")) {
|
||||
if(count($_FILES) > 0) {
|
||||
} elseif ($event->get_arg(0) == "import") {
|
||||
if ($user->can("manage_alias_list")) {
|
||||
if (count($_FILES) > 0) {
|
||||
$tmp = $_FILES['alias_file']['tmp_name'];
|
||||
$contents = file_get_contents($tmp);
|
||||
$this->add_alias_csv($database, $contents);
|
||||
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);
|
||||
if($database->get_row("SELECT * FROM aliases WHERE oldtag=:oldtag AND lower(newtag)=lower(:newtag)", $pair)) {
|
||||
$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")) {
|
||||
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) {
|
||||
foreach ($aliases as $old => $new) {
|
||||
$csv .= "\"$old\",\"$new\"\n";
|
||||
}
|
||||
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) {
|
||||
foreach (explode("\n", $csv) as $line) {
|
||||
$parts = str_getcsv($line);
|
||||
if(count($parts) == 2) {
|
||||
if (count($parts) == 2) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<?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");
|
||||
if($can_manage) {
|
||||
if ($can_manage) {
|
||||
$h_action = "<th width='10%'>Action</th>";
|
||||
$h_add = "
|
||||
<tr>
|
||||
|
@ -21,19 +23,18 @@ class AliasEditorTheme extends Themelet {
|
|||
</form>
|
||||
</tr>
|
||||
";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$h_action = "";
|
||||
$h_add = "";
|
||||
}
|
||||
|
||||
$h_aliases = "";
|
||||
foreach($aliases as $old => $new) {
|
||||
foreach ($aliases as $old => $new) {
|
||||
$h_old = html_escape($old);
|
||||
$h_new = "<a href='".make_link("post/list/".url_escape($new)."/1")."'>".html_escape($new)."</a>";
|
||||
|
||||
$h_aliases .= "<tr><td>$h_old</td><td>$h_new</td>";
|
||||
if($can_manage) {
|
||||
if ($can_manage) {
|
||||
$h_aliases .= "
|
||||
<td>
|
||||
".make_form(make_link("alias/remove"))."
|
||||
|
@ -65,11 +66,10 @@ class AliasEditorTheme extends Themelet {
|
|||
$page->set_heading("Alias List");
|
||||
$page->add_block(new NavBlock());
|
||||
$page->add_block(new Block("Aliases", $html));
|
||||
if($can_manage) {
|
||||
if ($can_manage) {
|
||||
$page->add_block(new Block("Bulk Upload", $bulk_html, "main", 51));
|
||||
}
|
||||
|
||||
$this->display_paginator($page, "alias/list", null, $pageNumber, $totalPages);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,12 +28,13 @@ 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");
|
||||
$bucket = $config->get_string("amazon_s3_bucket");
|
||||
if(!empty($bucket)) {
|
||||
if (!empty($bucket)) {
|
||||
log_debug("amazon_s3", "Mirroring Image #".$event->image->id." to S3 #$bucket");
|
||||
$s3 = new S3($access, $secret);
|
||||
$s3->putBucket($bucket, S3::ACL_PUBLIC_READ);
|
||||
|
@ -39,32 +43,33 @@ 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");
|
||||
$bucket = $config->get_string("amazon_s3_bucket");
|
||||
if(!empty($bucket)) {
|
||||
if (!empty($bucket)) {
|
||||
log_debug("amazon_s3", "Deleting Image #".$event->image->id." from S3");
|
||||
$s3 = new S3($access, $secret);
|
||||
$s3->deleteObject($bucket, "images/" . $event->image->hash);
|
||||
|
@ -72,4 +77,3 @@ class UploadS3 extends Extension {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,8 +23,9 @@ class ArrowkeyNavigation extends Extension {
|
|||
/**
|
||||
* Adds functionality for post/list.
|
||||
*/
|
||||
public function onPageRequest(PageRequestEvent $event) {
|
||||
if($event->page_matches("post/list")) {
|
||||
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"]));
|
||||
$next_url = make_http(make_link("post/list/".$pageinfo["next"]));
|
||||
|
@ -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,41 +55,48 @@ 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
|
||||
$images_per_page = $config->get_int('index_images');
|
||||
|
||||
// if there are no tags, use default
|
||||
if (is_null($event->get_arg(1))){
|
||||
if (is_null($event->get_arg(1))) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
* Documentation:
|
||||
*
|
||||
*/
|
||||
class AuthorSetEvent extends Event {
|
||||
class AuthorSetEvent extends Event
|
||||
{
|
||||
/** @var \Image */
|
||||
public $image;
|
||||
/** @var \User */
|
||||
|
@ -16,38 +17,44 @@ 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()) {
|
||||
if (!$user->is_anonymous()) {
|
||||
$event->add_part($this->theme->get_author_editor_html($artistName), 42);
|
||||
}
|
||||
}
|
||||
|
||||
public function onSearchTermParse(SearchTermParseEvent $event) {
|
||||
$matches = array();
|
||||
if(preg_match("/^author[=|:](.*)$/i", $event->term, $matches)) {
|
||||
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,47 +107,53 @@ 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")) {
|
||||
switch($event->get_arg(0)) {
|
||||
if ($event->page_matches("artist")) {
|
||||
switch ($event->get_arg(0)) {
|
||||
//*************ARTIST SECTION**************
|
||||
case "list":
|
||||
{
|
||||
|
@ -150,10 +163,9 @@ class Artists extends Extension {
|
|||
}
|
||||
case "new":
|
||||
{
|
||||
if(!$user->is_anonymous()) {
|
||||
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;
|
||||
|
@ -166,17 +178,15 @@ class Artists extends Extension {
|
|||
}
|
||||
case "create":
|
||||
{
|
||||
if(!$user->is_anonymous()) {
|
||||
if (!$user->is_anonymous()) {
|
||||
$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;
|
||||
|
@ -215,13 +225,12 @@ class Artists extends Extension {
|
|||
$members = $this->get_members($artistID);
|
||||
$urls = $this->get_urls($artistID);
|
||||
|
||||
if(!$user->is_anonymous()) {
|
||||
if (!$user->is_anonymous()) {
|
||||
$this->theme->show_artist_editor($artist, $aliases, $members, $urls);
|
||||
|
||||
$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"];
|
||||
|
||||
|
@ -672,27 +706,30 @@ class Artists extends Extension {
|
|||
//$artistID = "";
|
||||
|
||||
//// WE CHECK IF THE ARTIST ALREADY EXISTS ON DATABASE; IF NOT WE CREATE
|
||||
if(!$this->artist_exists($name)) {
|
||||
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"]);
|
||||
|
@ -873,7 +921,7 @@ class Artists extends Extension {
|
|||
ON a.id = aa.artist_id
|
||||
");
|
||||
|
||||
$totalPages = ceil ($count / $artistsPerPage);
|
||||
$totalPages = ceil($count / $artistsPerPage);
|
||||
|
||||
$this->theme->list_artists($listing, $pageNumber + 1, $totalPages);
|
||||
}
|
||||
|
@ -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"]);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,19 +15,20 @@ 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 = "";
|
||||
|
||||
if($mode == "neutral"){
|
||||
if ($mode == "neutral") {
|
||||
$html = "<form method='post' action='".make_link("artist/new_artist")."'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='submit' name='edit' id='edit' value='New Artist'/>
|
||||
</form>";
|
||||
}
|
||||
|
||||
if($mode == "editor"){
|
||||
if ($mode == "editor") {
|
||||
$html = "<form method='post' action='".make_link("artist/new_artist")."'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='submit' name='edit' id='edit' value='New Artist'/>
|
||||
|
@ -37,7 +40,7 @@ class ArtistsTheme extends Themelet {
|
|||
<input type='hidden' name='artist_id' value='".$artistID."'>
|
||||
</form>";
|
||||
|
||||
if($is_admin){
|
||||
if ($is_admin) {
|
||||
$html .= "<form method='post' action='".make_link("artist/nuke_artist")."'>
|
||||
".$user->get_auth_html()."
|
||||
<input type='submit' name='edit' id='edit' value='Delete Artist'/>
|
||||
|
@ -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>
|
||||
|
@ -366,7 +400,7 @@ class ArtistsTheme extends Themelet {
|
|||
|
||||
//we show the images for the artist
|
||||
$artist_images = "";
|
||||
foreach($images as $image) {
|
||||
foreach ($images as $image) {
|
||||
$thumb_html = $this->build_thumb_html($image);
|
||||
|
||||
$artist_images .= '<span class="thumb">'.
|
||||
|
@ -377,9 +411,10 @@ 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) {
|
||||
if (count($aliases) > 0) {
|
||||
$aliasViewLink = str_replace("_", " ", $aliases[0]['alias_name']); // no link anymore
|
||||
$aliasEditLink = "<a href='" . make_link("artist/alias/edit/" . $aliases[0]['alias_id']) . "'>Edit</a>";
|
||||
$aliasDeleteLink = "<a href='" . make_link("artist/alias/delete/" . $aliases[0]['alias_id']) . "'>Delete</a>";
|
||||
|
@ -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'> </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,9 +456,10 @@ 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) {
|
||||
if (count($members) > 0) {
|
||||
$memberViewLink = str_replace("_", " ", $members[0]['name']); // no link anymore
|
||||
$memberEditLink = "<a href='" . make_link("artist/member/edit/" . $members[0]['id']) . "'>Edit</a>";
|
||||
$memberDeleteLink = "<a href='" . make_link("artist/member/delete/" . $members[0]['id']) . "'>Delete</a>";
|
||||
|
@ -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'> </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,9 +499,10 @@ 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) {
|
||||
if (count($urls) > 0) {
|
||||
$urlViewLink = "<a href='" . str_replace("_", " ", $urls[0]['url']) . "' target='_blank'>" . str_replace("_", " ", $urls[0]['url']) . "</a>";
|
||||
$urlEditLink = "<a href='" . make_link("artist/url/edit/" . $urls[0]['id']) . "'>Edit</a>";
|
||||
$urlDeleteLink = "<a href='" . make_link("artist/url/delete/" . $urls[0]['id']) . "'>Delete</a>";
|
||||
|
@ -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'> </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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -5,20 +5,27 @@
|
|||
* 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 ($event->page_matches("api/internal/autocomplete")) {
|
||||
if (!isset($_GET["s"])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page->set_mode("data");
|
||||
$page->set_type("application/json");
|
||||
|
||||
$s = strtolower($_GET["s"]);
|
||||
if(
|
||||
if (
|
||||
$s == '' ||
|
||||
$s[0] == '_' ||
|
||||
$s[0] == '%' ||
|
||||
|
@ -31,22 +38,24 @@ class AutoComplete extends Extension {
|
|||
//$limit = 0;
|
||||
$cache_key = "autocomplete-$s";
|
||||
$limitSQL = "";
|
||||
$SQLarr = array("search"=>"$s%");
|
||||
if(isset($_GET["limit"]) && $_GET["limit"] !== 0){
|
||||
$SQLarr = ["search"=>"$s%"];
|
||||
if (isset($_GET["limit"]) && $_GET["limit"] !== 0) {
|
||||
$limitSQL = "LIMIT :limit";
|
||||
$SQLarr['limit'] = $_GET["limit"];
|
||||
$cache_key .= "-" . $_GET["limit"];
|
||||
}
|
||||
|
||||
$res = $database->cache->get($cache_key);
|
||||
if(!$res) {
|
||||
$res = $database->get_pairs($database->scoreql_to_sql("
|
||||
if (!$res) {
|
||||
$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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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,34 +55,38 @@ xanax
|
|||
");
|
||||
}
|
||||
|
||||
public function onCommentPosting(CommentPostingEvent $event) {
|
||||
public function onCommentPosting(CommentPostingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
if(!$user->can("bypass_comment_checks")) {
|
||||
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();
|
||||
foreach($this->get_words() as $word) {
|
||||
if($word[0] == '/') {
|
||||
if(preg_match($word, "") === false) {
|
||||
$failed = [];
|
||||
foreach ($this->get_words() as $word) {
|
||||
if ($word[0] == '/') {
|
||||
if (preg_match($word, "") === false) {
|
||||
$failed[] = $word;
|
||||
}
|
||||
}
|
||||
}
|
||||
if($failed) {
|
||||
if ($failed) {
|
||||
$sb->add_label("Failed regexes: ".join(", ", $failed));
|
||||
}
|
||||
$event->panel->add_block($sb);
|
||||
|
@ -89,33 +95,34 @@ 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) {
|
||||
if($word[0] == '/') {
|
||||
foreach ($this->get_words() as $word) {
|
||||
if ($word[0] == '/') {
|
||||
// lines that start with slash are regex
|
||||
if(preg_match($word, $comment) === 1) {
|
||||
if (preg_match($word, $comment) === 1) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// other words are literal
|
||||
if(strpos($comment, $word) !== false) {
|
||||
if (strpos($comment, $word) !== false) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
foreach (explode("\n", $banned) as $word) {
|
||||
$word = trim(strtolower($word));
|
||||
if(strlen($word) == 0) {
|
||||
if (strlen($word) == 0) {
|
||||
// line is blank
|
||||
continue;
|
||||
}
|
||||
|
@ -125,6 +132,8 @@ xanax
|
|||
return $words;
|
||||
}
|
||||
|
||||
public function get_priority(): int {return 30;}
|
||||
public function get_priority(): int
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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/');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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('!^>>([^\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) {
|
||||
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
|
||||
|
@ -120,14 +136,20 @@ class BBCode extends FormatterExtension {
|
|||
|
||||
$l1 = strlen("[code]");
|
||||
$l2 = strlen("[/code]");
|
||||
while(true) {
|
||||
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) {
|
||||
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)));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
* 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) {
|
||||
if ($config->get_int("ext_blocks_version") < 1) {
|
||||
$database->create_table("blocks", "
|
||||
id SCORE_AIPK,
|
||||
pages VARCHAR(128) NOT NULL,
|
||||
|
@ -19,73 +21,72 @@ 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")) {
|
||||
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");
|
||||
if($blocks === false) {
|
||||
if ($blocks === false) {
|
||||
$blocks = $database->get_all("SELECT * FROM blocks");
|
||||
$database->cache->set("blocks", $blocks, 600);
|
||||
}
|
||||
foreach($blocks as $block) {
|
||||
foreach ($blocks as $block) {
|
||||
$path = implode("/", $event->args);
|
||||
if(strlen($path) < 4000 && fnmatch($block['pages'], $path)) {
|
||||
if (strlen($path) < 4000 && fnmatch($block['pages'], $path)) {
|
||||
$b = new Block($block['title'], $block['content'], $block['area'], $block['priority']);
|
||||
$b->is_content = false;
|
||||
$page->add_block($b);
|
||||
}
|
||||
}
|
||||
|
||||
if($event->page_matches("blocks") && $user->can("manage_blocks")) {
|
||||
if($event->get_arg(0) == "add") {
|
||||
if($user->check_auth_token()) {
|
||||
if ($event->page_matches("blocks") && $user->can("manage_blocks")) {
|
||||
if ($event->get_arg(0) == "add") {
|
||||
if ($user->check_auth_token()) {
|
||||
$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");
|
||||
$page->set_redirect(make_link("blocks/list"));
|
||||
}
|
||||
}
|
||||
if($event->get_arg(0) == "update") {
|
||||
if($user->check_auth_token()) {
|
||||
if(!empty($_POST['delete'])) {
|
||||
if ($event->get_arg(0) == "update") {
|
||||
if ($user->check_auth_token()) {
|
||||
if (!empty($_POST['delete'])) {
|
||||
$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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<?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%;'>";
|
||||
foreach($blocks as $block) {
|
||||
foreach ($blocks as $block) {
|
||||
$html .= make_form(make_link("blocks/update"));
|
||||
$html .= "<input type='hidden' name='id' value='".html_escape($block['id'])."'>";
|
||||
$html .= "<tr>";
|
||||
|
@ -43,4 +45,3 @@ class BlocksTheme extends Themelet {
|
|||
$page->add_block(new Block("Block Editor", $html));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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...
|
||||
*/
|
||||
|
@ -20,7 +22,7 @@ class Blotter extends Extension {
|
|||
*
|
||||
* REMINDER: If I change the database tables, I must change up version by 1.
|
||||
*/
|
||||
if($version < 1) {
|
||||
if ($version < 1) {
|
||||
/**
|
||||
* Installer
|
||||
*/
|
||||
|
@ -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,30 +47,33 @@ 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()) {
|
||||
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)) {
|
||||
if ($event->page_matches("blotter")) {
|
||||
switch ($event->get_arg(0)) {
|
||||
case "editor":
|
||||
/**
|
||||
* Displays the blotter editor.
|
||||
*/
|
||||
if(!$user->is_admin()) {
|
||||
if (!$user->is_admin()) {
|
||||
$this->theme->display_permission_denied();
|
||||
} else {
|
||||
$entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC");
|
||||
|
@ -77,15 +84,23 @@ class Blotter extends Extension {
|
|||
/**
|
||||
* Adds an entry
|
||||
*/
|
||||
if(!$user->is_admin() || !$user->check_auth_token()) {
|
||||
if (!$user->is_admin() || !$user->check_auth_token()) {
|
||||
$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"));
|
||||
|
@ -95,12 +110,14 @@ class Blotter extends Extension {
|
|||
/**
|
||||
* Removes an entry
|
||||
*/
|
||||
if(!$user->is_admin() || !$user->check_auth_token()) {
|
||||
if (!$user->is_admin() || !$user->check_auth_token()) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
@ -110,7 +120,7 @@ class BlotterTheme extends Themelet {
|
|||
$messy_date = $entries[$i]['entry_date'];
|
||||
$clean_date = date("y/m/d", strtotime($messy_date));
|
||||
$entry_text = $entries[$i]['entry_text'];
|
||||
if($entries[$i]['important'] == 'Y') {
|
||||
if ($entries[$i]['important'] == 'Y') {
|
||||
$i_open = "<font color='#{$i_color}'>";
|
||||
$i_close="</font>";
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -137,7 +148,7 @@ class BlotterTheme extends Themelet {
|
|||
$messy_date = $entries[$i]['entry_date'];
|
||||
$clean_date = date("m/d/y", strtotime($messy_date));
|
||||
$entry_text = $entries[$i]['entry_text'];
|
||||
if($entries[$i]['important'] == 'Y') {
|
||||
if ($entries[$i]['important'] == 'Y') {
|
||||
$i_open = "<font color='#{$i_color}'>";
|
||||
$i_close="</font>";
|
||||
}
|
||||
|
@ -147,16 +158,15 @@ class BlotterTheme extends Themelet {
|
|||
$pos_break = "";
|
||||
$pos_align = "text-align: right; position: absolute; right: 0px;";
|
||||
|
||||
if($position === "left") {
|
||||
if ($position === "left") {
|
||||
$pos_break = "<br />";
|
||||
$pos_align = "";
|
||||
}
|
||||
|
||||
if(count($entries) === 0) {
|
||||
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>";
|
||||
|
|
|
@ -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
|
||||
|
@ -29,7 +32,7 @@ class BrowserSearch extends Extension {
|
|||
$page->add_html_header("<link rel='search' type='application/opensearchdescription+xml' title='$search_title' href='$search_file_url'>");
|
||||
|
||||
// The search.xml file that is generated on the fly
|
||||
if($event->page_matches("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml")) {
|
||||
if ($event->page_matches("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml")) {
|
||||
// First, we need to build all the variables we'll need
|
||||
$search_title = $config->get_string('title');
|
||||
$search_form_url = make_link('post/list/{searchTerms}');
|
||||
|
@ -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")
|
||||
) {
|
||||
|
@ -64,10 +65,10 @@ class BrowserSearch extends Extension {
|
|||
$tag_search = $event->get_arg(0);
|
||||
|
||||
// Now to get DB results
|
||||
if($config->get_string("search_suggestions_results_order") == "a") {
|
||||
$tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30",array($tag_search."%"));
|
||||
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", [$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,9 +76,9 @@ class BrowserSearch extends Extension {
|
|||
// ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]]
|
||||
$json_tag_list = "";
|
||||
|
||||
$tags_array = array();
|
||||
foreach($tags as $tag) {
|
||||
array_push($tags_array,$tag['tag']);
|
||||
$tags_array = [];
|
||||
foreach ($tags as $tag) {
|
||||
array_push($tags_array, $tag['tag']);
|
||||
}
|
||||
|
||||
$json_tag_list .= implode("\",\"", $tags_array);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,28 +15,33 @@
|
|||
* <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'])) {
|
||||
if ($event->page_matches("bulk_add")) {
|
||||
if ($user->is_admin() && $user->check_auth_token() && isset($_POST['dir'])) {
|
||||
set_time_limit(0);
|
||||
$bae = new BulkAddEvent($_POST['dir']);
|
||||
send_event($bae);
|
||||
if(is_array($bae->results)) {
|
||||
foreach($bae->results as $result) {
|
||||
if (is_array($bae->results)) {
|
||||
foreach ($bae->results as $result) {
|
||||
$this->theme->add_status("Adding files", $result);
|
||||
}
|
||||
} else if(strlen($bae->results) > 0) {
|
||||
} elseif (strlen($bae->results) > 0) {
|
||||
$this->theme->add_status("Adding files", $bae->results);
|
||||
}
|
||||
$this->theme->display_upload_results($page);
|
||||
|
@ -44,13 +49,14 @@ class BulkAdd extends Extension {
|
|||
}
|
||||
}
|
||||
|
||||
public function onCommand(CommandEvent $event) {
|
||||
if($event->cmd == "help") {
|
||||
public function onCommand(CommandEvent $event)
|
||||
{
|
||||
if ($event->cmd == "help") {
|
||||
print "\tbulk-add [directory]\n";
|
||||
print "\t\tImport this directory\n\n";
|
||||
}
|
||||
if($event->cmd == "bulk-add") {
|
||||
if(count($event->args) == 1) {
|
||||
if ($event->cmd == "bulk-add") {
|
||||
if (count($event->args) == 1) {
|
||||
$bae = new BulkAddEvent($event->args[0]);
|
||||
send_event($bae);
|
||||
print(implode("\n", $bae->results));
|
||||
|
@ -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) {
|
||||
if(is_dir($event->dir) && is_readable($event->dir)) {
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
<?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());
|
||||
$html = "";
|
||||
foreach($this->messages as $block) {
|
||||
foreach ($this->messages as $block) {
|
||||
$html .= "<br/>" . $block->body;
|
||||
}
|
||||
$page->add_block(new Block("Results", $html));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
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'])) {
|
||||
if ($event->page_matches("bulk_add_csv")) {
|
||||
if ($user->is_admin() && $user->check_auth_token() && isset($_POST['csv'])) {
|
||||
set_time_limit(0);
|
||||
$this->add_csv($_POST['csv']);
|
||||
$this->theme->display_upload_results($page);
|
||||
|
@ -30,49 +32,52 @@ class BulkAddCSV extends Extension {
|
|||
}
|
||||
}
|
||||
|
||||
public function onCommand(CommandEvent $event) {
|
||||
if($event->cmd == "help") {
|
||||
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";
|
||||
}
|
||||
if($event->cmd == "bulk-add-csv") {
|
||||
if ($event->cmd == "bulk-add-csv") {
|
||||
global $user;
|
||||
|
||||
//Nag until CLI is admin by default
|
||||
if (!$user->is_admin()) {
|
||||
print "Not running as an admin, which can cause problems.\n";
|
||||
print "Please add the parameter: -u admin_username";
|
||||
} elseif(count($event->args) == 1) {
|
||||
} elseif (count($event->args) == 1) {
|
||||
$this->add_csv($event->args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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);
|
||||
$metadata['source'] = $source;
|
||||
$event = new DataUploadEvent($tmpname, $metadata);
|
||||
send_event($event);
|
||||
if($event->image_id == -1) {
|
||||
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,8 +87,9 @@ class BulkAddCSV extends Extension {
|
|||
}
|
||||
}
|
||||
|
||||
private function add_csv(string $csvfile) {
|
||||
if(!file_exists($csvfile)) {
|
||||
private function add_csv(string $csvfile)
|
||||
{
|
||||
if (!file_exists($csvfile)) {
|
||||
$this->theme->add_status("Error", "$csvfile not found");
|
||||
return;
|
||||
}
|
||||
|
@ -96,9 +102,9 @@ class BulkAddCSV extends Extension {
|
|||
$list = "";
|
||||
$csvhandle = fopen($csvfile, "r");
|
||||
|
||||
while (($csvdata = fgetcsv($csvhandle, 0, ",")) !== FALSE) {
|
||||
if(count($csvdata) != 5) {
|
||||
if(strlen($list) > 0) {
|
||||
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);
|
||||
fclose($csvhandle);
|
||||
return;
|
||||
|
@ -117,11 +123,10 @@ class BulkAddCSV extends Extension {
|
|||
$shortpath = $pathinfo["basename"];
|
||||
$list .= "<br>".html_escape("$shortpath (".str_replace(" ", ", ", $tags).")... ");
|
||||
if (file_exists($csvdata[0]) && is_file($csvdata[0])) {
|
||||
try{
|
||||
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 {
|
||||
|
@ -130,10 +135,9 @@ class BulkAddCSV extends Extension {
|
|||
$linenum += 1;
|
||||
}
|
||||
|
||||
if(strlen($list) > 0) {
|
||||
if (strlen($list) > 0) {
|
||||
$this->theme->add_status("Adding $csvfile", $list);
|
||||
}
|
||||
fclose($csvhandle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<?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());
|
||||
foreach($this->messages as $block) {
|
||||
foreach ($this->messages as $block) {
|
||||
$page->add_block($block);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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->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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ include '../php/functions.php';
|
|||
include '../php/yshout.class.php';
|
||||
include '../php/ajaxcall.class.php';
|
||||
|
||||
if (isset($_POST['mode']))
|
||||
switch($_POST['mode']) {
|
||||
if (isset($_POST['mode'])) {
|
||||
switch ($_POST['mode']) {
|
||||
case 'login':
|
||||
doLogin();
|
||||
break;
|
||||
|
@ -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();
|
||||
|
@ -245,7 +272,7 @@ function bansList() {
|
|||
$html = '<ul id="bans-list">';
|
||||
|
||||
$hasBans = false;
|
||||
foreach($bans as $ban) {
|
||||
foreach ($bans as $ban) {
|
||||
$hasBans = true;
|
||||
$html .= '
|
||||
<li>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,9 @@
|
|||
</form>
|
||||
</div>
|
||||
<?php
|
||||
if (loggedIn()) echo cp();
|
||||
if (loggedIn()) {
|
||||
echo cp();
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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,10 +35,11 @@
|
|||
<span class="ys-post-message">Hey, there aren\'t any posts in this log.</span>
|
||||
</div>
|
||||
';
|
||||
}
|
||||
|
||||
$id = 0;
|
||||
|
||||
foreach($posts as $post) {
|
||||
foreach ($posts as $post) {
|
||||
$id++;
|
||||
|
||||
$banned = $ys->banned($post['adminInfo']['ip']);
|
||||
|
@ -49,7 +47,7 @@
|
|||
|
||||
$ts = '';
|
||||
|
||||
switch($prefs['timestamp']) {
|
||||
switch ($prefs['timestamp']) {
|
||||
case 12:
|
||||
$ts = date('h:i', $post['timestamp']);
|
||||
break;
|
||||
|
@ -115,15 +113,16 @@ if (isset($_POST['p'])) {
|
|||
<div id="top">
|
||||
<h1>YShout.History</h1>
|
||||
<div id="controls">
|
||||
<?php if($admin) : ?>
|
||||
<?php if ($admin) : ?>
|
||||
<a id="clear-log" href="#">Clear this log</a>, or
|
||||
<a id="clear-logs" href="#">Clear all logs</a>.
|
||||
<?php endif; ?>
|
||||
|
||||
<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>
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
include 'php/functions.php';
|
||||
include 'php/yshout.class.php';
|
||||
include 'php/ajaxcall.class.php';
|
||||
|
||||
|
|
|
@ -8,13 +8,15 @@
|
|||
* 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
|
||||
$root = get_base_href();
|
||||
$yPath = make_http( $root . "/ext/chatbox/");
|
||||
$yPath = make_http($root . "/ext/chatbox/");
|
||||
$page->add_html_header("
|
||||
<script src=\"http://code.jquery.com/jquery-migrate-1.2.1.js\" type=\"text/javascript\"></script>
|
||||
<script src=\"$root/ext/chatbox/js/yshout.js\" type=\"text/javascript\"></script>
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
<?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() {
|
||||
switch($this->reqType) {
|
||||
public function process()
|
||||
{
|
||||
switch ($this->reqType) {
|
||||
case 'init':
|
||||
|
||||
$this->initSession();
|
||||
|
@ -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,13 +85,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
function doBan() {
|
||||
public function doBan()
|
||||
{
|
||||
$ip = $_POST['ip'];
|
||||
$nickname = $_POST['nickname'];
|
||||
$send = array();
|
||||
$send = [];
|
||||
$ys = ys($_SESSION['yLog']);
|
||||
|
||||
switch(true) {
|
||||
switch (true) {
|
||||
case !loggedIn():
|
||||
$send['error'] = 'admin';
|
||||
break;
|
||||
|
@ -90,20 +101,22 @@
|
|||
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) {
|
||||
switch (true) {
|
||||
case !loggedIn():
|
||||
$send['error'] = 'admin';
|
||||
break;
|
||||
|
@ -118,12 +131,13 @@
|
|||
echo json_encode($send);
|
||||
}
|
||||
|
||||
function doDelete() {
|
||||
public function doDelete()
|
||||
{
|
||||
$uid = $_POST['uid'];
|
||||
$send = array();
|
||||
$send = [];
|
||||
$ys = ys($_SESSION['yLog']);
|
||||
|
||||
switch(true) {
|
||||
switch (true) {
|
||||
case !loggedIn():
|
||||
$send['error'] = 'admin';
|
||||
break;
|
||||
|
@ -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);
|
||||
$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,29 +248,34 @@
|
|||
$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) {
|
||||
switch (true) {
|
||||
case !loggedIn():
|
||||
$send['error'] = 'admin';
|
||||
break;
|
||||
|
@ -256,15 +287,16 @@
|
|||
echo json_encode($send);
|
||||
}
|
||||
|
||||
function clearLogs() {
|
||||
public function clearLogs()
|
||||
{
|
||||
global $prefs;
|
||||
|
||||
//$log = $_POST['log'];
|
||||
$send = array();
|
||||
$send = [];
|
||||
|
||||
//$ys = ys($_SESSION['yLog']);
|
||||
|
||||
switch(true) {
|
||||
switch (true) {
|
||||
case !loggedIn():
|
||||
$send['error'] = 'admin';
|
||||
break;
|
||||
|
@ -280,5 +312,3 @@
|
|||
echo json_encode($send);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
setcookie ($name, false, time() - 42);
|
||||
}
|
||||
|
||||
function getVar($name) {
|
||||
if (isset($_POST[$name])) { return $_POST[$name]; }
|
||||
if (isset($_GET[$name])) { return $_GET[$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];
|
||||
}
|
||||
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');
|
||||
// cookieClear('yLoginHash');
|
||||
}
|
||||
|
||||
function loggedIn() {
|
||||
function loggedIn()
|
||||
{
|
||||
global $prefs;
|
||||
|
||||
$loginHash = cookieGet('yLoginHash', false);
|
||||
// echo 'loggedin: ' . $loginHash . "\n";
|
||||
// echo 'pw: ' . $prefs['password'] . "\n";
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
@ -43,16 +49,18 @@ class YShout {
|
|||
$posts[] = $post;
|
||||
} */
|
||||
|
||||
foreach($allPosts as $post) {
|
||||
if ($post['timestamp'] > $ts)
|
||||
foreach ($allPosts as $post) {
|
||||
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,54 +161,59 @@ 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');
|
||||
$bans = $s->open(true);
|
||||
$s->close($null);
|
||||
|
||||
foreach($bans as $ban) {
|
||||
if ($ban['ip'] == $ip)
|
||||
foreach ($bans as $ban) {
|
||||
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;
|
||||
|
||||
|
||||
|
@ -201,13 +231,13 @@ class YShout {
|
|||
|
||||
$posts = $s['posts'];
|
||||
|
||||
foreach($posts as $key=>$value) {
|
||||
if (!isset($value['uid']))
|
||||
foreach ($posts as $key=>$value) {
|
||||
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']);
|
||||
foreach ($post as $key=>$value) {
|
||||
if ($this->banned($value['adminInfo']['ip'])) {
|
||||
$post[$key]['banned'] = true;
|
||||
}
|
||||
if (!$this->admin) {
|
||||
unset($post[$key]['adminInfo']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ ob_start();
|
|||
set_error_handler('errorOccurred');
|
||||
include 'include.php';
|
||||
|
||||
if (isset($_POST['reqFor']))
|
||||
switch($_POST['reqFor']) {
|
||||
if (isset($_POST['reqFor'])) {
|
||||
switch ($_POST['reqFor']) {
|
||||
case 'shout':
|
||||
|
||||
$ajax = new AjaxCall();
|
||||
|
@ -22,17 +22,18 @@ if (isset($_POST['reqFor']))
|
|||
|
||||
default:
|
||||
exit;
|
||||
} else {
|
||||
include 'example.html';
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -87,9 +108,9 @@ class CommentList extends Extension {
|
|||
$config->set_default_int('comment_count', 5);
|
||||
$config->set_default_bool('comment_captcha', false);
|
||||
|
||||
if($config->get_int("ext_comments_version") < 3) {
|
||||
if ($config->get_int("ext_comments_version") < 3) {
|
||||
// shortcut to latest
|
||||
if($config->get_int("ext_comments_version") < 1) {
|
||||
if ($config->get_int("ext_comments_version") < 1) {
|
||||
$database->create_table("comments", "
|
||||
id SCORE_AIPK,
|
||||
image_id INTEGER NOT NULL,
|
||||
|
@ -100,14 +121,14 @@ 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);
|
||||
}
|
||||
|
||||
// the whole history
|
||||
if($config->get_int("ext_comments_version") < 1) {
|
||||
if ($config->get_int("ext_comments_version") < 1) {
|
||||
$database->create_table("comments", "
|
||||
id SCORE_AIPK,
|
||||
image_id INTEGER NOT NULL,
|
||||
|
@ -116,17 +137,17 @@ 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);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_comments_version") == 1) {
|
||||
if ($config->get_int("ext_comments_version") == 1) {
|
||||
$database->Execute("CREATE INDEX comments_owner_ip ON comments(owner_ip)");
|
||||
$database->Execute("CREATE INDEX comments_posted ON comments(posted)");
|
||||
$config->set_int("ext_comments_version", 2);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_comments_version") == 2) {
|
||||
if ($config->get_int("ext_comments_version") == 2) {
|
||||
$config->set_int("ext_comments_version", 3);
|
||||
$database->Execute("ALTER TABLE comments ADD FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE");
|
||||
$database->Execute("ALTER TABLE comments ADD FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT");
|
||||
|
@ -136,9 +157,10 @@ class CommentList extends Extension {
|
|||
}
|
||||
}
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event) {
|
||||
if($event->page_matches("comment")) {
|
||||
switch($event->get_arg(0)) {
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
if ($event->page_matches("comment")) {
|
||||
switch ($event->get_arg(0)) {
|
||||
case "add": $this->onPageRequest_add(); break;
|
||||
case "delete": $this->onPageRequest_delete($event); break;
|
||||
case "bulk_delete": $this->onPageRequest_bulk_delete(); break;
|
||||
|
@ -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,10 +216,10 @@ 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) {
|
||||
foreach ($comment_ids as $cid) {
|
||||
send_event(new CommentDeletionEvent($cid));
|
||||
}
|
||||
flash_message("Deleted $num comments");
|
||||
|
@ -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,26 +250,29 @@ 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) {
|
||||
if ($cc > 0) {
|
||||
$recent = $database->cache->get("recent_comments");
|
||||
if(empty($recent)) {
|
||||
if (empty($recent)) {
|
||||
$recent = $this->get_recent_comments($cc);
|
||||
$database->cache->set("recent_comments", $recent, 60);
|
||||
}
|
||||
if(count($recent) > 0) {
|
||||
if (count($recent) > 0) {
|
||||
$this->theme->display_recent_comments($recent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,38 +328,38 @@ 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)) {
|
||||
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)) {
|
||||
if (!is_null($user)) {
|
||||
$user_id = $user->id;
|
||||
} else {
|
||||
$user_id = -1;
|
||||
}
|
||||
|
||||
$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) {
|
||||
// page building {{{
|
||||
private function build_page(int $current_page)
|
||||
{
|
||||
global $database, $user;
|
||||
|
||||
$where = SPEED_HAX ? "WHERE posted > now() - interval '24 hours'" : "";
|
||||
|
||||
$total_pages = $database->cache->get("comment_pages");
|
||||
if(empty($total_pages)) {
|
||||
if (empty($total_pages)) {
|
||||
$total_pages = (int)($database->get_one("
|
||||
SELECT COUNT(c1)
|
||||
FROM (SELECT COUNT(image_id) AS c1 FROM comments $where GROUP BY image_id) AS s1
|
||||
|
@ -346,38 +380,39 @@ 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();
|
||||
while($row = $result->fetch()) {
|
||||
$images = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$image = Image::by_id($row["image_id"]);
|
||||
if(
|
||||
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)) {
|
||||
if (!is_null($image)) {
|
||||
$comments = $this->get_comments($image->id);
|
||||
$images[] = array($image, $comments);
|
||||
$images[] = [$image, $comments];
|
||||
}
|
||||
}
|
||||
|
||||
$this->theme->display_comment_list($images, $current_page, $total_pages, $user->can("create_comment"));
|
||||
}
|
||||
// }}}
|
||||
// }}}
|
||||
|
||||
// get comments {{{
|
||||
// get comments {{{
|
||||
/**
|
||||
* #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();
|
||||
foreach($rows as $row) {
|
||||
$comments = [];
|
||||
foreach ($rows as $row) {
|
||||
$comments[] = new Comment($row);
|
||||
}
|
||||
return $comments;
|
||||
|
@ -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 {
|
||||
// add / remove / edit comments {{{
|
||||
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,28 +515,30 @@ 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(
|
||||
if (strlen($config->get_string('comment_wordpress_key')) > 0) {
|
||||
$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
|
||||
if(!isset($_SERVER['HTTP_REFERER'])) {
|
||||
if (!isset($_SERVER['HTTP_REFERER'])) {
|
||||
$comment['referrer'] = 'none';
|
||||
log_warning("comment", "User '{$user->name}' commented with no referrer: $text");
|
||||
}
|
||||
if(!isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
if (!isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
$comment['user_agent'] = 'none';
|
||||
log_warning("comment", "User '{$user->name}' commented with no user-agent: $text");
|
||||
}
|
||||
|
@ -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()) {
|
||||
if ($akismet->errorsExist()) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return $akismet->isSpam();
|
||||
}
|
||||
}
|
||||
|
@ -512,83 +559,81 @@ 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
|
||||
// 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")) {
|
||||
if (!$user->can("bypass_comment_checks")) {
|
||||
// will raise an exception if anything is wrong
|
||||
$this->comment_checks($image_id, $user, $comment);
|
||||
}
|
||||
|
||||
// all checks passed
|
||||
if($user->is_anonymous()) {
|
||||
if ($user->is_anonymous()) {
|
||||
$page->add_cookie("nocache", "Anonymous Commenter", time()+60*60*24, "/");
|
||||
}
|
||||
$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")) {
|
||||
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
|
||||
else if(strlen($comment)/strlen(gzcompress($comment)) > 10) {
|
||||
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
|
||||
else if($this->is_comment_limit_hit()) {
|
||||
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
|
||||
else if($config->get_bool('comment_captcha') && !captcha_check()) {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// }}}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -62,10 +62,10 @@ class CommentListTest extends ShimmiePHPUnitTestCase {
|
|||
$this->assert_text("むちむち");
|
||||
|
||||
# test that search by comment metadata works
|
||||
// $this->get_page("post/list/commented_by=test/1");
|
||||
// $this->assert_title("Image $image_id: pbx");
|
||||
// $this->get_page("post/list/comments=2/1");
|
||||
// $this->assert_title("Image $image_id: pbx");
|
||||
// $this->get_page("post/list/commented_by=test/1");
|
||||
// $this->assert_title("Image $image_id: pbx");
|
||||
// $this->get_page("post/list/comments=2/1");
|
||||
// $this->assert_title("Image $image_id: pbx");
|
||||
|
||||
$this->log_out();
|
||||
|
||||
|
@ -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();
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
<?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) {
|
||||
if(is_null($this->ct)) {
|
||||
private function get_anon_colour($ip)
|
||||
{
|
||||
if (is_null($this->ct)) {
|
||||
$this->ct = hsl_rainbow();
|
||||
}
|
||||
if(!array_key_exists($ip, $this->anon_map)) {
|
||||
if (!array_key_exists($ip, $this->anon_map)) {
|
||||
$this->anon_map[$ip] = $this->ct[$this->anon_cid++ % count($this->ct)];
|
||||
}
|
||||
return $this->anon_map[$ip];
|
||||
|
@ -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
|
||||
|
@ -52,7 +55,7 @@ class CommentListTheme extends Themelet {
|
|||
$comment_limit = $config->get_int("comment_list_count", 10);
|
||||
$comment_captcha = $config->get_bool('comment_captcha');
|
||||
|
||||
foreach($images as $pair) {
|
||||
foreach ($images as $pair) {
|
||||
$image = $pair[0];
|
||||
$comments = $pair[1];
|
||||
|
||||
|
@ -60,28 +63,26 @@ class CommentListTheme extends Themelet {
|
|||
$comment_html = "";
|
||||
|
||||
$comment_count = count($comments);
|
||||
if($comment_limit > 0 && $comment_count > $comment_limit) {
|
||||
if ($comment_limit > 0 && $comment_count > $comment_limit) {
|
||||
$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;
|
||||
foreach($comments as $comment) {
|
||||
foreach ($comments as $comment) {
|
||||
$comment_html .= $this->comment_to_html($comment);
|
||||
}
|
||||
if(!$user->is_anonymous()) {
|
||||
if($can_post) {
|
||||
if (!$user->is_anonymous()) {
|
||||
if ($can_post) {
|
||||
$comment_html .= $this->build_postbox($image->id);
|
||||
}
|
||||
} else {
|
||||
if ($can_post) {
|
||||
if(!$comment_captcha) {
|
||||
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>";
|
||||
}
|
||||
|
@ -95,12 +96,13 @@ class CommentListTheme extends Themelet {
|
|||
</tr></table>
|
||||
';
|
||||
|
||||
$page->add_block(new Block( $image->id.': '.$image->get_tag_list(), $html, "main", $position++, "comment-list-list"));
|
||||
$page->add_block(new Block($image->id.': '.$image->get_tag_list(), $html, "main", $position++, "comment-list-list"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function display_admin_block() {
|
||||
public function display_admin_block()
|
||||
{
|
||||
global $page;
|
||||
|
||||
$html = '
|
||||
|
@ -122,11 +124,12 @@ 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 = "";
|
||||
foreach($comments as $comment) {
|
||||
foreach ($comments as $comment) {
|
||||
$html .= $this->comment_to_html($comment, true);
|
||||
}
|
||||
$html .= "<a class='more' href='".make_link("comment/list")."'>Full List</a>";
|
||||
|
@ -139,14 +142,15 @@ 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 = "";
|
||||
foreach($comments as $comment) {
|
||||
foreach ($comments as $comment) {
|
||||
$html .= $this->comment_to_html($comment);
|
||||
}
|
||||
if($postbox) {
|
||||
if ($postbox) {
|
||||
$html .= $this->build_postbox($image->id);
|
||||
}
|
||||
$page->add_block(new Block("Comments", $html, "main", 30, "comment-list-image"));
|
||||
|
@ -158,32 +162,33 @@ 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) {
|
||||
foreach ($comments as $comment) {
|
||||
$html .= $this->comment_to_html($comment, true);
|
||||
}
|
||||
if(empty($html)) {
|
||||
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));
|
||||
assert(is_numeric($total_pages));
|
||||
|
||||
$html = "";
|
||||
foreach($comments as $comment) {
|
||||
foreach ($comments as $comment) {
|
||||
$html .= $this->comment_to_html($comment, true);
|
||||
}
|
||||
if(empty($html)) {
|
||||
if (empty($html)) {
|
||||
$html = '<p>No comments by this user.</p>';
|
||||
}
|
||||
$page->add_block(new Block("Comments", $html, "main", 70, "comment-list-user"));
|
||||
|
@ -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);
|
||||
|
@ -218,41 +224,39 @@ class CommentListTheme extends Themelet {
|
|||
$i_comment_id = int_escape($comment->comment_id);
|
||||
$i_image_id = int_escape($comment->image_id);
|
||||
|
||||
if($i_uid == $config->get_int("anon_id")) {
|
||||
if ($i_uid == $config->get_int("anon_id")) {
|
||||
$anoncode = "";
|
||||
$anoncode2 = "";
|
||||
if($this->show_anon_id) {
|
||||
if ($this->show_anon_id) {
|
||||
$anoncode = '<sup>'.$this->anon_id.'</sup>';
|
||||
if(!array_key_exists($comment->poster_ip, $this->anon_map)) {
|
||||
if (!array_key_exists($comment->poster_ip, $this->anon_map)) {
|
||||
$this->anon_map[$comment->poster_ip] = $this->anon_id;
|
||||
}
|
||||
#if($user->can("view_ip")) {
|
||||
#$style = " style='color: ".$this->get_anon_colour($comment->poster_ip).";'";
|
||||
if($user->can("view_ip") || $config->get_bool("comment_samefags_public", false)) {
|
||||
if($this->anon_map[$comment->poster_ip] != $this->anon_id) {
|
||||
if ($user->can("view_ip") || $config->get_bool("comment_samefags_public", false)) {
|
||||
if ($this->anon_map[$comment->poster_ip] != $this->anon_id) {
|
||||
$anoncode2 = '<sup>('.$this->anon_map[$comment->poster_ip].')</sup>';
|
||||
}
|
||||
}
|
||||
}
|
||||
$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>';
|
||||
}
|
||||
|
||||
$hb = ($comment->owner_class == "hellbanned" ? "hb" : "");
|
||||
if($trim) {
|
||||
if ($trim) {
|
||||
$html = "
|
||||
<div class=\"comment $hb\">
|
||||
$h_userlink: $h_comment
|
||||
<a href=\"".make_link("post/view/$i_image_id#c$i_comment_id")."\">>>></a>
|
||||
</div>
|
||||
";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$h_avatar = "";
|
||||
if(!empty($comment->owner_email)) {
|
||||
if (!empty($comment->owner_email)) {
|
||||
$hash = md5(strtolower($comment->owner_email));
|
||||
$cb = date("Y-m-d");
|
||||
$h_avatar = "<img src=\"//www.gravatar.com/avatar/$hash.jpg?cacheBreak=$cb\"><br>";
|
||||
|
@ -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 {
|
|||
';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,26 +41,26 @@ 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" )) {
|
||||
$this->upload_key = $config->get_string ( "cron_uploader_key", "" );
|
||||
if ($event->page_matches("cron_upload")) {
|
||||
$this->upload_key = $config->get_string("cron_uploader_key", "");
|
||||
|
||||
// If the key is in the url, upload
|
||||
if ($this->upload_key != "" && $event->get_arg ( 0 ) == $this->upload_key) {
|
||||
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,47 +138,50 @@ 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", "")) {
|
||||
$this->upload_key = $this->generate_key ();
|
||||
$this->upload_key = $this->generate_key();
|
||||
|
||||
$config->set_default_int ( 'cron_uploader_count', 1 );
|
||||
$config->set_default_string ( 'cron_uploader_key', $this->upload_key );
|
||||
$config->set_default_int('cron_uploader_count', 1);
|
||||
$config->set_default_string('cron_uploader_key', $this->upload_key);
|
||||
$this->set_dir();
|
||||
}
|
||||
}
|
||||
|
||||
public function onSetupBuilding(SetupBuildingEvent $event) {
|
||||
public function onSetupBuilding(SetupBuildingEvent $event)
|
||||
{
|
||||
$this->set_dir();
|
||||
|
||||
$cron_url = make_http(make_link("/cron_upload/" . $this->upload_key));
|
||||
$cron_cmd = "curl --silent $cron_url";
|
||||
$documentation_link = make_http(make_link("cron_upload"));
|
||||
|
||||
$sb = new SetupBlock ( "Cron Uploader" );
|
||||
$sb->add_label ( "<b>Settings</b><br>" );
|
||||
$sb->add_int_option ( "cron_uploader_count", "How many to upload each time" );
|
||||
$sb->add_text_option ( "cron_uploader_dir", "<br>Set Cron Uploader root directory<br>");
|
||||
$sb = new SetupBlock("Cron Uploader");
|
||||
$sb->add_label("<b>Settings</b><br>");
|
||||
$sb->add_int_option("cron_uploader_count", "How many to upload each time");
|
||||
$sb->add_text_option("cron_uploader_dir", "<br>Set Cron Uploader root directory<br>");
|
||||
|
||||
$sb->add_label ("<br>Cron Command: <input type='text' size='60' value='$cron_cmd'><br>
|
||||
$sb->add_label("<br>Cron Command: <input type='text' size='60' value='$cron_cmd'><br>
|
||||
Create a cron job with the command above.<br/>
|
||||
<a href='$documentation_link'>Read the documentation</a> if you're not sure what to do.");
|
||||
|
||||
$event->panel->add_block ( $sb );
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates a unique key for the website to prevent unauthorized access.
|
||||
*/
|
||||
private function generate_key() {
|
||||
private function generate_key()
|
||||
{
|
||||
$length = 20;
|
||||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$randomString = '';
|
||||
|
||||
for($i = 0; $i < $length; $i ++) {
|
||||
$randomString .= $characters [rand ( 0, strlen ( $characters ) - 1 )];
|
||||
for ($i = 0; $i < $length; $i ++) {
|
||||
$randomString .= $characters [rand(0, strlen($characters) - 1)];
|
||||
}
|
||||
|
||||
return $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)
|
||||
|
||||
|
@ -195,16 +200,19 @@ class CronUploader extends Extension {
|
|||
// Sets new default dir if not in config yet/anymore
|
||||
if ($dir == "") {
|
||||
$dir = data_path("cron_uploader");
|
||||
$config->set_string ('cron_uploader_dir', $dir);
|
||||
$config->set_string('cron_uploader_dir', $dir);
|
||||
}
|
||||
|
||||
// Make the directory if it doesn't exist yet
|
||||
if (!is_dir($dir . "/queue/"))
|
||||
mkdir ( $dir . "/queue/", 0775, true );
|
||||
if (!is_dir($dir . "/uploaded/"))
|
||||
mkdir ( $dir . "/uploaded/", 0775, true );
|
||||
if (!is_dir($dir . "/failed_to_upload/"))
|
||||
mkdir ( $dir . "/failed_to_upload/", 0775, true );
|
||||
if (!is_dir($dir . "/queue/")) {
|
||||
mkdir($dir . "/queue/", 0775, true);
|
||||
}
|
||||
if (!is_dir($dir . "/uploaded/")) {
|
||||
mkdir($dir . "/uploaded/", 0775, true);
|
||||
}
|
||||
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) {
|
||||
assert ( file_exists ( $tmpname ) );
|
||||
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" );
|
||||
$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 );
|
||||
$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,43 +340,47 @@ 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();
|
||||
}
|
||||
|
||||
foreach ( glob ( "$base/$subdir/*" ) as $fullpath ) {
|
||||
$fullpath = str_replace ( "//", "/", $fullpath );
|
||||
if (! is_dir($base)) {
|
||||
$this->add_upload_info("Image Queue Directory could not be found at \"$base\".");
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach (glob("$base/$subdir/*") as $fullpath) {
|
||||
$fullpath = str_replace("//", "/", $fullpath);
|
||||
//$shortpath = str_replace ( $base, "", $fullpath );
|
||||
|
||||
if (is_link ( $fullpath )) {
|
||||
if (is_link($fullpath)) {
|
||||
// ignore
|
||||
} else if (is_dir ( $fullpath )) {
|
||||
$this->generate_image_queue ( $base, str_replace ( $base, "", $fullpath ) );
|
||||
} elseif (is_dir($fullpath)) {
|
||||
$this->generate_image_queue($base, str_replace($base, "", $fullpath));
|
||||
} else {
|
||||
$pathinfo = pathinfo ( $fullpath );
|
||||
$matches = array ();
|
||||
$pathinfo = pathinfo($fullpath);
|
||||
$matches = [];
|
||||
|
||||
if (preg_match ( "/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches )) {
|
||||
if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches)) {
|
||||
$tags = $matches [1];
|
||||
} else {
|
||||
$tags = $subdir;
|
||||
$tags = str_replace ( "/", " ", $tags );
|
||||
$tags = str_replace ( "__", " ", $tags );
|
||||
if ($tags == "") $tags = " ";
|
||||
$tags = trim ( $tags );
|
||||
$tags = str_replace("/", " ", $tags);
|
||||
$tags = str_replace("__", " ", $tags);
|
||||
if ($tags == "") {
|
||||
$tags = " ";
|
||||
}
|
||||
$tags = trim($tags);
|
||||
}
|
||||
|
||||
$img = array (
|
||||
$img = [
|
||||
0 => $fullpath,
|
||||
1 => $pathinfo ["basename"],
|
||||
2 => $tags
|
||||
);
|
||||
array_push ($this->image_queue, $img );
|
||||
];
|
||||
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);
|
||||
file_put_contents($log_path, $content);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <head></head> on all pages<br>");
|
||||
$sb->add_longtext_option(
|
||||
"custom_html_headers",
|
||||
"HTML Code to place within <head></head> 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);
|
||||
$header = $config->get_string('custom_html_headers', '');
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,30 +47,29 @@ Completely compatibility will probably involve a rewrite with a different URL
|
|||
|
||||
*/
|
||||
|
||||
class DanbooruApi extends Extension {
|
||||
public function onPageRequest(PageRequestEvent $event) {
|
||||
if($event->page_matches("api") && ($event->get_arg(0) == 'danbooru')) {
|
||||
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");
|
||||
|
||||
if(($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) {
|
||||
if (($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) {
|
||||
// No XML data is returned from this function
|
||||
$page->set_type("text/plain");
|
||||
$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());
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ class DanbooruApi extends Extension {
|
|||
// Shimmie view page
|
||||
// Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123
|
||||
// This redirects that to http://shimmie/post/view/123
|
||||
elseif(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) {
|
||||
elseif (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) {
|
||||
$fixedlocation = make_link("post/view/" . $event->get_arg(3));
|
||||
$page->set_mode("redirect");
|
||||
$page->set_redirect($fixedlocation);
|
||||
|
@ -91,19 +90,19 @@ 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'])) {
|
||||
if (isset($_REQUEST['login']) && isset($_REQUEST['password'])) {
|
||||
// Get this user from the db, if it fails the user becomes anonymous
|
||||
// Code borrowed from /ext/user
|
||||
$name = $_REQUEST['login'];
|
||||
$pass = $_REQUEST['password'];
|
||||
$duser = User::by_name_and_pass($name, $pass);
|
||||
if(!is_null($duser)) {
|
||||
if (!is_null($duser)) {
|
||||
$user = $duser;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$user = User::by_id($config->get_int("anon_id", 0));
|
||||
}
|
||||
}
|
||||
|
@ -118,58 +117,58 @@ 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();
|
||||
if(isset($_GET['id'])) {
|
||||
$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']];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags
|
||||
elseif(false && isset($_GET['tags'])) {
|
||||
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,38 +188,38 @@ class DanbooruApi extends Extension {
|
|||
*
|
||||
* #return string
|
||||
*/
|
||||
private function api_find_posts() {
|
||||
$results = array();
|
||||
private function api_find_posts()
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$this->authenticate_user();
|
||||
$start = 0;
|
||||
|
||||
if(isset($_GET['md5'])) {
|
||||
if (isset($_GET['md5'])) {
|
||||
$md5list = explode(",", $_GET['md5']);
|
||||
foreach ($md5list as $md5) {
|
||||
$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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -12,24 +12,30 @@
|
|||
* 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")) {
|
||||
if(!$user->can("ignore_downtime") && !$this->is_safe_page($event)) {
|
||||
if ($config->get_bool("downtime")) {
|
||||
if (!$user->can("ignore_downtime") && !$this->is_safe_page($event)) {
|
||||
$msg = $config->get_string("downtime_message");
|
||||
$this->theme->display_message($msg);
|
||||
if(!defined("UNITTEST")) { // hax D:
|
||||
if (!defined("UNITTEST")) { // hax D:
|
||||
header("HTTP/1.0 {$page->code} Downtime");
|
||||
print($page->data);
|
||||
exit;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
if($event->page_matches("emote/list")) {
|
||||
class EmoticonList extends Extension
|
||||
{
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
if ($event->page_matches("emote/list")) {
|
||||
$this->theme->display_emotes(glob("ext/emoticons/default/*"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
<?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>";
|
||||
$html .= "<table><tr>";
|
||||
$n = 1;
|
||||
foreach($list as $item) {
|
||||
foreach ($list as $item) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,19 +12,22 @@
|
|||
* 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")) {
|
||||
if ($event->page_matches("system_info")) {
|
||||
if ($user->can("view_sysinfo")) {
|
||||
$this->theme->display_info_page($this->get_info());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
|
||||
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
if($user->can("view_sysinfo")) {
|
||||
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();
|
||||
foreach(get_declared_classes() as $class) {
|
||||
$els = [];
|
||||
foreach (get_declared_classes() as $class) {
|
||||
$rclass = new ReflectionClass($class);
|
||||
if($rclass->isAbstract()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -30,120 +40,119 @@ class ExtensionInfo {
|
|||
$this->name = $this->ext_name;
|
||||
$this->enabled = $this->is_enabled($this->ext_name);
|
||||
|
||||
for($i=0; $i<$number_of_lines; $i++) {
|
||||
for ($i=0; $i<$number_of_lines; $i++) {
|
||||
$line = $lines[$i];
|
||||
if(preg_match("/Name: (.*)/", $line, $matches)) {
|
||||
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] == "/") {
|
||||
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);
|
||||
while(substr($lines[$i+1], 0, $start_len) == $start) {
|
||||
while (substr($lines[$i+1], 0, $start_len) == $start) {
|
||||
$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);
|
||||
while(substr($lines[$i+1], 0, $start_len) == $start) {
|
||||
while (substr($lines[$i+1], 0, $start_len) == $start) {
|
||||
$this->documentation .= " ".substr($lines[$i+1], $start_len);
|
||||
$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")) {
|
||||
if($event->get_arg(0) == "set" && $user->check_auth_token()) {
|
||||
if(is_writable("data/config")) {
|
||||
if ($event->page_matches("ext_manager")) {
|
||||
if ($user->can("manage_extension_list")) {
|
||||
if ($event->get_arg(0) == "set" && $user->check_auth_token()) {
|
||||
if (is_writable("data/config")) {
|
||||
$this->set_things($_POST);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if($event->page_matches("ext_doc")) {
|
||||
if ($event->page_matches("ext_doc")) {
|
||||
$ext = $event->get_arg(0);
|
||||
if(file_exists("ext/$ext/main.php")) {
|
||||
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) {
|
||||
if($event->cmd == "help") {
|
||||
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());
|
||||
if ($event->cmd == "disable-all-ext") {
|
||||
$this->write_config([]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
|
||||
public function onUserBlockBuilding(UserBlockBuildingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
if($user->can("manage_extension_list")) {
|
||||
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,31 +160,32 @@ class ExtManager extends Extension {
|
|||
/**
|
||||
* #return ExtensionInfo[]
|
||||
*/
|
||||
private function get_extensions(bool $all): array {
|
||||
$extensions = array();
|
||||
if($all) {
|
||||
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) {
|
||||
foreach ($exts as $main) {
|
||||
$extensions[] = new ExtensionInfo($main);
|
||||
}
|
||||
usort($extensions, "__extman_extcmp");
|
||||
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();
|
||||
foreach (glob("ext/*/main.php") as $main) {
|
||||
$matches = [];
|
||||
preg_match("#ext/(.*)/main.php#", $main, $matches);
|
||||
$fname = $matches[1];
|
||||
|
||||
if(!in_array($fname, $core) && isset($settings["ext_$fname"])) {
|
||||
if (!in_array($fname, $core) && isset($settings["ext_$fname"])) {
|
||||
$extras[] = $fname;
|
||||
}
|
||||
}
|
||||
|
@ -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".
|
||||
|
@ -197,7 +208,7 @@ class ExtManager extends Extension {
|
|||
// when the list of active extensions changes, we can be
|
||||
// pretty sure that the list of who reacts to what will
|
||||
// change too
|
||||
if(file_exists("data/cache/event_listeners.php")) {
|
||||
if (file_exists("data/cache/event_listeners.php")) {
|
||||
unlink("data/cache/event_listeners.php");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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"))."
|
||||
|
@ -19,13 +21,15 @@ class ExtManagerTheme extends Themelet {
|
|||
</thead>
|
||||
<tbody>
|
||||
";
|
||||
foreach($extensions as $extension) {
|
||||
if(!$editable && $extension->visibility == "admin") continue;
|
||||
foreach ($extensions as $extension) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,45 +33,50 @@ 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) {
|
||||
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()) {
|
||||
if (!$user->is_anonymous()) {
|
||||
$user_id = $user->id;
|
||||
$image_id = $event->image->id;
|
||||
|
||||
$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) {
|
||||
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()) {
|
||||
if ($event->page_matches("change_favorite") && !$user->is_anonymous() && $user->check_auth_token()) {
|
||||
$image_id = int_escape($_POST['image_id']);
|
||||
if((($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) && ($image_id > 0)) {
|
||||
if($_POST['favorite_action'] == "set") {
|
||||
if ((($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) && ($image_id > 0)) {
|
||||
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,17 +86,19 @@ 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(
|
||||
if (
|
||||
in_array('favorite_action', $_POST) &&
|
||||
(($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset"))
|
||||
) {
|
||||
|
@ -97,59 +106,62 @@ 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();
|
||||
if(preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) {
|
||||
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)) {
|
||||
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;
|
||||
|
||||
if($config->get_int("ext_favorites_version") < 1) {
|
||||
if ($config->get_int("ext_favorites_version") < 1) {
|
||||
$database->Execute("ALTER TABLE images ADD COLUMN favorites INTEGER NOT NULL DEFAULT 0");
|
||||
$database->Execute("CREATE INDEX images__favorites ON images(favorites)");
|
||||
$database->create_table("user_favorites", "
|
||||
|
@ -160,11 +172,11 @@ 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);
|
||||
}
|
||||
|
||||
if($config->get_int("ext_favorites_version") < 2) {
|
||||
if ($config->get_int("ext_favorites_version") < 2) {
|
||||
log_info("favorites", "Cleaning user favourites");
|
||||
$database->Execute("DELETE FROM user_favorites WHERE user_id NOT IN (SELECT id FROM users)");
|
||||
$database->Execute("DELETE FROM user_favorites WHERE image_id NOT IN (SELECT id FROM images)");
|
||||
|
@ -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]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -24,7 +27,7 @@ class FavoritesTheme extends Themelet {
|
|||
|
||||
reset($username_array); // rewind to first element in array.
|
||||
|
||||
foreach($username_array as $row) {
|
||||
foreach ($username_array as $row) {
|
||||
$username = html_escape($row);
|
||||
$html .= "<br><a href='".make_link("user/$username")."'>$username</a>";
|
||||
}
|
||||
|
@ -32,5 +35,3 @@ class FavoritesTheme extends Themelet {
|
|||
$page->add_block(new Block("Favorited By", $html, "left", 25));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,19 +19,22 @@
|
|||
* 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()) {
|
||||
if($user->can("edit_feature") && isset($_POST['image_id'])) {
|
||||
if ($event->page_matches("featured_image")) {
|
||||
if ($event->get_arg(0) == "set" && $user->check_auth_token()) {
|
||||
if ($user->can("edit_feature") && isset($_POST['image_id'])) {
|
||||
$id = int_escape($_POST['image_id']);
|
||||
if($id > 0) {
|
||||
if ($id > 0) {
|
||||
$config->set_int("featured_id", $id);
|
||||
log_info("featured", "Featured image set to $id", "Featured image set");
|
||||
$page->set_mode("redirect");
|
||||
|
@ -39,38 +42,39 @@ class Featured extends Extension {
|
|||
}
|
||||
}
|
||||
}
|
||||
if($event->get_arg(0) == "download") {
|
||||
if ($event->get_arg(0) == "download") {
|
||||
$image = Image::by_id($config->get_int("featured_id"));
|
||||
if(!is_null($image)) {
|
||||
if (!is_null($image)) {
|
||||
$page->set_mode("data");
|
||||
$page->set_type($image->get_mime_type());
|
||||
$page->set_data(file_get_contents($image->get_image_filename()));
|
||||
}
|
||||
}
|
||||
if($event->get_arg(0) == "view") {
|
||||
if ($event->get_arg(0) == "view") {
|
||||
$image = Image::by_id($config->get_int("featured_id"));
|
||||
if(!is_null($image)) {
|
||||
if (!is_null($image)) {
|
||||
send_event(new DisplayingImageEvent($image));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onPostListBuilding(PostListBuildingEvent $event) {
|
||||
public function onPostListBuilding(PostListBuildingEvent $event)
|
||||
{
|
||||
global $config, $database, $page, $user;
|
||||
$fid = $config->get_int("featured_id");
|
||||
if($fid > 0) {
|
||||
if ($fid > 0) {
|
||||
$image = $database->cache->get("featured_image_object:$fid");
|
||||
if($image === false) {
|
||||
if ($image === false) {
|
||||
$image = Image::by_id($fid);
|
||||
if($image) { // make sure the object is fully populated before saving
|
||||
if ($image) { // make sure the object is fully populated before saving
|
||||
$image->get_tag_array();
|
||||
}
|
||||
$database->cache->set("featured_image_object:$fid", $image, 600);
|
||||
}
|
||||
if(!is_null($image)) {
|
||||
if(ext_is_live("Ratings")) {
|
||||
if(strpos(Ratings::get_user_privs($user), $image->rating) === FALSE) {
|
||||
if (!is_null($image)) {
|
||||
if (ext_is_live("Ratings")) {
|
||||
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")) {
|
||||
if ($user->can("edit_feature")) {
|
||||
$event->add_part($this->theme->get_buttons_html($event->image->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
|||
";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
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);
|
||||
}
|
||||
|
@ -324,17 +319,19 @@ class Forum extends Extension {
|
|||
$title = html_escape($_POST["title"]);
|
||||
$sticky = !empty($_POST["sticky"]) ? html_escape($_POST["sticky"]) : "N";
|
||||
|
||||
if($sticky == ""){
|
||||
if ($sticky == "") {
|
||||
$sticky = "N";
|
||||
}
|
||||
|
||||
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,30 +374,31 @@ 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));
|
||||
if ($result==1){
|
||||
$result=$database->get_one("SELECT EXISTS (SELECT * FROM forum_threads WHERE id= ?)", [$threadID]);
|
||||
if ($result==1) {
|
||||
return true;
|
||||
}else{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,18 +25,20 @@ 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;'>
|
||||
<tr><td>Title:</td><td><input type='text' name='title' value='$threadTitle'></td></tr>
|
||||
<tr><td>Message:</td><td><textarea id='message' name='message' >$threadText</textarea></td></tr>
|
||||
<tr><td></td><td><small>Max characters alowed: $max_characters.</small></td></tr>";
|
||||
if($user->is_admin()){
|
||||
if ($user->is_admin()) {
|
||||
$html .= "<tr><td colspan='2'><label for='sticky'>Sticky:</label><input name='sticky' type='checkbox' value='Y' /></td></tr>";
|
||||
}
|
||||
$html .= "<tr><td colspan='2'><input type='submit' value='Submit' /></td></tr>
|
||||
|
@ -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"];
|
||||
|
||||
|
@ -125,9 +127,9 @@ class ForumTheme extends Themelet {
|
|||
//$delete_link = "";
|
||||
//}
|
||||
|
||||
if($showAdminOptions){
|
||||
if ($showAdminOptions) {
|
||||
$delete_link = "<a href=".make_link("forum/delete/".$threadID."/".$postID).">Delete</a>";
|
||||
}else{
|
||||
} else {
|
||||
$delete_link = "";
|
||||
}
|
||||
|
||||
|
@ -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,22 +190,20 @@ 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 {
|
||||
$title = $thread["title"];
|
||||
}
|
||||
|
||||
if($thread["sticky"] == "Y"){
|
||||
if ($thread["sticky"] == "Y") {
|
||||
$sticky = "Sticky: ";
|
||||
} else {
|
||||
$sticky = "";
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue