diff --git a/core/_install.php b/core/_install.php index a37553da..3f799d46 100644 --- a/core/_install.php +++ b/core/_install.php @@ -74,8 +74,7 @@ if (is_readable("data/config/shimmie.conf.php")) { do_install(); -// utilities {{{ - // TODO: Can some of these be pushed into "core/???.inc.php" ? +// TODO: Can some of these be pushed into "core/???.inc.php" ? function check_gd_version(): int { @@ -99,10 +98,9 @@ function check_im_version(): int return (empty($convert_check) ? 0 : 1); } -// }}} function do_install() -{ // {{{ +{ if (file_exists("data/config/auto_install.conf.php")) { require_once "data/config/auto_install.conf.php"; } elseif (@$_POST["database_type"] == DatabaseDriver::SQLITE) { @@ -118,10 +116,10 @@ function do_install() define("CACHE_DSN", null); define("DATABASE_KA", true); install_process(); -} // }}} +} function ask_questions() -{ // {{{ +{ $warnings = []; $errors = []; @@ -232,21 +230,21 @@ function ask_questions() EOD; -} // }}} +} /** * This is where the install really takes place. */ function install_process() -{ // {{{ +{ build_dirs(); create_tables(); insert_defaults(); write_config(); -} // }}} +} function create_tables() -{ // {{{ +{ try { $db = new Database(); @@ -331,10 +329,10 @@ EOD; } catch (Exception $e) { handle_db_errors(false, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 4); } -} // }}} +} function insert_defaults() -{ // {{{ +{ try { $db = new Database(); @@ -350,10 +348,10 @@ function insert_defaults() } 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() -{ // {{{ +{ $data_exists = file_exists("data") || mkdir("data"); $data_writable = is_writable("data") || chmod("data", 0755); @@ -373,10 +371,10 @@ function build_dirs() "; exit(7); } -} // }}} +} function write_config() -{ // {{{ +{ $file_content = '<' . '?php' . "\n" . "define('DATABASE_DSN', '".DATABASE_DSN."');\n" . '?' . '>'; @@ -417,7 +415,7 @@ EOD; EOD; } echo "\n"; -} // }}} +} function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessage2, int $exitCode) { diff --git a/core/database.php b/core/database.php index 3c06c5a3..535548a7 100644 --- a/core/database.php +++ b/core/database.php @@ -178,9 +178,18 @@ class Database $this->dbtime += $dur; } - public function execute(string $query, array $args=[]): PDOStatement + public function set_timeout(int $time): void + { + $this->engine->set_timeout($this->db, $time); + } + + public function execute(string $query, array $args=[], bool $scoreql = false): PDOStatement { try { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } + if (is_null($this->db)) { $this->connect_db(); } @@ -211,8 +220,12 @@ class Database /** * Execute an SQL query and return a 2D array. */ - public function get_all(string $query, array $args=[]): array + public function get_all(string $query, array $args=[], bool $scoreql = false): array { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } + $_start = microtime(true); $data = $this->execute($query, $args)->fetchAll(); $this->count_time("get_all", $_start, $query, $args); @@ -222,8 +235,11 @@ class Database /** * Execute an SQL query and return a iterable object for use with generators. */ - public function get_all_iterable(string $query, array $args=[]): PDOStatement + public function get_all_iterable(string $query, array $args=[], bool $scoreql = false): PDOStatement { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } $_start = microtime(true); $data = $this->execute($query, $args); $this->count_time("get_all_iterable", $_start, $query, $args); @@ -233,19 +249,40 @@ 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=[], bool $scoreql = false): ?array { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); $this->count_time("get_row", $_start, $query, $args); return $row ? $row : null; } + + /** + * Execute an SQL query and return a boolean based on whether it returns a result + */ + public function exists(string $query, array $args=[], bool $scoreql = false): bool + { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } + $_start = microtime(true); + $result = $this->execute($query, $args); + $this->count_time("exists", $_start, $query, $args); + return $result->rowCount()>0; + } + /** * Execute an SQL query and return the first column of each row. */ - public function get_col(string $query, array $args=[]): array + public function get_col(string $query, array $args=[], bool $scoreql = false): array { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } $_start = microtime(true); $res = $this->execute($query, $args)->fetchAll(PDO::FETCH_COLUMN); $this->count_time("get_col", $_start, $query, $args); @@ -255,8 +292,11 @@ class Database /** * Execute an SQL query and return the first column of each row as a single iterable object. */ - public function get_col_iterable(string $query, array $args=[]): Generator + public function get_col_iterable(string $query, array $args=[], bool $scoreql = false): Generator { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } $_start = microtime(true); $stmt = $this->execute($query, $args); $this->count_time("get_col_iterable", $_start, $query, $args); @@ -268,8 +308,11 @@ class Database /** * Execute an SQL query and return the the first column => the second column. */ - public function get_pairs(string $query, array $args=[]): array + public function get_pairs(string $query, array $args=[], bool $scoreql = false): array { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } $_start = microtime(true); $res = $this->execute($query, $args)->fetchAll(PDO::FETCH_KEY_PAIR); $this->count_time("get_pairs", $_start, $query, $args); @@ -279,8 +322,11 @@ class Database /** * Execute an SQL query and return a single value. */ - public function get_one(string $query, array $args=[]) + public function get_one(string $query, array $args=[], bool $scoreql = false) { + if ($scoreql===true) { + $query = $this->scoreql_to_sql($query); + } $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); $this->count_time("get_one", $_start, $query, $args); @@ -354,7 +400,7 @@ class MockDatabase extends Database $this->responses = $responses; } - public function execute(string $query, array $params=[]): PDOStatement + public function execute(string $query, array $params=[], bool $scoreql = false): PDOStatement { log_debug( "mock-database", @@ -376,23 +422,23 @@ class MockDatabase extends Database return $this->responses[$this->query_id++]; } - public function get_all(string $query, array $args=[]): array + public function get_all(string $query, array $args=[], bool $scoreql = false): array { return $this->_execute($query, $args); } - public function get_row(string $query, array $args=[]): ?array + public function get_row(string $query, array $args=[], bool $scoreql = false): ?array { return $this->_execute($query, $args); } - public function get_col(string $query, array $args=[]): array + public function get_col(string $query, array $args=[], bool $scoreql = false): array { return $this->_execute($query, $args); } - public function get_pairs(string $query, array $args=[]): array + public function get_pairs(string $query, array $args=[], bool $scoreql = false): array { return $this->_execute($query, $args); } - public function get_one(string $query, array $args=[]) + public function get_one(string $query, array $args=[], bool $scoreql = false) { return $this->_execute($query, $args); } diff --git a/core/dbengine.php b/core/dbengine.php index 03b4b851..86b1a9d6 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -12,7 +12,7 @@ abstract class SCORE const ILIKE = "SCORE_ILIKE"; } -class DBEngine +abstract class DBEngine { /** @var null|string */ public $name = null; @@ -33,6 +33,8 @@ class DBEngine { return 'CREATE TABLE '.$name.' ('.$data.')'; } + + abstract public function set_timeout(PDO $db, int $time); } class MySQL extends DBEngine @@ -68,6 +70,12 @@ class MySQL extends DBEngine $ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'"; return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes; } + + public function set_timeout(PDO $db, int $time): void + { + // These only apply to read-only queries, which appears to be the best we can to mysql-wise + $db->exec("SET SESSION MAX_EXECUTION_TIME=".$time.";"); + } } class PostgreSQL extends DBEngine @@ -87,7 +95,7 @@ class PostgreSQL extends DBEngine } else { $db->exec("SET application_name TO 'shimmie [local]';"); } - $db->exec("SET statement_timeout TO ".DATABASE_TIMEOUT.";"); + $this->set_timeout($db, DATABASE_TIMEOUT); } public function scoreql_to_sql(string $data): string @@ -109,6 +117,11 @@ class PostgreSQL extends DBEngine $data = $this->scoreql_to_sql($data); return "CREATE TABLE $name ($data)"; } + + public function set_timeout(PDO $db, int $time): void + { + $db->exec("SET statement_timeout TO ".$time.";"); + } } // shimmie functions for export to sqlite @@ -213,4 +226,9 @@ class SQLite extends DBEngine $cols_redone = implode(", ", $cols); return "CREATE TABLE $name ($cols_redone); $extras"; } + + public function set_timeout(PDO $db, int $time): void + { + // There doesn't seem to be such a thing for SQLite, so it does nothing + } } diff --git a/core/event.php b/core/event.php index 5ae1ad55..248bf6ae 100644 --- a/core/event.php +++ b/core/event.php @@ -154,7 +154,7 @@ class PageRequestEvent extends Event public function get_page_size(): int { global $config; - return $config->get_int('index_images'); + return $config->get_int(IndexConfig::IMAGES); } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 4277443c..7a66ec04 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -115,7 +115,7 @@ class Image if ($max < 1) { return null; } // From Issue #22 - opened by HungryFeline on May 30, 2011. - if ($max > $limit_range) { + if ($limit_range > 0 && $max > $limit_range) { $max = $limit_range; } $rand = mt_rand(0, $max-1); @@ -150,7 +150,7 @@ class Image $result = Image::get_accelerated_result($tag_conditions, $img_conditions, $start, $limit); if (!$result) { $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); - $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order")))); + $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string(IndexConfig::ORDER)))); if ($limit!=null) { $querylet->append(new Querylet(" LIMIT :limit ", ["limit" => $limit])); $querylet->append(new Querylet(" OFFSET :offset ", ["offset"=>$start])); @@ -334,7 +334,7 @@ class Image public static function count_pages(array $tags=[]): float { global $config; - return ceil(Image::count_images($tags) / $config->get_int('index_images')); + return ceil(Image::count_images($tags) / $config->get_int(IndexConfig::IMAGES)); } private static function terms_to_conditions(array $terms): array @@ -731,8 +731,10 @@ class Image ["tag"=>$tag] ); $database->execute( + $database->scoreql_to_sql( "INSERT INTO image_tags(image_id, tag_id) - VALUES(:id, (SELECT id FROM tags WHERE tag = :tag))", + VALUES(:id, (SELECT id FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)))" + ), ["id"=>$this->id, "tag"=>$tag] ); } else { diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php index 7fa8b0b7..0deeeef7 100644 --- a/core/imageboard/tag.php +++ b/core/imageboard/tag.php @@ -29,23 +29,7 @@ class Tag $tags = explode(' ', trim($tags)); /* sanitise by removing invisible / dodgy characters */ - $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) { - flash_message("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n"); - continue; - } - - if (!empty($tag)) { - $tag_array[] = $tag; - } - } + $tag_array = self::sanitize_array($tags); /* if user supplied a blank string, add "tagme" */ if (count($tag_array) === 0 && $tagme) { @@ -101,6 +85,74 @@ class Tag return $tag_array; } + public static function sanitize(string $tag): string + { + $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) { + throw new Exception("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n"); + } + return $tag; + } + + public static function compare(array $tags1, array $tags2): bool + { + if (count($tags1)!==count($tags2)) { + return false; + } + + $tags1 = array_map("strtolower", $tags1); + $tags2 = array_map("strtolower", $tags2); + natcasesort($tags1); + natcasesort($tags2); + + + for ($i = 0; $i < count($tags1); $i++) { + if ($tags1[$i]!==$tags2[$i]) { + var_dump($tags1); + var_dump($tags2); + return false; + } + } + return true; + } + + public static function get_diff_tags(array $source, array $remove): array + { + $before = array_map('strtolower', $source); + $remove = array_map('strtolower', $remove); + $after = []; + foreach ($before as $tag) { + if (!in_array($tag, $remove)) { + $after[] = $tag; + } + } + return $after; + } + + public static function sanitize_array(array $tags): array + { + $tag_array = []; + foreach ($tags as $tag) { + try { + $tag = Tag::sanitize($tag); + } catch (Exception $e) { + flash_message($e->getMessage()); + continue; + } + + if (!empty($tag)) { + $tag_array[] = $tag; + } + } + return $tag_array; + } + + public static function sqlify(string $term): string { global $database; diff --git a/core/permissions.php b/core/permissions.php index bd60b0c0..84eb292e 100644 --- a/core/permissions.php +++ b/core/permissions.php @@ -80,4 +80,7 @@ abstract class Permissions public const NOTES_ADMIN = "notes_admin"; public const POOLS_ADMIN = "pools_admin"; public const TIPS_ADMIN = "tips_admin"; + public const CRON_ADMIN = "cron_admin"; + public const APPROVE_IMAGE = "approve_image"; + public const APPROVE_COMMENT = "approve_comment"; } diff --git a/core/polyfills.php b/core/polyfills.php index 87e739e3..b5ec84c8 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -502,7 +502,7 @@ function bool_escape($input): bool */ if (is_bool($input)) { return $input; - } elseif (is_int($input)) { + } elseif (is_numeric($input)) { return ($input === 1); } else { $value = filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); diff --git a/core/userclass.php b/core/userclass.php index 5c60c9a0..f474d154 100644 --- a/core/userclass.php +++ b/core/userclass.php @@ -150,6 +150,10 @@ new UserClass("base", null, [ Permissions::NOTES_ADMIN => false, Permissions::POOLS_ADMIN => false, Permissions::TIPS_ADMIN => false, + Permissions::CRON_ADMIN => false, + + Permissions::APPROVE_IMAGE => false, + Permissions::APPROVE_COMMENT => false, ]); new UserClass("anonymous", "base", [ @@ -226,6 +230,9 @@ new UserClass("admin", "base", [ Permissions::NOTES_ADMIN => true, Permissions::POOLS_ADMIN => true, Permissions::TIPS_ADMIN => true, + Permissions::CRON_ADMIN => true, + Permissions::APPROVE_IMAGE => true, + Permissions::APPROVE_COMMENT => true, ]); new UserClass("hellbanned", "user", [ diff --git a/core/util.php b/core/util.php index 70b5f2ea..44f05c6c 100644 --- a/core/util.php +++ b/core/util.php @@ -350,6 +350,54 @@ function join_url(string $base, string ...$paths) return $output; } +function get_dir_contents(string $dir): array +{ + if (empty($dir)) { + throw new Exception("dir required"); + } + if (!is_dir($dir)) { + return []; + } + $results = array_diff( + scandir( + $dir + ), + ['..', '.'] + ); + + return $results; +} + +/** + * Returns amount of files & total size of dir. + */ +function scan_dir(string $path): array +{ + $bytestotal = 0; + $nbfiles = 0; + + $ite = new RecursiveDirectoryIterator( + $path, + FilesystemIterator::KEY_AS_PATHNAME | + FilesystemIterator::CURRENT_AS_FILEINFO | + FilesystemIterator::SKIP_DOTS + ); + foreach (new RecursiveIteratorIterator($ite) as $filename => $cur) { + try { + $filesize = $cur->getSize(); + $bytestotal += $filesize; + $nbfiles++; + } catch (RuntimeException $e) { + // This usually just means that the file got eaten by the import + continue; + } + } + + $size_mb = $bytestotal / 1048576; // to mb + $size_mb = number_format($size_mb, 2, '.', ''); + return ['path' => $path, 'total_files' => $nbfiles, 'total_mb' => $size_mb]; +} + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Debugging functions * diff --git a/ext/admin/info.php b/ext/admin/info.php index 84db9927..bdaafa4a 100644 --- a/ext/admin/info.php +++ b/ext/admin/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Various things to make admins' lives easier - * Documentation: - - */ - class AdminPageInfo extends ExtensionInfo { public const KEY = "admin"; diff --git a/ext/alias_editor/info.php b/ext/alias_editor/info.php index 4990ac6d..b846b815 100644 --- a/ext/alias_editor/info.php +++ b/ext/alias_editor/info.php @@ -1,14 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Edit the alias list - * Documentation: - */ - class AliasEditorInfo extends ExtensionInfo { public const KEY = "alias_editor"; diff --git a/ext/approval/info.php b/ext/approval/info.php new file mode 100644 index 00000000..c2af31cd --- /dev/null +++ b/ext/approval/info.php @@ -0,0 +1,13 @@ +"matthew@darkholme.net"]; + public $license = self::LICENSE_WTFPL; + public $description = "Adds an approval step to the upload/import process."; + public $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; +} diff --git a/ext/approval/main.php b/ext/approval/main.php new file mode 100644 index 00000000..f012b2e0 --- /dev/null +++ b/ext/approval/main.php @@ -0,0 +1,260 @@ +set_default_bool(ApprovalConfig::IMAGES, false); + $config->set_default_bool(ApprovalConfig::COMMENTS, false); + + if ($config->get_int(ApprovalConfig::VERSION) < 1) { + $this->install(); + } + } + + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + + if ($event->page_matches("approve_image") && $user->can(Permissions::APPROVE_IMAGE)) { + // Try to get the image ID + $image_id = int_escape($event->get_arg(0)); + if (empty($image_id)) { + $image_id = isset($_POST['image_id']) ? $_POST['image_id'] : null; + } + if (empty($image_id)) { + throw new SCoreException("Can not approve image: No valid Image ID given."); + } + + self::approve_image($image_id); + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("post/view/" . $image_id)); + } + + if ($event->page_matches("disapprove_image") && $user->can(Permissions::APPROVE_IMAGE)) { + // Try to get the image ID + $image_id = int_escape($event->get_arg(0)); + if (empty($image_id)) { + $image_id = isset($_POST['image_id']) ? $_POST['image_id'] : null; + } + if (empty($image_id)) { + throw new SCoreException("Can not disapprove image: No valid Image ID given."); + } + + self::disapprove_image($image_id); + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("post/view/".$image_id)); + } + } + + public function onSetupBuilding(SetupBuildingEvent $event) + { + $this->theme->display_admin_block($event); + } + + public function onAdminBuilding(AdminBuildingEvent $event) + { + global $config; + + $this->theme->display_admin_form(); + } + + public function onAdminAction(AdminActionEvent $event) + { + global $database, $user; + + $action = $event->action; + $event->redirect = true; + if ($action==="approval") { + $approval_action = $_POST["approval_action"]; + switch ($approval_action) { + case "approve_all": + $database->set_timeout(300000); // These updates can take a little bit + $database->execute( + $database->scoreql_to_sql( + "UPDATE images SET approved = SCORE_BOOL_Y, approved_by_id = :approved_by_id WHERE approved = SCORE_BOOL_N" + ), + ["approved_by_id"=>$user->id] + ); + break; + case "disapprove_all": + $database->set_timeout(300000); // These updates can take a little bit + $database->execute($database->scoreql_to_sql( + "UPDATE images SET approved = SCORE_BOOL_N, approved_by_id = NULL WHERE approved = SCORE_BOOL_Y" + )); + break; + default: + + break; + } + } + } + + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $user, $page, $config; + + if ($config->get_bool(ApprovalConfig::IMAGES) && $event->image->approved===false && !$user->can(Permissions::APPROVE_IMAGE)) { + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("post/list")); + } + } + + public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) + { + global $user; + if ($event->parent=="posts") { + if ($user->can(Permissions::APPROVE_IMAGE)) { + $event->add_nav_link("posts_unapproved", new Link('/post/list/approved%3Ano/1'), "Pending Approval", null, 60); + } + } + } + + + const SEARCH_REGEXP = "/^approved:(yes|no)/"; + public function onSearchTermParse(SearchTermParseEvent $event) + { + global $user, $database, $config; + + if ($config->get_bool(ApprovalConfig::IMAGES)) { + $matches = []; + + if (is_null($event->term) && $this->no_approval_query($event->context)) { + $event->add_querylet(new Querylet($database->scoreql_to_sql("approved = SCORE_BOOL_Y "))); + } + + + if (preg_match(self::SEARCH_REGEXP, strtolower($event->term), $matches)) { + if ($user->can(Permissions::APPROVE_IMAGE) && $matches[1] == "no") { + $event->add_querylet(new Querylet($database->scoreql_to_sql("approved = SCORE_BOOL_N "))); + } else { + $event->add_querylet(new Querylet($database->scoreql_to_sql("approved = SCORE_BOOL_Y "))); + } + } + } + } + + public function onHelpPageBuilding(HelpPageBuildingEvent $event) + { + global $user, $config; + if ($event->key===HelpPages::SEARCH) { + if ($user->can(Permissions::APPROVE_IMAGE) && $config->get_bool(ApprovalConfig::IMAGES)) { + $block = new Block(); + $block->header = "Approval"; + $block->body = $this->theme->get_help_html(); + $event->add_block($block); + } + } + } + + + private function no_approval_query(array $context): bool + { + foreach ($context as $term) { + if (preg_match(self::SEARCH_REGEXP, $term)) { + return false; + } + } + return true; + } + + public static function approve_image($image_id) + { + global $database, $user; + + $database->execute( + $database->scoreql_to_sql( + "UPDATE images SET approved = SCORE_BOOL_Y, approved_by_id = :approved_by_id WHERE id = :id AND approved = SCORE_BOOL_N" + ), + ["approved_by_id"=>$user->id, "id"=>$image_id] + ); + } + + public static function disapprove_image($image_id) + { + global $database, $user; + + $database->execute( + $database->scoreql_to_sql( + "UPDATE images SET approved = SCORE_BOOL_N, approved_by_id = NULL WHERE id = :id AND approved = SCORE_BOOL_Y" + ), + ["id"=>$image_id] + ); + } + + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $user, $config; + if ($user->can(Permissions::APPROVE_IMAGE) && $config->get_bool(ApprovalConfig::IMAGES)) { + $event->add_part($this->theme->get_image_admin_html($event->image)); + } + } + + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + { + global $user, $config; + + if ($user->can(Permissions::APPROVE_IMAGE)&& $config->get_bool(ApprovalConfig::IMAGES)) { + if (in_array("approved:no", $event->search_terms)) { + $event->add_action("bulk_approve_image", "Approve", "a"); + } else { + $event->add_action("bulk_disapprove_image", "Disapprove"); + } + } + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user; + + switch ($event->action) { + case "bulk_approve_image": + if ($user->can(Permissions::APPROVE_IMAGE)) { + $total = 0; + foreach ($event->items as $image) { + self::approve_image($image->id); + $total++; + } + flash_message("Approved $total items"); + } + break; + case "bulk_disapprove_image": + if ($user->can(Permissions::APPROVE_IMAGE)) { + $total = 0; + foreach ($event->items as $image) { + self::disapprove_image($image->id); + $total++; + } + flash_message("Disapproved $total items"); + } + break; + } + } + + + private function install() + { + global $database, $config; + + if ($config->get_int(ApprovalConfig::VERSION) < 1) { + $database->Execute($database->scoreql_to_sql( + "ALTER TABLE images ADD COLUMN approved SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N" + )); + $database->Execute($database->scoreql_to_sql( + "ALTER TABLE images ADD COLUMN approved_by_id INTEGER NULL" + )); + + $database->Execute("CREATE INDEX images_approved_idx ON images(approved)"); + $config->set_int(ApprovalConfig::VERSION, 1); + } + } +} diff --git a/ext/approval/theme.php b/ext/approval/theme.php new file mode 100644 index 00000000..b7ca5b23 --- /dev/null +++ b/ext/approval/theme.php @@ -0,0 +1,58 @@ +approved===true) { + $html = " + ".make_form(make_link('disapprove_image/'.$image->id), 'POST')." + + + + "; + } else { + $html = " + ".make_form(make_link('approve_image/'.$image->id), 'POST')." + + + + "; + } + + return $html; + } + + + public function get_help_html() + { + return '

Search for images that are approved/not approved.

+
+
approved:yes
+

Returns images that have been approved.

+
+
+
approved:no
+

Returns images that have not been approved.

+
+ '; + } + + public function display_admin_block(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Approval"); + $sb->add_bool_option(ApprovalConfig::IMAGES, "Images: "); + $event->panel->add_block($sb); + } + + public function display_admin_form() + { + global $page; + + $html = make_form(make_link("admin/approval"), "POST"); + $html .= "
"; + $html .= ""; + $html .= "\n"; + $page->add_block(new Block("Approval", $html)); + } +} diff --git a/ext/arrowkey_navigation/info.php b/ext/arrowkey_navigation/info.php index ee6b88f2..4d0a2f38 100644 --- a/ext/arrowkey_navigation/info.php +++ b/ext/arrowkey_navigation/info.php @@ -1,13 +1,5 @@ - * Link: http://www.drudexsoftware.com/ - * License: GPLv2 - * Description: Allows viewers no navigate between images using the left & right arrow keys. - * Documentation: - * Simply enable this extention in the extention manager to enable arrow key navigation. - */ + class ArrowkeyNavigationInfo extends ExtensionInfo { public const KEY = "arrowkey_navigation"; diff --git a/ext/arrowkey_navigation/main.php b/ext/arrowkey_navigation/main.php index 640d2b12..6464f592 100644 --- a/ext/arrowkey_navigation/main.php +++ b/ext/arrowkey_navigation/main.php @@ -52,7 +52,7 @@ class ArrowkeyNavigation extends Extension global $config, $database; // get the amount of images per page - $images_per_page = $config->get_int('index_images'); + $images_per_page = $config->get_int(IndexConfig::IMAGES); // if there are no tags, use default if (is_null($event->get_arg(1))) { diff --git a/ext/artists/info.php b/ext/artists/info.php index 5da88896..efdc77a9 100644 --- a/ext/artists/info.php +++ b/ext/artists/info.php @@ -1,14 +1,5 @@ - * Alpha - * License: GPLv2 - * Description: Simple artists extension - * Documentation: - * - */ class ArtistsInfo extends ExtensionInfo { public const KEY = "artists"; diff --git a/ext/artists/main.php b/ext/artists/main.php index e0a4cf7e..2fbd3500 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -905,7 +905,7 @@ class Artists extends Extension $pageNumber * $artistsPerPage , $artistsPerPage ] - ); + ); $number_of_listings = count($listing); diff --git a/ext/autocomplete/info.php b/ext/autocomplete/info.php index 3d420496..259ab70b 100644 --- a/ext/autocomplete/info.php +++ b/ext/autocomplete/info.php @@ -1,11 +1,5 @@ - * Description: Adds autocomplete to search & tagging. - */ - class AutoCompleteInfo extends ExtensionInfo { public const KEY = "autocomplete"; diff --git a/ext/ban_words/info.php b/ext/ban_words/info.php index 6b56c045..b816b89c 100644 --- a/ext/ban_words/info.php +++ b/ext/ban_words/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: For stopping spam and other comment abuse - * Documentation: - * - */ - class BanWordsInfo extends ExtensionInfo { public const KEY = "ban_words"; diff --git a/ext/bbcode/info.php b/ext/bbcode/info.php index 06798aab..d97ae411 100644 --- a/ext/bbcode/info.php +++ b/ext/bbcode/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Turns BBCode into HTML - */ - class BBCodeInfo extends ExtensionInfo { public const KEY = "bbcode"; diff --git a/ext/blocks/info.php b/ext/blocks/info.php index 23d24604..2bf68f2b 100644 --- a/ext/blocks/info.php +++ b/ext/blocks/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Add HTML to some space (News, Ads, etc) - */ - class BlocksInfo extends ExtensionInfo { public const KEY = "blocks"; diff --git a/ext/blotter/info.php b/ext/blotter/info.php index d03891ec..7fab798c 100644 --- a/ext/blotter/info.php +++ b/ext/blotter/info.php @@ -1,11 +1,5 @@ [http://seemslegit.com/] - * License: GPLv2 - * Description: - */ class BlotterInfo extends ExtensionInfo { public const KEY = "blotter"; diff --git a/ext/browser_search/info.php b/ext/browser_search/info.php index ba353e4c..34360c5b 100644 --- a/ext/browser_search/info.php +++ b/ext/browser_search/info.php @@ -1,17 +1,5 @@ - * Some code (and lots of help) by Artanis (Erik Youngren ) from the 'tagger' extention - Used with permission - * Link: http://atravelinggeek.com/ - * License: GPLv2 - * Description: Allows the user to add a browser 'plugin' to search the site with real-time suggestions - * Version: 0.1c, October 26, 2007 - * Documentation: - * - */ - class BrowserSearchInfo extends ExtensionInfo { public const KEY = "browser_search"; diff --git a/ext/bulk_actions/info.php b/ext/bulk_actions/info.php index 00c66576..76158a76 100644 --- a/ext/bulk_actions/info.php +++ b/ext/bulk_actions/info.php @@ -1,15 +1,5 @@ , contributions by Shish and Agasa. - */ - - class BulkActionsInfo extends ExtensionInfo { public const KEY = "bulk_actions"; diff --git a/ext/bulk_add/info.php b/ext/bulk_add/info.php index 333bf0ba..1d25d373 100644 --- a/ext/bulk_add/info.php +++ b/ext/bulk_add/info.php @@ -1,17 +1,8 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Bulk add server-side images - * Documentation: - */ - class BulkAddInfo extends ExtensionInfo { - public const KEY = "builk_add"; + public const KEY = "bulk_add"; public $key = self::KEY; public $name = "Bulk Add"; @@ -20,7 +11,7 @@ class BulkAddInfo extends ExtensionInfo public $license = self::LICENSE_GPLV2; public $description = "Bulk add server-side images"; public $documentation = -" Upload the images into a new directory via ftp or similar, go to +"Upload the images into a new directory via ftp or similar, go to shimmie's admin page and put that directory in the bulk add box. If there are subdirectories, they get used as tags (eg if you upload into /home/bob/uploads/holiday/2008/ and point diff --git a/ext/bulk_add_csv/info.php b/ext/bulk_add_csv/info.php index a2e6af69..30a407eb 100644 --- a/ext/bulk_add_csv/info.php +++ b/ext/bulk_add_csv/info.php @@ -1,15 +1,5 @@ - * License: GPLv2 - * Description: Bulk add server-side images with metadata from CSV file - * Documentation: - * - * - */ - class BulkAddCSVInfo extends ExtensionInfo { public const KEY = "bulk_add_csv"; diff --git a/ext/bulk_remove/info.php b/ext/bulk_remove/info.php index 7f90b825..6a48ee2b 100644 --- a/ext/bulk_remove/info.php +++ b/ext/bulk_remove/info.php @@ -1,14 +1,5 @@ - * Link: http://www.drudexsoftware.com/ - * License: GPLv2 - * Description: Allows admin to delete many images at once through Board Admin. - * Documentation: - * - */ class BulkRemoveInfo extends ExtensionInfo { public const KEY = "bulk_remove"; diff --git a/ext/bulk_remove/main.php b/ext/bulk_remove/main.php index 1d2517b3..50ea8edb 100644 --- a/ext/bulk_remove/main.php +++ b/ext/bulk_remove/main.php @@ -123,7 +123,7 @@ class BulkRemove extends Extension $page->add_block(new Block( "Bulk Remove Error", "Please use Board Admin to use bulk remove." - )); + )); } // diff --git a/ext/comment/info.php b/ext/comment/info.php index 600d1eab..38191656 100644 --- a/ext/comment/info.php +++ b/ext/comment/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Allow users to make comments on images - * Documentation: - * Formatting is done with the standard formatting API (normally BBCode) - */ - class CommentListInfo extends ExtensionInfo { public const KEY = "comment"; diff --git a/ext/comment/main.php b/ext/comment/main.php index 3791ea00..8dca6983 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -367,7 +367,6 @@ class CommentList extends Extension } } - // page building {{{ private function build_page(int $current_page) { global $cache, $database, $user; @@ -417,9 +416,7 @@ class CommentList extends Extension $this->theme->display_comment_list($images, $current_page, $total_pages, $user->can(Permissions::CREATE_COMMENT)); } - // }}} - // get comments {{{ /** * #return Comment[] */ @@ -488,9 +485,7 @@ class CommentList extends Extension ORDER BY comments.id ASC ", ["image_id"=>$image_id]); } - // }}} - // add / remove / edit comments {{{ private function is_comment_limit_hit(): bool { global $config, $database; @@ -651,5 +646,4 @@ class CommentList extends Extension throw new CommentPostingException("Akismet thinks that your comment is spam. Try rewriting the comment, or logging in."); } } - // }}} } diff --git a/ext/cron_uploader/config.php b/ext/cron_uploader/config.php new file mode 100644 index 00000000..aa6637dc --- /dev/null +++ b/ext/cron_uploader/config.php @@ -0,0 +1,97 @@ +set_default_int(self::COUNT, 1); + $config->set_default_string(self::DIR, data_path(self::DEFAULT_PATH)); + + $upload_key = $config->get_string(self::KEY, ""); + if (empty($upload_key)) { + $upload_key = self::generate_key(); + + $config->set_string(self::KEY, $upload_key); + } + } + + public static function get_user(): int + { + global $config; + return $config->get_int(self::USER); + } + + public static function set_user(int $value): void + { + global $config; + $config->set_int(self::USER, $value); + } + + + public static function get_key(): string + { + global $config; + return $config->get_string(self::KEY); + } + + public static function set_key(string $value): void + { + global $config; + $config->set_string(self::KEY, $value); + } + + public static function get_count(): int + { + global $config; + return $config->get_int(self::COUNT); + } + + public static function set_count(int $value): int + { + global $config; + $config->get_int(self::COUNT, $value); + } + + public static function get_dir(): string + { + global $config; + $value = $config->get_string(self::DIR); + if (empty($value)) { + $value = data_path("cron_uploader"); + self::set_dir($value); + } + return $value; + } + + public static function set_dir(string $value): void + { + global $config; + $config->set_string(self::DIR, $value); + } + + + /* + * Generates a unique key for the website to prevent unauthorized access. + */ + private static function generate_key() + { + $length = 20; + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $randomString = ''; + + for ($i = 0; $i < $length; $i++) { + $randomString .= $characters [rand(0, strlen($characters) - 1)]; + } + + return $randomString; + } +} diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 68a85635..49f4f171 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -1,43 +1,31 @@ parent=="system") { + $event->add_nav_link("cron_docs", new Link('cron_upload'), "Cron Upload"); + } + } /** * Checks if the cron upload page has been accessed @@ -45,308 +33,354 @@ class CronUploader extends Extension */ public function onPageRequest(PageRequestEvent $event) { - global $config, $user; + global $user; if ($event->page_matches("cron_upload")) { - $this->upload_key = $config->get_string(self::CONFIG_KEY, ""); - - // If the key is in the url, upload - if ($this->upload_key != "" && $event->get_arg(0) == $this->upload_key) { - // log in as admin - $this->set_dir(); - - $lockfile = fopen($this->root_dir . "/.lock", "w"); - if (!flock($lockfile, LOCK_EX | LOCK_NB)) { - throw new Exception("Cron upload process is already running"); - } - try { - $this->process_upload(); // Start upload - } finally { - flock($lockfile, LOCK_UN); - fclose($lockfile); - } - } elseif ($user->can(Permissions::BULK_ADD)) { - $this->set_dir(); + $key = $event->get_arg(0); + if (!empty($key)) { + $this->process_upload($key); // Start upload + } elseif ($user->can(Permissions::CRON_ADMIN)) { $this->display_documentation(); } } } - private function display_documentation() - { - global $page; - $this->set_dir(); // Determines path to cron_uploader_dir - - - $queue_dir = $this->root_dir . "/" . self::QUEUE_DIR; - $uploaded_dir = $this->root_dir . "/" . self::UPLOADED_DIR; - $failed_dir = $this->root_dir . "/" . self::FAILED_DIR; - - $queue_dirinfo = $this->scan_dir($queue_dir); - $uploaded_dirinfo = $this->scan_dir($uploaded_dir); - $failed_dirinfo = $this->scan_dir($failed_dir); - - $cron_url = make_http(make_link("/cron_upload/" . $this->upload_key)); - $cron_cmd = "curl --silent $cron_url"; - $log_path = $this->root_dir . "/uploads.log"; - - $info_html = "Information -
- - - - - - - - - - - - - - - - - - - - - -
DirectoryFilesSize (MB)Directory Path
Queue{$queue_dirinfo['total_files']}{$queue_dirinfo['total_mb']}
Uploaded{$uploaded_dirinfo['total_files']}{$uploaded_dirinfo['total_mb']}
Failed{$failed_dirinfo['total_files']}{$failed_dirinfo['total_mb']}
- -
Cron Command:
- Create a cron job with the command above.
- Read the documentation if you're not sure what to do.
"; - - $install_html = " - This cron uploader is fairly easy to use but has to be configured first. -
1. Install & activate this plugin. -
-
2. Upload your images you want to be uploaded to the queue directory using your FTP client. -
($queue_dir) -
This also supports directory names to be used as tags. -
-
3. Go to the Board Config to the Cron Uploader menu and copy the Cron Command. -
($cron_cmd) -
-
4. Create a cron job or something else that can open a url on specified times. -
If you're not sure how to do this, you can give the command to your web host and you can ask them to create the cron job for you. -
When you create the cron job, you choose when to upload new images. -
-
5. When the cron command is set up, your image queue will upload x file(s) at the specified times. -
You can see any uploads or failed uploads in the log file. ($log_path) -
Your uploaded images will be moved to the 'uploaded' directory, it's recommended that you remove everything out of this directory from time to time. -
($uploaded_dir) -
-
Whenever the url in that cron job command is opened, a new file will upload from the queue. -
So when you want to manually upload an image, all you have to do is open the link once. -
This link can be found under 'Cron Command' in the board config, just remove the 'wget ' part and only the url remains. -
($cron_url)"; - - $page->set_title("Cron Uploader"); - $page->set_heading("Cron Uploader"); - - $block = new Block("Cron Uploader", $info_html, "main", 10); - $block_install = new Block("Installation Guide", $install_html, "main", 20); - $page->add_block($block); - $page->add_block($block_install); - } - - public function onInitExt(InitExtEvent $event) - { - global $config; - // Set default values - $config->set_default_int(self::CONFIG_COUNT, 1); - $this->set_dir(); - - $this->upload_key = $config->get_string(self::CONFIG_KEY, ""); - if (empty($this->upload_key)) { - $this->upload_key = $this->generate_key(); - - $config->set_string(self::CONFIG_KEY, $this->upload_key); - } - } - public function onSetupBuilding(SetupBuildingEvent $event) { - $this->set_dir(); + global $database; - $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("Settings
"); - $sb->add_int_option(self::CONFIG_COUNT, "How many to upload each time"); - $sb->add_text_option(self::CONFIG_DIR, "
Set Cron Uploader root directory
"); + $users = $database->get_pairs("SELECT name, id FROM users UNION ALL SELECT '', null order by name"); - $sb->add_label("
Cron Command:
- Create a cron job with the command above.
- Read the documentation if you're not sure what to do."); + $sb = new SetupBlock("Cron Uploader"); + $sb->start_table(); + $sb->add_int_option(CronUploaderConfig::COUNT, "Upload per run", true); + $sb->add_text_option(CronUploaderConfig::DIR, "Root dir", true); + $sb->add_text_option(CronUploaderConfig::KEY, "Key", true); + $sb->add_choice_option(CronUploaderConfig::USER, $users, "User", true); + $sb->end_table(); + $sb->add_label("Read the documentation for cron setup instructions."); $event->panel->add_block($sb); } - /* - * Generates a unique key for the website to prevent unauthorized access. - */ - private function generate_key() + public function onAdminBuilding(AdminBuildingEvent $event) { - $length = 20; - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $randomString = ''; + $failed_dir = $this->get_failed_dir(); + $results = get_dir_contents($failed_dir); - for ($i = 0; $i < $length; $i++) { - $randomString .= $characters [rand(0, strlen($characters) - 1)]; + $failed_dirs = []; + foreach ($results as $result) { + $path = join_path($failed_dir, $result); + if (is_dir($path)) { + $failed_dirs[] = $result; + } } - return $randomString; + $this->theme->display_form($failed_dirs); } - /* - * Set the directory for the image queue. If no directory was given, set it to the default directory. - */ - private function set_dir() + public function onAdminAction(AdminActionEvent $event) { - global $config; - // Determine directory (none = default) - - $dir = $config->get_string(self::CONFIG_DIR, ""); - - // Sets new default dir if not in config yet/anymore - if ($dir == "") { - $dir = data_path("cron_uploader"); - $config->set_string(self::CONFIG_DIR, $dir); + $action = $event->action; + switch ($action) { + case "cron_uploader_clear_queue": + $event->redirect = true; + $this->clear_folder(self::QUEUE_DIR); + break; + case "cron_uploader_clear_uploaded": + $event->redirect = true; + $this->clear_folder(self::UPLOADED_DIR); + break; + case "cron_uploader_clear_failed": + $event->redirect = true; + $this->clear_folder(self::FAILED_DIR); + break; + case "cron_uploader_restage": + $event->redirect = true; + if (array_key_exists("failed_dir", $_POST) && !empty($_POST["failed_dir"])) { + $this->restage_folder($_POST["failed_dir"]); + } + break; } + } + + private function restage_folder(string $folder) + { + if (empty($folder)) { + throw new Exception("folder empty"); + } + $queue_dir = $this->get_queue_dir(); + $stage_dir = join_path($this->get_failed_dir(), $folder); + + if (!is_dir($stage_dir)) { + throw new Exception("Could not find $stage_dir"); + } + + $this->prep_root_dir(); + + $results = get_dir_contents($queue_dir); + + if (count($results) > 0) { + flash_message("Queue folder must be empty to re-stage", "error"); + return; + } + + $results = get_dir_contents($stage_dir); + + if (count($results) == 0) { + if (rmdir($stage_dir)===false) { + flash_message("Nothing to stage from $folder, cannot remove folder"); + } else { + flash_message("Nothing to stage from $folder, removing folder"); + } + return; + } + + foreach ($results as $result) { + $original_path = join_path($stage_dir, $result); + $new_path = join_path($queue_dir, $result); + + rename($original_path, $new_path); + } + + flash_message("Re-staged $folder to queue"); + rmdir($stage_dir); + } + + private function clear_folder($folder) + { + $path = join_path(CronUploaderConfig::get_dir(), $folder); + deltree($path); + flash_message("Cleared $path"); + } + + + private function get_cron_url() + { + return make_http(make_link("/cron_upload/" . CronUploaderConfig::get_key())); + } + + private function get_cron_cmd() + { + return "curl --silent " . $this->get_cron_url(); + } + + private function display_documentation() + { + global $database; + + $this->prep_root_dir(); + + $queue_dir = $this->get_queue_dir(); + $uploaded_dir = $this->get_uploaded_dir(); + $failed_dir = $this->get_failed_dir(); + + $queue_dirinfo = scan_dir($queue_dir); + $uploaded_dirinfo = scan_dir($uploaded_dir); + $failed_dirinfo = scan_dir($failed_dir); + + + $running = false; + $lockfile = fopen($this->get_lock_file(), "w"); + try { + if (!flock($lockfile, LOCK_EX | LOCK_NB)) { + $running = true; + } else { + flock($lockfile, LOCK_UN); + } + } finally { + fclose($lockfile); + } + + $logs = []; + if (Extension::is_enabled(LogDatabaseInfo::KEY)) { + $logs = $database->get_all( + "SELECT * FROM score_log WHERE section = :section ORDER BY date_sent DESC LIMIT 100", + ["section" => self::NAME] + ); + } + + $this->theme->display_documentation( + $running, + $queue_dirinfo, + $uploaded_dirinfo, + $failed_dirinfo, + $this->get_cron_cmd(), + $this->get_cron_url(), + $logs + ); + } + + public function get_queue_dir() + { + $dir = CronUploaderConfig::get_dir(); + return join_path($dir, self::QUEUE_DIR); + } + + public function get_uploaded_dir() + { + $dir = CronUploaderConfig::get_dir(); + return join_path($dir, self::UPLOADED_DIR); + } + + public function get_failed_dir() + { + $dir = CronUploaderConfig::get_dir(); + return join_path($dir, self::FAILED_DIR); + } + + private function prep_root_dir(): string + { + // Determine directory (none = default) + $dir = CronUploaderConfig::get_dir(); // Make the directory if it doesn't exist yet - if (!is_dir($dir . "/" . self::QUEUE_DIR . "/")) { - mkdir($dir . "/" . self::QUEUE_DIR . "/", 0775, true); + if (!is_dir($this->get_queue_dir())) { + mkdir($this->get_queue_dir(), 0775, true); } - if (!is_dir($dir . "/" . self::UPLOADED_DIR . "/")) { - mkdir($dir . "/" . self::UPLOADED_DIR . "/", 0775, true); + if (!is_dir($this->get_uploaded_dir())) { + mkdir($this->get_uploaded_dir(), 0775, true); } - if (!is_dir($dir . "/" . self::FAILED_DIR . "/")) { - mkdir($dir . "/" . self::FAILED_DIR . "/", 0775, true); + if (!is_dir($this->get_failed_dir())) { + mkdir($this->get_failed_dir(), 0775, true); } - $this->root_dir = $dir; return $dir; } - /** - * Returns amount of files & total size of dir. - */ - public function scan_dir(string $path): array + private function get_lock_file(): string { - $bytestotal = 0; - $nbfiles = 0; - - $ite = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); - foreach (new RecursiveIteratorIterator($ite) as $filename => $cur) { - $filesize = $cur->getSize(); - $bytestotal += $filesize; - $nbfiles++; - } - - $size_mb = $bytestotal / 1048576; // to mb - $size_mb = number_format($size_mb, 2, '.', ''); - return ['total_files' => $nbfiles, 'total_mb' => $size_mb]; + $root_dir = CronUploaderConfig::get_dir(); + return join_path($root_dir, ".lock"); } /** * Uploads the image & handles everything */ - public function process_upload(int $upload_count = 0): bool + public function process_upload(string $key, ?int $upload_count = null): bool { - global $config, $database; + global $database; - //set_time_limit(0); - - - $output_subdir = date('Ymd-His', time()) . "/"; - $this->generate_image_queue(); - - // Gets amount of imgs to upload - if ($upload_count == 0) { - $upload_count = $config->get_int(self::CONFIG_COUNT, 1); + if ($key!=CronUploaderConfig::get_key()) { + throw new SCoreException("Cron upload key incorrect"); + } + $user_id = CronUploaderConfig::get_user(); + if (empty($user_id)) { + throw new SCoreException("Cron upload user not set"); + } + $user = User::by_id($user_id); + if ($user == null) { + throw new SCoreException("No user found for cron upload user $user_id"); } - // Throw exception if there's nothing in the queue - if (count($this->image_queue) == 0) { - $this->add_upload_info("Your queue is empty so nothing could be uploaded."); - $this->handle_log(); - return false; + send_event(new UserLoginEvent($user)); + $this->log_message(SCORE_LOG_INFO, "Logged in as user {$user->name}"); + + $lockfile = fopen($this->get_lock_file(), "w"); + if (!flock($lockfile, LOCK_EX | LOCK_NB)) { + throw new SCoreException("Cron upload process is already running"); } - // Randomize Images - //shuffle($this->image_queue); + try { + //set_time_limit(0); - $merged = 0; - $added = 0; - $failed = 0; - - // Upload the file(s) - for ($i = 0; $i < $upload_count && sizeof($this->image_queue) > 0; $i++) { - $img = array_pop($this->image_queue); - - $database->beginTransaction(); - try { - $this->add_upload_info("Adding file: {$img[1]} - tags: {$img[2]}"); - $result = $this->add_image($img[0], $img[1], $img[2]); - $database->commit(); - $this->move_uploaded($img[0], $img[1], $output_subdir, false); - if ($result->merged) { - $merged++; - } else { - $added++; - } - } catch (Exception $e) { - $database->rollback(); - $failed++; - $this->move_uploaded($img[0], $img[1], $output_subdir, true); - $msgNumber = $this->add_upload_info("(" . gettype($e) . ") " . $e->getMessage()); - $msgNumber = $this->add_upload_info($e->getTraceAsString()); + // Gets amount of imgs to upload + if ($upload_count == null) { + $upload_count = CronUploaderConfig::get_count(); } + + $output_subdir = date('Ymd-His', time()); + $image_queue = $this->generate_image_queue($upload_count); + + + // Throw exception if there's nothing in the queue + if (count($image_queue) == 0) { + $this->log_message(SCORE_LOG_WARNING, "Your queue is empty so nothing could be uploaded."); + $this->handle_log(); + return false; + } + + // Randomize Images + //shuffle($this->image_queue); + + $merged = 0; + $added = 0; + $failed = 0; + + // Upload the file(s) + for ($i = 0; $i < $upload_count && sizeof($image_queue) > 0; $i++) { + $img = array_pop($image_queue); + + try { + $database->beginTransaction(); + $this->log_message(SCORE_LOG_INFO, "Adding file: {$img[0]} - tags: {$img[2]}"); + $result = $this->add_image($img[0], $img[1], $img[2]); + $database->commit(); + $this->move_uploaded($img[0], $img[1], $output_subdir, false); + if ($result->merged) { + $merged++; + } else { + $added++; + } + } catch (Exception $e) { + try { + $database->rollback(); + } catch (Exception $e) { + } + + $failed++; + $this->move_uploaded($img[0], $img[1], $output_subdir, true); + $this->log_message(SCORE_LOG_ERROR, "(" . gettype($e) . ") " . $e->getMessage()); + $this->log_message(SCORE_LOG_ERROR, $e->getTraceAsString()); + } + } + + + $this->log_message(SCORE_LOG_INFO, "Items added: $added"); + $this->log_message(SCORE_LOG_INFO, "Items merged: $merged"); + $this->log_message(SCORE_LOG_INFO, "Items failed: $failed"); + + + // Display upload log + $this->handle_log(); + + return true; + } finally { + flock($lockfile, LOCK_UN); + fclose($lockfile); } - - $msgNumber = $this->add_upload_info("Items added: $added"); - $msgNumber = $this->add_upload_info("Items merged: $merged"); - $msgNumber = $this->add_upload_info("Items failed: $failed"); - - // Display & save upload log - $this->handle_log(); - - return true; } - private function move_uploaded($path, $filename, $output_subdir, $corrupt = false) + private function move_uploaded(string $path, string $filename, string $output_subdir, bool $corrupt = false) { - // Create - $newDir = $this->root_dir; + $relativeDir = dirname(substr($path, strlen(CronUploaderConfig::get_dir()) + 7)); - $relativeDir = dirname(substr($path, strlen($this->root_dir) + 7)); + if ($relativeDir==".") { + $relativeDir = ""; + } // Determine which dir to move to if ($corrupt) { // Move to corrupt dir - $newDir .= "/" . self::FAILED_DIR . "/" . $output_subdir . $relativeDir; - $info = "ERROR: Image was not uploaded."; + $newDir = join_path($this->get_failed_dir(), $output_subdir, $relativeDir); + $info = "ERROR: Image was not uploaded. "; } else { - $newDir .= "/" . self::UPLOADED_DIR . "/" . $output_subdir . $relativeDir; + $newDir = join_path($this->get_uploaded_dir(), $output_subdir, $relativeDir); $info = "Image successfully uploaded. "; } - $newDir = str_replace("//", "/", $newDir . "/"); + $newDir = str_replace(DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, $newDir); if (!is_dir($newDir)) { mkdir($newDir, 0775, true); } + $newFile = join_path($newDir, $filename); // move file to correct dir - rename($path, $newDir . $filename); + rename($path, $newFile); - $this->add_upload_info($info . "Image \"$filename\" moved from queue to \"$newDir\"."); + $this->log_message(SCORE_LOG_INFO, $info . "Image \"$filename\" moved from queue to \"$newDir\"."); } /** @@ -357,7 +391,7 @@ class CronUploader extends Extension assert(file_exists($tmpname)); $tagArray = Tag::explode($tags); - if (count($tagArray)==0) { + if (count($tagArray) == 0) { $tagArray[] = "tagme"; } @@ -377,11 +411,11 @@ class CronUploader extends Extension if ($event->image_id == -1) { throw new Exception("File type not recognised. Filename: {$filename}"); } elseif ($event->merged === true) { - $infomsg = "Image merged. ID: {$event->image_id} Filename: {$filename}"; + $infomsg = "Image merged. ID: {$event->image_id} - Filename: {$filename}"; } else { $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename}"; } - $msgNumber = $this->add_upload_info($infomsg); + $this->log_message(SCORE_LOG_INFO, $infomsg); // Set tags $img = Image::by_id($event->image_id); @@ -390,18 +424,32 @@ class CronUploader extends Extension return $event; } - private function generate_image_queue(): void + private const PARTIAL_DOWNLOAD_EXTENSIONS = ['crdownload','part']; + + private function is_skippable_file(string $path) { - $base = $this->root_dir . "/" . self::QUEUE_DIR; + $info = pathinfo($path); + + if (in_array(strtolower($info['extension']), self::PARTIAL_DOWNLOAD_EXTENSIONS)) { + return true; + } + + return false; + } + + private function generate_image_queue(string $root_dir, ?int $limit = null): array + { + $base = $this->get_queue_dir(); + $output = []; if (!is_dir($base)) { - $this->add_upload_info("Image Queue Directory could not be found at \"$base\"."); - return; + $this->log_message(SCORE_LOG_WARNING, "Image Queue Directory could not be found at \"$base\"."); + return []; } $ite = new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS); foreach (new RecursiveIteratorIterator($ite) as $fullpath => $cur) { - if (!is_link($fullpath) && !is_dir($fullpath)) { + if (!is_link($fullpath) && !is_dir($fullpath) && !$this->is_skippable_file($fullpath)) { $pathinfo = pathinfo($fullpath); $relativePath = substr($fullpath, strlen($base)); @@ -412,34 +460,33 @@ class CronUploader extends Extension 1 => $pathinfo ["basename"], 2 => $tags ]; - array_push($this->image_queue, $img); + $output[] = $img; + if (!empty($limit) && count($output) >= $limit) { + break; + } } } + return $output; } - /** - * Adds a message to the info being published at the end - */ - private function add_upload_info(string $text, int $addon = 0): int + + private function log_message(int $severity, string $message): void { - $info = $this->upload_info; + global $database; + + log_msg(self::NAME, $severity, $message); + $time = "[" . date('Y-m-d H:i:s') . "]"; + $this->output_buffer[] = $time . " " . $message; - // If addon function is not used - if ($addon == 0) { - $this->upload_info .= "$time $text\r\n"; + $log_path = $this->get_log_file(); - // Returns the number of the current line - $currentLine = substr_count($this->upload_info, "\n") - 1; - return $currentLine; - } + file_put_contents($log_path, $time . " " . $message); + } - // else if addon function is used, select the line & modify it - $lines = substr($info, "\n"); // Seperate the string to array in lines - $lines[$addon] = "$lines[$addon] $text"; // Add the content to the line - $this->upload_info = implode("\n", $lines); // Put string back together & update - - return $addon; // Return line number + private function get_log_file(): string + { + return join_path(CronUploaderConfig::get_dir(), "uploads.log"); } /** @@ -452,18 +499,6 @@ class CronUploader extends Extension // Display message $page->set_mode(PageMode::DATA); $page->set_type("text/plain"); - $page->set_data($this->upload_info); - - // Save log - $log_path = $this->root_dir . "/uploads.log"; - - if (file_exists($log_path)) { - $prev_content = file_get_contents($log_path); - } else { - $prev_content = ""; - } - - $content = $prev_content . "\r\n" . $this->upload_info; - file_put_contents($log_path, $content); + $page->set_data(implode("\r\n", $this->output_buffer)); } } diff --git a/ext/cron_uploader/style.css b/ext/cron_uploader/style.css new file mode 100644 index 00000000..2643a6a1 --- /dev/null +++ b/ext/cron_uploader/style.css @@ -0,0 +1,3 @@ +table.log th { + width: 200px; +} \ No newline at end of file diff --git a/ext/cron_uploader/theme.php b/ext/cron_uploader/theme.php new file mode 100644 index 00000000..0b53dbf8 --- /dev/null +++ b/ext/cron_uploader/theme.php @@ -0,0 +1,132 @@ +Information +
+ + " . ($running ? "" : "") . " + + + + + + + + + + + + + + + + + + + + +
Cron upload is currently running
DirectoryFilesSize (MB)Directory Path
Queue{$queue_dirinfo['total_files']}{$queue_dirinfo['total_mb']}{$queue_dirinfo['path']}
Uploaded{$uploaded_dirinfo['total_files']}{$uploaded_dirinfo['total_mb']}{$uploaded_dirinfo['path']}
Failed{$failed_dirinfo['total_files']}{$failed_dirinfo['total_mb']}{$failed_dirinfo['path']}
+ +
Cron Command:
+ Create a cron job with the command above.
+ Read the documentation if you're not sure what to do.
"; + + $install_html = " + This cron uploader is fairly easy to use but has to be configured first. +
    +
  1. Install & activate this plugin.
  2. +
  3. Go to the Board Config and change any settings to match your preference.
  4. +
  5. Copy the cron command above.
  6. +
  7. Create a cron job or something else that can open a url on specified times. +
    cron is a service that runs commands over and over again on a a schedule. You can set up cron (or any similar tool) to run the command above to trigger the import on whatever schedule you desire. +
    If you're not sure how to do this, you can give the command to your web host and you can ask them to create the cron job for you. +
    When you create the cron job, you choose when to upload new images.
  8. +
"; + + $usage_html = "Upload your images you want to be uploaded to the queue directory using your FTP client or other means. +
({$queue_dirinfo['path']}) +
    +
  1. Any sub-folders will be turned into tags.
  2. +
  3. If the file name matches \"## - tag1 tag2.png\" the tags will be used.
  4. +
  5. If both are found, they will all be used.
  6. +
  7. The character \";\" will be changed into \":\" in any tags.
  8. +
  9. You can inherit categories by creating a folder that ends with \";\". For instance category;\\tag1 would result in the tag category:tag1. This allows creating a category folder, then creating many subfolders that will use that category.
  10. +
+ The cron uploader works by importing files from the queue folder whenever this url is visited: +
$cron_url
+ + + "; + + $page->set_title("Cron Uploader"); + $page->set_heading("Cron Uploader"); + + $block = new Block("Cron Uploader", $info_html, "main", 10); + $block_install = new Block("Setup Guide", $install_html, "main", 30); + $block_usage= new Block("Usage Guide", $usage_html, "main", 20); + $page->add_block($block); + $page->add_block($block_install); + $page->add_block($block_usage); + + if (!empty($log_entries)) { + $log_html = ""; + foreach ($log_entries as $entry) { + $log_html .= ""; + } + $log_html .= "
{$entry["date_sent"]}{$entry["message"]}
"; + $block = new Block("Log", $log_html, "main", 40); + $page->add_block($block); + } + } + + public function display_form(array $failed_dirs) + { + global $page, $database; + + $link = make_http(make_link("cron_upload")); + $html = "Cron uploader documentation"; + + $html .= make_form(make_link("admin/cron_uploader_restage")); + $html .= ""; + $html .= ""; + $html .= ""; + $html .= "
Failed dir
"; + + $html .= make_form(make_link("admin/cron_uploader_clear_queue"), "POST", false, "", "return confirm('Are you sure you want to delete everything in the queue folder?');") + ."
" + ."
"; + $html .= make_form(make_link("admin/cron_uploader_clear_uploaded"), "POST", false, "", "return confirm('Are you sure you want to delete everything in the uploaded folder?');") + ."
" + ."
"; + $html .= make_form(make_link("admin/cron_uploader_clear_failed"), "POST", false, "", "return confirm('Are you sure you want to delete everything in the failed folder?');") + ."
" + ."
"; + $html .= "\n"; + $page->add_block(new Block("Cron Upload", $html)); + } +} diff --git a/ext/custom_html_headers/info.php b/ext/custom_html_headers/info.php index 5ac483d3..617ce791 100644 --- a/ext/custom_html_headers/info.php +++ b/ext/custom_html_headers/info.php @@ -1,14 +1,5 @@ - * Link: http://www.drudexsoftware.com - * License: GPLv2 - * Description: Allows admins to modify & set custom <head> content - * Documentation: - * - */ class CustomHtmlHeadersInfo extends ExtensionInfo { public const KEY = "custom_html_headers"; diff --git a/ext/danbooru_api/info.php b/ext/danbooru_api/info.php index 89047d3b..fd68c229 100644 --- a/ext/danbooru_api/info.php +++ b/ext/danbooru_api/info.php @@ -1,13 +1,5 @@ -Description: Allow Danbooru apps like Danbooru Uploader for Firefox to communicate with Shimmie -Documentation: - -*/ - class DanbooruApiInfo extends ExtensionInfo { public const KEY = "danbooru_api"; diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index aa0ec9a7..dfbce69a 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -91,7 +91,9 @@ class DanbooruApi extends Extension $namelist = explode(",", $_GET['name']); foreach ($namelist as $name) { $sqlresult = $database->get_all( - "SELECT id,tag,count FROM tags WHERE tag = ?", + $database->scoreql_to_sql( + "SELECT id,tag,count FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(?)" + ), [$name] ); foreach ($sqlresult as $row) { diff --git a/ext/downtime/info.php b/ext/downtime/info.php index fd8df943..671af143 100644 --- a/ext/downtime/info.php +++ b/ext/downtime/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Show a "down for maintenance" page - * Documentation: - * - */ - class DowntimeInfo extends ExtensionInfo { public const KEY = "downtime"; diff --git a/ext/downtime/theme.php b/ext/downtime/theme.php index 99c4cffc..1399dbbb 100644 --- a/ext/downtime/theme.php +++ b/ext/downtime/theme.php @@ -66,6 +66,6 @@ class DowntimeTheme extends Themelet EOD -); + ); } } diff --git a/ext/emoticons/info.php b/ext/emoticons/info.php index 8c19165d..e2f44cdd 100644 --- a/ext/emoticons/info.php +++ b/ext/emoticons/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Lets users use graphical smilies - * Documentation: - * - */ - class EmoticonsInfo extends ExtensionInfo { public const KEY = "emoticons"; diff --git a/ext/et/info.php b/ext/et/info.php index 3245f10c..297647aa 100644 --- a/ext/et/info.php +++ b/ext/et/info.php @@ -1,13 +1,5 @@ - * License: GPLv2 - * Description: Show various bits of system information - * Documentation: - */ - class ETInfo extends ExtensionInfo { public const KEY = "et"; diff --git a/ext/ext_manager/baseline_open_in_new_black_18dp.png b/ext/ext_manager/baseline_open_in_new_black_18dp.png new file mode 100644 index 00000000..48a6da8e Binary files /dev/null and b/ext/ext_manager/baseline_open_in_new_black_18dp.png differ diff --git a/ext/ext_manager/info.php b/ext/ext_manager/info.php index 80ade6e9..6c979d5e 100644 --- a/ext/ext_manager/info.php +++ b/ext/ext_manager/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Visibility: admin - * Description: A thing for point & click extension management - * Documentation: - */ - class ExtManagerInfo extends ExtensionInfo { public const KEY = "ext_manager"; diff --git a/ext/ext_manager/theme.php b/ext/ext_manager/theme.php index 27cc896b..173e2e56 100644 --- a/ext/ext_manager/theme.php +++ b/ext/ext_manager/theme.php @@ -37,7 +37,7 @@ class ExtManagerTheme extends Themelet //baseline_open_in_new_black_18dp.png $h_enabled_box = $editable ? "" : ""; - $h_docs = ($extension->documentation ? "â– " : ""); //TODO: A proper "docs" symbol would be preferred here. + $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. $html .= " diff --git a/ext/favorites/info.php b/ext/favorites/info.php index 0e49b8d1..7d6ab844 100644 --- a/ext/favorites/info.php +++ b/ext/favorites/info.php @@ -1,13 +1,5 @@ - * License: GPLv2 - * Description: Allow users to favorite images - * Documentation: - */ - class FavoritesInfo extends ExtensionInfo { public const KEY = "favorites"; diff --git a/ext/favorites/main.php b/ext/favorites/main.php index cd246b32..66200eaf 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -167,6 +167,45 @@ class Favorites extends Extension } } + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + { + global $user; + + if (!$user->is_anonymous()) { + $event->add_action("bulk_favorite", "Favorite"); + $event->add_action("bulk_unfavorite", "Un-Favorite"); + } + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user; + + switch ($event->action) { + case "bulk_favorite": + if (!$user->is_anonymous()) { + $total = 0; + foreach ($event->items as $image) { + send_event(new FavoriteSetEvent($image->id, $user, true)); + $total++; + } + flash_message("Added $total items to favorites"); + } + break; + case "bulk_unfavorite": + if (!$user->is_anonymous()) { + $total = 0; + foreach ($event->items as $image) { + send_event(new FavoriteSetEvent($image->id, $user, false)); + $total++; + } + flash_message("Removed $total items from favorites"); + } + break; + } + } + + private function install() { global $database; @@ -203,10 +242,12 @@ class Favorites extends Extension { global $database; if ($do_set) { - $database->Execute( - "INSERT INTO user_favorites(image_id, user_id, created_at) VALUES(:image_id, :user_id, NOW())", - ["image_id"=>$image_id, "user_id"=>$user_id] - ); + if (!$database->exists("select 1 from user_favorites where image_id=:image_id and user_id=:user_id", ["image_id"=>$image_id, "user_id"=>$user_id])) { + $database->Execute( + "INSERT INTO user_favorites(image_id, user_id, created_at) VALUES(:image_id, :user_id, NOW())", + ["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", diff --git a/ext/featured/info.php b/ext/featured/info.php index b28277e1..c677f582 100644 --- a/ext/featured/info.php +++ b/ext/featured/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Bring a specific image to the users' attentions - * Documentation: - * - */ - class FeaturedInfo extends ExtensionInfo { public const KEY = "featured"; diff --git a/ext/forum/info.php b/ext/forum/info.php index 5671e8b2..a7d5a72d 100644 --- a/ext/forum/info.php +++ b/ext/forum/info.php @@ -1,14 +1,5 @@ - * Alpha - * License: GPLv2 - * Description: Rough forum extension - * Documentation: - */ - class ForumInfo extends ExtensionInfo { public const KEY = "dorum"; diff --git a/ext/forum/main.php b/ext/forum/main.php index e4c60df4..a1c5d410 100644 --- a/ext/forum/main.php +++ b/ext/forum/main.php @@ -269,7 +269,7 @@ class Forum extends Extension "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", ["limit"=>$threadsPerPage, "offset"=>$pageNumber * $threadsPerPage] - ); + ); $this->theme->display_thread_list($page, $threads, $showAdminOptions, $pageNumber + 1, $totalPages); } @@ -302,7 +302,7 @@ class Forum extends Extension "ORDER BY p.date ASC ". "LIMIT :limit OFFSET :offset", ["thread_id"=>$threadID, "offset"=>$pageNumber * $postsPerPage, "limit"=>$postsPerPage] - ); + ); $this->theme->display_thread($posts, $showAdminOptions, $threadTitle, $threadID, $pageNumber + 1, $totalPages); } diff --git a/ext/google_analytics/info.php b/ext/google_analytics/info.php index a62cc880..f81d4fde 100644 --- a/ext/google_analytics/info.php +++ b/ext/google_analytics/info.php @@ -1,14 +1,5 @@ - * Link: http://drudexsoftware.com - * License: GPLv2 - * Description: Integrates Google Analytics tracking - * Documentation: - * - */ class GoogleAnalyticsInfo extends ExtensionInfo { public const KEY = "google_analytics"; diff --git a/ext/handle_404/info.php b/ext/handle_404/info.php index f18367c8..f32d74cd 100644 --- a/ext/handle_404/info.php +++ b/ext/handle_404/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Visibility: admin - * Description: If no other extension puts anything onto the page, show 404 - */ class Handle404Info extends ExtensionInfo { public const KEY = "handle_404"; diff --git a/ext/handle_archive/info.php b/ext/handle_archive/info.php index 174e18d3..078d2001 100644 --- a/ext/handle_archive/info.php +++ b/ext/handle_archive/info.php @@ -1,13 +1,5 @@ - * Description: Allow users to upload archives (zip, etc) - * Documentation: - * - */ - class ArchiveFileHandlerInfo extends ExtensionInfo { public const KEY = "handle_archive"; diff --git a/ext/handle_flash/info.php b/ext/handle_flash/info.php index 9d997aaa..642cf64c 100644 --- a/ext/handle_flash/info.php +++ b/ext/handle_flash/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle Flash files. - */ - class FlashFileHandlerInfo extends ExtensionInfo { public const KEY = "handle_flash"; diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 9b738ee6..fe63c723 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -13,6 +13,7 @@ class FlashFileHandler extends DataHandlerExtension if (!$info) { return null; } + $event->image = false; $event->width = $info[0]; $event->height = $info[1]; diff --git a/ext/handle_ico/info.php b/ext/handle_ico/info.php index 64ba9984..90419d79 100644 --- a/ext/handle_ico/info.php +++ b/ext/handle_ico/info.php @@ -1,11 +1,5 @@ - * Description: Handle windows icons - */ - class IcoFileHandlerInfo extends ExtensionInfo { public const KEY = "handle_ico"; diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 70d9b753..2975f529 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -11,6 +11,8 @@ class IcoFileHandler extends DataHandlerExtension $event->lossless = true; $event->video = false; $event->audio = false; + $event->image = ($event->ext!="ani"); + $fp = fopen($event->file_name, "r"); try { diff --git a/ext/handle_mp3/info.php b/ext/handle_mp3/info.php index df17f2ad..9bf9093b 100644 --- a/ext/handle_mp3/info.php +++ b/ext/handle_mp3/info.php @@ -1,11 +1,5 @@ - * Description: Handle MP3 files - */ - class MP3FileHandlerInfo extends ExtensionInfo { public const KEY = "handle_mp3"; diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 53987eea..2af95d19 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -9,6 +9,7 @@ class MP3FileHandler extends DataHandlerExtension $event->audio = true; $event->video = false; $event->lossless = false; + $event->image = false; break; } // TODO: Buff out audio format support, length scanning diff --git a/ext/handle_pixel/info.php b/ext/handle_pixel/info.php index 2dc2d17c..03095f7c 100644 --- a/ext/handle_pixel/info.php +++ b/ext/handle_pixel/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle JPEG, PNG, GIF, WEBP, etc files - */ - class PixelFileHandlerInfo extends ExtensionInfo { public const KEY = "handle_pixel"; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 3016e8e3..31d38d53 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -29,6 +29,7 @@ class PixelFileHandler extends DataHandlerExtension $event->video = false; break; } + $event->image = !$event->video; $info = getimagesize($event->file_name); if (!$info) { diff --git a/ext/handle_static/info.php b/ext/handle_static/info.php index 1ba6ea9c..14a61a34 100644 --- a/ext/handle_static/info.php +++ b/ext/handle_static/info.php @@ -1,14 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Visibility: admin - * Description: If Shimmie can't handle a request, check static files ($theme/static/$filename, then ext/handle_static/static/$filename) - */ - class HandleStaticInfo extends ExtensionInfo { public const KEY = "handle_static"; diff --git a/ext/handle_svg/info.php b/ext/handle_svg/info.php index 68e59cb8..50acfdce 100644 --- a/ext/handle_svg/info.php +++ b/ext/handle_svg/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle static SVG files. - */ - class SVGFileHandlerInfo extends ExtensionInfo { public const KEY = "handle_svg"; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 4e8a173d..fc45626d 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -10,6 +10,7 @@ class SVGFileHandler extends DataHandlerExtension $event->lossless = true; $event->video = false; $event->audio = false; + $event->image = true; $msp = new MiniSVGParser($event->file_name); $event->width = $msp->width; diff --git a/ext/handle_video/info.php b/ext/handle_video/info.php index 52c357c3..7c7ad713 100644 --- a/ext/handle_video/info.php +++ b/ext/handle_video/info.php @@ -1,14 +1,5 @@ - * Modified By: Shish , jgen , im-mi - * License: GPLv2 - * Description: Handle FLV, MP4, OGV and WEBM video files. - * Documentation: - */ - class VideoFileHandlerInfo extends ExtensionInfo { public const KEY = "handle_video"; diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index fbd98b65..d5220d31 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -38,6 +38,7 @@ class VideoFileHandler extends DataHandlerExtension { if (in_array($event->ext, self::SUPPORTED_EXT)) { $event->video = true; + $event->image = false; try { $data = Media::get_ffprobe_data($event->file_name); diff --git a/ext/help_pages/info.php b/ext/help_pages/info.php index 58845be2..1375ef51 100644 --- a/ext/help_pages/info.php +++ b/ext/help_pages/info.php @@ -1,11 +1,5 @@ - * Description: Provides documentation screens - */ - class HelpPagesInfo extends ExtensionInfo { public const KEY = "help_pages"; diff --git a/ext/help_pages/main.php b/ext/help_pages/main.php index ac7f4cf1..8238f875 100644 --- a/ext/help_pages/main.php +++ b/ext/help_pages/main.php @@ -33,22 +33,38 @@ class HelpPages extends Extension { public const SEARCH = "search"; + private $pages; + + private function get_pages(): array + { + if ($this->pages==null) { + $e = new HelpPageListBuildingEvent(); + send_event($e); + $this->pages = $e->pages; + } + return $this->pages; + } + public function onPageRequest(PageRequestEvent $event) { global $page; - if ($event->page_matches("help")) { - $e = new HelpPageListBuildingEvent(); - send_event($e); - $page->set_mode(PageMode::PAGE); + $pages = $this->get_pages(); + if ($event->page_matches("help")) { if ($event->count_args() == 0) { - $this->theme->display_list_page($e->pages); + $name = array_key_first($pages); + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("help/".$name)); + return; } else { + $page->set_mode(PageMode::PAGE); $name = $event->get_arg(0); $title = $name; - if (array_key_exists($name, $e->pages)) { - $title = $e->pages[$name]; + if (array_key_exists($name, $pages)) { + $title = $pages[$name]; + } else { + return; } $this->theme->display_help_page($title); @@ -77,6 +93,16 @@ class HelpPages extends Extension $event->add_nav_link("help", new Link('help'), "Help"); } + public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) + { + if ($event->parent=="help") { + $pages = $this->get_pages(); + foreach ($pages as $key=>$value) { + $event->add_nav_link("help_".$key, new Link('help/'.$key), $value); + } + } + } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) { $event->add_link("Help", make_link("help")); diff --git a/ext/holiday/info.php b/ext/holiday/info.php index 92bcd521..6f291436 100644 --- a/ext/holiday/info.php +++ b/ext/holiday/info.php @@ -1,12 +1,5 @@ - * Link: http://www.codeanimu.net - * License: GPLv2 - * Description: Use an additional stylesheet on certain holidays. - */ class HolidayInfo extends ExtensionInfo { public const KEY = "holiday"; diff --git a/ext/home/info.php b/ext/home/info.php index 4d248371..84087de8 100644 --- a/ext/home/info.php +++ b/ext/home/info.php @@ -1,15 +1,5 @@ -* License: GPLv2 -* Visibility: admin -* Description: Displays a front page with logo, search box and image count -* Documentation: -* -*/ - class HomeInfo extends ExtensionInfo { public const KEY = "home"; diff --git a/ext/home/theme.php b/ext/home/theme.php index 8dfc666d..d0ec1f2d 100644 --- a/ext/home/theme.php +++ b/ext/home/theme.php @@ -22,7 +22,7 @@ class HomeTheme extends Themelet EOD -); + ); } public function build_body(string $sitename, string $main_links, string $main_text, string $contact_link, $num_comma, string $counter_text) diff --git a/ext/image/info.php b/ext/image/info.php index 8dee38fd..30ad53bd 100644 --- a/ext/image/info.php +++ b/ext/image/info.php @@ -1,14 +1,5 @@ - * Modified by: jgen - * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle the image database - * Visibility: admin - */ - class ImageIOInfo extends ExtensionInfo { public const KEY = "image"; diff --git a/ext/image/main.php b/ext/image/main.php index 2dc8f72b..941e7492 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -159,8 +159,6 @@ class ImageIO extends Extension $event->panel->add_block($sb); } - - // add image {{{ private function add_image(ImageAdditionEvent $event) { global $user, $database, $config; @@ -237,9 +235,7 @@ class ImageIO extends Extension log_warning("add_image", "Error while running update_image_media_properties: ".$e->getMessage()); } } - // }}} end add - // fetch image {{{ private function send_file(int $image_id, string $type) { global $config; @@ -298,9 +294,7 @@ class ImageIO extends Extension )); } } - // }}} end fetch - // replace image {{{ private function replace_image(int $id, Image $image) { global $database; @@ -362,5 +356,4 @@ class ImageIO extends Extension log_info("image", "Replaced Image #{$id} with ({$image->hash})"); } - // }}} end replace -} // end of class ImageIO +} diff --git a/ext/image_hash_ban/info.php b/ext/image_hash_ban/info.php index d165286f..a3080331 100644 --- a/ext/image_hash_ban/info.php +++ b/ext/image_hash_ban/info.php @@ -1,15 +1,5 @@ - * Link: http://atravelinggeek.com/ - * License: GPLv2 - * Description: Ban images based on their hash - * Based on the ResolutionLimit and IPban extensions by Shish - * Version 0.1, October 21, 2007 - */ - class ImageBanInfo extends ExtensionInfo { public const KEY = "image_hash_ban"; diff --git a/ext/image_hash_ban/main.php b/ext/image_hash_ban/main.php index 33ff65d5..0617b503 100644 --- a/ext/image_hash_ban/main.php +++ b/ext/image_hash_ban/main.php @@ -1,6 +1,5 @@ hash = $hash; } } -// }}} -// AddImageHashBanEvent {{{ + class AddImageHashBanEvent extends Event { public $hash; @@ -23,7 +21,7 @@ class AddImageHashBanEvent extends Event $this->reason = $reason; } } -// }}} + class ImageBan extends Extension { public function onInitExt(InitExtEvent $event) diff --git a/ext/image_hash_ban/theme.php b/ext/image_hash_ban/theme.php index 53bf6139..5a3afd99 100644 --- a/ext/image_hash_ban/theme.php +++ b/ext/image_hash_ban/theme.php @@ -1,14 +1,4 @@ - * Link: http://atravelinggeek.com/ - * License: GPLv2 - * Description: Ban images based on their hash - * Based on the ResolutionLimit and IPban extensions by Shish - * Version 0.1 - * October 21, 2007 - */ class ImageBanTheme extends Themelet { diff --git a/ext/image_view_counter/info.php b/ext/image_view_counter/info.php index 2cea0069..e9dede91 100644 --- a/ext/image_view_counter/info.php +++ b/ext/image_view_counter/info.php @@ -1,14 +1,5 @@ - * Link: http://www.drudexsoftware.com/ - * License: GPLv2 - * Description: Tracks & displays how many times an image is viewed - * Documentation: - * - */ class ImageViewCounterInfo extends ExtensionInfo { public const KEY = "image_view_counter"; diff --git a/ext/index/config.php b/ext/index/config.php new file mode 100644 index 00000000..91329926 --- /dev/null +++ b/ext/index/config.php @@ -0,0 +1,11 @@ +term = $term; + $this->context = $context; + } + + public function is_querylet_set(): bool + { + return (count($this->querylets) > 0); + } + + public function get_querylets(): array + { + return $this->querylets; + } + + public function add_querylet(Querylet $q) + { + $this->querylets[] = $q; + } +} + +class SearchTermParseException extends SCoreException +{ +} + +class PostListBuildingEvent extends Event +{ + /** @var array */ + public $search_terms = []; + + /** @var array */ + public $parts = []; + + /** + * #param string[] $search + */ + public function __construct(array $search) + { + $this->search_terms = $search; + } + + public function add_control(string $html, int $position=50) + { + while (isset($this->parts[$position])) { + $position++; + } + $this->parts[$position] = $html; + } +} diff --git a/ext/index/info.php b/ext/index/info.php index 9987f569..6f46c2ef 100644 --- a/ext/index/info.php +++ b/ext/index/info.php @@ -1,160 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Show a list of uploaded images - * Documentation: - * Here is a list of the search methods available out of the box; - * Shimmie extensions may provide other filters: - *
    - *
  • by tag, eg - *
      - *
    • cat - *
    • pie - *
    • somethi* -- wildcards are supported - *
    - *
  • size (=, <, >, <=, >=) width x height, eg - *
      - *
    • size=1024x768 -- a specific wallpaper size - *
    • size>=500x500 -- no small images - *
    • size<1000x1000 -- no large images - *
    - *
  • width (=, <, >, <=, >=) width, eg - *
      - *
    • width=1024 -- find images with 1024 width - *
    • width>2000 -- find images bigger than 2000 width - *
    - *
  • height (=, <, >, <=, >=) height, eg - *
      - *
    • height=768 -- find images with 768 height - *
    • height>1000 -- find images bigger than 1000 height - *
    - *
  • ratio (=, <, >, <=, >=) width : height, eg - *
      - *
    • ratio=4:3, ratio=16:9 -- standard wallpaper - *
    • ratio=1:1 -- square images - *
    • ratio<1:1 -- tall images - *
    • ratio>1:1 -- wide images - *
    - *
  • filesize (=, <, >, <=, >=) size, eg - *
      - *
    • filesize>1024 -- no images under 1KB - *
    • filesize<=3MB -- shorthand filesizes are supported too - *
    - *
  • id (=, <, >, <=, >=) number, eg - *
      - *
    • id<20 -- search only the first few images - *
    • id>=500 -- search later images - *
    - *
  • user=Username & poster=Username, eg - *
      - *
    • user=Shish -- find all of Shish's posts - *
    • poster=Shish -- same as above - *
    - *
  • user_id=userID & poster_id=userID, eg - *
      - *
    • user_id=2 -- find all posts by user id 2 - *
    • poster_id=2 -- same as above - *
    - *
  • hash=md5sum & md5=md5sum, eg - *
      - *
    • hash=bf5b59173f16b6937a4021713dbfaa72 -- find the "Taiga want up!" image - *
    • md5=bf5b59173f16b6937a4021713dbfaa72 -- same as above - *
    - *
  • filetype=type & ext=type, eg - *
      - *
    • filetype=png -- find all PNG images - *
    • ext=png -- same as above - *
    - *
  • filename=blah & name=blah, eg - *
      - *
    • filename=kitten -- find all images with "kitten" in the original filename - *
    • name=kitten -- same as above - *
    - *
  • posted (=, <, >, <=, >=) date, eg - *
      - *
    • posted>=2009-12-25 posted<=2010-01-01 -- find images posted between christmas and new year - *
    - *
  • tags (=, <, >, <=, >=) count, eg - *
      - *
    • tags=1 -- search for images with only 1 tag - *
    • tags>=10 -- search for images with 10 or more tags - *
    • tags<25 -- search for images with less than 25 tags - *
    - *
  • source=(URL, any, none) eg - *
      - *
    • source=http://example.com -- find all images with "http://example.com" in the source - *
    • source=any -- find all images with a source - *
    • source=none -- find all images without a source - *
    - *
  • order=(id, width, height, filesize, filename)_(ASC, DESC), eg - *
      - *
    • order=width -- find all images sorted from highest > lowest width - *
    • order=filesize_asc -- find all images sorted from lowest > highest filesize - *
    - *
  • order=random_####, eg - *
      - *
    • order=random_8547 -- find all images sorted randomly using 8547 as a seed - *
    - *
- *

Search items can be combined to search for images which match both, - * or you can stick "-" in front of an item to search for things that don't - * match it. - *

Metatags can be followed by ":" rather than "=" if you prefer. - *
I.E: "posted:2014-01-01", "id:>=500" etc. - *

Some search methods provided by extensions: - *

    - *
  • Numeric Score - *
      - *
    • score (=, <, >, <=, >=) number -- seach by score - *
    • upvoted_by=Username -- search for a user's likes - *
    • downvoted_by=Username -- search for a user's dislikes - *
    • upvoted_by_id=UserID -- search for a user's likes by user ID - *
    • downvoted_by_id=UserID -- search for a user's dislikes by user ID - *
    • order=score_(ASC, DESC) -- find all images sorted from by score - *
    - *
  • Image Rating - *
      - *
    • rating=se -- find safe and explicit images, ignore questionable and unknown - *
    - *
  • Favorites - *
      - *
    • favorites (=, <, >, <=, >=) number -- search for images favourited a certain number of times - *
    • favourited_by=Username -- search for a user's choices by username - *
    • favorited_by_userno=UserID -- search for a user's choice by userID - *
    - *
  • Notes - *
      - *
    • notes (=, <, >, <=, >=) number -- search by the number of notes an image has - *
    • notes_by=Username -- search for images containing notes created by username - *
    • notes_by_userno=UserID -- search for images containing notes created by userID - *
    - *
  • Artists - *
      - *
    • author=ArtistName -- search for images by artist - *
    - *
  • Image Comments - *
      - *
    • comments (=, <, >, <=, >=) number -- search for images by number of comments - *
    • commented_by=Username -- search for images containing user's comments by username - *
    • commented_by_userno=UserID -- search for images containing user's comments by userID - *
    - *
  • Pools - *
      - *
    • pool=(PoolID, any, none) -- search for images in a pool by PoolID. - *
    • pool_by_name=PoolName -- search for images in a pool by PoolName. underscores are replaced with spaces - *
    - *
  • Post Relationships - *
      - *
    • parent=(parentID, any, none) -- search for images by parentID / if they have, do not have a parent - *
    • child=(any, none) -- search for images which have, or do not have children - *
    - *
- */ - class IndexInfo extends ExtensionInfo { public const KEY = "index"; @@ -166,4 +11,151 @@ class IndexInfo extends ExtensionInfo public $license = self::LICENSE_GPLV2; public $description = "Show a list of uploaded images"; public $core = true; + public $documentation = "Here is a list of the search methods available out of the box; +Shimmie extensions may provide other filters: +
    +
  • by tag, eg +
      +
    • cat +
    • pie +
    • somethi* -- wildcards are supported +
    +
  • size (=, <, >, <=, >=) width x height, eg +
      +
    • size=1024x768 -- a specific wallpaper size +
    • size>=500x500 -- no small images +
    • size<1000x1000 -- no large images +
    +
  • width (=, <, >, <=, >=) width, eg +
      +
    • width=1024 -- find images with 1024 width +
    • width>2000 -- find images bigger than 2000 width +
    +
  • height (=, <, >, <=, >=) height, eg +
      +
    • height=768 -- find images with 768 height +
    • height>1000 -- find images bigger than 1000 height +
    +
  • ratio (=, <, >, <=, >=) width : height, eg +
      +
    • ratio=4:3, ratio=16:9 -- standard wallpaper +
    • ratio=1:1 -- square images +
    • ratio<1:1 -- tall images +
    • ratio>1:1 -- wide images +
    +
  • filesize (=, <, >, <=, >=) size, eg +
      +
    • filesize>1024 -- no images under 1KB +
    • filesize<=3MB -- shorthand filesizes are supported too +
    +
  • id (=, <, >, <=, >=) number, eg +
      +
    • id<20 -- search only the first few images +
    • id>=500 -- search later images +
    +
  • user=Username & poster=Username, eg +
      +
    • user=Shish -- find all of Shish's posts +
    • poster=Shish -- same as above +
    +
  • user_id=userID & poster_id=userID, eg +
      +
    • user_id=2 -- find all posts by user id 2 +
    • poster_id=2 -- same as above +
    +
  • hash=md5sum & md5=md5sum, eg +
      +
    • hash=bf5b59173f16b6937a4021713dbfaa72 -- find the \"Taiga want up!\" image +
    • md5=bf5b59173f16b6937a4021713dbfaa72 -- same as above +
    +
  • filetype=type & ext=type, eg +
      +
    • filetype=png -- find all PNG images +
    • ext=png -- same as above +
    +
  • filename=blah & name=blah, eg +
      +
    • filename=kitten -- find all images with \"kitten\" in the original filename +
    • name=kitten -- same as above +
    +
  • posted (=, <, >, <=, >=) date, eg +
      +
    • posted>=2009-12-25 posted<=2010-01-01 -- find images posted between christmas and new year +
    +
  • tags (=, <, >, <=, >=) count, eg +
      +
    • tags=1 -- search for images with only 1 tag +
    • tags>=10 -- search for images with 10 or more tags +
    • tags<25 -- search for images with less than 25 tags +
    +
  • source=(URL, any, none) eg +
      +
    • source=http://example.com -- find all images with \"http://example.com\" in the source +
    • source=any -- find all images with a source +
    • source=none -- find all images without a source +
    +
  • order=(id, width, height, filesize, filename)_(ASC, DESC), eg +
      +
    • order=width -- find all images sorted from highest > lowest width +
    • order=filesize_asc -- find all images sorted from lowest > highest filesize +
    +
  • order=random_####, eg +
      +
    • order=random_8547 -- find all images sorted randomly using 8547 as a seed +
    +
+

Search items can be combined to search for images which match both, +or you can stick \"-\" in front of an item to search for things that don't +match it. +

Metatags can be followed by \":\" rather than \"=\" if you prefer. +
I.E: \"posted:2014-01-01\", \"id:>=500\" etc. +

Some search methods provided by extensions: +

    +
  • Numeric Score +
      +
    • score (=, <, >, <=, >=) number -- seach by score +
    • upvoted_by=Username -- search for a user's likes +
    • downvoted_by=Username -- search for a user's dislikes +
    • upvoted_by_id=UserID -- search for a user's likes by user ID +
    • downvoted_by_id=UserID -- search for a user's dislikes by user ID +
    • order=score_(ASC, DESC) -- find all images sorted from by score +
    +
  • Image Rating +
      +
    • rating=se -- find safe and explicit images, ignore questionable and unknown +
    +
  • Favorites +
      +
    • favorites (=, <, >, <=, >=) number -- search for images favourited a certain number of times +
    • favourited_by=Username -- search for a user's choices by username +
    • favorited_by_userno=UserID -- search for a user's choice by userID +
    +
  • Notes +
      +
    • notes (=, <, >, <=, >=) number -- search by the number of notes an image has +
    • notes_by=Username -- search for images containing notes created by username +
    • notes_by_userno=UserID -- search for images containing notes created by userID +
    +
  • Artists +
      +
    • author=ArtistName -- search for images by artist +
    +
  • Image Comments +
      +
    • comments (=, <, >, <=, >=) number -- search for images by number of comments +
    • commented_by=Username -- search for images containing user's comments by username +
    • commented_by_userno=UserID -- search for images containing user's comments by userID +
    +
  • Pools +
      +
    • pool=(PoolID, any, none) -- search for images in a pool by PoolID. +
    • pool_by_name=PoolName -- search for images in a pool by PoolName. underscores are replaced with spaces +
    +
  • Post Relationships +
      +
    • parent=(parentID, any, none) -- search for images by parentID / if they have, do not have a parent +
    • child=(any, none) -- search for images which have, or do not have children +
    +
+"; } diff --git a/ext/index/main.php b/ext/index/main.php index a5a23764..2bda7b09 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -1,68 +1,7 @@ term = $term; - $this->context = $context; - } - - public function is_querylet_set(): bool - { - return (count($this->querylets) > 0); - } - - public function get_querylets(): array - { - return $this->querylets; - } - - public function add_querylet(Querylet $q) - { - $this->querylets[] = $q; - } -} - -class SearchTermParseException extends SCoreException -{ -} - -class PostListBuildingEvent extends Event -{ - /** @var array */ - public $search_terms = []; - - /** @var array */ - public $parts = []; - - /** - * #param string[] $search - */ - public function __construct(array $search) - { - $this->search_terms = $search; - } - - public function add_control(string $html, int $position=50) - { - while (isset($this->parts[$position])) { - $position++; - } - $this->parts[$position] = $html; - } -} +require_once "config.php"; +require_once "events.php"; class Index extends Extension { @@ -72,9 +11,9 @@ class Index extends Extension public function onInitExt(InitExtEvent $event) { global $config; - $config->set_default_int("index_images", 24); - $config->set_default_bool("index_tips", true); - $config->set_default_string("index_order", "id DESC"); + $config->set_default_int(IndexConfig::IMAGES, 24); + $config->set_default_bool(IndexConfig::TIPS, true); + $config->set_default_string(IndexConfig::ORDER, "id DESC"); } public function onPageRequest(PageRequestEvent $event) @@ -167,7 +106,7 @@ class Index extends Extension $sb->position = 20; $sb->add_label("Show "); - $sb->add_int_option("index_images"); + $sb->add_int_option(IndexConfig::IMAGES); $sb->add_label(" images on the post list"); $event->panel->add_block($sb); diff --git a/ext/ipban/info.php b/ext/ipban/info.php index 83e595f8..92605d22 100644 --- a/ext/ipban/info.php +++ b/ext/ipban/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Ban IP addresses - * Documentation: - * - */ - class IPBanInfo extends ExtensionInfo { public const KEY = "ipban"; diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 52b92fd9..9ebcca21 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -1,6 +1,5 @@ id = $id; } } -// }}} -// AddIPBanEvent {{{ + class AddIPBanEvent extends Event { public $ip; @@ -25,7 +23,6 @@ class AddIPBanEvent extends Event $this->end = trim($end); } } -// }}} class IPBan extends Extension { @@ -131,7 +128,6 @@ class IPBan extends Extension } } - // installer {{{ protected function install() { global $database; @@ -209,8 +205,7 @@ class IPBan extends Extension $config->set_int("ext_ipban_version", 8); } } - // }}} - // deal with banned person {{{ + private function check_ip_ban() { $remote = $_SERVER['REMOTE_ADDR']; @@ -266,8 +261,7 @@ class IPBan extends Extension log_error("ipban", "block($remote) called but no bans matched"); exit; } - // }}} - // database {{{ + private function get_bans() { global $database; @@ -328,5 +322,4 @@ class IPBan extends Extension $cache->set("ip_bans_sorted", $sorted, 600); return $sorted; } - // }}} } diff --git a/ext/link_image/info.php b/ext/link_image/info.php index cbc46255..1e97c2bc 100644 --- a/ext/link_image/info.php +++ b/ext/link_image/info.php @@ -1,11 +1,5 @@ - * Description: Show various forms of link to each image, for copy & paste - */ - class LinkImageInfo extends ExtensionInfo { public const KEY = "link_image"; @@ -14,4 +8,27 @@ class LinkImageInfo extends ExtensionInfo public $name = "Link to Image"; public $authors = ["Artanis"=>"artanis.00@gmail.com"]; public $description = "Show various forms of link to each image, for copy & paste"; + public $license = self::LICENSE_GPLV2; + public $documentation = "There is one option in Board Config: Text Link Format. +It takes the following arguments as well as plain text. + +
+|| arguments    || replacement                      ||
+|| \$id          || The image ID.                    ||
+|| \$hash        || The MD5 hash of the image.       ||
+|| \$tags        || The image's tag list.            ||
+|| \$base        || The base HREF as set in Config.  ||
+|| \$ext         || The image's extension.           ||
+|| \$size        || The image's display size.        ||
+|| \$filesize    || The image's size in KB.          ||
+|| \$filename    || The image's original filename.   ||
+|| \$title       || The site title as set in Config. ||
+
+ +

Link to Image will default this option to '\$title - \$id (\$ext \$size \$filesize)'. + +

To reset to the default, simply clear the current setting. Link to Image +will then fill in the default value after the save. + +

To leave the setting blank for any reason, leave a space (' ') in it."; } diff --git a/ext/link_image/readme.txt b/ext/link_image/readme.txt deleted file mode 100644 index 6051103a..00000000 --- a/ext/link_image/readme.txt +++ /dev/null @@ -1,82 +0,0 @@ -Link to Image adds BBCode and HTML link codes to the image view. Offers code for a customizable text link, thumbnail links, and full image inline. - -Author: Erik Youngren - -License: GPLv2 - -Submit a Bug Report or Suggestion for Link to Image: - * http://trac.shishnet.org/shimmie2/newticket?owner=artanis.00@gmail.com&component=third%20party%20extensions&keywords=link_to_image - -= Use = -There is one option in Board Config: Text Link Format. -It takes the following arguments as well as plain text. -|| arguments || replacement || -|| $id || The image ID. || -|| $hash || The MD5 hash of the image. || -|| $tags || The image's tag list. || -|| $base || The base HREF as set in Config. || -|| $ext || The image's extension. || -|| $size || The image's display size. || -|| $filesize || The image's size in KB. || -|| $filename || The image's original filename. || -|| $title || The site title as set in Config. || -Link to Image will default this option to '$title - $id ($ext $size $filesize)'. -To reset to the default, simply clear the current setting. Link to Image will then fill in the default value after the save. - -To leave the setting blank for any reason, leave a space (' ') in it. - -= Install = - 1. Copy the folder {{{contrib/link_image/}}} to {{{ext/}}}. - 2. In the Config panel, make sure Base URL is set (you may as well set Data URL while you're there, if you haven't already.) - 3. Make sure Image Link, Thumb Link, and Short Link all contain the full path ("http://" and onward,) either by using $base or plain text. Link to Image will not be able to retrieve the correct paths without these variables. - -= Change Log = -== Version 0.3.0 == - * Moved Link to Image over to the official theme engine. This functions basically the same as what the prototype was, but it's more thought out and nicer. - * Cleaned up the insides a bit. - -== Version 0.2.0 == - * Changed the HTML generation to use a prototype theme engine. All HTML generation is now contained within {{{link_image.html.php}}}, which may be copied to the current theme folder and edited from there. - -== Version 0.1.4 - 20070510 == - * Style changes. - * Added output containing only the locations of the thumb, image and post. - * Added a link to wikipedia's HTML page, just as BBCode has a wikipedia link. - -== Version 0.1.3b - 20070509 == - * Renamed style.css to _style.css to avoid the auto loader. - -== Version 0.1.3 - 20070508 == - * Created Readme.txt - * Merged 0.1.2 into 0.1.2b - * Removed uneeded documentation from main.php - * Rewrote the css to be unique. Previously used CSS I wrote for elsewhere. Styled to reduce space consumption. - * Added code to insert the CSS import. - * Updated Nice URLs to allow access to the /ext/ folder. (Why is my stylesheet returning HTML instead of CSS?) - * First SVN update. - -== Version 0.1.2b - 20070507 == -(fairly simultaneous with 0.1.2) - * shish: - * Updated to new extension format - * Created folder link_image in trunk/contrib - * Renamed link_image.ext.php to main.php and moved to /link_image/ - * Created style.css {{{ /* 404'd :|*/ }}}. - * Documentation (different from mine.) - * Changed add_text_option() and added add_label() in SetupBuildingEvent because I was using an edited version of the function that shish didn't know about. It was a wonder that didn't throw massive errors. - * Published on SVN. - -== Version 0.1.2 - 20070506 == - * Textboxes now select-all when they gain focus. - * Commenting and documentation. - -== Version 0.1.1 - 20070506 == - * Fixed HTML thumbnail link code. (image tag was being html_escaped twice, resulting in "$gt;" and "<" from the first escape becoming "&gt;" and "&lt;") It turns out that html_escape was completely unnecessary, all I had to do was replace the single-quotes around the attributes with escaped double-quotes ('\"'.) - -== Version 0.1.0 - 20070506 == - * Release. - -= Links = - * http://trac.shishnet.org/shimmie2/wiki/Contrib/Extensions/LinkToImage - Home - * http://forum.shishnet.org/viewtopic.php?p=153 - Discussion - * http://trac.shishnet.org/shimmie2/browser/trunk/contrib/link_image - Shimmie2 Trac SVN diff --git a/ext/livefeed/info.php b/ext/livefeed/info.php index 5aeb4ea4..e5a888fc 100644 --- a/ext/livefeed/info.php +++ b/ext/livefeed/info.php @@ -1,14 +1,5 @@ -* License: GPLv2 -* Visibility: admin -* Description: Logs user-safe (no IPs) data to a UDP socket, eg IRCCat -* Documentation: -*/ - class LiveFeedInfo extends ExtensionInfo { public const KEY = "livefeed"; diff --git a/ext/log_db/info.php b/ext/log_db/info.php index 0757b4df..177cef81 100644 --- a/ext/log_db/info.php +++ b/ext/log_db/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Keep a record of SCore events (in the database). - * Visibility: admin - */ - class LogDatabaseInfo extends ExtensionInfo { public const KEY = "log_db"; diff --git a/ext/log_db/main.php b/ext/log_db/main.php index 97958a15..c83b4dda 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -57,21 +57,21 @@ class LogDatabase extends Extension $args["time_end"] = $_GET["time-end"]; } if (!empty($_GET["module"])) { - $wheres[] = "section = :module"; + $wheres[] = $database->scoreql_to_sql("SCORE_STRNORM(section) = SCORE_STRNORM(:module)"); $args["module"] = $_GET["module"]; } if (!empty($_GET["user"])) { if ($database->get_driver_name() == DatabaseDriver::PGSQL) { if (preg_match("#\d+\.\d+\.\d+\.\d+(/\d+)?#", $_GET["user"])) { - $wheres[] = "(username = :user1 OR text(address) = :user2)"; + $wheres[] = $database->scoreql_to_sql("(SCORE_STRNORM(username) = SCORE_STRNORM(:user1) OR SCORE_STRNORM(text(address)) = SCORE_STRNORM(:user2))"); $args["user1"] = $_GET["user"]; $args["user2"] = $_GET["user"] . "/32"; } else { - $wheres[] = "lower(username) = lower(:user)"; + $wheres[] = $database->scoreql_to_sql("SCORE_STRNORM(username) = SCORE_STRNORM(:user)"); $args["user"] = $_GET["user"]; } } else { - $wheres[] = "(username = :user1 OR address = :user2)"; + $wheres[] = $database->scoreql_to_sql("(SCORE_STRNORM(username) = SCORE_STRNORM(:user1) OR SCORE_STRNORM(address) = SCORE_STRNORM(:user2))"); $args["user1"] = $_GET["user"]; $args["user2"] = $_GET["user"]; } diff --git a/ext/log_db/theme.php b/ext/log_db/theme.php index 8c1356fc..94d656b8 100644 --- a/ext/log_db/theme.php +++ b/ext/log_db/theme.php @@ -31,7 +31,7 @@ class LogDatabaseTheme extends Themelet - + ".make_form("log/view", "GET")." @@ -40,11 +40,11 @@ class LogDatabaseTheme extends Themelet @@ -53,7 +53,7 @@ class LogDatabaseTheme extends Themelet \n"; reset($events); // rewind to first element in array. - + foreach ($events as $event) { $c = $this->pri_to_col($event['priority']); $table .= ""; diff --git a/ext/log_logstash/info.php b/ext/log_logstash/info.php index 6f35d526..d3413048 100644 --- a/ext/log_logstash/info.php +++ b/ext/log_logstash/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Send log events to a network port. - * Visibility: admin - */ - class LogLogstashInfo extends ExtensionInfo { public const KEY = "log_logstash"; diff --git a/ext/log_net/info.php b/ext/log_net/info.php index a053a994..7aa660d5 100644 --- a/ext/log_net/info.php +++ b/ext/log_net/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Send log events to a network port. - * Visibility: admin - */ - class LogNetInfo extends ExtensionInfo { public const KEY = "log_net"; diff --git a/ext/mail/info.php b/ext/mail/info.php index bb370eae..b5c1044d 100644 --- a/ext/mail/info.php +++ b/ext/mail/info.php @@ -1,13 +1,5 @@ - * Link: http://seemslegit.com - * License: GPLv2 - * Description: Provides an interface for sending and receiving mail. - */ - class MailInfo extends ExtensionInfo { public const KEY = "mail"; diff --git a/ext/media/config.php b/ext/media/config.php new file mode 100644 index 00000000..99a18aab --- /dev/null +++ b/ext/media/config.php @@ -0,0 +1,9 @@ +engine = $engine; + $this->input_path = $input_path; + $this->input_type = $input_type; + $this->output_path = $output_path; + $this->target_height = $target_height; + $this->target_width = $target_width; + $this->target_format = $target_format; + $this->target_quality = $target_quality; + $this->minimize = $minimize; + $this->ignore_aspect_ratio = $ignore_aspect_ratio; + $this->allow_upscale = $allow_upscale; + } +} + +class MediaCheckPropertiesEvent extends Event +{ + public $file_name; + public $ext; + public $lossless = null; + public $audio = null; + public $video = null; + public $image = null; + public $length = null; + public $height = null; + public $width = null; + + public function __construct(string $file_name, string $ext) + { + $this->file_name = $file_name; + $this->ext = $ext; + } +} diff --git a/ext/media/info.php b/ext/media/info.php index abafffbe..3a514557 100644 --- a/ext/media/info.php +++ b/ext/media/info.php @@ -1,11 +1,5 @@ - * Description: Provides common functions and settings used for media operations. - */ - class MediaInfo extends ExtensionInfo { public const KEY = "media"; diff --git a/ext/media/main.php b/ext/media/main.php index d862f120..ea7c0d81 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -1,149 +1,16 @@ [ - "gif", - "jpg", - "png", - "webp", - Media::WEBP_LOSSY, - ], - MediaEngine::IMAGICK => [ - "gif", - "jpg", - "png", - "webp", - Media::WEBP_LOSSY, - Media::WEBP_LOSSLESS, - ], - MediaEngine::FFMPEG => [ - "jpg", - "webp", - "png" - ] - ]; - public const INPUT_SUPPORT = [ - MediaEngine::GD => [ - "bmp", - "gif", - "jpg", - "png", - "webp", - Media::WEBP_LOSSY, - Media::WEBP_LOSSLESS - ], - MediaEngine::IMAGICK => [ - "bmp", - "gif", - "jpg", - "png", - "psd", - "tiff", - "webp", - Media::WEBP_LOSSY, - Media::WEBP_LOSSLESS, - "ico", - ], - MediaEngine::FFMPEG => [ - "avi", - "mkv", - "webm", - "mp4", - "mov", - "flv" - ] - ]; -} - class MediaException extends SCoreException { } -class MediaResizeEvent extends Event -{ - public $engine; - public $input_path; - public $input_type; - public $output_path; - public $target_format; - public $target_width; - public $target_height; - public $target_quality; - public $minimize; - public $ignore_aspect_ratio; - public $allow_upscale; - - public function __construct( - String $engine, - string $input_path, - string $input_type, - string $output_path, - int $target_width, - int $target_height, - bool $ignore_aspect_ratio = false, - string $target_format = null, - int $target_quality = 80, - bool $minimize = false, - bool $allow_upscale = true - ) { - assert(in_array($engine, MediaEngine::ALL)); - $this->engine = $engine; - $this->input_path = $input_path; - $this->input_type = $input_type; - $this->output_path = $output_path; - $this->target_height = $target_height; - $this->target_width = $target_width; - $this->target_format = $target_format; - $this->target_quality = $target_quality; - $this->minimize = $minimize; - $this->ignore_aspect_ratio = $ignore_aspect_ratio; - $this->allow_upscale = $allow_upscale; - } -} - -class MediaCheckPropertiesEvent extends Event -{ - public $file_name; - public $ext; - public $lossless = null; - public $audio = null; - public $video = null; - public $length = null; - public $height = null; - public $width = null; - - public function __construct(string $file_name, string $ext) - { - $this->file_name = $file_name; - $this->ext = $ext; - } -} - - class Media extends Extension { const WEBP_LOSSY = "webp-lossy"; @@ -211,41 +78,8 @@ class Media extends Extension $config->set_default_string(MediaConfig::CONVERT_PATH, 'convert'); - if ($config->get_int(MediaConfig::VERSION) < 1) { - $current_value = $config->get_string("thumb_ffmpeg_path"); - if (!empty($current_value)) { - $config->set_string(MediaConfig::FFMPEG_PATH, $current_value); - } elseif ($ffmpeg = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffmpeg')) { - //ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static - if (is_executable(strtok($ffmpeg, PHP_EOL))) { - $config->set_default_string(MediaConfig::FFMPEG_PATH, 'ffmpeg'); - } - } - - if ($ffprobe = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffprobe')) { - //ffprobe exists in PATH, check if it's executable, and if so, default to it instead of static - if (is_executable(strtok($ffprobe, PHP_EOL))) { - $config->set_default_string(MediaConfig::FFPROBE_PATH, 'ffprobe'); - } - } - - $current_value = $config->get_string("thumb_convert_path"); - if (!empty($current_value)) { - $config->set_string(MediaConfig::CONVERT_PATH, $current_value); - } elseif ($convert = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' convert')) { - //ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static - if (is_executable(strtok($convert, PHP_EOL))) { - $config->set_default_string(MediaConfig::CONVERT_PATH, 'convert'); - } - } - - $current_value = $config->get_int("thumb_mem_limit"); - if (!empty($current_value)) { - $config->set_int(MediaConfig::MEM_LIMIT, $current_value); - } - - $config->set_int(MediaConfig::VERSION, 1); - log_info("media", "extension installed"); + if ($config->get_int(MediaConfig::VERSION) < 2) { + $this->setup(); } } @@ -421,7 +255,7 @@ class Media extends Extension } - const CONTENT_SEARCH_TERM_REGEX = "/^content[=|:]((video)|(audio))$/i"; + const CONTENT_SEARCH_TERM_REGEX = "/^content[=|:]((video)|(audio)|(image)|(unknown))$/i"; public function onSearchTermParse(SearchTermParseEvent $event) @@ -430,8 +264,12 @@ class Media extends Extension $matches = []; if (preg_match(self::CONTENT_SEARCH_TERM_REGEX, $event->term, $matches)) { - $field = $matches[2]; - $event->add_querylet(new Querylet($database->scoreql_to_sql("$field = SCORE_BOOL_Y"))); + $field = $matches[1]; + if ($field==="unknown") { + $event->add_querylet(new Querylet($database->scoreql_to_sql("video IS NULL OR audio IS NULL OR image IS NULL"))); + } else { + $event->add_querylet(new Querylet($database->scoreql_to_sql("$field = SCORE_BOOL_Y"))); + } } } @@ -485,8 +323,8 @@ class Media extends Extension $database->execute( "UPDATE images SET - lossless = :lossless, video = :video, audio = :audio, - height = :height, width = :width, + lossless = :lossless, video = :video, audio = :audio,image = :image, + height = :height, width = :width, length = :length WHERE hash = :hash", [ "hash" => $hash, @@ -494,6 +332,7 @@ class Media extends Extension "height" => $mcpe->height ?? 0, "lossless" => $database->scoresql_value_prepare($mcpe->lossless), "video" => $database->scoresql_value_prepare($mcpe->video), + "image" => $database->scoresql_value_prepare($mcpe->image), "audio" => $database->scoresql_value_prepare($mcpe->audio), "length" => $mcpe->length ] @@ -975,7 +814,7 @@ class Media extends Extension $new_height, $width, $height - ) === false) { + ) === false) { throw new MediaException("Unable to copy resized image data to new image"); } @@ -1154,4 +993,75 @@ class Media extends Extension log_debug('Media', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); return $size; } + + private function setup() + { + global $config, $database; + if ($config->get_int(MediaConfig::VERSION) < 1) { + $current_value = $config->get_string("thumb_ffmpeg_path"); + if (!empty($current_value)) { + $config->set_string(MediaConfig::FFMPEG_PATH, $current_value); + } elseif ($ffmpeg = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffmpeg')) { + //ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static + if (is_executable(strtok($ffmpeg, PHP_EOL))) { + $config->set_default_string(MediaConfig::FFMPEG_PATH, 'ffmpeg'); + } + } + + if ($ffprobe = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffprobe')) { + //ffprobe exists in PATH, check if it's executable, and if so, default to it instead of static + if (is_executable(strtok($ffprobe, PHP_EOL))) { + $config->set_default_string(MediaConfig::FFPROBE_PATH, 'ffprobe'); + } + } + + $current_value = $config->get_string("thumb_convert_path"); + if (!empty($current_value)) { + $config->set_string(MediaConfig::CONVERT_PATH, $current_value); + } elseif ($convert = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' convert')) { + //ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static + if (is_executable(strtok($convert, PHP_EOL))) { + $config->set_default_string(MediaConfig::CONVERT_PATH, 'convert'); + } + } + + $current_value = $config->get_int("thumb_mem_limit"); + if (!empty($current_value)) { + $config->set_int(MediaConfig::MEM_LIMIT, $current_value); + } + + $config->set_int(MediaConfig::VERSION, 1); + log_info("media", "extension installed"); + } + + if ($config->get_int(MediaConfig::VERSION) < 2) { + $database->execute($database->scoreql_to_sql( + "ALTER TABLE images ADD COLUMN image SCORE_BOOL NULL" + )); + + switch ($database->get_driver_name()) { + case DatabaseDriver::PGSQL: + case DatabaseDriver::SQLITE: + $database->execute('CREATE INDEX images_image_idx ON images(image) WHERE image IS NOT NULL'); + break; + default: + $database->execute('CREATE INDEX images_image_idx ON images(image)'); + break; + } + + $database->set_timeout(300000); // These updates can take a little bit + + if ($database->transaction === true) { + $database->commit(); // Each of these commands could hit a lot of data, combining them into one big transaction would not be a good idea. + } + log_info("upgrade", "Setting predictable media values for known file types"); + $database->execute($database->scoreql_to_sql("UPDATE images SET image = SCORE_BOOL_N WHERE ext IN ('swf','mp3','ani','flv','mp4','m4v','ogv','webm')")); + $database->execute($database->scoreql_to_sql("UPDATE images SET image = SCORE_BOOL_Y WHERE ext IN ('jpg','jpeg''ico','cur','png')")); + + $config->set_int(MediaConfig::VERSION, 2); + log_info("media", "extension at version 2"); + + $database->beginTransaction(); + } + } } diff --git a/ext/media/media_engine.php b/ext/media/media_engine.php new file mode 100644 index 00000000..28c753bb --- /dev/null +++ b/ext/media/media_engine.php @@ -0,0 +1,67 @@ + [ + "gif", + "jpg", + "png", + "webp", + Media::WEBP_LOSSY, + ], + MediaEngine::IMAGICK => [ + "gif", + "jpg", + "png", + "webp", + Media::WEBP_LOSSY, + Media::WEBP_LOSSLESS, + ], + MediaEngine::FFMPEG => [ + "jpg", + "webp", + "png" + ] + ]; + public const INPUT_SUPPORT = [ + MediaEngine::GD => [ + "bmp", + "gif", + "jpg", + "png", + "webp", + Media::WEBP_LOSSY, + Media::WEBP_LOSSLESS + ], + MediaEngine::IMAGICK => [ + "bmp", + "gif", + "jpg", + "png", + "psd", + "tiff", + "webp", + Media::WEBP_LOSSY, + Media::WEBP_LOSSLESS, + "ico", + ], + MediaEngine::FFMPEG => [ + "avi", + "mkv", + "webm", + "mp4", + "mov", + "flv" + ] + ]; +} diff --git a/ext/not_a_tag/info.php b/ext/not_a_tag/info.php index ddd97f82..4f4224c7 100644 --- a/ext/not_a_tag/info.php +++ b/ext/not_a_tag/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Redirect users to the rules if they use bad tags - */ class NotATagInfo extends ExtensionInfo { public const KEY = "not_a_tag"; diff --git a/ext/not_a_tag/main.php b/ext/not_a_tag/main.php index cb59717c..83004f49 100644 --- a/ext/not_a_tag/main.php +++ b/ext/not_a_tag/main.php @@ -89,7 +89,7 @@ class NotATag extends Extension $page->set_redirect($_SERVER['HTTP_REFERER']); } elseif ($event->get_arg(0) == "remove") { if (isset($_POST['tag'])) { - $database->Execute("DELETE FROM untags WHERE tag = ?", [$_POST['tag']]); + $database->Execute($database->scoreql_to_sql("DELETE FROM untags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(?)"), [$_POST['tag']]); flash_message("Image ban removed"); $page->set_mode(PageMode::REDIRECT); diff --git a/ext/notes/info.php b/ext/notes/info.php index 9296b7b8..fcb6d4e9 100644 --- a/ext/notes/info.php +++ b/ext/notes/info.php @@ -1,13 +1,5 @@ - * License: GPLv2 - * Description: Annotate images - * Documentation: - */ - class NotesInfo extends ExtensionInfo { public const KEY = "notes"; diff --git a/ext/numeric_score/info.php b/ext/numeric_score/info.php index ca2b77ab..bd7ffa27 100644 --- a/ext/numeric_score/info.php +++ b/ext/numeric_score/info.php @@ -1,14 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Allow users to score images - * Documentation: - */ - class NumericScoreInfo extends ExtensionInfo { public const KEY = "numeric_score"; diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index bff6701b..5e6d0f22 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -129,7 +129,7 @@ class NumericScore extends Extension $sql = "SELECT id FROM images WHERE EXTRACT(YEAR FROM posted) = :year "; - $args = ["limit" => $config->get_int("index_images"), "year" => $year]; + $args = ["limit" => $config->get_int(IndexConfig::IMAGES), "year" => $year]; if ($event->page_matches("popular_by_day")) { $sql .= diff --git a/ext/oekaki/info.php b/ext/oekaki/info.php index 2600bf7f..99ec45b7 100644 --- a/ext/oekaki/info.php +++ b/ext/oekaki/info.php @@ -1,11 +1,5 @@ - * Description: Ouroboros-like API for Shimmie - * Version: 0.2 - * Documentation: - * - */ - - class OuroborosAPIInfo extends ExtensionInfo { public const KEY = "ouroboros_api"; diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index dfc4aa5c..4766950b 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -472,7 +472,7 @@ class OuroborosAPI extends Extension if (empty($post->file) && !empty($post->file_url) && filter_var( $post->file_url, FILTER_VALIDATE_URL - ) !== false + ) !== false ) { // Transload from source $meta['file'] = tempnam('/tmp', 'shimmie_transload_' . $config->get_string('transload_engine')); diff --git a/ext/pm/info.php b/ext/pm/info.php index 795ee31f..93986fca 100644 --- a/ext/pm/info.php +++ b/ext/pm/info.php @@ -1,14 +1,5 @@ - * License: GPLv2 - * Description: Allow users to send messages to eachother - * Documentation: - * - */ - class PrivMsgInfo extends ExtensionInfo { public const KEY = "pm"; diff --git a/ext/pm_triggers/info.php b/ext/pm_triggers/info.php index d36ca3b5..1fd356c1 100644 --- a/ext/pm_triggers/info.php +++ b/ext/pm_triggers/info.php @@ -1,12 +1,5 @@ - * License: GPLv2 - * Description: Send PMs in response to certain events (eg image deletion) - */ - class PMTriggerInfo extends ExtensionInfo { public const KEY = "pm_triggers"; diff --git a/ext/pools/info.php b/ext/pools/info.php index 5acf4629..2cfa72d6 100644 --- a/ext/pools/info.php +++ b/ext/pools/info.php @@ -1,13 +1,5 @@ , jgen , Daku - * License: GPLv2 - * Description: Allow users to create groups of images and order them. - * Documentation: - */ - class PoolsInfo extends ExtensionInfo { public const KEY = "pools"; diff --git a/ext/pools/main.php b/ext/pools/main.php index 15e36c99..6205a8f2 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -818,7 +818,7 @@ class Pools extends Extension SELECT COUNT(*) FROM pool_images p $query", ["pid" => $poolID] - ) / $imagesPerPage); + ) / $imagesPerPage); diff --git a/ext/post_titles/info.php b/ext/post_titles/info.php index 208cf231..87c22267 100644 --- a/ext/post_titles/info.php +++ b/ext/post_titles/info.php @@ -1,11 +1,5 @@ - * Description: Add titles to media posts - */ - class PostTitlesInfo extends ExtensionInfo { public const KEY = "post_titles"; diff --git a/ext/qr_code/info.php b/ext/qr_code/info.php index f8fe2f5e..d97d9c11 100644 --- a/ext/qr_code/info.php +++ b/ext/qr_code/info.php @@ -1,9 +1,5 @@ [http://seemslegit.com] - * Description: - */ + class QRImageInfo extends ExtensionInfo { public const KEY = "qr_code"; diff --git a/ext/random_image/info.php b/ext/random_image/info.php index 3143f883..389f52a1 100644 --- a/ext/random_image/info.php +++ b/ext/random_image/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Do things with a random image - * Documentation: - * - */ - class RandomImageInfo extends ExtensionInfo { public const KEY = "random_image"; diff --git a/ext/random_list/info.php b/ext/random_list/info.php index f7d9dd12..226619af 100644 --- a/ext/random_list/info.php +++ b/ext/random_list/info.php @@ -1,15 +1,5 @@ - * Link: http://www.drudexsoftware.com - * License: GPLv2 - * Description: Allows displaying a page with random images - * Documentation: - * - */ - class RandomListInfo extends ExtensionInfo { public const KEY = "random_list"; diff --git a/ext/rating/info.php b/ext/rating/info.php index c53335ef..529b9b2a 100644 --- a/ext/rating/info.php +++ b/ext/rating/info.php @@ -1,14 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Allow users to rate images "safe", "questionable" or "explicit" - * Documentation: - */ - class RatingsInfo extends ExtensionInfo { public const KEY = "rating"; diff --git a/ext/rating/main.php b/ext/rating/main.php index aa18cabd..c939b00b 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -341,7 +341,7 @@ class Ratings extends Extension $old = $_POST["rating_old"]; $new = $_POST["rating_new"]; - if ($user->can("bulk_edit_image_rating")) { + if ($user->can(Permissions::BULK_EDIT_IMAGE_RATING)) { $database->execute("UPDATE images SET rating = :new WHERE rating = :old", ["new"=>$new, "old"=>$old ]); } @@ -506,7 +506,7 @@ class Ratings extends Extension private function can_rate(): bool { global $user; - if ($user->can("edit_image_rating")) { + if ($user->can(Permissions::EDIT_IMAGE_RATING)) { return true; } return false; @@ -578,9 +578,7 @@ class Ratings extends Extension break; } - if ($database->get_driver_name()==DatabaseDriver::PGSQL) { // These updates can take a little bit - $database->execute("SET statement_timeout TO 300000;"); - } + $database->set_timeout(300000); // These updates can take a little bit $database->execute("UPDATE images SET rating = :new WHERE rating = :old", ["new"=>'?', "old"=>'u' ]); diff --git a/ext/regen_thumb/info.php b/ext/regen_thumb/info.php index 8f272915..86075141 100644 --- a/ext/regen_thumb/info.php +++ b/ext/regen_thumb/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: - * Documentation: - */ class RegenThumbInfo extends ExtensionInfo { public const KEY = "regen_thumb"; diff --git a/ext/relationships/info.php b/ext/relationships/info.php index 941e202b..024b5c5b 100644 --- a/ext/relationships/info.php +++ b/ext/relationships/info.php @@ -1,12 +1,5 @@ - * License: GPLv2 - * Description: Allow posts to have relationships (parent/child). - */ - class RelationshipsInfo extends ExtensionInfo { public const KEY = "relationships"; diff --git a/ext/report_image/info.php b/ext/report_image/info.php index 937f0a6e..4477fbb9 100644 --- a/ext/report_image/info.php +++ b/ext/report_image/info.php @@ -1,15 +1,5 @@ - * Link: http://atravelinggeek.com/ - * License: GPLv2 - * Description: Report images as dupes/illegal/etc - * Version 0.3a - See changelog in main.php - * November 06, 2007 - */ - class ReportImageInfo extends ExtensionInfo { public const KEY = "report_image"; diff --git a/ext/report_image/theme.php b/ext/report_image/theme.php index f4de5934..6c34178a 100644 --- a/ext/report_image/theme.php +++ b/ext/report_image/theme.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Allows the admin to set min / max image dimensions - */ class ResolutionLimitInfo extends ExtensionInfo { public const KEY = "res_limit"; diff --git a/ext/resize/info.php b/ext/resize/info.php index 72ea0bd1..326eafe3 100644 --- a/ext/resize/info.php +++ b/ext/resize/info.php @@ -1,16 +1,9 @@ - * Description: Allows admins to resize images. - * License: GPLv2 - * Version: 0.1 * Notice: * The image resize and resample code is based off of the "smart_resize_image" * function copyright 2008 Maxim Chernyak, released under a MIT-style license. - * Documentation: - * This extension allows admins to resize images. */ class ResizeImageInfo extends ExtensionInfo diff --git a/ext/rotate/info.php b/ext/rotate/info.php index de7bb471..b10e4ad0 100644 --- a/ext/rotate/info.php +++ b/ext/rotate/info.php @@ -1,16 +1,9 @@ / Agasa - * Description: Allows admins to rotate images. - * License: GPLv2 - * Version: 0.1 * Notice: * The image resize and resample code is based off of the "smart_resize_image" * function copyright 2008 Maxim Chernyak, released under a MIT-style license. - * Documentation: - * This extension allows admins to rotate images. */ class RotateImageInfo extends ExtensionInfo diff --git a/ext/rss_comments/info.php b/ext/rss_comments/info.php index f6074f62..a69d58b0 100644 --- a/ext/rss_comments/info.php +++ b/ext/rss_comments/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Self explanatory - */ - class RSSCommentsInfo extends ExtensionInfo { public const KEY = "rss_comments"; diff --git a/ext/rss_images/info.php b/ext/rss_images/info.php index 33f814ed..bc58134c 100644 --- a/ext/rss_images/info.php +++ b/ext/rss_images/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Self explanatory - */ class RSSImagesInfo extends ExtensionInfo { public const KEY = "rss_images"; diff --git a/ext/rule34/info.php b/ext/rule34/info.php index 606e9607..11988432 100644 --- a/ext/rule34/info.php +++ b/ext/rule34/info.php @@ -1,14 +1,5 @@ - * License: GPLv2 - * Description: Extra site-specific bits - * Documentation: - * - */ - class Rule34Info extends ExtensionInfo { public const KEY = "rule34"; diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 1aa80d7d..f7e5a343 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -71,9 +71,7 @@ class Rule34 extends Extension { global $database, $page, $user; - if ($user->can(Permissions::DELETE_USER)) { // deleting users can take a while - $database->execute("SET statement_timeout TO ".(DATABASE_TIMEOUT+15000).";"); - } + $database->set_timeout(DATABASE_TIMEOUT+15000); // deleting users can take a while if (function_exists("sd_notify_watchdog")) { sd_notify_watchdog(); diff --git a/ext/setup/info.php b/ext/setup/info.php index 03fb0774..7fa3e5ef 100644 --- a/ext/setup/info.php +++ b/ext/setup/info.php @@ -1,12 +1,5 @@ config = $config; } } -// }}} -/* SetupBuildingEvent {{{ - * + +/* * Sent when the setup page is ready to be added to */ class SetupBuildingEvent extends Event @@ -32,10 +30,7 @@ class SetupBuildingEvent extends Event $this->panel = $panel; } } -// }}} -/* SetupPanel {{{ - * - */ + class SetupPanel { /** @var SetupBlock[] */ @@ -46,10 +41,7 @@ class SetupPanel $this->blocks[] = $block; } } -// }}} -/* SetupBlock {{{ - * - */ + class SetupBlock extends Block { /** @var string */ @@ -57,8 +49,6 @@ class SetupBlock extends Block /** @var string */ public $body; - - public function __construct(string $title) { $this->header = $title; @@ -134,8 +124,6 @@ class SetupBlock extends Block $this->end_table_header_cell(); } - - private function format_option(string $name, $html, ?string $label, bool $table_row) { if ($table_row) { @@ -274,7 +262,6 @@ class SetupBlock extends Block $this->format_option($name, $html, $label, $table_row); } } -// }}} class Setup extends Extension { diff --git a/ext/shimmie_api/info.php b/ext/shimmie_api/info.php index af1ded44..d180e6f0 100644 --- a/ext/shimmie_api/info.php +++ b/ext/shimmie_api/info.php @@ -1,14 +1,5 @@ - * Description: A JSON interface to shimmie data [WARNING] - * Documentation: - * - */ - - class ShimmieApiInfo extends ExtensionInfo { public const KEY = "shimmie_api"; diff --git a/ext/site_description/info.php b/ext/site_description/info.php index 2bbc25e7..7a5da7bc 100644 --- a/ext/site_description/info.php +++ b/ext/site_description/info.php @@ -1,15 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Visibility: admin - * Description: A description for search engines - * Documentation: - * - */ class SiteDescriptionInfo extends ExtensionInfo { public const KEY = "site_description"; diff --git a/ext/sitemap/info.php b/ext/sitemap/info.php index 3cee3e3a..5f7cb12e 100644 --- a/ext/sitemap/info.php +++ b/ext/sitemap/info.php @@ -1,15 +1,5 @@ - * Author: Drudex Software - * Link: http://drudexsoftware.com - * License: GPLv2 - * Description: Sitemap with caching & advanced priorities - * Documentation: - */ - class XMLSitemapInfo extends ExtensionInfo { public const KEY = "sitemap"; diff --git a/ext/source_history/info.php b/ext/source_history/info.php index cce9a369..07784147 100644 --- a/ext/source_history/info.php +++ b/ext/source_history/info.php @@ -1,11 +1,5 @@ -* License: GPLv2 -* Visibility: admin -* Description: Sends Shimmie stats to a StatsD server -* Documentation: -* -*/ - class StatsDInterfaceInfo extends ExtensionInfo { public const KEY = "statsd"; diff --git a/ext/system/info.php b/ext/system/info.php index 7f32847f..04ed13d3 100644 --- a/ext/system/info.php +++ b/ext/system/info.php @@ -1,11 +1,5 @@ - * Description: Provides system screen - */ - class SystemInfo extends ExtensionInfo { public const KEY = "system"; diff --git a/ext/tag_categories/info.php b/ext/tag_categories/info.php index 7c3ebe1c..4b382754 100644 --- a/ext/tag_categories/info.php +++ b/ext/tag_categories/info.php @@ -1,11 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Let tags be split into 'categories', like Danbooru's tagging - */ class TagCategoriesInfo extends ExtensionInfo { public const KEY = "tag_categories"; diff --git a/ext/tag_edit/info.php b/ext/tag_edit/info.php index d19e1f45..4fb4fdd8 100644 --- a/ext/tag_edit/info.php +++ b/ext/tag_edit/info.php @@ -1,12 +1,5 @@ , modified by jgen - * Description: Keep a record of tag changes, and allows you to revert changes. - */ - class TagHistoryInfo extends ExtensionInfo { public const KEY = "tag_history"; diff --git a/ext/tag_history/theme.php b/ext/tag_history/theme.php index 099b8215..353901bb 100644 --- a/ext/tag_history/theme.php +++ b/ext/tag_history/theme.php @@ -1,9 +1,4 @@ , modified by jgen - */ - class TagHistoryTheme extends Themelet { private $messages = []; @@ -117,11 +112,11 @@ class TagHistoryTheme extends Themelet public function display_admin_block(string $validation_msg='') { global $page; - + if (!empty($validation_msg)) { $validation_msg = '
'. $validation_msg .''; } - + $html = ' Revert tag changes by a specific IP address or username, optionally limited to recent changes. '.$validation_msg.' @@ -137,7 +132,7 @@ class TagHistoryTheme extends Themelet "; $page->add_block(new Block("Mass Tag Revert", $html)); } - + /* * Show a standard page for results to be put into */ diff --git a/ext/tag_list/info.php b/ext/tag_list/info.php index a44adfe3..a3fa78f2 100644 --- a/ext/tag_list/info.php +++ b/ext/tag_list/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Show the tags in various ways - */ - class TagListInfo extends ExtensionInfo { public const KEY = "tag_list"; diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 24bdb92a..0b0f1b93 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -159,8 +159,7 @@ class TagList extends Extension $sb->end_table(); $event->panel->add_block($sb); } - // }}} - // misc {{{ + private function tag_link(string $tag): string { $u_tag = url_escape($tag); @@ -253,8 +252,6 @@ class TagList extends Extension return $html; } - // }}} - // maps {{{ private function build_navigation(): string { @@ -453,8 +450,7 @@ class TagList extends Extension return $html; } - // }}} - // blocks {{{ + private function add_related_block(Page $page, Image $image): void { global $database, $config; @@ -653,7 +649,4 @@ class TagList extends Extension return $related_tags; } } - - - // }}} } diff --git a/ext/tagger/info.php b/ext/tagger/info.php index b240b70b..79d975b4 100644 --- a/ext/tagger/info.php +++ b/ext/tagger/info.php @@ -1,12 +1,5 @@ - * Do not remove this notice. - */ - class TaggerInfo extends ExtensionInfo { public const KEY = "tagger"; diff --git a/ext/tips/info.php b/ext/tips/info.php index acacf98e..f5822c7c 100644 --- a/ext/tips/info.php +++ b/ext/tips/info.php @@ -1,15 +1,5 @@ - * License: GPLv2 - * Description: Show a random line of text in the subheader space - * Documentation: - * Formatting is done with HTML - */ - - class TipsInfo extends ExtensionInfo { public const KEY = "tips"; diff --git a/ext/transcode/config.php b/ext/transcode/config.php new file mode 100644 index 00000000..2146aba0 --- /dev/null +++ b/ext/transcode/config.php @@ -0,0 +1,11 @@ + - * Description: Allows admins to automatically and manually transcode images. - * Documentation: - * - */ - class TranscodeImageInfo extends ExtensionInfo { public const KEY = "transcode"; diff --git a/ext/transcode/main.php b/ext/transcode/main.php index 73623dab..71e40469 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -1,14 +1,6 @@ - * Description: Provides "Trash" or "Recycle Bin"-type functionality, storing delete images for later recovery - * Documentation: - */ - class TrashInfo extends ExtensionInfo { public const KEY = "trash"; diff --git a/ext/trash/main.php b/ext/trash/main.php index caef44aa..d3772a68 100644 --- a/ext/trash/main.php +++ b/ext/trash/main.php @@ -60,6 +60,16 @@ class Trash extends Extension } } + public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) + { + global $user; + if ($event->parent=="posts") { + if ($user->can(Permissions::VIEW_TRASH)) { + $event->add_nav_link("posts_trash", new Link('/post/list/in%3Atrash/1'), "Trash", null, 60); + } + } + } + const SEARCH_REGEXP = "/^in:trash$/"; public function onSearchTermParse(SearchTermParseEvent $event) diff --git a/ext/update/info.php b/ext/update/info.php index 979bcbab..9e3631b3 100644 --- a/ext/update/info.php +++ b/ext/update/info.php @@ -1,13 +1,5 @@ - * Link: http://www.codeanimu.net - * License: GPLv2 - * Description: Shimmie updater! (Requires admin panel extension & transload engine (cURL/fopen/Wget)) - */ - class UpdateInfo extends ExtensionInfo { public const KEY = "update"; diff --git a/ext/upgrade/info.php b/ext/upgrade/info.php index 6ece153b..8432ccb6 100644 --- a/ext/upgrade/info.php +++ b/ext/upgrade/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Keeps things happy behind the scenes - * Visibility: admin - */ - class UpgradeInfo extends ExtensionInfo { public const KEY = "upgrade"; diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 491cb183..0424f269 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -191,9 +191,7 @@ class Upgrade extends Extension break; } - if ($database->get_driver_name()==DatabaseDriver::PGSQL) { // These updates can take a little bit - $database->execute("SET statement_timeout TO 300000;"); - } + $database->set_timeout(300000); // These updates can take a little bit log_info("upgrade", "Setting index for ext column"); $database->execute('CREATE INDEX images_ext_idx ON images(ext)'); diff --git a/ext/upload/info.php b/ext/upload/info.php index c14ae1d3..b8e18692 100644 --- a/ext/upload/info.php +++ b/ext/upload/info.php @@ -1,12 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * Description: Allows people to upload files to the website - */ - class UploadInfo extends ExtensionInfo { public const KEY = "upload"; diff --git a/ext/upload/main.php b/ext/upload/main.php index 356db988..9bc90995 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -138,7 +138,10 @@ class Upload extends Extension public function onPageNavBuilding(PageNavBuildingEvent $event) { - $event->add_nav_link("upload", new Link('upload'), "Upload"); + global $user; + if ($user->can(Permissions::CREATE_IMAGE)) { + $event->add_nav_link("upload", new Link('upload'), "Upload"); + } } public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) @@ -288,8 +291,6 @@ class Upload extends Extension return $tags; } - // do things {{{ - /** * Returns a descriptive error message for the specified PHP error code. * @@ -472,5 +473,4 @@ class Upload extends Extension return $ok; } - // }}} } diff --git a/ext/user/info.php b/ext/user/info.php index f8b5943e..04664feb 100644 --- a/ext/user/info.php +++ b/ext/user/info.php @@ -1,11 +1,5 @@ theme->display_user_block($page, $user, $ubbe->parts); } } - // }}} - // Things done *with* the user {{{ + private function page_login($name, $pass) { global $config, $page; @@ -490,8 +489,7 @@ class UserPage extends Extension '/' ); } - //}}} - // Things done *to* the user {{{ + private function user_can_edit_user(User $a, User $b): bool { if ($a->is_anonymous()) { @@ -581,8 +579,7 @@ class UserPage extends Extension $this->redirect_to_user($duser); } } - // }}} - // ips {{{ + private function count_upload_ips(User $duser): array { global $database; @@ -683,5 +680,4 @@ class UserPage extends Extension $page->set_redirect(make_link("post/list")); } } - // }}} } diff --git a/ext/user/theme.php b/ext/user/theme.php index 06ebc887..3c41641d 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -339,7 +339,6 @@ class UserPageTheme extends Themelet } return $html; } - // }}} public function get_help_html() { diff --git a/ext/user_config/info.php b/ext/user_config/info.php index 75538a41..394d4137 100644 --- a/ext/user_config/info.php +++ b/ext/user_config/info.php @@ -1,12 +1,5 @@ - * Description: Provides system-wide support for user-specific settings - * Visibility: admin - */ - class UserConfigInfo extends ExtensionInfo { public const KEY = "user_config"; diff --git a/ext/varnish/info.php b/ext/varnish/info.php index eacaa0cd..4c0c575d 100644 --- a/ext/varnish/info.php +++ b/ext/varnish/info.php @@ -1,13 +1,5 @@ -* License: GPLv2 -* Visibility: admin -* Description: Sends PURGE requests when a /post/view is updated -*/ - class VarnishPurgerInfo extends ExtensionInfo { public const KEY = "varnish"; diff --git a/ext/view/info.php b/ext/view/info.php index a8e9cc81..55253ddf 100644 --- a/ext/view/info.php +++ b/ext/view/info.php @@ -1,12 +1,5 @@ - * License: GPLv2 - * Description: A simple wiki, for those who don't want the hugeness of mediawiki - * Documentation: - * - */ - class WikiInfo extends ExtensionInfo { public const KEY = "wiki"; diff --git a/ext/wiki/main.php b/ext/wiki/main.php index 189a7797..d52b3059 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -265,7 +265,6 @@ class Wiki extends Extension return new WikiPage($row); } - // php-diff {{{ /** Diff implemented in pure php, written from scratch. Copyright (C) 2003 Daniel Unterberger @@ -502,5 +501,4 @@ class Wiki extends Extension throw new Exception("stat needs to be =, + or -"); } } - // }}} } diff --git a/ext/word_filter/info.php b/ext/word_filter/info.php index 89b76079..8575ef98 100644 --- a/ext/word_filter/info.php +++ b/ext/word_filter/info.php @@ -1,13 +1,5 @@ - * Link: http://code.shishnet.org/shimmie2/ - * License: GPLv2 - * Description: Simple search and replace - */ - class WordFilterInfo extends ExtensionInfo { public const KEY = "word_filter"; diff --git a/themes/danbooru2/ext_manager.theme.php b/themes/danbooru2/ext_manager.theme.php index 247406c8..270ec929 100644 --- a/themes/danbooru2/ext_manager.theme.php +++ b/themes/danbooru2/ext_manager.theme.php @@ -8,7 +8,7 @@ class CustomExtManagerTheme extends ExtManagerTheme parent::display_table($page, $extensions, $editable); } - public function display_doc(Page $page, ExtensionInfo $info) + public function display_doc(Page $page, ExtensionManagerInfo $info) { $page->disable_left(); parent::display_doc($page, $info); diff --git a/themes/danbooru2/view.theme.php b/themes/danbooru2/view.theme.php index c655ecb6..89266795 100644 --- a/themes/danbooru2/view.theme.php +++ b/themes/danbooru2/view.theme.php @@ -51,8 +51,10 @@ class CustomViewImageTheme extends ViewImageTheme if ($image->rating == null || $image->rating == "?") { $image->rating = "?"; } - $h_rating = Ratings::rating_to_human($image->rating); - $html .= "
Rating: $h_rating"; + if (Extension::is_enabled(RatingsInfo::KEY)) { + $h_rating = Ratings::rating_to_human($image->rating); + $html .= "
Rating: $h_rating"; + } } return $html; diff --git a/themes/material/home.theme.php b/themes/material/home.theme.php index ada70b17..c878d3c2 100644 --- a/themes/material/home.theme.php +++ b/themes/material/home.theme.php @@ -25,7 +25,7 @@ class CustomHomeTheme extends HomeTheme EOD -); + ); } public function build_body(string $sitename, string $main_links, string $main_text, string $contact_link, $num_comma, string $counter_text)
TimeModuleUserMessage